Testing Bitcoin-Like POW Difficulty with Ruby

Bitcoin and other POW blockchains systems create difficulty by generating a hash based on a nonce; where the leading number of zeros in the SHA256 hash matches some number of zeros.

Today I was playing around with this in Ruby thinking about how I might tinker with this process; so I wrote some Ruby to get started:

module Bitcoin
    class Test
        attr_reader :difficulty
        def initialize(difficulty)
            @difficulty = difficulty
        end

        def check_nonce
            count = 0
            zeros = 0
            while true
            a = (Digest::SHA2.hexdigest rand(1..100000000000000).to_s)[0,@difficulty]
            count += 1
            zeros += 1 if a == "#{'0' * @difficulty}"
            percent = (zeros.to_f.round(10)/count.to_f.round(10)).to_f.round(10) * 100.00000
            puts "Count #{count} Zeros #{zeros} Percent #{percent.round(10)}% Checking #{a}"
            end
        end
    end
end

mytest = Bitcoin::Test.new(5)
mytest.check_nonce

Example Results

Count 25926366 Zeros 3 Percent 1.157e-05% Checking 66c0f2
Count 25926367 Zeros 3 Percent 1.157e-05% Checking 9bd17f
Count 25926368 Zeros 3 Percent 1.157e-05% Checking 15ff9d
Count 25926369 Zeros 3 Percent 1.157e-05% Checking 882a43
Count 25926370 Zeros 3 Percent 1.157e-05% Checking 2a2e0f
Count 25926371 Zeros 3 Percent 1.157e-05% Checking 6679ab
Count 25926372 Zeros 3 Percent 1.157e-05% Checking 65384b
Count 25926373 Zeros 3 Percent 1.157e-05% Checking 08854c
Count 25926374 Zeros 3 Percent 1.157e-05% Checking e97601
Count 25926375 Zeros 3 Percent 1.157e-05% Checking 03ba42
Count 25926376 Zeros 3 Percent 1.157e-05% Checking e8b96f
Count 25926377 Zeros 3 Percent 1.157e-05% Checking 1b69eb
Count 25926378 Zeros 3 Percent 1.157e-05% Checking b14f7a
Count 25926379 Zeros 3 Percent 1.157e-05% Checking 91bf75

... and so forth.

Need to think about this more ......

mytest = Bitcoin::Test.new(9)
mytest.check_nonce
Count 34929821 Zeros 0 Percent 0.0% Checking 51e1e081c
Count 34929822 Zeros 0 Percent 0.0% Checking 83b087c8d
Count 34929823 Zeros 0 Percent 0.0% Checking 366759e37
Count 34929824 Zeros 0 Percent 0.0% Checking e9eb03aff
Count 34929825 Zeros 0 Percent 0.0% Checking 630b7a9d4
Count 34929826 Zeros 0 Percent 0.0% Checking 4e1a8fd63
Count 34929827 Zeros 0 Percent 0.0% Checking 3f18e9108
Count 34929828 Zeros 0 Percent 0.0% Checking 41594844f
Count 34929829 Zeros 0 Percent 0.0% Checking 326bef18c
Count 34929830 Zeros 0 Percent 0.0% Checking 3c0342d4e
Count 34929831 Zeros 0 Percent 0.0% Checking c9227ba06

We can see that an SHA246 hash is 64 chars (bytes) in length:

hash = (Digest::SHA2.hexdigest rand(1..100000000000000).to_s)
hash.length
=> 64

I wonder how and how much energy it would take to solve a puzzle where the leading number of zeros matches the full length of the SHA256 hash?

irb(main):045:0> '0'*64
=> "0000000000000000000000000000000000000000000000000000000000000000"

Googled around a bit and did not find an answer. Probably because it is such a nonsense question?!

Did some quick back-of-the-scrap-envelope checking and the odds of a match are roughly 1 divided by 2 to the 256 power:

length = 64
odds = 1 / ( 2**(4*length) )
odds = 1 / ( 2**256 )
denominator =  2**256
=> 115792089237316195423570985008687907853269984665640564039457584007913129639936

So, finding the answer when the difficulty is the full length of the SHA64 hash is roughly (on a Mac Studio M1, using Ruby):

1 in 115792089237316195423570985008687907853269984665640564039457584007913129639936

In other notation:

( 2**256 ).to_f
=> 1.157920892373162e+77

With these numbers in hand, I found this reference quickly:

Screen Shot 2022-06-02 at 7.06.44 PM

Reference:

What does all this mean?

Someone please reply!

:slight_smile:

Using a rough timing method to get hash rates, etc.

module Bitcoin
 
    class Test
        attr_reader :difficulty
        def initialize(difficulty)
            @difficulty = difficulty
        end

        def time_nonce
            count = 0
            zeros = 0
            start = Time.now
            samples = ((128+@difficulty**2)/(@difficulty**2)).to_i
            while true
            a = (Digest::SHA2.hexdigest rand(1..100000000000000).to_s)[0,@difficulty]
            count += 1
            if a == "#{'0' * @difficulty}"
                zeros += 1
               
                if zeros == samples
                    percent = (zeros.to_f.round(10)/count.to_f.round(10)).to_f.round(10) * 100.00000
                    time = Time.now - start
                    avg_time = (time/samples).round(4)
                    hash_rate = (count / time).round(1)
                    puts "Count #{count} Zeros #{zeros} Percent #{percent.round(10)}%  Avg Time: #{avg_time} sec/match HashRate #{hash_rate} hashes/sec Checking #{a}"
                    break
                end
            end
             
            end
        end
    end
end

check = Bitcoin::Test.new(3)
check.time_nonce

Yields Sample Results (MacStudio M1)

Note: "zeros" mean "matches" (need to rename some vars)

irb(main):266:0> a = Bitcoin::Test.new(3)
irb(main):267:0> a.time_nonce
Count 104000 Zeros 15 Percent 0.01442308%  Avg Time: 0.0108sec HashRate 639720.4914 hashes/sec Checking 000
=> nil
irb(main):268:0> a = Bitcoin::Test.new(3)
irb(main):269:0> a.time_nonce
Count 60610 Zeros 15 Percent 0.02474839%  Avg Time: 0.0071sec HashRate 571862.5869 hashes/sec Checking 000
=> nil
irb(main):270:0> a = Bitcoin::Test.new(3)
irb(main):271:0> a.time_nonce
Count 65658 Zeros 15 Percent 0.02284565%  Avg Time: 0.0075sec HashRate 584723.3478 hashes/sec Checking 000
=> nil
irb(main):272:0> a = Bitcoin::Test.new(3)
irb(main):273:0> a.time_nonce
Count 24996 Zeros 15 Percent 0.0600096%  Avg Time: 0.0038sec HashRate 443065.7969 hashes/sec Checking 000
=> nil
irb(main):274:0> a = Bitcoin::Test.new(3)
irb(main):275:0> a.time_nonce
Count 71388 Zeros 15 Percent 0.02101193%  Avg Time: 0.0079sec HashRate 599722.7706 hashes/sec Checking 000
=> nil
irb(main):276:0> a = Bitcoin::Test.new(3)
irb(main):277:0> a.time_nonce
Count 52131 Zeros 15 Percent 0.02877367%  Avg Time: 0.0063sec HashRate 553630.9764 hashes/sec Checking 000
=> nil
irb(main):278:0> a = Bitcoin::Test.new(5)
irb(main):279:0> a.time_nonce
Count 9028333 Zeros 6 Percent 6.646e-05%  Avg Time: 1.9876sec HashRate 757064.0282 hashes/sec Checking 00000
=> nil
irb(main):280:0> a = Bitcoin::Test.new(5)
irb(main):281:0> a.time_nonce
Count 3833895 Zeros 6 Percent 0.0001565%  Avg Time: 0.8454sec HashRate 755859.5506 hashes/sec Checking 00000
=> nil
irb(main):282:0> a = Bitcoin::Test.new(5)
irb(main):283:0> a.time_nonce
Count 6592169 Zeros 6 Percent 9.102e-05%  Avg Time: 1.4515sec HashRate 756935.9714 hashes/sec Checking 00000
=> nil
irb(main):284:0> a = Bitcoin::Test.new(6)
irb(main):285:0> a.time_nonce
Count 93871743 Zeros 4 Percent 4.26e-06%  Avg Time: 30.9887sec HashRate 757305.3744 hashes/sec Checking 000000
=> nil
irb(main):286:0> a = Bitcoin::Test.new(6)
irb(main):287:0> a.time_nonce
Count 113081247 Zeros 4 Percent 3.54e-06%  Avg Time: 37.3552sec HashRate 756797.1909 hashes/sec Checking 000000
=> nil
irb(main):288:0> a = Bitcoin::Test.new(7)
irb(main):289:0> a.time_nonce

... still running

So slooooowwww..... maybe because we are running Ruby in console mode?

rb(main):288:0> a = Bitcoin::Test.new(7)
irb(main):289:0> a.time_nonce
Count 1110118974 Zeros 3 Percent 2.7e-07%  Avg Time: 494.3442sec HashRate 748546.6506 hashes/sec Checking 0000000
=> nil

Fun Trivia!

Based on the tests above and the global BTC Hash rate (reference below), it would take

255,356,813,180,107 MacStudios doing plain-ole single-threaded CPU mining with Ruby in the console to match the current global hash rate.

Total Macs = Current Global BTC Hashrate / MacStudio Hash Rate

255,356,813,180,107 = 193,253,270,144,265,900,000/756,797

LOL

Reference:

more results, 8 bytes '00000000' found in 4807.62 secs after 3624371356 tries.

irb(main):077:0> a = Bitcoin::Test.new(8)
irb(main):078:0> a.time_nonce
Count 3624371356 Zeros 1 Percent 3.0e-08%  Avg Time: 4807.62 sec/match HashRate 753880.6 hashes/sec Checking 00000000
=> nil

its a winner :smiley:

Now testing '000000000' ....

OBTW, here is the code I wrote for this test:

module Bitcoin
 
    class Test
       
        def self.time_nonce(difficulty)
            count = 0
            zeros = 0
            start = Time.now
            if difficulty < 8
                samples = ((128+difficulty**2)/(difficulty**2)).to_i
            else
                samples = 1   # 8 and over difficulty takes too long on mac
            end

            while true
            a = (Digest::SHA2.hexdigest rand(1..100000000000000).to_s)[0,@difficulty]
            count += 1
            if a == "#{'0' * difficulty}"
                zeros += 1
               
                if zeros == samples
                    percent = (zeros.to_f.round(10)/count.to_f.round(10)).to_f.round(10) * 100.00000
                    time = Time.now - start
                    avg_time = (time/samples).round(4)
                    hash_rate = (count / time).round(1)
                    puts "Count #{count} Zeros #{zeros} Percent #{percent.round(10)}%  Avg Time: #{avg_time} sec/match HashRate #{hash_rate} hashes/sec Checking #{a}"
                    break
                end
            end
             
            end
        end
    end
end

Bitcoin::Test.time_nonce(4)

So we can either have Bitcoin or Electric cars, but not both.

LOL.

No. Can and will have both. This is 100% certain. Bitcoin is here to stay. Electric cars are here to stay.

No amount of FUD will stop electric calls nor cryptocurrency.

The makers of gas powered cars tried to stop electric cars (before, but the eventually capitulated).
Bankers and governments are trying to stop Bitcoin, but they will also eventually capitulate.

There is no turning back.

we need hydrogen powered <your machine here>

Soon cars with be fully autonomous and need no human owner nor driver.

Cars will drive themselves to the shop to change tires and pay with cryptocurrency. Then they will do the same at the charging station and the car wash. They will receive instructions on where to go and who to pickup and verify the person with a face scan and a code / token / password.

This is coming sooner than many people realize.