mirror of
https://github.com/bitcoinbook/bitcoinbook
synced 2024-11-22 08:08:11 +00:00
CH12: simplify PoW example
Previous example used python to describe how PoW works. Replace the first example with a bash one-liner and remove the unnecessary details. Inspired by review comments from Jorge Lesmes
This commit is contained in:
parent
114d7bf25a
commit
43ae026233
146
ch10.asciidoc
146
ch10.asciidoc
@ -683,27 +683,16 @@ way as to produce a desired digest, other than trying random
|
|||||||
inputs.
|
inputs.
|
||||||
|
|
||||||
With SHA256, the output is always 256 bits long, regardless of the size
|
With SHA256, the output is always 256 bits long, regardless of the size
|
||||||
of the input. In <<sha256_example1>>, we will use the Python interpreter
|
of the input. For example, we will to calculate the SHA256 hash of the
|
||||||
to calculate the SHA256 hash of the phrase, "I am Satoshi Nakamoto."
|
phrase, "Hello, World!"
|
||||||
|
|
||||||
[[sha256_example1]]
|
|
||||||
.SHA256 example
|
|
||||||
====
|
|
||||||
[source,bash]
|
|
||||||
----
|
----
|
||||||
$ python
|
$ echo "Hello, world!" | sha256sum
|
||||||
|
d9014c4624844aa5bac314773d6b689ad467fa4e1d1a50a1b8a99d5a95f72ff5 -
|
||||||
----
|
----
|
||||||
[source,pycon]
|
|
||||||
----
|
|
||||||
Python 2.7.1
|
|
||||||
>>> import hashlib
|
|
||||||
>>> print hashlib.sha256("I am Satoshi Nakamoto").hexdigest()
|
|
||||||
5d7c7ba21cbbcd75d14800b100252d5b428e5b1213d27c385bc141ca6b47989e
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
This
|
This
|
||||||
256-bit number is the _hash_ or _digest_ of the phrase and depends on
|
256-bit number (represented in hex) is the _hash_ or _digest_ of the phrase and depends on
|
||||||
every part of the phrase. Adding a single letter, punctuation mark, or
|
every part of the phrase. Adding a single letter, punctuation mark, or
|
||||||
any other character will produce a different hash.
|
any other character will produce a different hash.
|
||||||
|
|
||||||
@ -713,10 +702,21 @@ this case to vary the SHA256 fingerprint of the phrase.
|
|||||||
|
|
||||||
To make a challenge out of this algorithm, let's set a target: find a
|
To make a challenge out of this algorithm, let's set a target: find a
|
||||||
phrase that produces a hexadecimal hash that starts with a zero.
|
phrase that produces a hexadecimal hash that starts with a zero.
|
||||||
Fortunately, this isn't difficult! <<sha256_example_generator_output>>
|
Fortunately, this isn't difficult:
|
||||||
shows that the phrase "I am Satoshi Nakamoto13" produces the hash
|
|
||||||
+0ebc56d59a34f5082aaef3d66b37a661696c2b618e62432727216ba9531041a5+,
|
[[sha256_example_generator_output]]
|
||||||
which fits our criteria. It took 13 attempts to find it. In terms of
|
----
|
||||||
|
$ for nonce in $( seq 100 ) ; do echo "Hello, world! $nonce" | sha256sum ; done
|
||||||
|
3194835d60e85bf7f728f3e3f4e4e1f5c752398cbcc5c45e048e4dbcae6be782 -
|
||||||
|
bfa474bbe2d9626f578d7d8c3acc1b604ec4a7052b188453565a3c77df41b79e -
|
||||||
|
[...]
|
||||||
|
f75a100821c34c84395403afd1a8135f685ca69ccf4168e61a90e50f47552f61 -
|
||||||
|
09cb91f8250df04a3db8bd98f47c7cecb712c99835f4123e8ea51460ccbec314 -
|
||||||
|
----
|
||||||
|
|
||||||
|
The phrase "Hello, World! 32" produces the hash
|
||||||
|
+09cb91f8250df04a3db8bd98f47c7cecb712c99835f4123e8ea51460ccbec314+,
|
||||||
|
which fits our criteria. It took 32 attempts to find it. In terms of
|
||||||
probabilities, if the output of the hash function is evenly distributed
|
probabilities, if the output of the hash function is evenly distributed
|
||||||
we would expect to find a result with a 0 as the hexadecimal prefix once
|
we would expect to find a result with a 0 as the hexadecimal prefix once
|
||||||
every 16 hashes (one out of 16 hexadecimal digits 0 through F). In
|
every 16 hashes (one out of 16 hexadecimal digits 0 through F). In
|
||||||
@ -754,13 +754,18 @@ any possible outcome can be calculated in advance. Therefore, an outcome
|
|||||||
of specified difficulty constitutes proof of a specific amount of work.
|
of specified difficulty constitutes proof of a specific amount of work.
|
||||||
====
|
====
|
||||||
|
|
||||||
In <<sha256_example_generator_output>>, the winning "nonce" is 13 and
|
In <<sha256_example_generator_output>>, the winning "nonce" is 32 and
|
||||||
this result can be confirmed by anyone independently. Anyone can add the
|
this result can be confirmed by anyone independently. Anyone can add the
|
||||||
number 13 as a suffix to the phrase "I am Satoshi Nakamoto" and compute
|
number 32 as a suffix to the phrase "Hello, world!" and compute
|
||||||
the hash, verifying that it is less than the target. The successful
|
the hash, verifying that it is less than the target.
|
||||||
result is also Proof-of-Work, because it proves we did the work to find
|
|
||||||
that nonce. While it only takes one hash computation to verify, it took
|
----
|
||||||
us 13 hash computations to find a nonce that worked. If we had a lower
|
$ echo "Hello, world! 32" | sha256sum
|
||||||
|
09cb91f8250df04a3db8bd98f47c7cecb712c99835f4123e8ea51460ccbec314 -
|
||||||
|
----
|
||||||
|
|
||||||
|
Although it only takes one hash computation to verify, it took
|
||||||
|
us 32 hash computations to find a nonce that worked. If we had a lower
|
||||||
target (higher difficulty) it would take many more hash computations to
|
target (higher difficulty) it would take many more hash computations to
|
||||||
find a suitable nonce, but only one hash computation for anyone to
|
find a suitable nonce, but only one hash computation for anyone to
|
||||||
verify. Furthermore, by knowing the target, anyone can estimate the
|
verify. Furthermore, by knowing the target, anyone can estimate the
|
||||||
@ -785,95 +790,6 @@ current difficulty in the Bitcoin network, miners have to try
|
|||||||
a huge number of times before finding a nonce that results in a low
|
a huge number of times before finding a nonce that results in a low
|
||||||
enough block header hash.
|
enough block header hash.
|
||||||
|
|
||||||
A very simplified Proof-of-Work algorithm is implemented in Python in
|
|
||||||
<<pow_example1>>.
|
|
||||||
|
|
||||||
[[pow_example1]]
|
|
||||||
.Simplified Proof-of-Work implementation
|
|
||||||
====
|
|
||||||
[source, python]
|
|
||||||
----
|
|
||||||
include::code/proof-of-work-example.py[]
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
Running this code, you can set the desired difficulty (in bits, how many
|
|
||||||
of the leading bits must be zero) and see how long it takes for your
|
|
||||||
computer to find a solution. In <<pow_example_outputs>>, you can see how
|
|
||||||
it works on an average laptop.
|
|
||||||
|
|
||||||
[[pow_example_outputs]]
|
|
||||||
.Running the Proof-of-Work example for various difficulties
|
|
||||||
====
|
|
||||||
[source, bash]
|
|
||||||
----
|
|
||||||
$ python proof-of-work-example.py*
|
|
||||||
----
|
|
||||||
|
|
||||||
----
|
|
||||||
Difficulty: 1 (0 bits)
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Difficulty: 8 (3 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 9
|
|
||||||
Hash is 1c1c105e65b47142f028a8f93ddf3dabb9260491bc64474738133ce5256cb3c1
|
|
||||||
Elapsed Time: 0.0004 seconds
|
|
||||||
Hashing Power: 25065 hashes per second
|
|
||||||
Difficulty: 16 (4 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 25
|
|
||||||
Hash is 0f7becfd3bcd1a82e06663c97176add89e7cae0268de46f94e7e11bc3863e148
|
|
||||||
Elapsed Time: 0.0005 seconds
|
|
||||||
Hashing Power: 52507 hashes per second
|
|
||||||
Difficulty: 32 (5 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 36
|
|
||||||
Hash is 029ae6e5004302a120630adcbb808452346ab1cf0b94c5189ba8bac1d47e7903
|
|
||||||
Elapsed Time: 0.0006 seconds
|
|
||||||
Hashing Power: 58164 hashes per second
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Difficulty: 4194304 (22 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 1759164
|
|
||||||
Hash is 0000008bb8f0e731f0496b8e530da984e85fb3cd2bd81882fe8ba3610b6cefc3
|
|
||||||
Elapsed Time: 13.3201 seconds
|
|
||||||
Hashing Power: 132068 hashes per second
|
|
||||||
Difficulty: 8388608 (23 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 14214729
|
|
||||||
Hash is 000001408cf12dbd20fcba6372a223e098d58786c6ff93488a9f74f5df4df0a3
|
|
||||||
Elapsed Time: 110.1507 seconds
|
|
||||||
Hashing Power: 129048 hashes per second
|
|
||||||
Difficulty: 16777216 (24 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 24586379
|
|
||||||
Hash is 0000002c3d6b370fccd699708d1b7cb4a94388595171366b944d68b2acce8b95
|
|
||||||
Elapsed Time: 195.2991 seconds
|
|
||||||
Hashing Power: 125890 hashes per second
|
|
||||||
|
|
||||||
[...]
|
|
||||||
|
|
||||||
Difficulty: 67108864 (26 bits)
|
|
||||||
Starting search...
|
|
||||||
Success with nonce 84561291
|
|
||||||
Hash is 0000001f0ea21e676b6dde5ad429b9d131a9f2b000802ab2f169cbca22b1e21a
|
|
||||||
Elapsed Time: 665.0949 seconds
|
|
||||||
Hashing Power: 127141 hashes per second
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
As you can see, increasing the difficulty by 1 bit causes a doubling in
|
|
||||||
the time it takes to find a solution. If you think of the entire 256-bit
|
|
||||||
number space, each time you constrain one more bit to zero, you decrease
|
|
||||||
the search space by half. In <<pow_example_outputs>>, it takes 84
|
|
||||||
million hash attempts to find a nonce that produces a hash with 26
|
|
||||||
leading bits as zero. Even at a speed of more than 120,000 hashes per
|
|
||||||
second, it still requires 10 minutes on a laptop to find this solution.
|
|
||||||
|
|
||||||
[[target_bits]]
|
[[target_bits]]
|
||||||
==== Target Representation
|
==== Target Representation
|
||||||
|
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# example of proof-of-work algorithm
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import time
|
|
||||||
|
|
||||||
try:
|
|
||||||
long # Python 2
|
|
||||||
xrange
|
|
||||||
except NameError:
|
|
||||||
long = int # Python 3
|
|
||||||
xrange = range
|
|
||||||
|
|
||||||
max_nonce = 2 ** 32 # 4 billion
|
|
||||||
|
|
||||||
|
|
||||||
def proof_of_work(header, difficulty_bits):
|
|
||||||
# calculate the difficulty target
|
|
||||||
target = 2 ** (256 - difficulty_bits)
|
|
||||||
|
|
||||||
for nonce in xrange(max_nonce):
|
|
||||||
hash_result = hashlib.sha256(str(header) + str(nonce)).hexdigest()
|
|
||||||
|
|
||||||
# check if this is a valid result, below the target
|
|
||||||
if long(hash_result, 16) < target:
|
|
||||||
print("Success with nonce %d" % nonce)
|
|
||||||
print("Hash is %s" % hash_result)
|
|
||||||
return (hash_result, nonce)
|
|
||||||
|
|
||||||
print("Failed after %d (max_nonce) tries" % nonce)
|
|
||||||
return nonce
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
nonce = 0
|
|
||||||
hash_result = ''
|
|
||||||
|
|
||||||
# difficulty from 0 to 31 bits
|
|
||||||
for difficulty_bits in xrange(32):
|
|
||||||
difficulty = 2 ** difficulty_bits
|
|
||||||
print("Difficulty: %ld (%d bits)" % (difficulty, difficulty_bits))
|
|
||||||
print("Starting search...")
|
|
||||||
|
|
||||||
# checkpoint the current time
|
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
# make a new block which includes the hash from the previous block
|
|
||||||
# we fake a block of transactions - just a string
|
|
||||||
new_block = 'test block with transactions' + hash_result
|
|
||||||
|
|
||||||
# find a valid nonce for the new block
|
|
||||||
(hash_result, nonce) = proof_of_work(new_block, difficulty_bits)
|
|
||||||
|
|
||||||
# checkpoint how long it took to find a result
|
|
||||||
end_time = time.time()
|
|
||||||
|
|
||||||
elapsed_time = end_time - start_time
|
|
||||||
print("Elapsed Time: %.4f seconds" % elapsed_time)
|
|
||||||
|
|
||||||
if elapsed_time > 0:
|
|
||||||
|
|
||||||
# estimate the hashes per second
|
|
||||||
hash_power = float(long(nonce) / elapsed_time)
|
|
||||||
print("Hashing Power: %ld hashes per second" % hash_power)
|
|
Loading…
Reference in New Issue
Block a user