|
7 | 7 | "outputs": [], |
8 | 8 | "source": [ |
9 | 9 | "import random\n", |
| 10 | + "from io import BytesIO\n", |
10 | 11 | "\n", |
11 | 12 | "import util\n", |
12 | 13 | "from test_framework.address import program_to_witness\n", |
|
22 | 23 | "source": [ |
23 | 24 | "# 2.2 TapTweak\n", |
24 | 25 | "\n", |
25 | | - "* Part 1: Tweaking the public key and commitment schemes with tweaks\n", |
| 26 | + "* Part 1: Tweaking the public key; commitment schemes with tweaks\n", |
26 | 27 | "* Part 2: Spending a (tweaked) taproot output along the key path\n", |
27 | | - "* Part 3 (Case Study): Contract commitments\n", |
| 28 | + "* Part 3 (Case Study): contract commitments\n", |
28 | 29 | "\n", |
29 | | - "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", |
| 30 | + "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", |
30 | 31 | "\n", |
31 | 32 | "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 | ] |
|
77 | 78 | "\n", |
78 | 79 | "print(\"Private key: {}\\nPublic key: {}\\n\".format(privkey.secret, pubkey.get_bytes().hex()))\n", |
79 | 80 | "\n", |
80 | | - "# Generate a random tweak 0 < t < SECP256K1_ORDER and its associated point\n", |
| 81 | + "# Generate a random tweak scalar 0 < t < SECP256K1_ORDER and derive its associated tweak point\n", |
81 | 82 | "tweak = random.randrange(1, SECP256K1_ORDER)\n", |
82 | | - "tweak_private = ECKey().set(tweak, True)\n", |
| 83 | + "tweak_private = ECKey().set(tweak)\n", |
83 | 84 | "tweak_point = tweak_private.get_pubkey()\n", |
84 | 85 | "print(\"Tweak scalar: {}\\nTweak point: {}\\n\".format(tweak_private.secret, tweak_point.get_bytes().hex()))\n", |
85 | 86 | "\n", |
|
103 | 104 | "\n", |
104 | 105 | "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", |
105 | 106 | "\n", |
106 | | - "_Question: Which participant(s) need to tweak their private keys?_" |
| 107 | + "_Question: How is the tweak incorporated into the final signature?_" |
107 | 108 | ] |
108 | 109 | }, |
109 | 110 | { |
|
172 | 173 | "source": [ |
173 | 174 | "#### Example 2.2.3: Tweaking a public key Q with commitment data\n", |
174 | 175 | "\n", |
175 | | - "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", |
| 176 | + "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", |
176 | 177 | "\n", |
177 | 178 | "First, we commit a contract between Alice and Bob and then demonstrate how this unsafe commitment can be changed.\n", |
178 | 179 | "\n", |
|
340 | 341 | "outputs": [], |
341 | 342 | "source": [ |
342 | 343 | "# Example key pair\n", |
343 | | - "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052, True)\n", |
| 344 | + "privkey = ECKey().set(102118636618570133408735518698955378316807974995033705330357303547139065928052)\n", |
344 | 345 | "internal_pubkey = privkey.get_pubkey()\n", |
345 | 346 | "\n", |
346 | 347 | "# Example tweak\n", |
|
513 | 514 | "\n", |
514 | 515 | "# Fetch the oldest unspent outpoint in the Bitcoin Core wallet\n", |
515 | 516 | "unspent_txid = node.listunspent(1)[-1][\"txid\"]\n", |
516 | | - "unspent_outpoint = COutPoint(int(unspent_txid,16), 0)" |
| 517 | + "unspent_outpoint = COutPoint(int(unspent_txid,16), 0)\n", |
| 518 | + "\n", |
| 519 | + "print(\"Unspent coin: txid:{}, n:{}\".format(unspent_outpoint.hash, unspent_outpoint.n))" |
517 | 520 | ] |
518 | 521 | }, |
519 | 522 | { |
|
522 | 525 | "source": [ |
523 | 526 | "#### Example 2.2.12: Create and broadcast a transaction with an OP_RETURN output\n", |
524 | 527 | "\n", |
525 | | - "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." |
| 528 | + "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)." |
526 | 529 | ] |
527 | 530 | }, |
528 | 531 | { |
|
538 | 541 | "op_return_tx_in = CTxIn(outpoint=unspent_outpoint, nSequence=0)\n", |
539 | 542 | "op_return_tx.vin = [op_return_tx_in]\n", |
540 | 543 | "\n", |
541 | | - "# Output 0) Alice's destination address\n", |
| 544 | + "# Output 0) Alice's change address\n", |
542 | 545 | "address_alice = node.getnewaddress(address_type=\"bech32\")\n", |
543 | 546 | "p2wpkh_output_script = bytes.fromhex(node.getaddressinfo(address_alice)['scriptPubKey'])\n", |
544 | | - "p2wpkh_output_amount_sat = 100_000_000\n", |
| 547 | + "p2wpkh_output_amount_sat = 4_950_000_000 # remove transaction fee from output amount\n", |
545 | 548 | "p2wpkh_output = CTxOut(nValue=p2wpkh_output_amount_sat, scriptPubKey=p2wpkh_output_script)\n", |
546 | 549 | "\n", |
547 | 550 | "# Output 1) OP_RETURN with Alice's commitment\n", |
|
562 | 565 | "print(\"The total transaction weight is: {}\\n\".format(op_return_tx_decoded['weight']))\n", |
563 | 566 | "\n", |
564 | 567 | "# Test mempool acceptance\n", |
| 568 | + "print(node.testmempoolaccept(rawtxs=[op_return_tx_hex_signed], maxfeerate=0))\n", |
565 | 569 | "assert node.testmempoolaccept(rawtxs=[op_return_tx_hex_signed], maxfeerate=0)[0]['allowed']\n", |
566 | 570 | "print(\"Success!\")" |
567 | 571 | ] |
|
0 commit comments