Skip to content

Commit ef10197

Browse files
ellemoutonjnewbery
authored andcommitted
Upgrade notebook 3.1 to use Bitcoin Core v0.21
1 parent 6307e3e commit ef10197

File tree

3 files changed

+72
-28
lines changed

3 files changed

+72
-28
lines changed

3.1-degrading-multisig-case-study.ipynb

+41-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"import random\n",
1111
"\n",
1212
"import util\n",
13-
"from test_framework.key import generate_key_pair, generate_schnorr_nonce, ECKey, ECPubKey, SECP256K1_FIELD_SIZE\n",
13+
"from test_framework.key import generate_key_pair, generate_bip340_key_pair, generate_schnorr_nonce, ECKey, ECPubKey, SECP256K1_FIELD_SIZE, SECP256K1, SECP256K1_ORDER\n",
1414
"from test_framework.musig import aggregate_musig_signatures, aggregate_schnorr_nonces, generate_musig_key, musig_digest, sign_musig\n",
1515
"from test_framework.script import TapLeaf, TapTree, TaprootSignatureHash, SIGHASH_ALL_TAPROOT\n",
1616
"from test_framework.address import program_to_witness\n",
@@ -104,18 +104,18 @@
104104
"outputs": [],
105105
"source": [
106106
"# Generate main wallet key pairs\n",
107-
"main_privkeyA, main_pubkeyA = generate_key_pair()\n",
108-
"main_privkeyB, main_pubkeyB = generate_key_pair()\n",
109-
"main_privkeyC, main_pubkeyC = generate_key_pair()\n",
107+
"main_privkeyA, main_pubkeyA = generate_bip340_key_pair()\n",
108+
"main_privkeyB, main_pubkeyB = generate_bip340_key_pair()\n",
109+
"main_privkeyC, main_pubkeyC = generate_bip340_key_pair()\n",
110110
"main_pubkeys = [main_pubkeyA.get_bytes().hex(),\n",
111111
" main_pubkeyB.get_bytes().hex(), \n",
112112
" main_pubkeyC.get_bytes().hex()]\n",
113113
"\n",
114114
"print(\"Main pubkeys: {}\\n\".format(main_pubkeys))\n",
115115
"\n",
116116
"# Generate back-up wallet key pairs\n",
117-
"backup_privkeyD, backup_pubkeyD = generate_key_pair()\n",
118-
"backup_privkeyE, backup_pubkeyE = generate_key_pair()\n",
117+
"backup_privkeyD, backup_pubkeyD = generate_bip340_key_pair()\n",
118+
"backup_privkeyE, backup_pubkeyE = generate_bip340_key_pair()\n",
119119
"backup_pubkeys = [backup_pubkeyD.get_bytes().hex(),\n",
120120
" backup_pubkeyE.get_bytes().hex()]\n",
121121
"\n",
@@ -130,6 +130,15 @@
130130
"main_pubkeyB_c = main_pubkeyA.mul(c_map[main_pubkeyB])\n",
131131
"main_pubkeyC_c = main_pubkeyA.mul(c_map[main_pubkeyC])\n",
132132
"\n",
133+
"if musig_ABC.get_y()%2 != 0:\n",
134+
" musig_ABC.negate()\n",
135+
" main_privkeyA_c.negate()\n",
136+
" main_privkeyB_c.negate()\n",
137+
" main_privkeyC_c.negate()\n",
138+
" main_pubkeyA_c.negate()\n",
139+
" main_pubkeyB_c.negate()\n",
140+
" main_pubkeyC_c.negate()\n",
141+
"\n",
133142
"print(\"MuSig pubkey: {}\".format(musig_ABC.get_bytes().hex()))"
134143
]
135144
},
@@ -180,7 +189,6 @@
180189
"taptweak = int.from_bytes(taptweak, 'big')\n",
181190
"output_pubkey = musig_ABC.tweak_add(taptweak)\n",
182191
"output_pubkey_b = output_pubkey.get_bytes()\n",
183-
"taproot_pubkey_v1 = bytes([output_pubkey_b[0] & 1]) + output_pubkey_b[1:]\n",
184192
"segwit_address = # TODO: implement\n",
185193
"print(\"Segwit Address:\", segwit_address)"
186194
]
@@ -234,7 +242,7 @@
234242
"outputs": [],
235243
"source": [
236244
"# Send funds to taproot output.\n",
237-
"txid = test.nodes[0].sendtoaddress(segwit_address, 0.5)\n",
245+
"txid = test.nodes[0].sendtoaddress(address=segwit_address, amount=0.5, fee_rate=25)\n",
238246
"print(\"Funding tx:\", txid)\n",
239247
"\n",
240248
"# Deserialize wallet transaction.\n",
@@ -243,6 +251,10 @@
243251
"tx.deserialize(BytesIO(bytes.fromhex(tx_hex)))\n",
244252
"tx.rehash()\n",
245253
"\n",
254+
"print(tapscript.hex())\n",
255+
"\n",
256+
"print(tx.vout)\n",
257+
"\n",
246258
"# The wallet randomizes the change output index for privacy\n",
247259
"# Loop through the outputs and return the first where the scriptPubKey matches the segwit v1 output\n",
248260
"output_index, output = next(out for out in enumerate(tx.vout) if out[1].scriptPubKey == tapscript)\n",
@@ -333,7 +345,7 @@
333345
"source": [
334346
"#### 3.1.4 _Programming Exercise:_ Create a valid key path output\n",
335347
"\n",
336-
"In this exercise, we'll spend the taproot output using the key path."
348+
"In this exercise, we'll spend the taproot output using the key path. Since the key path is used, there is no control block to indicate whether or not the public key (Q) has an even or odd y-coordinate and so it is assumed that the y-coordinate is odd. Therefore, if Q needs to be negated, then so do all the private keys as well as the tweak."
337349
]
338350
},
339351
{
@@ -342,6 +354,20 @@
342354
"metadata": {},
343355
"outputs": [],
344356
"source": [
357+
"# Negate keys if necessary\n",
358+
"output_keyPath = output_pubkey\n",
359+
"privKeyA_keyPath = main_privkeyA_c\n",
360+
"privKeyB_keyPath = main_privkeyB_c\n",
361+
"privKeyC_keyPath = main_privkeyC_c\n",
362+
"tweak_keyPath = taptweak\n",
363+
"\n",
364+
"if output_keyPath.get_y()%2 != 0:\n",
365+
" output_keyPath.negate()\n",
366+
" privKeyA_keyPath.negate()\n",
367+
" privKeyB_keyPath.negate()\n",
368+
" privKeyC_keyPath.negate()\n",
369+
" tweak_keyPath = SECP256K1_ORDER - taptweak\n",
370+
"\n",
345371
"# Create sighash for ALL\n",
346372
"sighash_musig = # TODO: implement\n",
347373
" \n",
@@ -354,7 +380,7 @@
354380
"sig_agg = # TODO: implement\n",
355381
"print(\"Aggregate signature is {}\\n\".format(sig_agg.hex()))\n",
356382
"\n",
357-
"assert output_pubkey.verify_schnorr(sig_agg, sighash_musig)\n",
383+
"assert output_keyPath.verify_schnorr(sig_agg, sighash_musig)\n",
358384
"\n",
359385
"# Construct transaction witness\n",
360386
"spending_tx.wit.vtxinwit.append( # TODO: implement\n",
@@ -395,7 +421,7 @@
395421
"spending_tx.vin = [spending_tx_in]\n",
396422
"spending_tx.vout = [dest_output]\n",
397423
"\n",
398-
"sighash = TaprootSignatureHash(spending_tx, [output], SIGHASH_ALL_TAPROOT, 0, scriptpath=True, tapscript=tapscript_2a.script)\n",
424+
"sighash = TaprootSignatureHash(spending_tx, [output], SIGHASH_ALL_TAPROOT, 0, scriptpath=True, script=tapscript_2a.script)\n",
399425
"\n",
400426
"witness_elements = []\n",
401427
"\n",
@@ -412,7 +438,7 @@
412438
"\n",
413439
"# Test timelock\n",
414440
"assert_equal(\n",
415-
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],\n",
441+
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final'}],\n",
416442
" test.nodes[0].testmempoolaccept([spending_tx_str])\n",
417443
")\n",
418444
"\n",
@@ -474,7 +500,7 @@
474500
"spending_tx.vin = [spending_tx_in]\n",
475501
"spending_tx.vout = [dest_output]\n",
476502
"\n",
477-
"# Derive the sighash. Use tapscript_3a\n",
503+
"# Derive the sighash. Use tapscript_3a.\n",
478504
"sighash = # TODO: implement\n",
479505
"\n",
480506
"witness_elements = []\n",
@@ -483,13 +509,13 @@
483509
"# Remember to reverse the order of signatures\n",
484510
"witness_elements = # TODO: implement\n",
485511
"\n",
486-
"# Add witness to transaction\n",
512+
"# Construct transaction witness\n",
487513
"spending_tx.wit.vtxinwit.append(CTxInWitness(witness_elements))\n",
488514
"spending_tx_str = spending_tx.serialize().hex()\n",
489515
"\n",
490516
"# Test timelock\n",
491517
"assert_equal(\n",
492-
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],\n",
518+
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final'}],\n",
493519
" test.nodes[0].testmempoolaccept([spending_tx_str])\n",
494520
")\n",
495521
"\n",

solutions/3.1-degrading-multisig-case-study-solutions.ipynb

+30-12
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"outputs": [],
5454
"source": [
5555
"# Tapscripts - 2 main keys & 1 backup key\n",
56+
"# Use construct_csa_delay() to construct the tapscript\n",
5657
"delay = 3*24*6\n",
5758
"tapscript_2a = TapLeaf().construct_csa_delay(3, [main_pubkeyA, main_pubkeyB, backup_pubkeyD], delay)\n",
5859
"tapscript_2b = TapLeaf().construct_csa_delay(3, [main_pubkeyA, main_pubkeyC, backup_pubkeyD], delay)\n",
@@ -73,6 +74,8 @@
7374
" tapscript_2d, tapscript_2e, tapscript_2f,\n",
7475
" tapscript_3a, tapscript_3b, tapscript_3c]\n",
7576
"\n",
77+
"assert len(backup_tapscripts) == 9\n",
78+
"\n",
7679
"# Construct taptree with huffman constructor\n",
7780
"tapscript_weights = [(2, tapscript_2a), (2, tapscript_2b), (2, tapscript_2c),\n",
7881
" (2, tapscript_2d), (2, tapscript_2e), (2, tapscript_2f),\n",
@@ -87,8 +90,7 @@
8790
"taptweak = int.from_bytes(taptweak, 'big')\n",
8891
"output_pubkey = musig_ABC.tweak_add(taptweak)\n",
8992
"output_pubkey_b = output_pubkey.get_bytes()\n",
90-
"taproot_pubkey_v1 = bytes([output_pubkey_b[0] & 1]) + output_pubkey_b[1:]\n",
91-
"segwit_address = program_to_witness(1, taproot_pubkey_v1)\n",
93+
"segwit_address = program_to_witness(1, output_pubkey_b)\n",
9294
"print(\"Segwit Address:\", segwit_address)"
9395
]
9496
},
@@ -105,6 +107,20 @@
105107
"metadata": {},
106108
"outputs": [],
107109
"source": [
110+
"# Negate keys if necessary\n",
111+
"output_keyPath = output_pubkey\n",
112+
"privKeyA_keyPath = main_privkeyA_c\n",
113+
"privKeyB_keyPath = main_privkeyB_c\n",
114+
"privKeyC_keyPath = main_privkeyC_c\n",
115+
"tweak_keyPath = taptweak\n",
116+
"\n",
117+
"if output_keyPath.get_y()%2 != 0:\n",
118+
" output_keyPath.negate()\n",
119+
" privKeyA_keyPath.negate()\n",
120+
" privKeyB_keyPath.negate()\n",
121+
" privKeyC_keyPath.negate()\n",
122+
" tweak_keyPath = SECP256K1_ORDER - taptweak\n",
123+
"\n",
108124
"# Create sighash for ALL\n",
109125
"sighash_musig = TaprootSignatureHash(spending_tx, [output], SIGHASH_ALL_TAPROOT)\n",
110126
" \n",
@@ -121,22 +137,23 @@
121137
"\n",
122138
"# Create an aggregate signature.\n",
123139
"# Remember to add a factor for the tweak\n",
124-
"sA = sign_musig(main_privkeyA_c, nonceA, R_agg, output_pubkey, sighash_musig)\n",
125-
"sB = sign_musig(main_privkeyB_c, nonceB, R_agg, output_pubkey, sighash_musig)\n",
126-
"sC = sign_musig(main_privkeyC_c, nonceC, R_agg, output_pubkey, sighash_musig)\n",
127-
"e = musig_digest(R_agg, output_pubkey, sighash_musig)\n",
128-
"sig_agg = aggregate_musig_signatures([sA, sB, sC, e * taptweak], R_agg)\n",
140+
"sA = sign_musig(privKeyA_keyPath, nonceA, R_agg, output_pubkey, sighash_musig)\n",
141+
"sB = sign_musig(privKeyB_keyPath, nonceB, R_agg, output_pubkey, sighash_musig)\n",
142+
"sC = sign_musig(privKeyC_keyPath, nonceC, R_agg, output_pubkey, sighash_musig)\n",
143+
"e = musig_digest(R_agg, output_keyPath, sighash_musig)\n",
144+
"sig_agg = aggregate_musig_signatures([sA, sB, sC, e * tweak_keyPath], R_agg)\n",
145+
"\n",
129146
"print(\"Aggregate signature is {}\\n\".format(sig_agg.hex()))\n",
130147
"\n",
131-
"assert output_pubkey.verify_schnorr(sig_agg, sighash_musig)\n",
148+
"assert output_keyPath.verify_schnorr(sig_agg, sighash_musig)\n",
132149
"\n",
133-
"# Add witness to transaction\n",
150+
"# Construct transaction witness\n",
134151
"spending_tx.wit.vtxinwit.append(CTxInWitness([sig_agg]))\n",
135152
" \n",
136153
"print(\"spending_tx: {}\\n\".format(spending_tx))\n",
137154
"\n",
138155
"# Test mempool acceptance\n",
139-
"spending_tx_str = spending_tx.serialize().hex()\n",
156+
"spending_tx_str = spending_tx.serialize().hex() \n",
140157
"assert test.nodes[0].testmempoolaccept([spending_tx_str])[0]['allowed']\n",
141158
"\n",
142159
"print(\"Key path spending transaction weight: {}\".format(test.nodes[0].decoderawtransaction(spending_tx_str)['weight']))\n",
@@ -167,7 +184,8 @@
167184
"spending_tx.vin = [spending_tx_in]\n",
168185
"spending_tx.vout = [dest_output]\n",
169186
"\n",
170-
"sighash = TaprootSignatureHash(spending_tx, [output], SIGHASH_ALL_TAPROOT, 0, scriptpath=True, tapscript=tapscript_3a.script)\n",
187+
"# Derive the sighash. Use tapscript_3a.\n",
188+
"sighash = TaprootSignatureHash(spending_tx, [output], SIGHASH_ALL_TAPROOT, 0, scriptpath=True, script=tapscript_3a.script)\n",
171189
"\n",
172190
"witness_elements = []\n",
173191
"\n",
@@ -184,7 +202,7 @@
184202
"\n",
185203
"# Test timelock\n",
186204
"assert_equal(\n",
187-
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],\n",
205+
" [{'txid': spending_tx.rehash(), 'allowed': False, 'reject-reason': 'non-BIP68-final'}],\n",
188206
" test.nodes[0].testmempoolaccept([spending_tx_str])\n",
189207
")\n",
190208
"\n",

test_framework/script.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,7 @@ def construct(self):
11171117
tweak = tagged_hash("TapTweak", self.key.get_bytes() + h)
11181118
tweaked = self.key.tweak_add(tweak)
11191119
control_map = dict((script, GetVersionTaggedPubKey(self.key, version, tweaked) + control) for version, script, control in ctrl)
1120-
return (CScript([OP_1, GetVersionTaggedPubKey(tweaked, TAPROOT_VER, tweaked)]), tweak, control_map)
1120+
return (CScript([OP_1, tweaked.get_bytes()]), tweak, control_map)
11211121

11221122
@staticmethod
11231123
def _constructor(node):

0 commit comments

Comments
 (0)