Skip to content

Commit 27b30b3

Browse files
committed
made each sentence its own line
1 parent 723a5e1 commit 27b30b3

15 files changed

+1230
-439
lines changed

ch01.asciidoc

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,23 @@ Mathematically, a Finite Field is defined as follows:
3434

3535
A finite set of numbers and two operations *+* (addition) and *⋅* (multiplication) that satisfy the following:
3636

37-
1. If *a* and *b* are in the set, *a+b* and *a⋅b* are in the set.
37+
1.
38+
If *a* and *b* are in the set, *a+b* and *a⋅b* are in the set.
3839
We call this property _closed_
39-
2. The additive identity, *0* exists.
40+
2.
41+
The additive identity, *0* exists.
4042
This means *a + 0 = a*
41-
3. The multiplicative identity, *1* exists. This means *a ⋅ 1 = a*
42-
4. If *a* is in the set, *-a* is in the set. *-a* is defined as the value that makes *a + (-a) = 0*. This is what we call the _additive inverse_.
43-
5. If *a* is in the set and is not 0, *a^-1^* is in the set. *a^-1^* is defined as the value that makes *a ⋅ a^-1^ = 1*. This is what we call the _multiplicative inverse_.
43+
3.
44+
The multiplicative identity, *1* exists.
45+
This means *a ⋅ 1 = a*
46+
4.
47+
If *a* is in the set, *-a* is in the set.
48+
*-a* is defined as the value that makes *a + (-a) = 0*.
49+
This is what we call the _additive inverse_.
50+
5.
51+
If *a* is in the set and is not 0, *a^-1^* is in the set.
52+
*a^-1^* is defined as the value that makes *a ⋅ a^-1^ = 1*.
53+
This is what we call the _multiplicative inverse_.
4454

4555
Let's unpack each of the following.
4656

ch02.asciidoc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,12 @@ While this doesn't prove the associativity of point addition, the visual should
200200

201201
To code point addition, we're going to split it up into 3 steps:
202202

203-
1. Where the points are in a vertical line or using the Identity.
204-
2. Where the points are not in a vertical line, but are different.
205-
3. Where the two points are the same.
203+
1.
204+
Where the points are in a vertical line or using the Identity.
205+
2.
206+
Where the points are not in a vertical line, but are different.
207+
3.
208+
Where the two points are the same.
206209

207210
=== Coding Point Addition
208211

@@ -235,10 +238,13 @@ include::code-ch02/ecc.py[tag=source2]
235238
236239
include::code-ch02/ecc.py[tag=source3]
237240
----
238-
<1> x-coordinate and y-coordinate being `None` is how we signify the point at infinity. Note that the next if statement will fail if we don't return here.
241+
<1> x-coordinate and y-coordinate being `None` is how we signify the point at infinity.
242+
Note that the next if statement will fail if we don't return here.
239243
<2> We overload the `+` operator here
240-
<3> `self.x` being `None` means that `self` is the point at infinity, or the additive identity. Thus, we return `other`
241-
<4> `self.x` being `None` means that `other` is the point at infinity, or the additive identity. Thus, we return `self`
244+
<3> `self.x` being `None` means that `self` is the point at infinity, or the additive identity.
245+
Thus, we return `other`
246+
<4> `self.x` being `None` means that `other` is the point at infinity, or the additive identity.
247+
Thus, we return `self`
242248

243249
include::code-ch02/answers.py[tag=exercise3]
244250

ch03.asciidoc

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@ Pi, sqrt(2), e+7th root of 19, etc are all part of real numbers.
1616
This worked because _real_ numbers are also a field.
1717
Note unlike a _finite_ field, there are an _infinite_ number of real numbers, but otherwise the same properties hold:
1818

19-
1. if *a* and *b* are in the set, *a+b* and *a⋅b* is in the set. We call this property _closed_
20-
2. The additive identity, *0* exists. This means *a + 0 = a*
21-
3. The multiplicative identity, *1* exists. This means *a ⋅ 1 = a*
22-
4. If *a* is in the set, *-a* is in the set. *-a* is defined as the value that makes *a + (-a) = 0*. This is what we call the _additive inverse_.
23-
5. If *a* is in the set and is not 0, *a^-1^* is in the set. *a^-1^* is defined as the value that makes *a ⋅ a^-1^ = 1*. This is what we call the _multiplicative inverse_.
19+
1.
20+
if *a* and *b* are in the set, *a+b* and *a⋅b* is in the set.
21+
We call this property _closed_
22+
2.
23+
The additive identity, *0* exists.
24+
This means *a + 0 = a*
25+
3.
26+
The multiplicative identity, *1* exists.
27+
This means *a ⋅ 1 = a*
28+
4.
29+
If *a* is in the set, *-a* is in the set.
30+
*-a* is defined as the value that makes *a + (-a) = 0*.
31+
This is what we call the _additive inverse_.
32+
5.
33+
If *a* is in the set and is not 0, *a^-1^* is in the set.
34+
*a^-1^* is defined as the value that makes *a ⋅ a^-1^ = 1*.
35+
This is what we call the _multiplicative inverse_.
2436

2537
Clearly, all of these are true as normal addition and multiplication apply for the first part, additive and multiplicative identities 0 and 1 exist, -x is the additive inverse and 1/x is the multiplicative inverse.
2638

@@ -86,7 +98,8 @@ We will do this using the results of Exercise 2.
8698
----
8799
include::code-ch03/ecc.py[tag=source2]
88100
----
89-
<1> We pass in `FieldElement` objects into the `Point` class for initialization. This will, in turn, use all the overloaded math operations in `FieldElement`
101+
<1> We pass in `FieldElement` objects into the `Point` class for initialization.
102+
This will, in turn, use all the overloaded math operations in `FieldElement`
90103

91104
We can now run this test like so:
92105

@@ -362,9 +375,13 @@ class Point:
362375
...
363376
include::code-ch03/ecc.py[tag=source3]
364377
----
365-
<1> `current` represents the point that's at the current bit. First time through the loop it represents 1*self, the second time, it will be 2*self, third time, 4*self, then 8*self and so on. We double the point each time. In binary the coefficients are 1, 10, 100, 1000, 10000, etc.
378+
<1> `current` represents the point that's at the current bit.
379+
First time through the loop it represents 1*self, the second time, it will be 2*self, third time, 4*self, then 8*self and so on.
380+
We double the point each time.
381+
In binary the coefficients are 1, 10, 100, 1000, 10000, etc.
366382
<2> We start the result at 0, or the point at infinity.
367-
<3> We are looking at whether the right-most bit is a 1. If it is, then we add the value of the current bit.
383+
<3> We are looking at whether the right-most bit is a 1.
384+
If it is, then we add the value of the current bit.
368385
<4> We need to double the point until we're past how big the coefficient can be.
369386
<5> We bit shift the coefficient to the right.
370387
@@ -477,7 +494,8 @@ include::code-ch03/ecc.py[tag=source7]
477494
478495
We now have an easier way to initialize a point on the secp256k1 curve, without having to define the a and b every time like we have to with the `Point` class.
479496
480-
We can also define `__rmul__` a bit more efficiently since we know the order of the group, `n`. Since we're coding Python, we'll name this with a capital `N` to make it clear that `N` is a constant.
497+
We can also define `__rmul__` a bit more efficiently since we know the order of the group, `n`.
498+
Since we're coding Python, we'll name this with a capital `N` to make it clear that `N` is a constant.
481499
482500
[source,python]
483501
----
@@ -487,7 +505,8 @@ class S256Point(Point):
487505
...
488506
include::code-ch03/ecc.py[tag=source8]
489507
----
490-
<1> We can mod by `n` because nG=0. That is, every `n` times we cycle back to zero or the point at infinity.
508+
<1> We can mod by `n` because nG=0.
509+
That is, every `n` times we cycle back to zero or the point at infinity.
491510
492511
We can now define G directly and keep it around since we'll be using it a lot going forward.
493512
@@ -678,10 +697,14 @@ The only way we could know the details of R beforehand is if we know `e`.
678697
679698
To whit, here are the steps:
680699
681-
1. We are given (r, s) as the signature, `z` as the hash of the thing being signed and P, the public key (or public point) of the signer.
682-
2. We calculate u = z/s, v = r/s
683-
3. We calculate uG + vP = R
684-
4. If R's `x` coordinate equals `r`, the signature is valid.
700+
1.
701+
We are given (r, s) as the signature, `z` as the hash of the thing being signed and P, the public key (or public point) of the signer.
702+
2.
703+
We calculate u = z/s, v = r/s
704+
3.
705+
We calculate uG + vP = R
706+
4.
707+
If R's `x` coordinate equals `r`, the signature is valid.
685708
686709
[NOTE]
687710
.Why two rounds of sha256?
@@ -708,7 +731,8 @@ include::code-ch03/examples.py[tag=example9]
708731
<1> Note that we use Fermat's Little Theorem for 1/s, since `n` is prime.
709732
<2> u = z/s
710733
<3> v = r/s
711-
<4> uG+vP = (r,y). We need to check that the x-coordinate is r
734+
<4> uG+vP = (r,y).
735+
We need to check that the x-coordinate is r
712736
713737
include::code-ch03/answers.py[tag=exercise6]
714738
@@ -733,8 +757,10 @@ class S256Point(Point):
733757
include::code-ch03/ecc.py[tag=source12]
734758
----
735759
<1> `s_inv` (1/s) is calculated using Fermat's Little Theorem on the order of the group `n` which is prime.
736-
<2> u = z/s. Note that we can mod by `n` as that's the order of the group.
737-
<3> v = r/s. Note that we can mod by `n` as that's the order of the group.
760+
<2> u = z/s.
761+
Note that we can mod by `n` as that's the order of the group.
762+
<3> v = r/s.
763+
Note that we can mod by `n` as that's the order of the group.
738764
<4> uG+vP should be R
739765
<5> We check that the x-coordinate is `r`
740766
@@ -748,11 +774,17 @@ We do this by choosing a random `k`.
748774
749775
Signing Procedure:
750776
751-
1. We are given `z`. We know `e` and eG=P.
752-
2. Choose a random `k`
753-
3. Calculate R=kG and r=x-coordinate of R
754-
4. Calculate s = (z+re)/k
755-
5. Signature is (r,s)
777+
1.
778+
We are given `z`.
779+
We know `e` and eG=P.
780+
2.
781+
Choose a random `k`
782+
3.
783+
Calculate R=kG and r=x-coordinate of R
784+
4.
785+
Calculate s = (z+re)/k
786+
5.
787+
Signature is (r,s)
756788
757789
Note that the pubkey `P` has to be transmitted to whoever wants to verify and `z` must be known by the verifier.
758790
We'll see later that `z` is computed and P is sent along with the signature.
@@ -771,11 +803,13 @@ This library is for teaching purposes only, so please don't use any of the code
771803
----
772804
include::code-ch03/examples.py[tag=example10]
773805
----
774-
<1> This is an example of a "brain wallet" which is a way to keep the private key in your head without having to memorize something too difficult. Please don't use this for a real secret.
806+
<1> This is an example of a "brain wallet" which is a way to keep the private key in your head without having to memorize something too difficult.
807+
Please don't use this for a real secret.
775808
<2> This is the signature hash, or hash of the message that we're signing.
776809
<3> We're going to use a fixed `k` here for demonstration purposes.
777810
<4> kG = (r,y) so we take the `x` coordinate only
778-
<5> s = (z+re)/k. We can mod by `n` because we know this is a cyclical group of order `n`.
811+
<5> s = (z+re)/k.
812+
We can mod by `n` because we know this is a cyclical group of order `n`.
779813
<6> The public point needs to be known by the verifier
780814
781815
include::code-ch03/answers.py[tag=exercise7]
@@ -807,11 +841,13 @@ class PrivateKey:
807841
s = N - s
808842
return Signature(r, s) # <6>
809843
----
810-
<1> `randint` chooses a random integer from `[0,n)`. Please don't use this function in production as the random number from this library is not nearly random enough.
844+
<1> `randint` chooses a random integer from `[0,n)`.
845+
Please don't use this function in production as the random number from this library is not nearly random enough.
811846
<2> `r` is the x-coordinate of kG
812847
<3> We use Fermat's Little Theorem again and `n`, which is prime
813848
<4> s = (z+re)/k
814-
<5> It turns out that using the low-s value will get nodes to relay our transactions easier. This is for malleability reasons
849+
<5> It turns out that using the low-s value will get nodes to relay our transactions easier.
850+
This is for malleability reasons
815851
<6> We return a `Signature` object from above.
816852
817853
.Importance of a unique `k`
@@ -844,7 +880,8 @@ class PrivateKey:
844880
...
845881
include::code-ch03/ecc.py[tag=source14]
846882
----
847-
<1> We are using the deterministic `k` instead of a random one. Everything else about `sign` remains the same.
883+
<1> We are using the deterministic `k` instead of a random one.
884+
Everything else about `sign` remains the same.
848885
<2> This algorithm returns a candidate that's suitable.
849886
850887
Deterministic `k` will be unique with very high probability.

ch04.asciidoc

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ There are two forms of SEC format that we need to be concerned with and the firs
2121

2222
Here is how the uncompressed SEC format for a given point P=(x,y) is generated:
2323

24-
1. Start with the prefix byte which is `0x04`.
25-
2. Next, append the x-coordinate in 32 bytes as a Big-Endian integer.
26-
3. Next, append the y-coordinate in 32 bytes as a Big-Endian integer.
24+
1.
25+
Start with the prefix byte which is `0x04`.
26+
2.
27+
Next, append the x-coordinate in 32 bytes as a Big-Endian integer.
28+
3.
29+
Next, append the y-coordinate in 32 bytes as a Big-Endian integer.
2730

2831
Here is what the uncompressed SEC format looks like:
2932

@@ -67,7 +70,8 @@ class S256Point(Point):
6770
return b'\x04' + self.x.num.to_bytes(32, 'big') \
6871
+ self.y.num.to_bytes(32, 'big') # <1>
6972
----
70-
<1> In Python 3, you can convert a number to `bytes` using the `to_bytes` method. The first argument is how many bytes it should take up and the second argument is the endianness (see _Big and Little Endian_).
73+
<1> In Python 3, you can convert a number to `bytes` using the `to_bytes` method.
74+
The first argument is how many bytes it should take up and the second argument is the endianness (see _Big and Little Endian_).
7175

7276
include::code-ch04/answers.py[tag=exercise1]
7377

@@ -95,8 +99,11 @@ We call this the *Compressed SEC format* because of how the y-coordinate is comp
9599

96100
Here is the serialization of the Compressed SEC format for a given point P=(x,y):
97101

98-
1. Start with the prefix byte. If `y` is even, it's `0x02`, otherwise it's `0x03`.
99-
2. Next, append the x-coordinate in 32 bytes as a Big-Endian integer.
102+
1.
103+
Start with the prefix byte.
104+
If `y` is even, it's `0x02`, otherwise it's `0x03`.
105+
2.
106+
Next, append the x-coordinate in 32 bytes as a Big-Endian integer.
100107

101108
The Compressed SEC format looks like this:
102109

@@ -205,12 +212,22 @@ This was most likely because the standard was already defined in 2008, supported
205212

206213
DER Signature format is defined like this:
207214

208-
1. Start with the `0x30` byte
209-
2. Encode the length of the rest of the signature (usually `0x44` or `0x45`) and append
210-
3. Append the marker byte `0x02`
211-
4. Encode `r` as a Big-Endian integer, but prepend with `0x00` byte if `r`'s first byte >= `0x80`. Prepend the resulting length to `r`. Add this to the result.
212-
5. Append the marker byte `0x02`
213-
6. Encode `s` as a Big-Endian integer, but prepend with `0x00` byte if `s`'s first byte >= `0x80`. Prepend the resulting length to `s`. Add this to the result.
215+
1.
216+
Start with the `0x30` byte
217+
2.
218+
Encode the length of the rest of the signature (usually `0x44` or `0x45`) and append
219+
3.
220+
Append the marker byte `0x02`
221+
4.
222+
Encode `r` as a Big-Endian integer, but prepend with `0x00` byte if `r`'s first byte >= `0x80`.
223+
Prepend the resulting length to `r`.
224+
Add this to the result.
225+
5.
226+
Append the marker byte `0x02`
227+
6.
228+
Encode `s` as a Big-Endian integer, but prepend with `0x00` byte if `s`'s first byte >= `0x80`.
229+
Prepend the resulting length to `s`.
230+
Add this to the result.
214231

215232
The rules for #4 and #6 with the first byte starting with something greater than or equal to `0x80` is because DER is a general encoding and allows for negative numbers to be encoded.
216233
The first bit being 1 means that the number is negative.
@@ -284,9 +301,12 @@ include::code-ch04/helper.py[tag=source1]
284301
...
285302
include::code-ch04/helper.py[tag=source2]
286303
----
287-
<1> The purpose of this loop is to determine how many of the bytes at the front are 0 bytes. We want to add them back at the end.
304+
<1> The purpose of this loop is to determine how many of the bytes at the front are 0 bytes.
305+
We want to add them back at the end.
288306
<2> This is the loop that figures out what Base58 digit to use.
289-
<3> Finally, we prepend all the zeros that we counted at the front because otherwise, they wouldn't show up as prefixed 1's. This annoyingly happens with pay-to-pubkey-hash (p2pkh). More on that in <<chapter_script>>.
307+
<3> Finally, we prepend all the zeros that we counted at the front because otherwise, they wouldn't show up as prefixed 1's.
308+
This annoyingly happens with pay-to-pubkey-hash (p2pkh).
309+
More on that in <<chapter_script>>.
290310

291311
This function will take any bytes in Python 3 and convert it to Base58.
292312

@@ -311,11 +331,16 @@ To both shorten and increase security, we can use the ripemd160 hash to compress
311331
By not using the SEC format directly, we can go from 33 bytes to 20 bytes, shortening the address significantly.
312332
Here is how a Bitcoin address is created:
313333

314-
1. For mainnet addresses, start with the prefix `0x00`, for testnet `0x6f`.
315-
2. Take the SEC format (compressed or uncompressed) and do a sha256 operation followed by the ripemd160 hash operation, the combination which is called a hash160 operation.
316-
3. Combine the prefix from #1 and resulting hash from #2
317-
4. Do a hash256 of the result from #3 and get the first 4 bytes.
318-
5. Take the combination of #3 and #4 and encode in Base58.
334+
1.
335+
For mainnet addresses, start with the prefix `0x00`, for testnet `0x6f`.
336+
2.
337+
Take the SEC format (compressed or uncompressed) and do a sha256 operation followed by the ripemd160 hash operation, the combination which is called a hash160 operation.
338+
3.
339+
Combine the prefix from #1 and resulting hash from #2
340+
4.
341+
Do a hash256 of the result from #3 and get the first 4 bytes.
342+
5.
343+
Take the combination of #3 and #4 and encode in Base58.
319344

320345
Step 4 of this process is called the checksum.
321346
We can do steps 4 and 5 in one go this way:
@@ -368,12 +393,18 @@ WIF uses the same Base58 encoding that addresses use.
368393

369394
Here is how the WIF format is created:
370395

371-
1. For mainnet private keys, start with the prefix `0x80`, for testnet `0xef`.
372-
2. Encode the secret in 32-byte Big-Endian.
373-
3. If the SEC format used for the public key address was compressed add a suffix of `0x01`.
374-
4. Combine the prefix from #1, serialized secret from #2 and suffix from #3
375-
5. Do a hash256 of the result from #4 and get the first 4 bytes.
376-
6. Take the combination of #4 and #5 and encode in Base58.
396+
1.
397+
For mainnet private keys, start with the prefix `0x80`, for testnet `0xef`.
398+
2.
399+
Encode the secret in 32-byte Big-Endian.
400+
3.
401+
If the SEC format used for the public key address was compressed add a suffix of `0x01`.
402+
4.
403+
Combine the prefix from #1, serialized secret from #2 and suffix from #3
404+
5.
405+
Do a hash256 of the result from #4 and get the first 4 bytes.
406+
6.
407+
Take the combination of #4 and #5 and encode in Base58.
377408

378409
We can now create the `wif` method on the `PrivateKey` class.
379410

0 commit comments

Comments
 (0)