Skip to content

Commit

Permalink
review fixups
Browse files Browse the repository at this point in the history
  • Loading branch information
jnewbery authored and jachiang committed Nov 28, 2019
1 parent 6fde706 commit 008ba57
Showing 1 changed file with 47 additions and 57 deletions.
104 changes: 47 additions & 57 deletions 2.2-taptweak.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@
"source": [
"# 2.2 TapTweak\n",
"\n",
"* Tweaking the Public Key\n",
"* Commitment Schemes with Tweaks\n",
"* Spending a (tweaked) taproot output along the key path\n",
"* Part 1: Tweaking the public key and commitment schemes with tweaks\n",
"* Part 2: Spending a (tweaked) taproot output along the key path\n",
"* Part 3 (Case Study): Contract commitments\n",
"\n",
"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",
"\n",
"## Part 1: Tweaking the Public Key\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 1: Tweaking the public key\n",
"\n",
"Instead of using our original public key as the witness program, we use a tweaked public key.\n",
"\n",
Expand Down Expand Up @@ -113,10 +120,10 @@
"c_map, agg_pubkey = generate_musig_key([pubkey1, pubkey2])\n",
"\n",
"# Apply challenge factors to keys\n",
"privkey1_c = privkey1.mul(c_map[pubkey1])\n",
"privkey2_c = privkey2.mul(c_map[pubkey2])\n",
"pubkey1_c = pubkey1.mul(c_map[pubkey1])\n",
"pubkey2_c = pubkey2.mul(c_map[pubkey2])\n",
"privkey1_c = privkey1 * c_map[pubkey1]\n",
"privkey2_c = privkey2 * c_map[pubkey2]\n",
"pubkey1_c = pubkey1 * c_map[pubkey1]\n",
"pubkey2_c = pubkey2 * c_map[pubkey2]\n",
"\n",
"# Tweak musig public key\n",
"# Method: ECPubKey.tweak_add()\n",
Expand Down Expand Up @@ -152,7 +159,7 @@
"\n",
"![test](images/taptweak0.jpg)\n",
"\n",
"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.** "
"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.**"
]
},
{
Expand Down Expand Up @@ -192,14 +199,14 @@
"msg = sha256(b'I agree to the committed contract')\n",
"sig = q_key.sign_schnorr(msg)\n",
"\n",
"# Given P, Q and the contract, Bob believes he can verify the following:\n",
"\n",
"# 1) The contract 'Alice agrees to pay 10 BTC to Bob' is committed to public key Q\n",
"print(\"'Alice agrees to pay 10 BTC to Bob' appears to be committed to Q: {}\".format(\\\n",
" P_key.tweak_add(sha256(contract.encode('utf-8'))) == Q_key))\n",
"# Bob can verify that sig is a valid signature for the public key Q:\n",
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
"\n",
"# 2) Alice is the owner of the public key Q = P + t*G\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig, msg)))"
"# Alice provides the untweaked public key P to Bob.\n",
"# Bob believes he can verify that the signature committed to the tweak t:\n",
"verify_tweak = P_key.tweak_add(sha256(contract.encode('utf-8'))) == Q_key\n",
"print(\"The signature appears to commit to '{}': {}\".format(contract, verify_tweak))"
]
},
{
Expand All @@ -224,33 +231,25 @@
"print(\"Tweak from original contract: {}\".format(t.hex()))\n",
"print(\"Tweak from modified contract: {}\\n\".format(t2.hex()))\n",
"\n",
"# Alice modifies her original secret and public key\n",
"# x` = x - t' + t\n",
"# Alice modifies her original private key and public key\n",
"# x2 = x - t2 + t\n",
"x_int = x_key.as_int()\n",
"t_int = int.from_bytes(t, \"big\") \n",
"t2_int = int.from_bytes(t2, \"big\") \n",
"x2_int = (x_int - t2_int + t_int) % SECP256K1_ORDER\n",
"x2_key = ECKey().set(x2_int, True)\n",
"P2_key = x2_key.get_pubkey()\n",
"\n",
"# The resulting tweaked public Q key remains the same\n",
"Q2_key = P2_key.tweak_add(t2)\n",
"print(\"Tweaked public key from original tweak t: {}\".format(Q_key.get_bytes().hex()))\n",
"print(\"Tweaked public key from modified tweak t2: {}\\n\".format(Q2_key.get_bytes().hex()))\n",
"x2_key, P2_key = generate_key_pair((x_int - t2_int + t_int) % SECP256K1_ORDER)\n",
"\n",
"# So Alice can still produce a valid signature for Q\n",
"# Alice can still produce a valid signature for Q\n",
"msg2 = sha256(b'I agree to the committed contract')\n",
"sig2 = q_key.sign_schnorr(msg2)\n",
"\n",
"# Given P2, Q and the modified contract, \n",
"# Alice can demonstrate the following to invalidate the original contract:\n",
"# Bob can verify that sig is a valid signature for the public key Q:\n",
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
"\n",
"# 1) The modified contract 'Alice agrees to pay 0.1 BTC to Bob' appears to be committed to public key Q\n",
"print(\"'Alice agrees to pay 0.1 BTC to Bob' appears to be committed to Q: {}\"\\\n",
" .format(P2_key.tweak_add(sha256(alternative_contract.encode('utf-8'))) == Q_key))\n",
"\n",
"# 2) Alice is still the owner of the public key Q = P2 + t2*G\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig2, msg)))"
"# Alice claims that P2 is the untweaked public key.\n",
"# Bob believes he can verify that the signature committed to the tweak t:\n",
"verify_tweak = P2_key.tweak_add(sha256(alternative_contract.encode('utf-8'))) == Q_key\n",
"print(\"The signature appears to commit to '{}': {}\".format(alternative_contract, verify_tweak))"
]
},
{
Expand All @@ -272,7 +271,7 @@
"source": [
"#### Example 2.2.5 - Pay-to-contract: Tweaking the pubkey with `H(P|msg)`\n",
"\n",
"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",
"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",
"\n",
"* Alice can now no longer invalidate her previous contract commitment with Bob."
]
Expand All @@ -294,38 +293,29 @@
"t = sha256(ss)\n",
"\n",
"# Alice tweaks her key pair\n",
"t_key = ECKey().set(t, True)\n",
"T_key = t_key.get_pubkey()\n",
"q_key = x_key + t_key\n",
"Q_key = P_key + T_key\n",
"print(\"Tweak scalar: {}\\nTweak point: {}\\n\".format(t_key.secret, T_key.get_bytes().hex()))\n",
"Q_key = P_key.tweak_add(t)\n",
"q_key = x_key.add(t)\n",
"print(\"Tweaked private key: {}\\nTweaked public key: {}\\n\".format(q_key.secret, Q_key.get_bytes().hex()))\n",
"\n",
"# Alice signs a valid message\n",
"msg = sha256(b'I agree to the committed contract')\n",
"sig = q_key.sign_schnorr(msg)\n",
"\n",
"# Given pubkey1, pubkey_tweaked and the contract, Bob can verify the following:\n",
"\n",
"# 1) The contract 'Alice agrees to pay 10 BTC to Bob' is committed to public key Q\n",
"print(\"'Alice agrees to pay 10 BTC to Bob' is committed to Q: {}\"\\\n",
" .format(P_key.tweak_add(sha256(P_key.get_bytes() + sha256(contract.encode('utf-8')))) == Q_key))\n",
"# Bob can verify that sig is a valid signature for the public key Q:\n",
"verify_sig = Q_key.verify_schnorr(sig, msg)\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(verify_sig))\n",
"\n",
"# 2) Alice is the owner of the public key P + t*G = Q\n",
"print(\"Alice has produced a valid signature for Q: {}\".format(Q_key.verify_schnorr(sig, msg)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**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."
"# Alice provides the untweaked public key P to Bob.\n",
"# Bob believes he can verify that the signature committed to the tweak t:\n",
"verify_tweak = P_key.tweak_add(sha256(P_key.get_bytes() + sha256(contract.encode('utf-8')))) == Q_key\n",
"print(\"The signature commits to '{}': {}\".format(contract, verify_tweak))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 2: Spending a taproot output along the key path\n",
"## Part 2: Spending a (tweaked) taproot output along the key path\n",
"\n",
"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",
"\n",
Expand Down Expand Up @@ -457,7 +447,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Part 3 (Case Study): Contract Commitments\n",
"## Part 3 (Case Study): Contract commitments\n",
"\n",
"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",
"\n",
Expand Down

0 comments on commit 008ba57

Please sign in to comment.