Write the corresponding method __ne__ which checks if two FieldElement objects are not equal to each other.
classFieldElement:...def__ne__(self,other):# this should be the inverse of the == operatorreturnnot(self==other)
Solve these problems in F57 (assume all +’s here are +f and -`s here -f)
44+33
9-29
17+42+49
52-30-38
>>>prime=57>>>((44+33)%prime)20>>>((9-29)%prime)37>>>((17+42+49)%prime)51>>>((52-30-38)%prime)41
Write the corresponding __sub__ method which defines the subtraction of two FieldElement objects.
classFieldElement:...def__sub__(self,other):ifself.prime!=other.prime:raiseTypeError('Cannot subtract two numbers in different Fields')# self.num and other.num are the actual values# self.prime is what we need to mod againstnum=(self.num-other.num)%self.prime# We return an element of the same classreturnself.__class__(num,self.prime)
Solve the following equations in F97 (again, assume ⋅ and exponentiation are field versions):
95⋅45⋅31
17⋅13⋅19⋅44
127⋅7749
>>>prime=97>>>(95*45*31%prime)23>>>(17*13*19*44%prime)68>>>(12**7*77**49%prime)63
For k = 1, 3, 7, 13, 18, what is this set in F19?
{k⋅0, k⋅1, k⋅2, k⋅3, … k⋅18}
Do you notice anything about these sets?
>>>prime=19>>>forkin(1,3,7,13,18):...([k*i%primeforiinrange(prime)])[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18][0,3,6,9,12,15,18,2,5,8,11,14,17,1,4,7,10,13,16][0,7,14,2,9,16,4,11,18,6,13,1,8,15,3,10,17,5,12][0,13,7,1,14,8,2,15,9,3,16,10,4,17,11,5,18,12,6][0,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]>>>forkin(1,3,7,13,18):...(sorted([k*i%primeforiinrange(prime)]))[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18][0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18][0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18][0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18][0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
When sorted, the results are always the same set.
Write the corresponding __mul__ method which defines the multiplication of two Finite Field elements.
classFieldElement:...def__mul__(self,other):ifself.prime!=other.prime:raiseTypeError('Cannot multiply two numbers in different Fields')# self.num and other.num are the actual values# self.prime is what we need to mod againstnum=(self.num*other.num)%self.prime# We return an element of the same classreturnself.__class__(num,self.prime)
For p = 7, 11, 17, 31, what is this set in Fp?
{1(p-1), 2(p-1), 3(p-1), 4(p-1), … (p-1)(p-1)}
>>>forprimein(7,11,17,31):...([pow(i,prime-1,prime)foriinrange(1,prime)])[1,1,1,1,1,1][1,1,1,1,1,1,1,1,1,1][1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1][1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,\1,1,1,1,1,1]
Solve the following equations in F31:
3 / 24
17-3
4-4⋅11
>>>prime=31>>>(3*pow(24,prime-2,prime)%prime)4>>>(pow(17,prime-4,prime))29>>>(pow(4,prime-5,prime)*11%prime)13
Write the corresponding __truediv__ method which defines the division of two field elements.
Note that in Python3, division is separated into __truediv__ and __floordiv__. The first does normal division, the second does integer division.
classFieldElement:...def__truediv__(self,other):ifself.prime!=other.prime:raiseTypeError('Cannot divide two numbers in different Fields')# use fermat's little theorem:# self.num**(p-1) % p == 1# this means:# 1/n == pow(n, p-2, p)# We return an element of the same classnum=self.num*pow(other.num,self.prime-2,self.prime)%self.primereturnself.__class__(num,self.prime)
Determine which of these points are on the curve y2=x3+5x+7:
(2,4), (-1,-1), (18,77), (5,7)
>>>defon_curve(x,y):...returny**2==x**3+5*x+7>>>(on_curve(2,4))False>>>(on_curve(-1,-1))True>>>(on_curve(18,77))True>>>(on_curve(5,7))False
Write the __ne__ method for Point.
classPoint:...def__ne__(self,other):returnnot(self==other)
Handle the case where the two points are additive inverses. That is, they have the same x, but a different y, causing a vertical line. This should return the point at infinity.
classPoint:...ifself.x==other.xandself.y!=other.y:returnself.__class__(None,None,self.a,self.b)
For the curve y2=x3+5x+7, what is (2,5) + (-1,-1)?
>>>x1,y1=2,5>>>x2,y2=-1,-1>>>s=(y2-y1)/(x2-x1)>>>x3=s**2-x1-x2>>>y3=s*(x1-x3)-y1>>>(x3,y3)3.0-7.0
Write the __add__ method where x1≠x2
classPoint:...def__add__(self,other):...ifself.x!=other.x:s=(other.y-self.y)/(other.x-self.x)x=s**2-self.x-other.xy=s*(self.x-x)-self.yreturnself.__class__(x,y,self.a,self.b)
For the curve y2=x3+5x+7, what is (2,5) + (-1,-1)?
>>>a,x1,y1=5,-1,1>>>s=(3*x1**2+a)/(2*y1)>>>x3=s**2-2*x1>>>y3=s*(x1-x3)-y1>>>(x3,y3)18.0-77.0
Write the __add__ method when P1=P2.
classPoint:...def__add__(self,other):...ifself==other:s=(3*self.x**2+self.a)/(2*self.y)x=s**2-2*self.xy=s*(self.x-x)-self.yreturnself.__class__(x,y,self.a,self.b)
Evaluate whether these points are on the curve y2=x3+7 over F223
(192,105), (17,56), (200,119), (1,193), (42,99)
>>>fromeccimportFieldElement>>>prime=223>>>a=FieldElement(0,prime)>>>b=FieldElement(7,prime)>>>defon_curve(x,y):...returny**2==x**3+a*x+b>>>(on_curve(FieldElement(192,prime),FieldElement(105,prime)))True>>>(on_curve(FieldElement(17,prime),FieldElement(56,prime)))True>>>(on_curve(FieldElement(200,prime),FieldElement(119,prime)))False>>>(on_curve(FieldElement(1,prime),FieldElement(193,prime)))True>>>(on_curve(FieldElement(42,prime),FieldElement(99,prime)))False
For the curve y2=x3+7 over F223, find:
(170,142) + (60,139)
(47,71) + (17,56)
(143,98) + (76,66)
>>>fromeccimportFieldElement,Point>>>prime=223>>>a=FieldElement(0,prime)>>>b=FieldElement(7,prime)>>>p1=Point(FieldElement(170,prime),FieldElement(142,prime),a,b)>>>p2=Point(FieldElement(60,prime),FieldElement(139,prime),a,b)>>>(p1+p2)Point(FieldElement_223(220),FieldElement_223(181))_FieldElement_223(0)_FieldElement_223(7)>>>p1=Point(FieldElement(47,prime),FieldElement(71,prime),a,b)>>>p2=Point(FieldElement(17,prime),FieldElement(56,prime),a,b)>>>(p1+p2)Point(FieldElement_223(215),FieldElement_223(68))_FieldElement_223(0)_FieldElement_223(7)>>>p1=Point(FieldElement(143,prime),FieldElement(98,prime),a,b)>>>p2=Point(FieldElement(76,prime),FieldElement(66,prime),a,b)>>>(p1+p2)Point(FieldElement_223(47),FieldElement_223(71))_FieldElement_223(0)_FieldElement_223(7)
Extend ECCTest to test for the additions from the previous exercise. Call this test_add.
deftest_add(self):prime=223a=FieldElement(0,prime)b=FieldElement(7,prime)additions=((192,105,17,56,170,142),(47,71,117,141,60,139),(143,98,76,66,47,71),)forx1_raw,y1_raw,x2_raw,y2_raw,x3_raw,y3_rawinadditions:x1=FieldElement(x1_raw,prime)y1=FieldElement(y1_raw,prime)p1=Point(x1,y1,a,b)x2=FieldElement(x2_raw,prime)y2=FieldElement(y2_raw,prime)p2=Point(x2,y2,a,b)x3=FieldElement(x3_raw,prime)y3=FieldElement(y3_raw,prime)p3=Point(x3,y3,a,b)self.assertEqual(p1+p2,p3)
For the curve y2=x3+7 over F223, find:
2⋅(192,105)
2⋅(143,98)
2⋅(47,71)
4⋅(47,71)
8⋅(47,71)
21⋅(47,71)
>>>fromeccimportFieldElement,Point>>>prime=223>>>a=FieldElement(0,prime)>>>b=FieldElement(7,prime)>>>x1=FieldElement(num=192,prime=prime)>>>y1=FieldElement(num=105,prime=prime)>>>p=Point(x1,y1,a,b)>>>(p+p)Point(FieldElement_223(49),FieldElement_223(71))_FieldElement_223(0)_FieldElement_223(7)>>>x1=FieldElement(num=143,prime=prime)>>>y1=FieldElement(num=98,prime=prime)>>>p=Point(x1,y1,a,b)>>>(p+p)Point(FieldElement_223(64),FieldElement_223(168))_FieldElement_223(0)_FieldElement_223(7)>>>x1=FieldElement(num=47,prime=prime)>>>y1=FieldElement(num=71,prime=prime)>>>p=Point(x1,y1,a,b)>>>(p+p)Point(FieldElement_223(36),FieldElement_223(111))_FieldElement_223(0)_FieldElement_223(7)>>>(p+p+p+p)Point(FieldElement_223(194),FieldElement_223(51))_FieldElement_223(0)_FieldElement_223(7)>>>(p+p+p+p+p+p+p+p)Point(FieldElement_223(116),FieldElement_223(55))_FieldElement_223(0)_FieldElement_223(7)>>>(p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p+p)Point(infinity)
For the curve y2=x3+7 over F223, find the order of the group generated by (15,86)
>>>prime=223>>>a=FieldElement(0,prime)>>>b=FieldElement(7,prime)>>>x=FieldElement(15,prime)>>>y=FieldElement(86,prime)>>>p=Point(x,y,a,b)>>>inf=Point(None,None,a,b)>>>product=p>>>count=1>>>whileproduct!=inf:...product+=p...count+=1>>>(count)7
Verify whether these signatures are valid:
P = (0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c,
0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34)
# signature 1
z = 0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60
r = 0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395
s = 0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4
# signature 2
z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
r = 0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c
s = 0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6
>>>fromeccimportS256Point,N,G>>>point=S256Point(...0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c,...0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34)>>>z=0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60>>>r=0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395>>>s=0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4>>>u=z*pow(s,N-2,N)%N>>>v=r*pow(s,N-2,N)%N>>>((u*G+v*point).x.num==r)True>>>z=0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d>>>r=0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c>>>s=0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6>>>u=z*pow(s,N-2,N)%N>>>v=r*pow(s,N-2,N)%N>>>((u*G+v*point).x.num==r)True
Sign the following message with the secret
e = 12345
z = int.from_bytes(hash256('Programming Bitcoin!'), 'big')
>>>fromeccimportS256Point,G,N>>>fromhelperimporthash256>>>e=12345>>>z=int.from_bytes(hash256(b'Programming Bitcoin!'),'big')>>>k=1234567890>>>r=(k*G).x.num>>>k_inv=pow(k,N-2,N)>>>s=(z+r*e)*k_inv%N>>>(e*G)S256Point(f01d6b9018ab421dd410404cb869072065522bf85734008f105cf385a023a80f,\0eba29d0f0c5408ed681984dc525982abefccd9f7ff01dd26da4999cf3f6a295)>>>(hex(z))0x969f6056aa26f7d2795fd013fe88868d09c9f6aed96965016e1936ae47060d48>>>(hex(r))0x2b698a0f0a4041b77e63488ad48c23e8e8838dd1fb7520408b121697b782ef22>>>(hex(s))0x1dbc63bfef4416705e602a7b564161167076d8b20990a0f26f316cff2cb0bc1a
Find the uncompressed SEC format for the Public Key where the Private Key secrets are:
5000
20185
0xdeadbeef12345
>>>fromeccimportPrivateKey>>>priv=PrivateKey(5000)>>>(priv.point.sec(compressed=False).hex())04ffe558e388852f0120e46af2d1b370f85854a8eb0841811ece0e3e03d282d57c315dc72890a4\f10a1481c031b03b351b0dc79901ca18a00cf009dbdb157a1d10>>>priv=PrivateKey(2018**5)>>>(priv.point.sec(compressed=False).hex())04027f3da1918455e03c46f659266a1bb5204e959db7364d2f473bdf8f0a13cc9dff87647fd023\c13b4a4994f17691895806e1b40b57f4fd22581a4f46851f3b06>>>priv=PrivateKey(0xdeadbeef12345)>>>(priv.point.sec(compressed=False).hex())04d90cd625ee87dd38656dd95cf79f65f60f7273b67d3096e68bd81e4f5342691f842efa762fd5\9961d0e99803c61edba8b3e3f7dc3a341836f97733aebf987121
Find the Compressed SEC format for the Public Key where the Private Key secrets are:
5001
20195
0xdeadbeef54321
>>>fromeccimportPrivateKey>>>priv=PrivateKey(5001)>>>(priv.point.sec(compressed=True).hex())0357a4f368868a8a6d572991e484e664810ff14c05c0fa023275251151fe0e53d1>>>priv=PrivateKey(2019**5)>>>(priv.point.sec(compressed=True).hex())02933ec2d2b111b92737ec12f1c5d20f3233a0ad21cd8b36d0bca7a0cfa5cb8701>>>priv=PrivateKey(0xdeadbeef54321)>>>(priv.point.sec(compressed=True).hex())0296be5b1292f6c856b3c5654e886fc13511462059089cdf9c479623bfcbe77690
Find the DER format for a signature whose r and s values are:
r =
0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6
s =
0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
>>>fromeccimportSignature>>>r=0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6>>>s=0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec>>>sig=Signature(r,s)>>>(sig.der().hex())3045022037206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6022100\8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
Convert the following hex to binary and then to Base58:
7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c
c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6
>>>fromhelperimportencode_base58>>>h='7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d'>>>(encode_base58(bytes.fromhex(h)).decode('ascii'))9MA8fRQrT4u8Zj8ZRd6MAiiyaxb2Y1CMpvVkHQu5hVM6>>>h='eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c'>>>(encode_base58(bytes.fromhex(h)).decode('ascii'))4fE3H2E6XMp4SsxtwinF7w9a34ooUrwWe4WsW1458Pd>>>h='c7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6'>>>(encode_base58(bytes.fromhex(h)).decode('ascii'))EQJsjkd6JaGwxrjEhfeqPenqHwrBmPQZjJGNSCHBkcF7
Find the address corresponding to Public Keys whose Private Key secrets are:
5002 (use uncompressed SEC, on testnet)
20205 (use compressed SEC, on testnet)
0x12345deadbeef (use compressed SEC on mainnet)
>>>fromeccimportPrivateKey>>>priv=PrivateKey(5002)>>>(priv.point.address(compressed=False,testnet=True))mmTPbXQFxboEtNRkwfh6K51jvdtHLxGeMA>>>priv=PrivateKey(2020**5)>>>(priv.point.address(compressed=True,testnet=True))mopVkxp8UhXqRYbCYJsbeE1h1fiF64jcoH>>>priv=PrivateKey(0x12345deadbeef)>>>(priv.point.address(compressed=True,testnet=False))1F1Pn2y6pDb68E5nYJJeba4TLg2U7B6KF1
Find the WIF for Private Key whose secrets are:
5003 (compressed, testnet)
20215 (uncompressed, testnet)
0x54321deadbeef (compressed, mainnet)
>>>fromeccimportPrivateKey>>>priv=PrivateKey(5003)>>>(priv.wif(compressed=True,testnet=True))cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN8rFTv2sfUK>>>priv=PrivateKey(2021**5)>>>(priv.wif(compressed=False,testnet=True))91avARGdfge8E4tZfYLoxeJ5sGBdNJQH4kvjpWAxgzczjbCwxic>>>priv=PrivateKey(0x54321deadbeef)>>>(priv.wif(compressed=True,testnet=False))KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgiuQJv1h8Ytr2S53a
Write a function little_endian_to_int which takes Python bytes, interprets those bytes in Little-Endian and returns the number.
deflittle_endian_to_int(b):'''little_endian_to_int takes byte sequence as a little-endian number.Returns an integer'''returnint.from_bytes(b,'little')
Write a function int_to_little_endian which does the reverse of the last exercise.
defint_to_little_endian(n,length):'''endian_to_little_endian takes an integer and returns the little-endianbyte sequence of length'''returnn.to_bytes(length,'little')
Create a testnet address for yourself using a long secret that only you know. This is important as there are bots on testnet trying to steal testnet coins. Make sure you write this secret down somewhere! You will be using the secret later to sign Transactions.
>>>fromeccimportPrivateKey>>>fromhelperimporthash256,little_endian_to_int>>>passphrase=b'jimmy@programmingblockchain.com my secret'>>>secret=little_endian_to_int(hash256(passphrase))>>>priv=PrivateKey(secret)>>>(priv.point.address(testnet=True))mft9LRNtaBNtpkknB8xgm17UvPedZ4ecYL
Write the version parsing part of the parse method that we’ve defined. To do this properly, you’ll have to convert 4 bytes into a Little-Endian integer.
classTx:...@classmethoddefparse(cls,s,testnet=False):version=little_endian_to_int(s.read(4))returncls(version,None,None,None,testnet=testnet)
Write the inputs parsing part of the parse method in Tx and the parse method for TxIn.
classTx:...@classmethoddefparse(cls,s,testnet=False):version=little_endian_to_int(s.read(4))num_inputs=read_varint(s)inputs=[]for_inrange(num_inputs):inputs.append(TxIn.parse(s))returncls(version,inputs,None,None,testnet=testnet)...classTxIn:...@classmethoddefparse(cls,s):'''Takes a byte stream and parses the tx_input at the startreturn a TxIn object'''prev_tx=s.read(32)[::-1]prev_index=little_endian_to_int(s.read(4))script_sig=Script.parse(s)sequence=little_endian_to_int(s.read(4))returncls(prev_tx,prev_index,script_sig,sequence)
Write the outputs parsing part of the parse method in Tx and the parse method for TxOut.
classTx:...classTx:...@classmethoddefparse(cls,s,testnet=False):version=little_endian_to_int(s.read(4))num_inputs=read_varint(s)inputs=[]for_inrange(num_inputs):inputs.append(TxIn.parse(s))num_outputs=read_varint(s)outputs=[]for_inrange(num_outputs):outputs.append(TxOut.parse(s))returncls(version,inputs,outputs,None,testnet=testnet)...classTxOut:...@classmethoddefparse(cls,s):'''Takes a byte stream and parses the tx_output at the startreturn a TxOut object'''amount=little_endian_to_int(s.read(8))script_pubkey=Script.parse(s)returncls(amount,script_pubkey)
Write the Locktime parsing part of the parse method in Tx.
classTx:...@classmethoddefparse(cls,s,testnet=False):version=little_endian_to_int(s.read(4))num_inputs=read_varint(s)inputs=[]for_inrange(num_inputs):inputs.append(TxIn.parse(s))num_outputs=read_varint(s)outputs=[]for_inrange(num_outputs):outputs.append(TxOut.parse(s))locktime=little_endian_to_int(s.read(4))returncls(version,inputs,outputs,locktime,testnet=testnet)
What is the ScriptSig from the second input, ScriptPubKey from the first output and the amount of the second output for this transaction?
010000000456919960ac691763688d3d3bcea9ad6ecaf875df5339e148a1fc61c6ed7a069e0100 00006a47304402204585bcdef85e6b1c6af5c2669d4830ff86e42dd205c0e089bc2a821657e951 c002201024a10366077f87d6bce1f7100ad8cfa8a064b39d4e8fe4ea13a7b71aa8180f012102f0 da57e85eec2934a82a585ea337ce2f4998b50ae699dd79f5880e253dafafb7feffffffeb8f51f4 038dc17e6313cf831d4f02281c2a468bde0fafd37f1bf882729e7fd3000000006a473044022078 99531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008b422690b84 61cb52c3cc30330b23d574351872b7c361e9aae3649071c1a7160121035d5c93d9ac96881f19ba 1f686f15f009ded7c62efe85a872e6a19b43c15a2937feffffff567bf40595119d1bb8a3037c35 6efd56170b64cbcc160fb028fa10704b45d775000000006a47304402204c7c7818424c7f7911da 6cddc59655a70af1cb5eaf17c69dadbfc74ffa0b662f02207599e08bc8023693ad4e9527dc42c3 4210f7a7d1d1ddfc8492b654a11e7620a0012102158b46fbdff65d0172b7989aec8850aa0dae49 abfb84c81ae6e5b251a58ace5cfeffffffd63a5e6c16e620f86f375925b21cabaf736c779f88fd 04dcad51d26690f7f345010000006a47304402200633ea0d3314bea0d95b3cd8dadb2ef79ea833 1ffe1e61f762c0f6daea0fabde022029f23b3e9c30f080446150b23852028751635dcee2be669c 2a1686a4b5edf304012103ffd6f4a67e94aba353a00882e563ff2722eb4cff0ad6006e86ee20df e7520d55feffffff0251430f00000000001976a914ab0c0b2e98b1ab6dbf67d4750b0a56244948 a87988ac005a6202000000001976a9143c82d7df364eb6c75be8c80df2b3eda8db57397088ac46 430600
>>>fromioimportBytesIO>>>fromtximportTx>>>hex_transaction='010000000456919960ac691763688d3d3bcea9ad6ecaf875df5339e\148a1fc61c6ed7a069e010000006a47304402204585bcdef85e6b1c6af5c2669d4830ff86e42dd\205c0e089bc2a821657e951c002201024a10366077f87d6bce1f7100ad8cfa8a064b39d4e8fe4e\a13a7b71aa8180f012102f0da57e85eec2934a82a585ea337ce2f4998b50ae699dd79f5880e253\dafafb7feffffffeb8f51f4038dc17e6313cf831d4f02281c2a468bde0fafd37f1bf882729e7fd\3000000006a47304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1c\dc26125022008b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a716012\1035d5c93d9ac96881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937feffffff567\bf40595119d1bb8a3037c356efd56170b64cbcc160fb028fa10704b45d775000000006a4730440\2204c7c7818424c7f7911da6cddc59655a70af1cb5eaf17c69dadbfc74ffa0b662f02207599e08\bc8023693ad4e9527dc42c34210f7a7d1d1ddfc8492b654a11e7620a0012102158b46fbdff65d0\172b7989aec8850aa0dae49abfb84c81ae6e5b251a58ace5cfeffffffd63a5e6c16e620f86f375\925b21cabaf736c779f88fd04dcad51d26690f7f345010000006a47304402200633ea0d3314bea\0d95b3cd8dadb2ef79ea8331ffe1e61f762c0f6daea0fabde022029f23b3e9c30f080446150b23\852028751635dcee2be669c2a1686a4b5edf304012103ffd6f4a67e94aba353a00882e563ff272\2eb4cff0ad6006e86ee20dfe7520d55feffffff0251430f00000000001976a914ab0c0b2e98b1a\b6dbf67d4750b0a56244948a87988ac005a6202000000001976a9143c82d7df364eb6c75be8c80\df2b3eda8db57397088ac46430600'>>>stream=BytesIO(bytes.fromhex(hex_transaction))>>>tx_obj=Tx.parse(stream)>>>(tx_obj.tx_ins[1].script_sig)304402207899531a52d59a6de200179928ca900254a36b8dff8bb75f5f5d71b1cdc26125022008\b422690b8461cb52c3cc30330b23d574351872b7c361e9aae3649071c1a71601035d5c93d9ac9\6881f19ba1f686f15f009ded7c62efe85a872e6a19b43c15a2937>>>(tx_obj.tx_outs[0].script_pubkey)OP_DUPOP_HASH160ab0c0b2e98b1ab6dbf67d4750b0a56244948a879OP_EQUALVERIFYOP_CHECKSIG>>>(tx_obj.tx_outs[1].amount)40000000
Write the fee method for the Tx class.
classTx:...deffee(self,testnet=False):input_sum,output_sum=0,0fortx_ininself.tx_ins:input_sum+=tx_in.value(testnet=testnet)fortx_outinself.tx_outs:output_sum+=tx_out.amountreturninput_sum-output_sum
Write the op_hash160 function.
defop_hash160(stack):iflen(stack)<1:returnFalseelement=stack.pop()h160=hash160(element)stack.append(h160)returnTrue
Write the op_checksig function in op.py
defop_checksig(stack,z):iflen(stack)<2:returnFalsesec_pubkey=stack.pop()der_signature=stack.pop()[:-1]try:point=S256Point.parse(sec_pubkey)sig=Signature.parse(der_signature)except(ValueError,SyntaxError)ase:returnFalseifpoint.verify(z,sig):stack.append(encode_num(1))else:stack.append(encode_num(0))returnTrue
Create a ScriptSig that can unlock this ScriptPubKey. Note OP_MUL multiplies the top two elements of the stack.
767695935687
56 = OP_6
76 = OP_DUP
87 = OP_EQUAL
93 = OP_ADD
95 = OP_MUL
>>>fromscriptimportScript>>>script_pubkey=Script([0x76,0x76,0x95,0x93,0x56,0x87])>>>script_sig=Script([0x52])>>>combined_script=script_sig+script_pubkey>>>(combined_script.evaluate(0))True
OP_2 or 52 will satisfy the equation x2+x-6=0.
Figure out what this Script is doing:
6e879169a77ca787
69 = OP_VERIFY
6e = OP_2DUP
7c = OP_SWAP
87 = OP_EQUAL
91 = OP_NOT
a7 = OP_SHA1
Use the Script.parse method and look up what various opcodes do at https://en.bitcoin.it/wiki/Script
>>>fromscriptimportScript>>>script_pubkey=Script([0x6e,0x87,0x91,0x69,0xa7,0x7c,0xa7,0x87])>>>c1='255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\fe017f46dc93a6b67e013b029aaa1db2560b45ca67d688c7f84b8c4c791fe02b3df614f86db169\0901c56b45c1530afedfb76038e972722fe7ad728f0e4904e046c230570fe9d41398abe12ef5bc\942be33542a4802d98b5d70f2a332ec37fac3514e74ddc0f2cc1a874cd0c78305a215664613097\89606bd0bf3f98cda8044629a1'>>>c2='255044462d312e330a25e2e3cfd30a0a0a312030206f626a0a3c3c2f576964746820\32203020522f4865696768742033203020522f547970652034203020522f537562747970652035\203020522f46696c7465722036203020522f436f6c6f7253706163652037203020522f4c656e67\74682038203020522f42697473506572436f6d706f6e656e7420383e3e0a73747265616d0affd8\fffe00245348412d3120697320646561642121212121852fec092339759c39b1a1c63c4c97e1ff\fe017346dc9166b67e118f029ab621b2560ff9ca67cca8c7f85ba84c79030c2b3de218f86db3a9\0901d5df45c14f26fedfb3dc38e96ac22fe7bd728f0e45bce046d23c570feb141398bb552ef5a0\a82be331fea48037b8b5d71f0e332edf93ac3500eb4ddc0decc1a864790c782c76215660dd3097\91d06bd0af3f98cda4bc4629b1'>>>collision1=bytes.fromhex(c1)>>>collision2=bytes.fromhex(c2)>>>script_sig=Script([collision1,collision2])>>>combined_script=script_sig+script_pubkey>>>(combined_script.evaluate(0))True

collision1 and collision2 are from the sha1 preimages that were found to collide from Google. (https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html)
This is looking for a sha1 Collision. The only way to satisfy this script is to give x and y such that x≠y but sha1(x)=sha1(y).
Write the sig_hash method for the Tx class.
classTx:...defsig_hash(self,input_index):s=int_to_little_endian(self.version,4)s+=encode_varint(len(self.tx_ins))fori,tx_ininenumerate(self.tx_ins):ifi==input_index:s+=TxIn(prev_tx=tx_in.prev_tx,prev_index=tx_in.prev_index,script_sig=tx_in.script_pubkey(self.testnet),sequence=tx_in.sequence,).serialize()else:s+=TxIn(prev_tx=tx_in.prev_tx,prev_index=tx_in.prev_index,sequence=tx_in.sequence,).serialize()s+=encode_varint(len(self.tx_outs))fortx_outinself.tx_outs:s+=tx_out.serialize()s+=int_to_little_endian(self.locktime,4)s+=int_to_little_endian(SIGHASH_ALL,4)h256=hash256(s)returnint.from_bytes(h256,'big')
Write the verify_input method for the Tx class. You will want to use the TxIn.script_pubkey(), Tx.sig_hash() and Script.evaluate() methods.
classTx:...defverify_input(self,input_index):tx_in=self.tx_ins[input_index]script_pubkey=tx_in.script_pubkey(testnet=self.testnet)z=self.sig_hash(input_index)combined=tx_in.script_sig+script_pubkeyreturncombined.evaluate(z)
Write the sign_input method for the Tx class.
classTx:...defsign_input(self,input_index,private_key):z=self.sig_hash(input_index)der=private_key.sign(z).der()sig=der+SIGHASH_ALL.to_bytes(1,'big')sec=private_key.point.sec()self.tx_ins[input_index].script_sig=Script([sig,sec])returnself.verify_input(input_index)
Create a testnet transaction that sends 60% of a single UTXO to mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv. The remaining amount minus fees should go back to your own change address. This should be a 1 input, 2 output transaction.
You can broadcast the transaction at https://testnet.blockchain.info/pushtx
>>>fromeccimportPrivateKey>>>fromhelperimportdecode_base58,SIGHASH_ALL>>>fromscriptimportp2pkh_script,Script>>>fromtximportTxIn,TxOut,Tx>>>prev_tx=bytes.fromhex('75a1c4bc671f55f626dda1074c7725991e6f68b8fcefcfca7\b64405ca3b45f1c')>>>prev_index=1>>>target_address='miKegze5FQNCnGw6PKyqUbYUeBa4x2hFeM'>>>target_amount=0.01>>>change_address='mzx5YhAH9kNHtcN481u6WkjeHjYtVeKVh2'>>>change_amount=0.009>>>secret=8675309>>>priv=PrivateKey(secret=secret)>>>tx_ins=[]>>>tx_ins.append(TxIn(prev_tx,prev_index))>>>tx_outs=[]>>>h160=decode_base58(target_address)>>>script_pubkey=p2pkh_script(h160)>>>target_satoshis=int(target_amount*100000000)>>>tx_outs.append(TxOut(target_satoshis,script_pubkey))>>>h160=decode_base58(change_address)>>>script_pubkey=p2pkh_script(h160)>>>change_satoshis=int(change_amount*100000000)>>>tx_outs.append(TxOut(change_satoshis,script_pubkey))>>>tx_obj=Tx(1,tx_ins,tx_outs,0,testnet=True)>>>(tx_obj.sign_input(0,priv))True>>>(tx_obj.serialize().hex())01000000011c5fb4a35c40647bcacfeffcb8686f1e9925774c07a1dd26f6551f67bcc4a1750100\00006b483045022100a08ebb92422b3599a2d2fcdaa11f8f807a66ccf33e7f4a9ff0a3c51f1b1e\c5dd02205ed21dfede5925362b8d9833e908646c54be7ac6664e31650159e8f69b6ca539012103\935581e52c354cd2f484fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67ffffffff024042\0f00000000001976a9141ec51b3654c1f1d0f4929d11a1f702937eaf50c888ac9fbb0d00000000\001976a914d52ad7ca9b3d096a38e752c2018e6fbc40cdf26f88ac00000000
Advanced: get some more testnet coins from a testnet faucet and create a 2 input, 1 output transaction. 1 input should be from the faucet, the other should be from the previous exercise, the output can be your own address.
You can broadcast the transaction at https://testnet.blockchain.info/pushtx
>>>fromeccimportPrivateKey>>>fromhelperimportdecode_base58,SIGHASH_ALL>>>fromscriptimportp2pkh_script,Script>>>fromtximportTxIn,TxOut,Tx>>>prev_tx_1=bytes.fromhex('11d05ce707c1120248370d1cbf5561d22c4f83aeba04367\92c82e0bd57fe2a2f')>>>prev_index_1=1>>>prev_tx_2=bytes.fromhex('51f61f77bd061b9a0da60d4bedaaf1b1fad0c11e65fdc74\4797ee22d20b03d15')>>>prev_index_2=1>>>target_address='mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'>>>target_amount=0.0429>>>secret=8675309>>>priv=PrivateKey(secret=secret)>>>tx_ins=[]>>>tx_ins.append(TxIn(prev_tx_1,prev_index_1))>>>tx_ins.append(TxIn(prev_tx_2,prev_index_2))>>>tx_outs=[]>>>h160=decode_base58(target_address)>>>script_pubkey=p2pkh_script(h160)>>>target_satoshis=int(target_amount*100000000)>>>tx_outs.append(TxOut(target_satoshis,script_pubkey))>>>tx_obj=Tx(1,tx_ins,tx_outs,0,testnet=True)>>>(tx_obj.sign_input(0,priv))True>>>(tx_obj.sign_input(1,priv))True>>>(tx_obj.serialize().hex())01000000022f2afe57bde0822c793604baae834f2cd26155bf1c0d37480212c107e75cd0110100\00006a47304402204cc5fe11b2b025f8fc9f6073b5e3942883bbba266b71751068badeb8f11f03\64022070178363f5dea4149581a4b9b9dbad91ec1fd990e3fa14f9de3ccb421fa5b26901210393\5581e52c354cd2f484fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67ffffffff153db020\2de27e7944c7fd651ec1d0fab1f1aaed4b0da60d9a1b06bd771ff651010000006b483045022100\b7a938d4679aa7271f0d32d83b61a85eb0180cf1261d44feaad23dfd9799dafb02205ff2f366dd\d9555f7146861a8298b7636be8b292090a224c5dc84268480d8be1012103935581e52c354cd2f4\84fe8ed83af7a3097005b2f9c60bff71d35bd795f54b67ffffffff01d0754100000000001976a9\14ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000
Write the op_checkmultisig function of op.py.
defop_checkmultisig(stack,z):iflen(stack)<1:returnFalsen=decode_num(stack.pop())iflen(stack)<n+1:returnFalsesec_pubkeys=[]for_inrange(n):sec_pubkeys.append(stack.pop())m=decode_num(stack.pop())iflen(stack)<m+1:returnFalseder_signatures=[]for_inrange(m):der_signatures.append(stack.pop()[:-1])stack.pop()try:points=[S256Point.parse(sec)forsecinsec_pubkeys]sigs=[Signature.parse(der)forderinder_signatures]forsiginsigs:iflen(points)==0:returnFalsewhilepoints:point=points.pop(0)ifpoint.verify(z,sig):breakstack.append(encode_num(1))except(ValueError,SyntaxError):returnFalsereturnTrue
Write h160_to_p2pkh_address that converts a 20-byte hash160 into a p2pkh address.
defh160_to_p2pkh_address(h160,testnet=False):iftestnet:prefix=b'\x6f'else:prefix=b'\x00'returnencode_base58_checksum(prefix+h160)
Write h160_to_p2sh_address that converts a 20-byte hash160 into a p2sh address.
defh160_to_p2sh_address(h160,testnet=False):iftestnet:prefix=b'\xc4'else:prefix=b'\x05'returnencode_base58_checksum(prefix+h160)
Validate the second signature from the transaction above.
>>>fromioimportBytesIO>>>fromeccimportS256Point,Signature>>>fromhelperimporthash256,int_to_little_endian>>>fromscriptimportScript>>>fromtximportTx,SIGHASH_ALL>>>hex_tx='0100000001868278ed6ddfb6c1ed3ad5f8181eb0c7a385aa0836f01d5e4789e6\bd304d87221a000000db00483045022100dc92655fe37036f47756db8102e0d7d5e28b3beb83a8\fef4f5dc0559bddfb94e02205a36d4e4e6c7fcd16658c50783e00c341609977aed3ad00937bf4e\e942a8993701483045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8eef\53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e754022\01475221022626e955ea6ea6d98850c994f9107b036b1334f18ca8830bfff1295d21cfdb702103\b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4bb7152aeffffffff04\d3b11400000000001976a914904a49878c0adfc3aa05de7afad2cc15f483a56a88ac7f40090000\0000001976a914418327e3f3dda4cf5b9089325a4b95abdfa0334088ac722c0c00000000001976\a914ba35042cfe9fc66fd35ac2224eebdafd1028ad2788acdc4ace020000000017a91474d691da\1574e6b3c192ecfb52cc8984ee7b6c568700000000'>>>hex_sec='03b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7fbdbd4b\b71'>>>hex_der='3045022100da6bee3c93766232079a01639d07fa869598749729ae323eab8ee\f53577d611b02207bef15429dcadce2121ea07f233115c6f09034c0be68db99980b9a6c5e75402\2'>>>hex_redeem_script='475221022626e955ea6ea6d98850c994f9107b036b1334f18ca88\30bfff1295d21cfdb702103b287eaf122eea69030a0e9feed096bed8045c8b98bec453e1ffac7f\bdbd4bb7152ae'>>>sec=bytes.fromhex(hex_sec)>>>der=bytes.fromhex(hex_der)>>>redeem_script=Script.parse(BytesIO(bytes.fromhex(hex_redeem_script)))>>>stream=BytesIO(bytes.fromhex(hex_tx))>>>tx_obj=Tx.parse(stream)>>>s=int_to_little_endian(tx_obj.version,4)>>>s+=encode_varint(len(tx_obj.tx_ins))>>>i=tx_obj.tx_ins[0]>>>s+=TxIn(i.prev_tx,i.prev_index,redeem_script,i.sequence).serialize()>>>s+=encode_varint(len(tx_obj.tx_outs))>>>fortx_outintx_obj.tx_outs:...s+=tx_out.serialize()>>>s+=int_to_little_endian(tx_obj.locktime,4)>>>s+=int_to_little_endian(SIGHASH_ALL,4)>>>z=int.from_bytes(hash256(s),'big')>>>point=S256Point.parse(sec)>>>sig=Signature.parse(der)>>>(point.verify(z,sig))True
Modify the sig_hash and verify_input methods to be able to verify p2sh transactions.
classTx:...defsig_hash(self,input_index,redeem_script=None):'''Returns the integer representation of the hash that needs to getsigned for index input_index'''s=int_to_little_endian(self.version,4)s+=encode_varint(len(self.tx_ins))fori,tx_ininenumerate(self.tx_ins):ifi==input_index:ifredeem_script:script_sig=redeem_scriptelse:script_sig=tx_in.script_pubkey(self.testnet)else:script_sig=Nones+=TxIn(prev_tx=tx_in.prev_tx,prev_index=tx_in.prev_index,script_sig=script_sig,sequence=tx_in.sequence,).serialize()s+=encode_varint(len(self.tx_outs))fortx_outinself.tx_outs:s+=tx_out.serialize()s+=int_to_little_endian(self.locktime,4)s+=int_to_little_endian(SIGHASH_ALL,4)h256=hash256(s)returnint.from_bytes(h256,'big')defverify_input(self,input_index):tx_in=self.tx_ins[input_index]script_pubkey=tx_in.script_pubkey(testnet=self.testnet)ifscript_pubkey.is_p2sh_script_pubkey():instruction=tx_in.script_sig.instructions[-1]raw_redeem=encode_varint(len(instruction))+instructionredeem_script=Script.parse(BytesIO(raw_redeem))else:redeem_script=Nonez=self.sig_hash(input_index,redeem_script)combined=tx_in.script_sig+script_pubkeyreturncombined.evaluate(z)
Write the is_coinbase method of the Tx class.
classTx:...defis_coinbase(self):iflen(self.tx_ins)!=1:returnFalsefirst_input=self.tx_ins[0]iffirst_input.prev_tx!=b'\x00'*32:returnFalseiffirst_input.prev_index!=0xffffffff:returnFalsereturnTrue
Write the coinbase_height method for the Tx class.
classTx:...defcoinbase_height(self):ifnotself.is_coinbase():returnNoneelement=self.tx_ins[0].script_sig.instructions[0]returnlittle_endian_to_int(element)
Write the parse for Block.
classBlock:...@classmethoddefparse(cls,s):version=little_endian_to_int(s.read(4))prev_block=s.read(32)[::-1]merkle_root=s.read(32)[::-1]timestamp=little_endian_to_int(s.read(4))bits=s.read(4)nonce=s.read(4)returncls(version,prev_block,merkle_root,timestamp,bits,nonce)
Write the serialize for Block.
classBlock:...defserialize(self):result=int_to_little_endian(self.version,4)result+=self.prev_block[::-1]result+=self.merkle_root[::-1]result+=int_to_little_endian(self.timestamp,4)result+=self.bitsresult+=self.noncereturnresult
Write the hash for Block.
classBlock:...defhash(self):s=self.serialize()sha=hash256(s)returnsha[::-1]
Write the bip9 method for the Block class.
classBlock:...defbip9(self):returnself.version>>29==0b001
Write the bip91 method for the Block class.
classBlock:...defbip91(self):returnself.version>>4&1==1
Write the bip141 method for the Block class.
classBlock:...defbip141(self):returnself.version>>1&1==1
Write the bits_to_target function in helper.py.
defbits_to_target(bits):exponent=bits[-1]coefficient=little_endian_to_int(bits[:-1])returncoefficient*256**(exponent-3)
Write the difficulty method for Block
classBlock:...defdifficulty(self):lowest=0xffff*256**(0x1d-3)returnlowest/self.target()
Write the check_pow method for Block.
classBlock:...defcheck_pow(self):sha=hash256(self.serialize())proof=little_endian_to_int(sha)returnproof<self.target()
Calculate the new bits given the first and last blocks of this 2016 block difficulty adjustment period:
Block 471744:
000000203471101bbda3fe307664b3283a9ef0e97d9a38a7eacd8800000000000000000010c8ab a8479bbaa5e0848152fd3c2289ca50e1c3e58c9a4faaafbdf5803c5448ddb845597e8b0118e43a 81d3
Block 473759:
02000020f1472d9db4b563c35f97c428ac903f23b7fc055d1cfc26000000000000000000b3f449 fcbe1bc4cfbcb8283a0d2c037f961a3fdf2b8bedc144973735eea707e1264258597e8b0118e5f0 0474
>>>fromioimportBytesIO>>>fromblockimportBlock>>>fromhelperimportTWO_WEEKS>>>fromhelperimporttarget_to_bits>>>block1_hex='000000203471101bbda3fe307664b3283a9ef0e97d9a38a7eacd88000000\00000000000010c8aba8479bbaa5e0848152fd3c2289ca50e1c3e58c9a4faaafbdf5803c5448dd\b845597e8b0118e43a81d3'>>>block2_hex='02000020f1472d9db4b563c35f97c428ac903f23b7fc055d1cfc26000000\000000000000b3f449fcbe1bc4cfbcb8283a0d2c037f961a3fdf2b8bedc144973735eea707e126\4258597e8b0118e5f00474'>>>last_block=Block.parse(BytesIO(bytes.fromhex(block1_hex)))>>>first_block=Block.parse(BytesIO(bytes.fromhex(block2_hex)))>>>time_differential=last_block.timestamp-first_block.timestamp>>>iftime_differential>TWO_WEEKS*4:...time_differential=TWO_WEEKS*4>>>iftime_differential<TWO_WEEKS//4:...time_differential=TWO_WEEKS//4>>>new_target=last_block.target()*time_differential//TWO_WEEKS>>>new_bits=target_to_bits(new_target)>>>(new_bits.hex())80df6217
Write the calculate_new_bits function in helper.py
defcalculate_new_bits(previous_bits,time_differential):iftime_differential>TWO_WEEKS*4:time_differential=TWO_WEEKS*4iftime_differential<TWO_WEEKS//4:time_differential=TWO_WEEKS//4new_target=bits_to_target(previous_bits)*time_differential//TWO_WEEKSreturntarget_to_bits(new_target)
Determine what this network message is:
f9beb4d976657261636b000000000000000000005df6e0e2
>>>fromnetworkimportNetworkEnvelope>>>fromioimportBytesIO>>>message_hex='f9beb4d976657261636b000000000000000000005df6e0e2'>>>stream=BytesIO(bytes.fromhex(message_hex))>>>envelope=NetworkEnvelope.parse(stream)>>>(envelope.command)b'verack'>>>(envelope.payload)b''
Write the parse method for NetworkEnvelope.
classNetworkEnvelope:...@classmethoddefparse(cls,s,testnet=False):magic=s.read(4)ifmagic==b'':raiseIOError('Connection reset!')iftestnet:expected_magic=TESTNET_NETWORK_MAGICelse:expected_magic=NETWORK_MAGICifmagic!=expected_magic:raiseSyntaxError('magic is not right {} vs {}'.format(magic.hex(),expected_magic.hex()))command=s.read(12)command=command.strip(b'\x00')payload_length=little_endian_to_int(s.read(4))checksum=s.read(4)payload=s.read(payload_length)calculated_checksum=hash256(payload)[:4]ifcalculated_checksum!=checksum:raiseIOError('checksum does not match')returncls(command,payload,testnet=testnet)
Write the serialize method for NetworkEnvelope.
classNetworkEnvelope:...defserialize(self):result=self.magicresult+=self.command+b'\x00'*(12-len(self.command))result+=int_to_little_endian(len(self.payload),4)result+=hash256(self.payload)[:4]result+=self.payloadreturnresult
Write the serialize method for VersionMessage.
classVersionMessage:...defserialize(self):result=int_to_little_endian(self.version,4)result+=int_to_little_endian(self.services,8)result+=int_to_little_endian(self.timestamp,8)result+=int_to_little_endian(self.receiver_services,8)result+=b'\x00'*10+b'\xff\xff'+self.receiver_ipresult+=int_to_little_endian(self.receiver_port,2)result+=int_to_little_endian(self.sender_services,8)result+=b'\x00'*10+b'\xff\xff'+self.sender_ipresult+=int_to_little_endian(self.sender_port,2)result+=self.nonceresult+=encode_varint(len(self.user_agent))result+=self.user_agentresult+=int_to_little_endian(self.latest_block,4)ifself.relay:result+=b'\x01'else:result+=b'\x00'returnresult
Write the handshake method for SimpleNode
classSimpleNode:...defhandshake(self):version=VersionMessage()self.send(version)self.wait_for(VerAckMessage)
Write the serialize method for GetHeadersMessage.
classGetHeadersMessage:...defserialize(self):result=int_to_little_endian(self.version,4)result+=encode_varint(self.num_hashes)result+=self.start_block[::-1]result+=self.end_block[::-1]returnresult
Write the merkle_parent function.
defmerkle_parent(hash1,hash2):'''Takes the binary hashes and calculates the hash256'''returnhash256(hash1+hash2)
Write the merkle_parent_level function.
defmerkle_parent_level(hashes):'''Takes a list of binary hashes and returns a list that's halfthe length'''iflen(hashes)==1:raiseRuntimeError('Cannot take a parent level with only 1 item')iflen(hashes)%2==1:hashes.append(hashes[-1])parent_level=[]foriinrange(0,len(hashes),2):parent=merkle_parent(hashes[i],hashes[i+1])parent_level.append(parent)returnparent_level
Write the merkle_root function.
defmerkle_root(hashes):'''Takes a list of binary hashes and returns the merkle root'''current_level=hasheswhilelen(current_level)>1:current_level=merkle_parent_level(current_level)returncurrent_level[0]
Write the validate_merkle_root method for Block.
classBlock:...defvalidate_merkle_root(self):hashes=[h[::-1]forhinself.tx_hashes]root=merkle_root(hashes)returnroot[::-1]==self.merkle_root
Create an empty Merkle Tree with 27 items and print each level.
>>>importmath>>>total=27>>>max_depth=math.ceil(math.log(total,2))>>>merkle_tree=[]>>>fordepthinrange(max_depth+1):...num_items=math.ceil(total/2**(max_depth-depth))...level_hashes=[None]*num_items...merkle_tree.append(level_hashes)>>>forlevelinmerkle_tree:...(level)[None][None,None][None,None,None,None][None,None,None,None,None,None,None][None,None,None,None,None,None,None,None,None,None,None,None,None,\None][None,None,None,None,None,None,None,None,None,None,None,None,None,\None,None,None,None,None,None,None,None,None,None,None,None,None,\None]
Write the parse method for MerkleBlock.
classMerkleBlock:...@classmethoddefparse(cls,s):version=little_endian_to_int(s.read(4))prev_block=s.read(32)[::-1]merkle_root=s.read(32)[::-1]timestamp=little_endian_to_int(s.read(4))bits=s.read(4)nonce=s.read(4)total=little_endian_to_int(s.read(4))num_txs=read_varint(s)hashes=[]for_inrange(num_txs):hashes.append(s.read(32)[::-1])flags_length=read_varint(s)flags=s.read(flags_length)returncls(version,prev_block,merkle_root,timestamp,bits,nonce,total,hashes,flags)
Write the is_valid method for MerkleBlock
classMerkleBlock:...defis_valid(self):flag_bits=bytes_to_bit_field(self.flags)hashes=[h[::-1]forhinself.hashes]merkle_tree=MerkleTree(self.total)merkle_tree.populate_tree(flag_bits,hashes)returnmerkle_tree.root()[::-1]==self.merkle_root
Calculate the Bloom Filter for hello world and goodbye using the hash160 hash function over a bit field of 10.
>>>fromhelperimporthash160>>>bit_field_size=10>>>bit_field=[0]*bit_field_size>>>foritemin(b'hello world',b'goodbye'):...h=hash160(item)...bit=int.from_bytes(h,'big')%bit_field_size...bit_field[bit]=1>>>(bit_field)[1,1,0,0,0,0,0,0,0,0]
Given a Bloom Filter with size=10, function count=5, tweak=99, what are the bytes that are set after adding these items? (use bit_field_to_bytes to convert to bytes)
b'Hello World'
b'Goodbye!'
>>>frombloomfilterimportBloomFilter,BIP37_CONSTANT>>>fromhelperimportbit_field_to_bytes,murmur3>>>field_size=10>>>function_count=5>>>tweak=99>>>items=(b'Hello World',b'Goodbye!')>>>bit_field_size=field_size*8>>>bit_field=[0]*bit_field_size>>>foriteminitems:...foriinrange(function_count):...seed=i*BIP37_CONSTANT+tweak...h=murmur3(item,seed=seed)...bit=h%bit_field_size...bit_field[bit]=1>>>(bit_field_to_bytes(bit_field).hex())4000600a080000010940
Write the add method for BloomFilter
classBloomFilter:...defadd(self,item):foriinrange(self.function_count):seed=i*BIP37_CONSTANT+self.tweakh=murmur3(item,seed=seed)bit=h%(self.size*8)self.bit_field[bit]=1
Write the filterload payload from the BloomFilter class.
classBloomFilter:...deffilterload(self,flag=1):payload=encode_varint(self.size)payload+=self.filter_bytes()payload+=int_to_little_endian(self.function_count,4)payload+=int_to_little_endian(self.tweak,4)payload+=int_to_little_endian(flag,1)returnGenericMessage(b'filterload',payload)
Write the serialize method for the GetDataMessage class.
classGetDataMessage:...defserialize(self):result=encode_varint(len(self.data))fordata_type,identifierinself.data:result+=int_to_little_endian(data_type,4)result+=identifier[::-1]returnresult
Get the current testnet block ID, send yourself some testnet coins, find the UTXO corresponding to the testnet coin without using a block explorer, create a transaction using that UTXO as an input and broadcast the tx message on the testnet network.
>>>importtime>>>fromblockimportBlock>>>frombloomfilterimportBloomFilter>>>fromeccimportPrivateKey>>>fromhelperimport(...decode_base58,...encode_varint,...hash256,...little_endian_to_int,...read_varint,...)>>>frommerkleblockimportMerkleBlock>>>fromnetworkimport(...GetDataMessage,...GetHeadersMessage,...HeadersMessage,...NetworkEnvelope,...SimpleNode,...TX_DATA_TYPE,...FILTERED_BLOCK_DATA_TYPE,...)>>>fromscriptimportp2pkh_script,Script>>>fromtximportTx,TxIn,TxOut>>>last_block_hex='00000000000000a03f9432ac63813c6710bfe41712ac5ef6faab093f\e2917636'>>>secret=little_endian_to_int(hash256(b'Jimmy Song'))>>>private_key=PrivateKey(secret=secret)>>>addr=private_key.point.address(testnet=True)>>>h160=decode_base58(addr)>>>target_address='mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'>>>target_h160=decode_base58(target_address)>>>target_script=p2pkh_script(target_h160)>>>fee=5000# fee in satoshis>>># connect to testnet.programmingbitcoin.com in testnet mode>>>node=SimpleNode('testnet.programmingbitcoin.com',testnet=True,logging=\False)>>># create a bloom filter of size 30 and 5 functions. Add a tweak.>>>bf=BloomFilter(30,5,90210)>>># add the h160 to the bloom filter>>>bf.add(h160)>>># complete the handshake>>>node.handshake()>>># load the bloom filter with the filterload command>>>node.send(bf.filterload())>>># set start block to last_block from above>>>start_block=bytes.fromhex(last_block_hex)>>># send a getheaders message with the starting block>>>getheaders=GetHeadersMessage(start_block=start_block)>>>node.send(getheaders)>>># wait for the headers message>>>headers=node.wait_for(HeadersMessage)>>># store the last block as None>>>last_block=None>>># initialize the GetDataMessage>>>getdata=GetDataMessage()>>># loop through the blocks in the headers>>>forbinheaders.blocks:...# check that the proof of work on the block is valid...ifnotb.check_pow():...raiseRuntimeError('proof of work is invalid')...# check that this block's prev_block is the last block...iflast_blockisnotNoneandb.prev_block!=last_block:...raiseRuntimeError('chain broken')...# add a new item to the getdata message...# should be FILTERED_BLOCK_DATA_TYPE and block hash...getdata.add_data(FILTERED_BLOCK_DATA_TYPE,b.hash())...# set the last block to the current hash...last_block=b.hash()>>># send the getdata message>>>node.send(getdata)>>># initialize prev_tx, prev_index and prev_amount to None>>>prev_tx,prev_index,prev_amount=None,None,None>>># loop while prev_tx is None>>>whileprev_txisNone:...# wait for the merkleblock or tx commands...message=node.wait_for(MerkleBlock,Tx)...# if we have the merkleblock command...ifmessage.command==b'merkleblock':...# check that the MerkleBlock is valid...ifnotmessage.is_valid():...raiseRuntimeError('invalid merkle proof')...# else we have the tx command...else:...# set the tx's testnet to be True...message.testnet=True...# loop through the tx outs...fori,tx_outinenumerate(message.tx_outs):...# if our output has the same address as our address we found it...iftx_out.script_pubkey.address(testnet=True)==addr:...# we found our utxo. set prev_tx, prev_index, and tx...prev_tx=message.hash()...prev_index=i...prev_amount=tx_out.amount...('found: {}:{}'.format(prev_tx.hex(),prev_index))found:b2cddd41d18d00910f88c31aa58c6816a190b8fc30fe7c665e1cd2ec60efdf3f:7>>># create the TxIn>>>tx_in=TxIn(prev_tx,prev_index)>>># calculate the output amount (previous amount minus the fee)>>>output_amount=prev_amount-fee>>># create a new TxOut to the target script with the output amount>>>tx_out=TxOut(output_amount,target_script)>>># create a new transaction with the one input and one output>>>tx_obj=Tx(1,[tx_in],[tx_out],0,testnet=True)>>># sign the only input of the transaction>>>(tx_obj.sign_input(0,private_key))True>>># serialize and hex to see what it looks like>>>(tx_obj.serialize().hex())01000000013fdfef60ecd21c5e667cfe30fcb890a116688ca51ac3880f91008dd141ddcdb20700\00006b483045022100ff77d2559261df5490ed00d231099c4b8ea867e6ccfe8e3e6d077313ed4f\1428022033a1db8d69eb0dc376f89684d1ed1be75719888090388a16f1e8eedeb8067768012103\dc585d46cfca73f3a75ba1ef0c5756a21c1924587480700c6eb64e3f75d22083ffffffff019334\e500000000001976a914ad346f8eb57dee9a37981716e498120ae80e44f788ac00000000>>># send this signed transaction on the network>>>node.send(tx_obj)>>># wait a sec so this message goes through with time.sleep(1)>>>time.sleep(1)>>># now ask for this transaction from the other node>>># create a GetDataMessage>>>getdata=GetDataMessage()>>># ask for our transaction by adding it to the message>>>getdata.add_data(TX_DATA_TYPE,tx_obj.hash())>>># send the message>>>node.send(getdata)>>># now wait for a Tx response>>>received_tx=node.wait_for(Tx)>>># if the received tx has the same id as our tx, we are done!>>>ifreceived_tx.id()==tx_obj.id():...('success!')success!