Skip to content

Commit 835225d

Browse files
jnewberyjachiang
authored andcommitted
review fixups
1 parent f62e881 commit 835225d

File tree

1 file changed

+47
-57
lines changed

1 file changed

+47
-57
lines changed

2.2-taptweak.ipynb

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,20 @@
2222
"source": [
2323
"# 2.2 TapTweak\n",
2424
"\n",
25-
"* Tweaking the Public Key\n",
26-
"* Commitment Schemes with Tweaks\n",
27-
"* Spending a (tweaked) taproot output along the key path\n",
25+
"* Part 1: Tweaking the public key and commitment schemes with tweaks\n",
26+
"* Part 2: Spending a (tweaked) taproot output along the key path\n",
27+
"* Part 3 (Case Study): Contract commitments\n",
2828
"\n",
2929
"The linear property of bip-schnorr means that we can encode a commitment into a public key, and then reveal that commitment when signing with the private key. We do that by _tweaking_ the private key with the commitment, and using the associated _tweaked_ pubkey. When signing, we can reveal that the original private key was tweaked by the commitment.\n",
3030
"\n",
31-
"## Part 1: Tweaking the Public Key\n",
31+
"In part 1, we'll learn about how private/public key pairs can be tweaked, and how we can use that to create a secure commitment scheme. In part 2, we'll create a segwit v1 output and spend it along the key path, using a tweaked private and public key. Part 3 of this chapter is a case study, showing how pay-to-contract with tweaked keys can be used instead of OP_RETURN outputs to create timestamped commitments."
32+
]
33+
},
34+
{
35+
"cell_type": "markdown",
36+
"metadata": {},
37+
"source": [
38+
"## Part 1: Tweaking the public key\n",
3239
"\n",
3340
"Instead of using our original public key as the witness program, we use a tweaked public key.\n",
3441
"\n",
@@ -113,10 +120,10 @@
113120
"c_map, agg_pubkey = generate_musig_key([pubkey1, pubkey2])\n",
114121
"\n",
115122
"# Apply challenge factors to keys\n",
116-
"privkey1_c = privkey1.mul(c_map[pubkey1])\n",
117-
"privkey2_c = privkey2.mul(c_map[pubkey2])\n",
118-
"pubkey1_c = pubkey1.mul(c_map[pubkey1])\n",
119-
"pubkey2_c = pubkey2.mul(c_map[pubkey2])\n",
123+
"privkey1_c = privkey1 * c_map[pubkey1]\n",
124+
"privkey2_c = privkey2 * c_map[pubkey2]\n",
125+
"pubkey1_c = pubkey1 * c_map[pubkey1]\n",
126+
"pubkey2_c = pubkey2 * c_map[pubkey2]\n",
120127
"\n",
121128
"# Tweak musig public key\n",
122129
"# Method: ECPubKey.tweak_add()\n",
@@ -152,7 +159,7 @@
152159
"\n",
153160
"![test](images/taptweak0.jpg)\n",
154161
"\n",
155-
"Instead, the committed value must first be hashed with the untweaked public key point. **This commitment scheme is called pay-to-contract. It does not allow the modification of a committed value for a given public key point Q.** "
162+
"Instead, the committed value must first be hashed with the untweaked public key point. This commitment scheme is called *pay-to-contract*. **It does not allow the modification of a committed value for a given public key point Q.**"
156163
]
157164
},
158165
{
@@ -192,14 +199,14 @@
192199
"msg = sha256(b'I agree to the committed contract')\n",
193200
"sig = q_key.sign_schnorr(msg)\n",
194201
"\n",
195-
"# Given P, Q and the contract, Bob believes he can verify the following:\n",
196-
"\n",
197-
"# 1) The contract 'Alice agrees to pay 10 BTC to Bob' is committed to public key Q\n",
198-
"print(\"'Alice agrees to pay 10 BTC to Bob' appears to be committed to Q: {}\".format(\\\n",
199-
" P_key.tweak_add(sha256(contract.encode('utf-8'))) == Q_key))\n",
202+
"# Bob can verify that sig is a valid signature for the public key Q:\n",
203+
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
204+
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
200205
"\n",
201-
"# 2) Alice is the owner of the public key Q = P + t*G\n",
202-
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig, msg)))"
206+
"# Alice provides the untweaked public key P to Bob.\n",
207+
"# Bob believes he can verify that the signature committed to the tweak t:\n",
208+
"verify_tweak = P_key.tweak_add(sha256(contract.encode('utf-8'))) == Q_key\n",
209+
"print(\"The signature appears to commit to '{}': {}\".format(contract, verify_tweak))"
203210
]
204211
},
205212
{
@@ -224,33 +231,25 @@
224231
"print(\"Tweak from original contract: {}\".format(t.hex()))\n",
225232
"print(\"Tweak from modified contract: {}\\n\".format(t2.hex()))\n",
226233
"\n",
227-
"# Alice modifies her original secret and public key\n",
228-
"# x` = x - t' + t\n",
234+
"# Alice modifies her original private key and public key\n",
235+
"# x2 = x - t2 + t\n",
229236
"x_int = x_key.as_int()\n",
230237
"t_int = int.from_bytes(t, \"big\") \n",
231238
"t2_int = int.from_bytes(t2, \"big\") \n",
232-
"x2_int = (x_int - t2_int + t_int) % SECP256K1_ORDER\n",
233-
"x2_key = ECKey().set(x2_int, True)\n",
234-
"P2_key = x2_key.get_pubkey()\n",
235-
"\n",
236-
"# The resulting tweaked public Q key remains the same\n",
237-
"Q2_key = P2_key.tweak_add(t2)\n",
238-
"print(\"Tweaked public key from original tweak t: {}\".format(Q_key.get_bytes().hex()))\n",
239-
"print(\"Tweaked public key from modified tweak t2: {}\\n\".format(Q2_key.get_bytes().hex()))\n",
239+
"x2_key, P2_key = generate_key_pair((x_int - t2_int + t_int) % SECP256K1_ORDER)\n",
240240
"\n",
241-
"# So Alice can still produce a valid signature for Q\n",
241+
"# Alice can still produce a valid signature for Q\n",
242242
"msg2 = sha256(b'I agree to the committed contract')\n",
243243
"sig2 = q_key.sign_schnorr(msg2)\n",
244244
"\n",
245-
"# Given P2, Q and the modified contract, \n",
246-
"# Alice can demonstrate the following to invalidate the original contract:\n",
245+
"# Bob can verify that sig is a valid signature for the public key Q:\n",
246+
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
247+
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
247248
"\n",
248-
"# 1) The modified contract 'Alice agrees to pay 0.1 BTC to Bob' appears to be committed to public key Q\n",
249-
"print(\"'Alice agrees to pay 0.1 BTC to Bob' appears to be committed to Q: {}\"\\\n",
250-
" .format(P2_key.tweak_add(sha256(alternative_contract.encode('utf-8'))) == Q_key))\n",
251-
"\n",
252-
"# 2) Alice is still the owner of the public key Q = P2 + t2*G\n",
253-
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig2, msg)))"
249+
"# Alice claims that P2 is the untweaked public key.\n",
250+
"# Bob believes he can verify that the signature committed to the tweak t:\n",
251+
"verify_tweak = P2_key.tweak_add(sha256(alternative_contract.encode('utf-8'))) == Q_key\n",
252+
"print(\"The signature appears to commit to '{}': {}\".format(alternative_contract, verify_tweak))"
254253
]
255254
},
256255
{
@@ -272,7 +271,7 @@
272271
"source": [
273272
"#### Example 2.2.5 - Pay-to-contract: Tweaking the pubkey with `H(P|msg)`\n",
274273
"\n",
275-
"In this example, we demonstrate a _secure_ commitment scheme called pay-to-contract. The private key is tweaked with the scalar `H(P|c)`. Since `P` appears both inside and outside the hash, it isn't possible to solve for a different `c` by modifying `x'`.\n",
274+
"In this example, we demonstrate a _secure_ commitment scheme called pay-to-contract. The private key is tweaked with the scalar `H(P|c)`. Since `P` appears both inside and outside the hash, it isn't possible to solve for a different contract `c` by modifying `x`.\n",
276275
"\n",
277276
"* Alice can now no longer invalidate her previous contract commitment with Bob."
278277
]
@@ -294,38 +293,29 @@
294293
"t = sha256(ss)\n",
295294
"\n",
296295
"# Alice tweaks her key pair\n",
297-
"t_key = ECKey().set(t, True)\n",
298-
"T_key = t_key.get_pubkey()\n",
299-
"q_key = x_key + t_key\n",
300-
"Q_key = P_key + T_key\n",
301-
"print(\"Tweak scalar: {}\\nTweak point: {}\\n\".format(t_key.secret, T_key.get_bytes().hex()))\n",
296+
"Q_key = P_key.tweak_add(t)\n",
297+
"q_key = x_key.add(t)\n",
298+
"print(\"Tweaked private key: {}\\nTweaked public key: {}\\n\".format(q_key.secret, Q_key.get_bytes().hex()))\n",
302299
"\n",
303300
"# Alice signs a valid message\n",
304301
"msg = sha256(b'I agree to the committed contract')\n",
305302
"sig = q_key.sign_schnorr(msg)\n",
306303
"\n",
307-
"# Given pubkey1, pubkey_tweaked and the contract, Bob can verify the following:\n",
308-
"\n",
309-
"# 1) The contract 'Alice agrees to pay 10 BTC to Bob' is committed to public key Q\n",
310-
"print(\"'Alice agrees to pay 10 BTC to Bob' is committed to Q: {}\"\\\n",
311-
" .format(P_key.tweak_add(sha256(P_key.get_bytes() + sha256(contract.encode('utf-8')))) == Q_key))\n",
304+
"# Bob can verify that sig is a valid signature for the public key Q:\n",
305+
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
306+
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
312307
"\n",
313-
"# 2) Alice is the owner of the public key P + t*G = Q\n",
314-
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig, msg)))"
315-
]
316-
},
317-
{
318-
"cell_type": "markdown",
319-
"metadata": {},
320-
"source": [
321-
"**Note: A pay-to-contract commitment does not require the schnorr signature scheme.** Since we are tweaking both private and public keys with the same tweak, the key pair remains valid for any compatible signature scheme, such as ECDSA."
308+
"# Alice provides the untweaked public key P to Bob.\n",
309+
"# Bob believes he can verify that the signature committed to the tweak t:\n",
310+
"verify_tweak = P_key.tweak_add(sha256(P_key.get_bytes() + sha256(contract.encode('utf-8')))) == Q_key\n",
311+
"print(\"The signature commits to '{}': {}\".format(contract, verify_tweak))"
322312
]
323313
},
324314
{
325315
"cell_type": "markdown",
326316
"metadata": {},
327317
"source": [
328-
"## Part 2: Spending a taproot output along the key path\n",
318+
"## Part 2: Spending a (tweaked) taproot output along the key path\n",
329319
"\n",
330320
"In this exercise, we'll create a segwit v1 output that sends to a tweaked public key. We'll then spend that output along the key path using the tweaked private key.\n",
331321
"\n",
@@ -457,7 +447,7 @@
457447
"cell_type": "markdown",
458448
"metadata": {},
459449
"source": [
460-
"## Part 3 (Case Study): Contract Commitments\n",
450+
"## Part 3 (Case Study): Contract commitments\n",
461451
"\n",
462452
"Alice currently commits contracts with Bob to unspendable OP_RETURN outputs, which contain 32B proof-of-existence commitments. Although this is a standard output with a zero amount, several disadvantages remain:\n",
463453
"\n",

0 commit comments

Comments
 (0)