Skip to content

Commit fceb694

Browse files
committed
more technical review items
1 parent aee6d7e commit fceb694

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+214
-149
lines changed

all.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
= Programming Bitcoin
22
Jimmy Song
33
v2.0, 2018-12-03
4-
:imagesdir: images
4+
:imagesdir: .
55
:doctype: book
66

77
include::preface.asciidoc[]

ch01.asciidoc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ This is what we call the _additive inverse_.
4343
5. If _a_ is in the set and is not 0, __a^–1^__ is in the set, which is defined as the value that makes __a ⋅ a^–1^ = 1__.
4444
This is what we call the _multiplicative inverse_.
4545

46-
Let's unpack each of the following.
46+
Let's unpack each of the above.
4747

4848
We have a set of numbers that's finite.
4949
Because the set is finite, we can designate a number _p_, which is how big the set is.
@@ -108,11 +108,11 @@ A Finite Field of order 17 looks like this:
108108
</ul>
109109
++++
110110

111-
A Finite Field of order 981 looks like this:
111+
A Finite Field of order 983 looks like this:
112112

113113
++++
114114
<ul class="simplelist">
115-
<li>F<sub>981</sub>= {0, 1, 2, ... 980}</li>
115+
<li>F<sub>983</sub>= {0, 1, 2, ... 982}</li>
116116
</ul>
117117
++++
118118

@@ -134,7 +134,7 @@ The bare bones of the class look like this:
134134
include::code-ch01/ecc.py[tag=source1]
135135
----
136136
<1> We first check that `num` is between `0` and `prime-1` inclusive.
137-
If not, we have an invalid Field Element and we raise a `ValueError`, which is what we should raise when we get an inappropriate value.
137+
If not, we get an invalid Field Element and we raise a `ValueError`, which is what we should raise when we get an inappropriate value.
138138
<2> The rest of the `__init__` method assigns the initialization values to the object.
139139
<3> The `__eq__` method checks if two objects of class `FieldElement` are equal.
140140
This is only true when the `num` and `prime` properties are equal.
@@ -506,7 +506,7 @@ include::code-ch01/answers.py[tag=exercise6,indent=0]
506506
==== Coding Exponentiation in Python
507507

508508
We need to define the exponentiation for `FieldElement`, which in Python can be defined with the `__pow__` method, overriding the `**` operator.
509-
The difference here is that the exponent is _not_ a `FieldElement`, so has to be treated a bit differently.
509+
The difference here is that the exponent is _not_ a `FieldElement`, so it has to be treated a bit differently.
510510
We want something like this to work:
511511

512512
[source,python]
@@ -747,7 +747,7 @@ class FieldElement:
747747
...
748748
include::code-ch01/ecc.py[tag=source3]
749749
----
750-
<1> Make the exponent into something within the 0 to p–1 range.
750+
<1> Make the exponent into something within the 0 to p-2 range, inclusive.
751751

752752
=== Conclusion
753753

ch02.asciidoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ Note that the next +if+ statement will fail if we don't return here.
290290
<2> We overload the `+` operator here
291291
<3> `self.x` being `None` means that `self` is the point at infinity, or the additive identity.
292292
Thus, we return `other`.
293-
<4> `self.x` being `None` means that `other` is the point at infinity, or the additive identity.
293+
<4> `other.x` being `None` means that `other` is the point at infinity, or the additive identity.
294294
Thus, we return `self`.
295295

296296
include::code-ch02/answers.py[tag=exercise3,indent=0]
@@ -338,7 +338,7 @@ Using this formula and plugging it into the Elliptic Curve equation, we get:
338338
339339
Gathering all the terms, we have this polynomial equation:
340340
341-
* __x__^3^ – __s__^2^x^2^ + (__a__ + 2__s__^2^__x__~1~ – 2__sy__~1~)__x__ + __b__ – __x__~1~^2^ + 2__sx__~1~__y__~1~ – __y__~1~^2^ = 0
341+
* __x__^3^ – __s__^2^x^2^ + (__a__ + 2__s__^2^__x__~1~ – 2__sy__~1~)__x__ + __b__ – __s__^2^__x__~1~^2^ + 2__sx__~1~__y__~1~ – __y__~1~^2^ = 0
342342
343343
We also know that __x__~1~, __x__~2~, and __x__~3~ are solutions to this equation, thus:
344344
@@ -347,7 +347,7 @@ We also know that __x__~1~, __x__~2~, and __x__~3~ are solutions to this equatio
347347
348348
From earlier, we know that:
349349
350-
* __x__^3^ – __s__^2^x^2^ + (__a__ + 2__s__^2^__x__~1~ – 2__sy__~1~)__x__ + __b__ – __x__~1~^2^ + 2__sx__~1~__y__~1~ – __y__~1~^2^ = 0
350+
* __x__^3^ – __s__^2^x^2^ + (__a__ + 2__s__^2^__x__~1~ – 2__sy__~1~)__x__ + __b__ – __s__^2^__x__~1~^2^ + 2__sx__~1~__y__~1~ – __y__~1~^2^ = 0
351351
352352
There's a result from what's called the https://en.wikipedia.org/wiki/Vieta%27s_formulas[Vieta's Formula], which states that the coefficients have to equal each other if the roots are the same.
353353
The first coefficient that's interesting is the coefficient in front of __x__^2^:

ch04.asciidoc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,6 @@ We can do steps 4 and 5 in one go this way:
370370
----
371371
include::code-ch04/helper.py[tag=source3]
372372
----
373-
<1> Note that the `.decode('ascii')` part is necessary to convert from Python 3 bytes to a Python 3 string.
374373

375374
[NOTE]
376375
.What Is Testnet?

ch05.asciidoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,19 @@ include::code-ch05/answers.py[tag=exercise3,indent=0]
305305
=== Locktime
306306

307307
Locktime is a way to time-delay a transaction.
308-
A transaction with a Locktime of 600,000 cannot go into the blockchain until block 600,000.
308+
A transaction with a Locktime of 600,000 cannot go into the blockchain until block 600,001.
309309
This was originally construed as a way to do "high-frequency trades" (see _Sequence and Locktime_), which turned out to be insecure.
310310
If the Locktime is greater than or equal to 500,000,000, Locktime is a UNIX timestamp.
311311
If Locktime is less than 500,000,000, it is a block number.
312312
This way, transactions can be signed, but unspendable until a certain point in UNIX time or block height.
313313

314-
The serialization is in Little-Endian and is 4 bytes long (<<locktime>>).
314+
.When Locktime is Ignored
315+
[WARNING]
316+
====
317+
Note that Locktime is ignored if the sequence numbers for every input is `ffffffff`.
318+
====
319+
320+
The serialization is in Little-Endian and 4-bytes (<<locktime>>).
315321

316322
[[locktime]]
317323
.Locktime

ch06.asciidoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Here are some operations and their byte codes:
150150
* `0x00` - `OP_0`
151151
* `0x51` - `OP_1`
152152
* `0x60` - `OP_16`
153-
* `0x75` - `OP_DUP`
153+
* `0x76` - `OP_DUP`
154154
* `0x93` - `OP_ADD`
155155
* `0xa9` - `OP_HASH160`
156156
* `0xac` - `OP_CHECKSIG`
@@ -284,7 +284,7 @@ Note the `OP_CHECKSIG`, as that will be very important.
284284
The ScriptSig is the part that unlocks the received bitcoins.
285285
The pubkey can be compressed or uncompressed, though early on in Bitcoin's history when p2pk was more prominent, uncompressed was the only one being used (see <<chapter_serialization>>).
286286

287-
For p2pk, the ScriptSig required to unlock the corresponding ScriptPubKey is just the signature as shown in <<pay_to_pubkey_p2pk_scriptsig>>.
287+
For p2pk, the ScriptSig required to unlock the corresponding ScriptPubKey is the signature followed by a single sighash byte as shown in <<pay_to_pubkey_p2pk_scriptsig>>:
288288

289289
[[pay_to_pubkey_p2pk_scriptsig]]
290290
.Pay-to-pubkey (p2pk) ScriptSig

ch09.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ To find a proof-of-work, the miners on the Bitcoin network have to churn through
247247
Like gold, verifying proof-of-work is much cheaper than actually finding it.
248248

249249
So what is proof-of-work?
250-
Let's look at the hash256 of the block we saw before to find out:
250+
Let's look at the hash256 of the block header we saw before to find out:
251251

252252
```
253253
020000208ec39428b17323fa0ddec8e887b4a7c53b8c0a0a220cfd000000000000000000
@@ -290,7 +290,7 @@ The mechanics of how the Merkle Root changes whenever any transaction in the blo
290290

291291
==== Target
292292

293-
Proof-of-work is the requirement that every block in Bitcoin must be below a certain target.
293+
Proof-of-work is the requirement that the hash of every block header in Bitcoin must be below a certain target.
294294
_Target_ is a 256-bit number that is computed directly from the bits field.
295295
The target is very small compared to an average 256-bit number.
296296

@@ -320,12 +320,12 @@ include::code-ch09/examples.py[tag=example6]
320320
----
321321
<1> We are purposefully printing this number as 64 hexadecimal digits to show how small the number is in 256-bit terms.
322322

323-
A valid proof-of-work is a hash of the block that, when interpreted as a Little-Endian integer, is below the target number.
323+
A valid proof-of-work is a hash of the block header that, when interpreted as a Little-Endian integer, is below the target number.
324324
Proof-of-work hashes are exceedingly rare and the process of mining is the process of finding one of these hashes.
325325
To find a single proof-of-work with the above target, the network as a whole must calculate 3.8 &#215; 10^21^ hashes, which, when this block was found, was roughly every 10 minutes.
326326
To give this number some context, the best GPU miner in the world would need to run for 50,000 years on average to find a single proof-of-work below this target.
327327

328-
We can check that this block's hash satisfies the proof-of-work:
328+
We can check that this block header's hash satisfies the proof-of-work:
329329

330330
[source,python]
331331
----

ch10.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ The timestamp field is 8 bytes (as opposed to 4 bytes in the block header) and i
7373

7474
IP addresses can be IPv6, IPv4, or OnionCat (a mapping of TOR's `.onion` addresses to IPv6).
7575
If IPv4, the first 12 bytes are `00000000000000000000ffff` and the last 4 bytes are the IP.
76-
The port is 2 bytes Little-Endian. The default on mainnet is 8333, which maps to `208d` in Little-Endian hex.
76+
The port is 2 bytes Little-Endian. The default on mainnet is 8333, which maps to `8d20` in Little-Endian hex.
7777

7878
A nonce is a number used by a node to detect a connection to itself.
7979
User Agent identifies the software being run.

ch12.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ include::code-ch12/answers.py[tag=exercise3,indent=0]
157157
=== Loading a Bloom Filter
158158

159159
Once a light client has created a Bloom Filter, it needs to let the full node know the details of the filter so the full node can send Proofs of Inclusion.
160-
The first thing a light client must do is set the optional relay flag in the version message (see <<chapter_networking>>) to 1.
160+
The first thing a light client must do is set the optional relay flag in the version message (see <<chapter_networking>>) to 0.
161161
This tells the full node not to send transaction messages unless they match a Bloom Filter or they have been specifically requested.
162162
After the relay flag, a light client then communicates to the full node the Bloom Filter itself.
163163
The command to set the Bloom Filter is called `filterload`.

ch13.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ Software that understands how to validate Segwit Version X will validate such tr
143143
=== p2sh-p2wpkh
144144

145145
p2wpkh is great, but unfortunately, this is a new type of Script and older wallets cannot send Bitcoins to p2wpkh ScriptPubKeys.
146-
p2wpkh uses a new address format called bech32, whose ScriptPubKeys older wallets don't know how to create.
146+
p2wpkh uses a new address format called bech32, defined in BIP173, whose ScriptPubKeys older wallets don't know how to create.
147147

148148
The Segwit authors found an ingenious way to make Segwit backward compatible by using p2sh.
149149
We can "wrap" p2wpkh inside a p2sh.

code-ch02/answers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
# tag::exercise6[]
4343
==== Exercise 6
4444
45-
For the curve __y__^2^ = __x__^3^ + 5__x__ + 7, what is (2,5) + (–1,1)?
45+
For the curve __y__^2^ = __x__^3^ + 5__x__ + 7, what is (-1,1) + (–1,-1)?
4646
# end::exercise6[]
4747
# tag::answer6[]
4848
>>> a, x1, y1 = 5, -1, 1

code-ch03/answers.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,15 @@
4848
>>> p1 = Point(FieldElement(170, prime), FieldElement(142, prime), a, b)
4949
>>> p2 = Point(FieldElement(60, prime), FieldElement(139, prime), a, b)
5050
>>> print(p1+p2)
51-
Point(FieldElement_223(220),FieldElement_223(181))_FieldElement_223(0)_
52-
FieldElement_223(7)
51+
Point(220,181)_0_7 FieldElement(223)
5352
>>> p1 = Point(FieldElement(47, prime), FieldElement(71, prime), a, b)
5453
>>> p2 = Point(FieldElement(17, prime), FieldElement(56, prime), a, b)
5554
>>> print(p1+p2)
56-
Point(FieldElement_223(215),FieldElement_223(68))_FieldElement_223(0)_
57-
FieldElement_223(7)
55+
Point(215,68)_0_7 FieldElement(223)
5856
>>> p1 = Point(FieldElement(143, prime), FieldElement(98, prime), a, b)
5957
>>> p2 = Point(FieldElement(76, prime), FieldElement(66, prime), a, b)
6058
>>> print(p1+p2)
61-
Point(FieldElement_223(47),FieldElement_223(71))_FieldElement_223(0)_
62-
FieldElement_223(7)
59+
Point(47,71)_0_7 FieldElement(223)
6360
6461
# end::answer2[]
6562
# tag::exercise4[]
@@ -83,26 +80,21 @@
8380
>>> y1 = FieldElement(num=105, prime=prime)
8481
>>> p = Point(x1,y1,a,b)
8582
>>> print(p+p)
86-
Point(FieldElement_223(49),FieldElement_223(71))_FieldElement_223(0)_
87-
FieldElement_223(7)
83+
Point(49,71)_0_7 FieldElement(223)
8884
>>> x1 = FieldElement(num=143, prime=prime)
8985
>>> y1 = FieldElement(num=98, prime=prime)
9086
>>> p = Point(x1,y1,a,b)
9187
>>> print(p+p)
92-
Point(FieldElement_223(64),FieldElement_223(168))_FieldElement_223(0)_
93-
FieldElement_223(7)
88+
Point(64,168)_0_7 FieldElement(223)
9489
>>> x1 = FieldElement(num=47, prime=prime)
9590
>>> y1 = FieldElement(num=71, prime=prime)
9691
>>> p = Point(x1,y1,a,b)
9792
>>> print(p+p)
98-
Point(FieldElement_223(36),FieldElement_223(111))_FieldElement_223(0)_
99-
FieldElement_223(7)
93+
Point(36,111)_0_7 FieldElement(223)
10094
>>> print(p+p+p+p)
101-
Point(FieldElement_223(194),FieldElement_223(51))_FieldElement_223(0)_
102-
FieldElement_223(7)
95+
Point(194,51)_0_7 FieldElement(223)
10396
>>> print(p+p+p+p+p+p+p+p)
104-
Point(FieldElement_223(116),FieldElement_223(55))_FieldElement_223(0)_
105-
FieldElement_223(7)
97+
Point(116,55)_0_7 FieldElement(223)
10698
>>> print(p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p)
10799
Point(infinity)
108100

code-ch03/ecc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ def __ne__(self, other):
156156
def __repr__(self):
157157
if self.x is None:
158158
return 'Point(infinity)'
159+
elif isinstance(self.x, FieldElement):
160+
return 'Point({},{})_{}_{} FieldElement({})'.format(
161+
self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime)
159162
else:
160163
return 'Point({},{})_{}_{}'.format(self.x, self.y, self.a, self.b)
161164

@@ -212,8 +215,8 @@ def __rmul__(self, coefficient):
212215
if coef & 1: # <3>
213216
result += current
214217
current += current # <4>
215-
coef >>= 1
216-
return result # <5>
218+
coef >>= 1 # <5>
219+
return result
217220
# end::source3[]
218221

219222

code-ch03/examples.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
>>> y = FieldElement(num=105, prime=223)
88
>>> p1 = Point(x, y, a, b)
99
>>> print(p1)
10-
Point(FieldElement_223(192),FieldElement_223(105))_FieldElement_223(0)_FieldElement_223(7)
10+
Point(192,105)_0_7 FieldElement(223)
1111
1212
# end::example1[]
1313
# tag::example3[]
@@ -22,8 +22,7 @@
2222
>>> p1 = Point(x1, y1, a, b)
2323
>>> p2 = Point(x2, y2, a, b)
2424
>>> print(p1+p2)
25-
Point(FieldElement_223(170),FieldElement_223(142))_FieldElement_223(0)_
26-
FieldElement_223(7)
25+
Point(170,142)_0_7 FieldElement(223)
2726
2827
# end::example3[]
2928
# tag::example4[]

code-ch04/answers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@
7777
# tag::answer4[]
7878
>>> from helper import encode_base58
7979
>>> h = '7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d'
80-
>>> print(encode_base58(bytes.fromhex(h)).decode('ascii'))
80+
>>> print(encode_base58(bytes.fromhex(h)))
8181
9MA8fRQrT4u8Zj8ZRd6MAiiyaxb2Y1CMpvVkHQu5hVM6
8282
>>> h = 'eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c'
83-
>>> print(encode_base58(bytes.fromhex(h)).decode('ascii'))
83+
>>> print(encode_base58(bytes.fromhex(h)))
8484
4fE3H2E6XMp4SsxtwinF7w9a34ooUrwWe4WsW1458Pd
8585
>>> h = 'c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6'
86-
>>> print(encode_base58(bytes.fromhex(h)).decode('ascii'))
86+
>>> print(encode_base58(bytes.fromhex(h)))
8787
EQJsjkd6JaGwxrjEhfeqPenqHwrBmPQZjJGNSCHBkcF7
8888
8989
# end::answer4[]

code-ch04/ecc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ def __ne__(self, other):
163163
def __repr__(self):
164164
if self.x is None:
165165
return 'Point(infinity)'
166+
elif isinstance(self.x, FieldElement):
167+
return 'Point({},{})_{}_{} FieldElement({})'.format(
168+
self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime)
166169
else:
167170
return 'Point({},{})_{}_{}'.format(self.x, self.y, self.a, self.b)
168171

code-ch04/helper.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
# tag::source1[]
7-
BASE58_ALPHABET = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
7+
BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
88
# end::source1[]
99

1010

@@ -34,25 +34,25 @@ def encode_base58(s):
3434
count += 1
3535
else:
3636
break
37-
prefix = b'1' * count
3837
num = int.from_bytes(s, 'big')
39-
result = bytearray()
38+
prefix = '1' * count
39+
result = ''
4040
while num > 0: # <2>
4141
num, mod = divmod(num, 58)
42-
result.insert(0, BASE58_ALPHABET[mod])
43-
return prefix + bytes(result) # <3>
42+
result = BASE58_ALPHABET[mod] + result
43+
return prefix + result # <3>
4444
# end::source2[]
4545

4646

4747
# tag::source3[]
48-
def encode_base58_checksum(s):
49-
return encode_base58(s + hash256(s)[:4]).decode('ascii') # <1>
48+
def encode_base58_checksum(b):
49+
return encode_base58(b + hash256(b)[:4])
5050
# end::source3[]
5151

5252

5353
def decode_base58(s):
5454
num = 0
55-
for c in s.encode('ascii'):
55+
for c in s:
5656
num *= 58
5757
num += BASE58_ALPHABET.index(c)
5858
combined = num.to_bytes(25, byteorder='big')

code-ch05/ecc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ def __ne__(self, other):
163163
def __repr__(self):
164164
if self.x is None:
165165
return 'Point(infinity)'
166+
elif isinstance(self.x, FieldElement):
167+
return 'Point({},{})_{}_{} FieldElement({})'.format(
168+
self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime)
166169
else:
167170
return 'Point({},{})_{}_{}'.format(self.x, self.y, self.a, self.b)
168171

0 commit comments

Comments
 (0)