From 33af341a24001860d1d7ef015064ef6f559e167b Mon Sep 17 00:00:00 2001 From: JamesC Date: Mon, 28 Oct 2019 13:55:37 +0100 Subject: [PATCH 1/6] Add contract commitment unsealing to 2.2.3, 2.2.4 --- 2.2-taptweak.ipynb | 183 ++++++++++++++++++------- solutions/2.2-taptweak-solutions.ipynb | 4 +- 2 files changed, 136 insertions(+), 51 deletions(-) diff --git a/2.2-taptweak.ipynb b/2.2-taptweak.ipynb index 99e3aad60..4a94ca350 100644 --- a/2.2-taptweak.ipynb +++ b/2.2-taptweak.ipynb @@ -28,7 +28,7 @@ "\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", - "## Tweaking the Public Key\n", + "## 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", @@ -156,16 +156,20 @@ "\n", "![test](images/taptweak0.jpg)\n", "\n", - "Instead, the committed value must first be hashed with the untweaked public key point. **This prevents modification of both untweaked secret and tweak for a given tweaked pubkey 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.** " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2.2.3: modifying the tweak for a tweaked public key Q\n", + "#### Example 2.2.3: Tweaking a public key Q with commitment data\n", "\n", - "In this example we demonstrate an insecure commitment scheme. Simply tweaking the private key with a value `c` allows the pubkey equation `Q = x'G + c'G` to be solved for any `c'` by modifying `x'`." + "In this example we demonstrate an insecure commitment scheme. The committed value `c` can be trivially modified to `c'`. The public key point equation `Q = x'G + c'G` still holds and is equal to the same point Q. An alternative secret `x` can also be solved for.\n", + "\n", + "First, we commit a contract between Alice and Bob and then demonstrate how this unsafe commitment can be changed.\n", + "\n", + "* The initial committed contract is: `Alice agrees to pay 10 BTC to Bob`" ] }, { @@ -174,38 +178,42 @@ "metadata": {}, "outputs": [], "source": [ - "# Generate a key pair\n", - "x, P = generate_key_pair()\n", - "print(\"Private key: {}\\nPublic key: {}\\n\".format(x.secret, P.get_bytes().hex()))\n", + "# Alice generates a key pair\n", + "x_key, P_key = generate_key_pair()\n", + "print(\"Private key: {}\\nPublic key: {}\\n\".format(x_key.secret, P_key.get_bytes().hex()))\n", "\n", - "# Tweak the public key\n", - "t = random.randrange(1, SECP256K1_ORDER)\n", - "print(\"Tweak: {}\".format(t))\n", - "Q = P.tweak_add(t)\n", + "# Alice generates the tweak from the contract\n", + "contract = \"Alice agrees to pay 10 BTC to Bob\"\n", + "t = sha256(contract.encode('utf-8'))\n", + "print(\"Tweak from original contract: {}\\n\".format(t.hex()))\n", + "\n", + "# Alice tweaks her key pair\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", - "# Create a fake tweak\n", - "t2 = random.randrange(1, SECP256K1_ORDER)\n", - "print(\"Tweak 2: {}\\n\".format(t2))\n", + "# Alice produces a valid signature for this tweaked public key\n", + "msg = sha256(b'I agree to the committed contract')\n", + "sig = q_key.sign_schnorr(msg)\n", "\n", - "# Solve: x` = x - t' + t\n", - "x_int = x.as_int()\n", - "x2_int = (x_int - t2 + t) % SECP256K1_ORDER\n", + "# Given P, Q and the contract, Bob believes he can verify the following:\n", "\n", - "x2_key = x = ECKey().set(x2_int, True)\n", - "P2 = x2_key.get_pubkey()\n", - "Q2 = P2.tweak_add(t2)\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", "\n", - "print(\"Tweaked pubkey for x tweaked by t: {}\".format(Q.get_bytes().hex()))\n", - "print(\"Tweaked pubkey for x2 tweaked by t2: {}\".format(Q2.get_bytes().hex()))" + "# 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)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2.2.4 - Tweaking the pubkey with `H(P|msg)`\n", + "#### Example 2.2.4: Modifying the commitment tweak of public key Q\n", "\n", - "In this example, we demonstrate a _secure_ commitment scheme. 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'`." + "However, note that is possible for Alice to modify this insecure commitment without changing the value of pub key `Q`.\n", + "* The committed contract is changed to : `Alice agrees to pay 0.1 BTC to Bob`" ] }, { @@ -214,48 +222,125 @@ "metadata": {}, "outputs": [], "source": [ - "# Key pair generation\n", - "privkey1, pubkey1 = generate_key_pair()\n", - "print(\"Private key: {}\\nPublic key: {}\\n\".format(x.secret, pubkey.get_bytes().hex()))\n", + "# Alice modifies the contract and produces an alternative tweak\n", + "alternative_contract = \"Alice agrees to pay 0.1 BTC to Bob\"\n", + "t2 = sha256(alternative_contract.encode('utf-8'))\n", + "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", + "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", + "\n", + "# So 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", + "\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)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Summary of 2.2.3, 2.2.4: Insecure practice of tweaking a public key with commitment data\n", + "\n", + "We have demonstrated how a simple key tweak with commitment data does not work as a commitment scheme.\n", + "* Tweaking the original public key `P` with commitment data hides the commitment.\n", + "* However, the original public key `P` can be recomputed (`P2`) for any modified commitment, without altering the tweaked public key `Q`.\n", + "\n", + "To any observer, both original and modified \"commitments\" appear to be valid for the same public key `Q`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "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", "\n", - "# Compute the tweak from H(P|msg)\n", - "commitment = b'commitment'\n", - "ss = sha256(pubkey.get_bytes())\n", - "ss += sha256(commitment)\n", + "* Alice can now no longer invalidate her previous contract commitment with Bob." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Alice generates a key pair\n", + "x_key, P_key = generate_key_pair()\n", + "print(\"Private key: {}\\nPublic key: {}\\n\".format(x_key.secret, P_key.get_bytes().hex()))\n", + "\n", + "# Alice computes the tweak from H(P|msg)\n", + "contract = \"Alice agrees to pay 10 BTC to Bob\"\n", + "ss = P_key.get_bytes()\n", + "ss += sha256(contract.encode('utf-8'))\n", "t = sha256(ss)\n", "\n", - "# Determine tweak point\n", - "tweak = ECKey().set(t, True)\n", - "tweak_point = tweak.get_pubkey()\n", - "print(\"Tweak scalar: {}\\nTweak point: {}\\n\".format(tweak.secret, tweak_point.get_bytes().hex()))\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", "\n", - "privkey_tweaked = privkey + tweak\n", - "pubkey_tweaked = pubkey + tweak_point\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", - "# Sign message and verify signature\n", - "msg = sha256(b'msg')\n", - "sig = privkey_tweaked.sign_schnorr(msg)\n", + "# Given pubkey1, pubkey_tweaked and the contract, Bob can verify the following:\n", "\n", - "assert pubkey_tweaked.verify_schnorr(sig, msg)\n", - "print(\"Success!\")" + "# 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", + "\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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Spending a taproot output along the key path\n", + "## Part 2: Spending a taproot output along the key path\n", "\n", - "In this exercise, we'll create a segwit version 1 output that sends to a tweaked public key. We'll them spend that output along the key path using the tweaked private key.\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", - "Such as spend does not reveal the committed tweak to the observer and is indistinguishable any other key path spend." + "Such as spend does not reveal the committed tweak to the observer and is indistinguishable from any other key path spend." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### _Programming Exercise 2.2.5:_ Construct taproot output with tweaked public key" + "#### _Programming Exercise 2.2.6:_ Construct taproot output with tweaked public key" ] }, { @@ -288,7 +373,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2.2.6: Start Bitcoin Core node and send coins to the taproot address\n", + "#### Example 2.2.7: Start Bitcoin Core node and send coins to the taproot address\n", "\n", "Only run setup once, or after a clean shutdown." ] @@ -313,7 +398,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2.2.7: Construct `CTransaction` and populate inputs\n", + "#### Example 2.2.8: Construct `CTransaction` and populate inputs\n", "\n", "We use the `create_spending_transaction(node, txid)` convenience function." ] @@ -333,7 +418,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### _Programming Exercise 2.2.8:_ Spend taproot output with key path" + "#### _Programming Exercise 2.2.9:_ Spend taproot output with key path" ] }, { diff --git a/solutions/2.2-taptweak-solutions.ipynb b/solutions/2.2-taptweak-solutions.ipynb index 00479af5f..80ef071bb 100644 --- a/solutions/2.2-taptweak-solutions.ipynb +++ b/solutions/2.2-taptweak-solutions.ipynb @@ -63,7 +63,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### _Programming Exercise 2.2.5:_ Construct taproot output with tweaked public key" + "#### _Programming Exercise 2.2.6:_ Construct taproot output with tweaked public key" ] }, { @@ -97,7 +97,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### _Programming Exercise 2.2.8:_ Spend taproot output with key path" + "#### _Programming Exercise 2.2.9:_ Spend taproot output with key path" ] }, { From a39c2cce51ec4a8871c58d7e7d4e1ce9dbaf7147 Mon Sep 17 00:00:00 2001 From: JamesC Date: Mon, 28 Oct 2019 11:55:39 +0100 Subject: [PATCH 2/6] Update taptweak image with pay-to-contract --- images/taptweak0.jpg | Bin 71412 -> 43321 bytes images/taptweak0.svg | 51 ++++++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/images/taptweak0.jpg b/images/taptweak0.jpg index 739fbded5e823083452553a9798df9f981758d22..c7220e9880491cbe491d32ef74992e5b088cd1ec 100644 GIT binary patch literal 43321 zcmeFY1z22J(lFdu1a}DT?he5T?(QVexJ%IB?(XjH4#9%EG)@x;E?KX0|2}l1prDozkvb%0Ax5gICwZ@ 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 literal 71412 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 diff --git a/images/taptweak0.svg b/images/taptweak0.svg index 9030f7643..ec1ba19fd 100644 --- a/images/taptweak0.svg +++ b/images/taptweak0.svg @@ -2,40 +2,41 @@ - Commitments with Pubkey Tweaks + Commitments with Pubkey Tweaks - + - + - Commitment Scheme + Pay-to-Contract Commitment - - Q = x’G + c’G + + Q = x’G + c’G - Cannot solvefor x + Cannot solve + for x - - Solve for x’ + + Solve for x’ - Q = P + cG + Q = P + cG - - x + + x - Q = P + H(P|c)G + Q = P + H(P|c)G - - Q != x’G + H(x’G|c’)G + + Q != x’G + H(x’G|c’)G + c0,1.406,1.149,2.556,2.557,2.556h28.346c1.406,0,2.558,1.15,2.558,2.556v4.694"/> + c0,1.406-1.15,2.556-2.558,2.556h-28.345c-1.406,0-2.557,1.15-2.557,2.556v4.694"/> @@ -75,7 +76,7 @@ - Modify c’ + Modify c’ @@ -83,7 +84,7 @@ - Modify c’ + Modify c’ @@ -91,7 +92,7 @@ - Public Key Tweak + Public Key Tweak @@ -107,7 +108,7 @@ - + From 984d7968896da0e28803366c1b6d62ff9192f12c Mon Sep 17 00:00:00 2001 From: JamesC Date: Mon, 28 Oct 2019 13:56:12 +0100 Subject: [PATCH 3/6] Add mini-case study: OP_RETURN vs pay-to-contract --- 2.2-taptweak.ipynb | 198 ++++++++++++++++++++++++- solutions/2.2-taptweak-solutions.ipynb | 36 +++++ 2 files changed, 231 insertions(+), 3 deletions(-) diff --git a/2.2-taptweak.ipynb b/2.2-taptweak.ipynb index 4a94ca350..dfcf04bde 100644 --- a/2.2-taptweak.ipynb +++ b/2.2-taptweak.ipynb @@ -10,10 +10,10 @@ "\n", "import util\n", "from test_framework.address import program_to_witness\n", - "from test_framework.key import ECKey, SECP256K1_ORDER, generate_key_pair, generate_schnorr_nonce\n", + "from test_framework.key import ECKey, ECPubKey, SECP256K1_ORDER, generate_key_pair, generate_schnorr_nonce\n", "from test_framework.messages import CTxInWitness, sha256\n", "from test_framework.musig import generate_musig_key, aggregate_schnorr_nonces, sign_musig, aggregate_musig_signatures, musig_digest\n", - "from test_framework.script import TaprootSignatureHash, SIGHASH_ALL_TAPROOT" + "from test_framework.script import CTransaction, OP_RETURN, SIGHASH_ALL_TAPROOT, TaprootSignatureHash" ] }, { @@ -457,6 +457,197 @@ "test.shutdown()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 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", + "* Committing data to an OP_RETURN output requires an additional output with a zero amount, resulting in a higher transaction fees.\n", + "* The OP_RETURN output reveals the presence of a data commitment to any on-chain observer. This reduces the privacy of Alice's commitments.\n", + "\n", + "In this chapter, we'll show how Alice can move her contract commitments to public key tweaks to reduce fees and improve the privacy of her commitments." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Committing contract data to an OP_RETURN output\n", + "\n", + "We'll first show Alice's current setup: An OP_RETURN script containing commitment data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.2.10: Create the contract commitment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "contract_bytes = \"Alice pays 10 BTC to Bob\".encode('utf-8')\n", + "commitment_bytes = sha256(contract_bytes)\n", + "print(\"The contract commitment is: {}\".format(commitment_bytes.hex()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.2.11: Start Bitcoin Core node and construct an unspent output\n", + "\n", + "Only run once, or after a clean shutdown. This constructs an unspent outpoint for example 2.2.12. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Start node\n", + "test = util.TestWrapper()\n", + "test.setup()\n", + "node = test.nodes[0]\n", + "\n", + "# Generate coins and send these to a new wallet address\n", + "node.generatetoaddress(101, node.getnewaddress(address_type=\"bech32\"))\n", + "\n", + "# Fetch the oldest unspent outpoint in the Bitcoin Core wallet\n", + "unspent_txid = node.listunspent(1)[-1][\"txid\"]\n", + "unspent_outpoint = [{\"txid\": unspent_txid, \"vout\": 0}]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.2.12: Create and broadcast a transaction with an OP_RETURN output\n", + "\n", + "We now construct a OP_RETURN output which contains the commitment data of Alice's contract with Bob, and then add it to a transaction with a regular P2WPKH output. This way, the commitment can be done more efficiently, by sharing transaction data with another spendable output." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Construct the two outputs:\n", + "# Output 0) Alice's destination address\n", + "# Output 1) OP_RETURN with Alice's commitment\n", + "address_alice = node.getnewaddress(address_type=\"bech32\")\n", + "outputs = [{address_alice: 1}, {\"data\": commitment_bytes.hex()}] \n", + "\n", + "# Construct and sign a transaction with the p2pkh and OP_RETURN outputs\n", + "op_return_tx_hex = node.createrawtransaction(inputs=unspent_outpoint, outputs=outputs)\n", + "op_return_tx_hex_signed = node.signrawtransactionwithwallet(hexstring=op_return_tx_hex)['hex']\n", + "\n", + "# Confirm details of the OP_RETURN output\n", + "op_return_tx_decoded = node.decoderawtransaction(op_return_tx_hex_signed)\n", + "op_return_vout = op_return_tx_decoded['vout'][1]\n", + "print(\"The OP_RETURN output script is: {}\".format(op_return_vout['scriptPubKey']['asm']))\n", + "print(\"The OP_RETURN output value is: {}\".format(int(op_return_vout['value'])))\n", + "\n", + "# Note the total weight of the transaction with a dedicated OP_RETURN commitment output\n", + "print(\"The total transaction weight is: {}\\n\".format(op_return_tx_decoded['weight']))\n", + "\n", + "# Test mempool acceptance\n", + "assert node.testmempoolaccept(rawtxs=[op_return_tx_hex_signed], maxfeerate=0)[0]['allowed']\n", + "print(\"Success!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Committing contract data with the pay-to-contract scheme\n", + "\n", + "Next, we will commit Alice's contract to a spendable pay-to-pubkey output with the pay-to-contract commitment scheme." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### _Programming Exercise 2.2.13:_ Generate segwit v1 address for a pay-to-contract public key\n", + "\n", + "Commit the contract to Alice's public key with the pay-to-contract commitment scheme, and then generate the corresponding segwit v1 address." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate a key pair\n", + "privkey, pubkey = generate_key_pair()\n", + "\n", + "# Generate the pay-to-contract tweak\n", + "contract_bytes = \"Alice pays 10 BTC to Bob\".encode('utf-8')\n", + "tweak_private = # TODO: implement\n", + "tweak_point = # TODO: implement\n", + "\n", + "# Tweak Alice's key pair with the pay-to-contract tweak\n", + "tweaked_pubkey = # TODO: implement\n", + "tweaked_privkey = # TODO: implement\n", + "\n", + "# Generate the segwit v1 address\n", + "tweaked_pubkey_data = # TODO: implement\n", + "tweaked_pubkey_program = # TODO: implement\n", + "version = 1\n", + "address = program_to_witness(version, tweaked_pubkey_program)\n", + "print(\"Address encoding the segwit v1 output: \", address)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2.2.14: Create a transaction with the Bitcoin Core wallet sending funds to the segwit v1 address\n", + "\n", + "The pay-to-contract output encoded in the segwit v1 address holds spendable value just like a regular, untweaked public key. It can be spent with the tweaked private key, as we learned in part 2 of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate coins and send to segwit v1 address containing the pay-to-contract public key\n", + "tx = node.generate_and_send_coins(address)\n", + "print(\"Transaction {}, output 0\\nSent to {}\\n\".format(tx.hash, address))\n", + "print(\"Transaction weight with pay-to-contract: {}\".format(node.decoderawtransaction(tx.serialize().hex())['weight']))\n", + "print(\"Transaction weight with OP_RETURN: {}\\n\".format(op_return_tx_decoded['weight']))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### _Shutdown TestWrapper_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Shutdown\n", + "test.shutdown()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -465,7 +656,8 @@ "\n", "- Learned how to tweak a public/private key pair with a value.\n", "- Created an _insecure_ commitment scheme (by tweaking the keys with the raw commitment value) and a _secure_ commitment scheme (by tweaking with a hash of the commitment and the public key).\n", - "- Sent coins to a segwit v1 output with a tweaked public key, and later spent that output by signing with the tweaked private key." + "- Sent coins to a segwit v1 output with a tweaked public key, and later spent that output by signing with the tweaked private key.\n", + "- Improved cost and privacy of a contract commitment by moving it from an unspendable OP_RETURN output to a pay-to-contract public key." ] } ], diff --git a/solutions/2.2-taptweak-solutions.ipynb b/solutions/2.2-taptweak-solutions.ipynb index 80ef071bb..af6ddbc48 100644 --- a/solutions/2.2-taptweak-solutions.ipynb +++ b/solutions/2.2-taptweak-solutions.ipynb @@ -119,6 +119,42 @@ "assert node.test_transaction(spending_tx)\n", "print(\"Success!\")" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### _Programming Exercise 2.2.13:_ Generate segwit v1 address for a pay-to-contract public key" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Generate a key pair\n", + "privkey, pubkey = generate_key_pair()\n", + "\n", + "# Generate the pay-to-contract tweak\n", + "contract_bytes = \"Alice pays 10 BTC to Bob\".encode('utf-8')\n", + "ss = pubkey.get_bytes()\n", + "ss += sha256(contract_bytes)\n", + "t = sha256(ss)\n", + "tweak_private = ECKey().set(t, True)\n", + "tweak_point = tweak_private.get_pubkey()\n", + "\n", + "# Tweak Alice's key pair with the pay-to-contract tweak\n", + "tweaked_pubkey = pubkey + tweak_point\n", + "tweaked_privkey = privkey + tweak_private\n", + "\n", + "# Generate the segwit v1 address\n", + "tweaked_pubkey_data = tweaked_pubkey.get_bytes()\n", + "tweaked_pubkey_program = bytes([tweaked_pubkey_data[0] & 1]) + tweaked_pubkey_data[1:]\n", + "version = 1\n", + "address = program_to_witness(version, tweaked_pubkey_program)\n", + "print(\"Address encoding the segwit v1 output: \", address)" + ] } ], "metadata": { From 3299c982de6a33dd94b0db688437de20bbe0fd06 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 8 Nov 2019 16:25:58 -0500 Subject: [PATCH 4/6] review fixups --- 2.2-taptweak.ipynb | 96 +++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 53 deletions(-) diff --git a/2.2-taptweak.ipynb b/2.2-taptweak.ipynb index dfcf04bde..d4e72ff2f 100644 --- a/2.2-taptweak.ipynb +++ b/2.2-taptweak.ipynb @@ -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", @@ -156,7 +163,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.**" ] }, { @@ -196,14 +203,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))" ] }, { @@ -228,33 +235,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))" ] }, { @@ -276,7 +275,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." ] @@ -298,38 +297,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", @@ -461,7 +451,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", From 9010e18f352a234306ccff56be044ee8982a71ee Mon Sep 17 00:00:00 2001 From: James Chiang Date: Fri, 29 Nov 2019 10:34:19 +0100 Subject: [PATCH 5/6] Rewrite 2.2.11 and 2.2.12 with CTransaction --- 2.2-taptweak.ipynb | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/2.2-taptweak.ipynb b/2.2-taptweak.ipynb index d4e72ff2f..7985fc372 100644 --- a/2.2-taptweak.ipynb +++ b/2.2-taptweak.ipynb @@ -11,9 +11,9 @@ "import util\n", "from test_framework.address import program_to_witness\n", "from test_framework.key import ECKey, ECPubKey, SECP256K1_ORDER, generate_key_pair, generate_schnorr_nonce\n", - "from test_framework.messages import CTxInWitness, sha256\n", + "from test_framework.messages import COutPoint, CTxIn, CTxInWitness, CTxOut, sha256\n", "from test_framework.musig import generate_musig_key, aggregate_schnorr_nonces, sign_musig, aggregate_musig_signatures, musig_digest\n", - "from test_framework.script import CTransaction, OP_RETURN, SIGHASH_ALL_TAPROOT, TaprootSignatureHash" + "from test_framework.script import CScript, CTransaction, OP_RETURN, SIGHASH_ALL_TAPROOT, TaprootSignatureHash" ] }, { @@ -513,7 +513,7 @@ "\n", "# Fetch the oldest unspent outpoint in the Bitcoin Core wallet\n", "unspent_txid = node.listunspent(1)[-1][\"txid\"]\n", - "unspent_outpoint = [{\"txid\": unspent_txid, \"vout\": 0}]" + "unspent_outpoint = COutPoint(int(unspent_txid,16), 0)" ] }, { @@ -531,15 +531,26 @@ "metadata": {}, "outputs": [], "source": [ - "# Construct the two outputs:\n", + "# Construct transaction spending previously generated outpoint\n", + "op_return_tx = CTransaction()\n", + "op_return_tx.nVersion = 1\n", + "op_return_tx.nLockTime = 0\n", + "op_return_tx_in = CTxIn(outpoint=unspent_outpoint, nSequence=0)\n", + "op_return_tx.vin = [op_return_tx_in]\n", + "\n", "# Output 0) Alice's destination address\n", - "# Output 1) OP_RETURN with Alice's commitment\n", "address_alice = node.getnewaddress(address_type=\"bech32\")\n", - "outputs = [{address_alice: 1}, {\"data\": commitment_bytes.hex()}] \n", + "p2wpkh_output_script = bytes.fromhex(node.getaddressinfo(address_alice)['scriptPubKey'])\n", + "p2wpkh_output_amount_sat = 100_000_000\n", + "p2wpkh_output = CTxOut(nValue=p2wpkh_output_amount_sat, scriptPubKey=p2wpkh_output_script)\n", + "\n", + "# Output 1) OP_RETURN with Alice's commitment\n", + "op_return_output_script = CScript([OP_RETURN, commitment_bytes])\n", + "op_return_output = CTxOut(nValue=0, scriptPubKey=op_return_output_script)\n", "\n", - "# Construct and sign a transaction with the p2pkh and OP_RETURN outputs\n", - "op_return_tx_hex = node.createrawtransaction(inputs=unspent_outpoint, outputs=outputs)\n", - "op_return_tx_hex_signed = node.signrawtransactionwithwallet(hexstring=op_return_tx_hex)['hex']\n", + "# Populate transaction with p2pkh and OP_RETURN outputs and add valid witness\n", + "op_return_tx.vout = [p2wpkh_output, op_return_output]\n", + "op_return_tx_hex_signed = node.signrawtransactionwithwallet(hexstring=op_return_tx.serialize().hex())['hex']\n", "\n", "# Confirm details of the OP_RETURN output\n", "op_return_tx_decoded = node.decoderawtransaction(op_return_tx_hex_signed)\n", From 407792b0629bdbe1809067ff7bfffcf92a35a242 Mon Sep 17 00:00:00 2001 From: John Newbery Date: Fri, 3 Jan 2020 17:21:04 -0500 Subject: [PATCH 6/6] Review markups for chapter 2.2 --- 2.2-taptweak.ipynb | 28 +++++++++++++++----------- solutions/2.2-taptweak-solutions.ipynb | 4 +++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/2.2-taptweak.ipynb b/2.2-taptweak.ipynb index 7985fc372..63bca8d05 100644 --- a/2.2-taptweak.ipynb +++ b/2.2-taptweak.ipynb @@ -7,6 +7,7 @@ "outputs": [], "source": [ "import random\n", + "from io import BytesIO\n", "\n", "import util\n", "from test_framework.address import program_to_witness\n", @@ -22,11 +23,11 @@ "source": [ "# 2.2 TapTweak\n", "\n", - "* Part 1: Tweaking the public key and commitment schemes with tweaks\n", + "* Part 1: Tweaking the public key; commitment schemes with tweaks\n", "* Part 2: Spending a (tweaked) taproot output along the key path\n", - "* Part 3 (Case Study): Contract commitments\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", + "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 keys were tweaked by the commitment.\n", "\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." ] @@ -77,9 +78,9 @@ "\n", "print(\"Private key: {}\\nPublic key: {}\\n\".format(privkey.secret, pubkey.get_bytes().hex()))\n", "\n", - "# Generate a random tweak 0 < t < SECP256K1_ORDER and its associated point\n", + "# Generate a random tweak scalar 0 < t < SECP256K1_ORDER and derive its associated tweak point\n", "tweak = random.randrange(1, SECP256K1_ORDER)\n", - "tweak_private = ECKey().set(tweak, True)\n", + "tweak_private = ECKey().set(tweak)\n", "tweak_point = tweak_private.get_pubkey()\n", "print(\"Tweak scalar: {}\\nTweak point: {}\\n\".format(tweak_private.secret, tweak_point.get_bytes().hex()))\n", "\n", @@ -103,7 +104,7 @@ "\n", "In this exercise, we tweak an MuSig aggregate pubkey, and then sign for it using the individual participant keys. The MuSig pubkey aggregation step is done for you.\n", "\n", - "_Question: Which participant(s) need to tweak their private keys?_" + "_Question: How is the tweak incorporated into the final signature?_" ] }, { @@ -172,7 +173,7 @@ "source": [ "#### Example 2.2.3: Tweaking a public key Q with commitment data\n", "\n", - "In this example we demonstrate an insecure commitment scheme. The committed value `c` can be trivially modified to `c'`. The public key point equation `Q = x'G + c'G` still holds and is equal to the same point Q. An alternative secret `x` can also be solved for.\n", + "In this example we demonstrate an insecure commitment scheme. The committed value `c` can be trivially modified to `c'`, and by setting `x'` to `x + c - c'`, the public key point equation `Q = x'G + c'G` still holds.\n", "\n", "First, we commit a contract between Alice and Bob and then demonstrate how this unsafe commitment can be changed.\n", "\n", @@ -340,7 +341,7 @@ "outputs": [], "source": [ "# Example key pair\n", - "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052, True)\n", + "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052)\n", "internal_pubkey = privkey.get_pubkey()\n", "\n", "# Example tweak\n", @@ -513,7 +514,9 @@ "\n", "# Fetch the oldest unspent outpoint in the Bitcoin Core wallet\n", "unspent_txid = node.listunspent(1)[-1][\"txid\"]\n", - "unspent_outpoint = COutPoint(int(unspent_txid,16), 0)" + "unspent_outpoint = COutPoint(int(unspent_txid,16), 0)\n", + "\n", + "print(\"Unspent coin: txid:{}, n:{}\".format(unspent_outpoint.hash, unspent_outpoint.n))" ] }, { @@ -522,7 +525,7 @@ "source": [ "#### Example 2.2.12: Create and broadcast a transaction with an OP_RETURN output\n", "\n", - "We now construct a OP_RETURN output which contains the commitment data of Alice's contract with Bob, and then add it to a transaction with a regular P2WPKH output. This way, the commitment can be done more efficiently, by sharing transaction data with another spendable output." + "We now construct a zero-value OP_RETURN output which contains the commitment data of Alice's contract with Bob. We also add a regular P2WPKH output back to Alice to return the funds from the transaction input (less the transaction fee)." ] }, { @@ -538,10 +541,10 @@ "op_return_tx_in = CTxIn(outpoint=unspent_outpoint, nSequence=0)\n", "op_return_tx.vin = [op_return_tx_in]\n", "\n", - "# Output 0) Alice's destination address\n", + "# Output 0) Alice's change address\n", "address_alice = node.getnewaddress(address_type=\"bech32\")\n", "p2wpkh_output_script = bytes.fromhex(node.getaddressinfo(address_alice)['scriptPubKey'])\n", - "p2wpkh_output_amount_sat = 100_000_000\n", + "p2wpkh_output_amount_sat = 4_950_000_000 # remove transaction fee from output amount\n", "p2wpkh_output = CTxOut(nValue=p2wpkh_output_amount_sat, scriptPubKey=p2wpkh_output_script)\n", "\n", "# Output 1) OP_RETURN with Alice's commitment\n", @@ -562,6 +565,7 @@ "print(\"The total transaction weight is: {}\\n\".format(op_return_tx_decoded['weight']))\n", "\n", "# Test mempool acceptance\n", + "print(node.testmempoolaccept(rawtxs=[op_return_tx_hex_signed], maxfeerate=0))\n", "assert node.testmempoolaccept(rawtxs=[op_return_tx_hex_signed], maxfeerate=0)[0]['allowed']\n", "print(\"Success!\")" ] diff --git a/solutions/2.2-taptweak-solutions.ipynb b/solutions/2.2-taptweak-solutions.ipynb index af6ddbc48..277771095 100644 --- a/solutions/2.2-taptweak-solutions.ipynb +++ b/solutions/2.2-taptweak-solutions.ipynb @@ -50,6 +50,8 @@ "# needs to be added to the list of partial signatures\n", "# Method: sign_musig(private_key, nonce_key, nonce_point, public_key, msg)\n", "# Method: aggregate_musig_signatures(partial_signature_list, aggregate nonce)\n", + "# Note: The tweak multiplied by the musig_digest is added to the partial signature list.\n", + "# This is equivalent to tweaking the private key for a single signer.\n", "e = musig_digest(R_agg, agg_pubkey_tweaked, msg)\n", "s1 = sign_musig(privkey1_c, k1, R_agg, agg_pubkey_tweaked, msg)\n", "s2 = sign_musig(privkey2_c, k2, R_agg, agg_pubkey_tweaked, msg)\n", @@ -73,7 +75,7 @@ "outputs": [], "source": [ "# Example key pair\n", - "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052, True)\n", + "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052)\n", "internal_pubkey = privkey.get_pubkey()\n", "\n", "# Example tweak\n",