Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 69 additions & 45 deletions 2.3-tapscript.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
"metadata": {},
"outputs": [],
"source": [
"import hashlib\n",
"\n",
"import util\n",
"from test_framework.address import program_to_witness\n",
"from test_framework.key import generate_key_pair\n",
"from test_framework.messages import CTxInWitness, ser_string, sha256\n",
"from test_framework.musig import generate_musig_key\n",
"from test_framework.script import tagged_hash, TapLeaf, TapTree, TaprootSignatureHash, SIGHASH_ALL_TAPROOT"
"from test_framework.script import hash160, SIGHASH_ALL_TAPROOT, tagged_hash, TapLeaf, TaprootSignatureHash, TapTree"
]
},
{
Expand Down Expand Up @@ -98,8 +96,7 @@
"privkey, pubkey = generate_key_pair()\n",
"\n",
"# Generate tapscript\n",
"pk_tapscript = TapLeaf()\n",
"pk_tapscript.construct_pk(pubkey)\n",
"pk_tapscript = TapLeaf().construct_pk(pubkey)\n",
"\n",
"print(\"Tapscript operations:\")\n",
"for op in pk_tapscript.script:\n",
Expand Down Expand Up @@ -187,8 +184,7 @@
"privkey3, pubkey3 = generate_key_pair()\n",
"\n",
"# Generate tapscript\n",
"csa_tapscript = TapLeaf()\n",
"csa_tapscript.construct_csa(2, [pubkey1, pubkey2, pubkey3])\n",
"csa_tapscript = TapLeaf().construct_csa(2, [pubkey1, pubkey2, pubkey3])\n",
"\n",
"print(\"CSA tapscript operations:\")\n",
"for op in csa_tapscript.script:\n",
Expand Down Expand Up @@ -270,7 +266,27 @@
"## Part 2: Tapscript descriptors\n",
"\n",
"A tapscript descriptor is a human-readable language expression which maps to a unique output. We propose each tapscript descriptor to be encapsulated by a tapscript tag `ts`, which can be updated in future tapleaf versions. \n",
"* `ts(pk(key))`, `ts(csa(key))`, ..."
"* `ts(pk(key))`, `ts(csa(key))`, ...\n",
"\n",
"### Combining public keys with hashlocks and delays\n",
"\n",
"We also propose tapscript descriptors which describe outputs combining public keys with both hashlocks and delays. The following is a brief overview of the hashlock and delay implementations used in this chapter.\n",
"\n",
"**A hashlock consumes a 32B preimage and checks the hash digest for correctness:**\n",
"\n",
"1. A 32B size guard checks that the spending preimage size is exactly 32B.\n",
"2. A `hash160` operation produces a 20B digest of the pre-image.\n",
"3. An equality check is performed for the hashed preimage and the hashlock digest.\n",
"\n",
"Where: `hash160(data) = ripemd160(sha256(data))`\n",
"\n",
"**Note:** The hashlock contains a size guard (1) to ensure that the preimage in the spending witness cannot be impractically large. Consider the example of an atomic swap between two chains. If the data push limits for the two chains differ, it will be possible that one transaction is spendable and the other is not, even though they feature identical hashlocks. The preimage may simply exceed the data push limit for one of the chains.\n",
"\n",
"**A delay is implemented with an nSequence check:**\n",
"\n",
"1. The `nSequence` field in a spending transaction input encodes a minimum delay between the confirmation of the referenced output and its spending.\n",
"2. The spending transaction must be encoded with `version >= 2` to activate `nSequence` delay encoding.\n",
"3. A `checksequenceverify` opcode in a delay-enforcing output script will check the `nSequence` value of the spending transaction input."
]
},
{
Expand All @@ -285,17 +301,19 @@
" * Witness: `[signature]`\n",
" \n",
" \n",
"* `ts(pkhash(key, 20B-hash-digest))`\n",
"* `ts(pk_hashlock(key, 20B-hash-digest))`\n",
" * Witness: `[signature]`,`[32B-preimage]`\n",
" \n",
" \n",
"* `ts(pkolder(key, delay))`\n",
" * Hashlock: `hash160(32B-preimage)`\n",
"\n",
"\n",
"* `ts(pk_delay(key, delay))`\n",
" * Witness: `[signature]`\n",
" * Spendable after delay (with `nSequence > delay`)\n",
" \n",
" \n",
"* `ts(pkhasholder(key, 20B-hash-digest, delay))`\n",
"* `ts(pk_hashlock_delay(key, 20B-hash-digest, delay))`\n",
" * Witness: `[signature]`,`[32B-preimage]`\n",
" * Hashlock: `hash160(32B-preimage)`\n",
" * Spendable after delay (with `nSequence > delay`)"
]
},
Expand All @@ -306,9 +324,9 @@
"We also provide pay-to-pubkey tapscript constructors for for the `TapLeaf` class. \n",
"\n",
"* `TapLeaf.construct_pk(ECPubKey)`\n",
"* `TapLeaf.construct_pkhash(ECPubKey, 20B-hash-digest)`\n",
"* `TapLeaf.construct_pkolder(ECPubKey, delay)`\n",
"* `TapLeaf.construct_pkhasholder(ECPubKey, 20B-hash-digest, delay)`\n",
"* `TapLeaf.construct_pk_hashlock(ECPubKey, 20B-hash-digest)`\n",
"* `TapLeaf.construct_pk_delay(ECPubKey, delay)`\n",
"* `TapLeaf.construct_pk_hashlock_delay(ECPubKey, 20B-hash-digest, delay)`\n",
"\n",
"The descriptor string can be recalled with:\n",
"* `TapLeaf.desc`\n",
Expand All @@ -320,9 +338,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2.3.4 Example: Generating a `pkolder` tapscript\n",
"#### 2.3.4 Example: Generating a `pk_delay` tapscript\n",
"\n",
"We construct a `pkolder` tapscript with the following locking conditions:\n",
"We construct a `pk_delay` tapscript with the following locking conditions:\n",
"\n",
"* 2-of-2 MuSig public key\n",
"* Delay of 20 blocks"
Expand All @@ -339,17 +357,16 @@
"privkey2, pubkey2 = generate_key_pair()\n",
"c_map, pk_musig = generate_musig_key([pubkey1, pubkey2])\n",
"\n",
"# Generate pkolder tapscript\n",
"pkolder_tapscript = TapLeaf()\n",
"pkolder_tapscript.construct_pkolder(pk_musig, 20)\n",
"print(\"Tapscript descriptor:\", pkolder_tapscript.desc, \"\\n\")\n",
"# Generate pk_delay tapscript\n",
"pk_delay_tapscript = TapLeaf().construct_pk_delay(pk_musig, 20)\n",
"print(\"Tapscript descriptor:\", pk_delay_tapscript.desc, \"\\n\")\n",
"\n",
"print(\"Tapscript operations:\")\n",
"for op in pkolder_tapscript.script:\n",
"for op in pk_delay_tapscript.script:\n",
" print(op.hex()) if isinstance(op, bytes) else print(op)\n",
"\n",
"print(\"\\nSatisfying witness elements:\")\n",
"for element, value in pkolder_tapscript.sat:\n",
"for element, value in pk_delay_tapscript.sat:\n",
" print(\"{}, {}\".format(element, value.hex()))"
]
},
Expand All @@ -366,16 +383,22 @@
" * Note: for n < m, empty signature elements (zero) must be provided.\n",
" \n",
"\n",
"* `ts(csaolder(k, [key0, key1, ...], hash))`\n",
" * Witness: `[signature], [signature], ...`\n",
"\n",
"\n",
"* `ts(csahash(k, [key0, key1, ...], hash, time))`\n",
"* `ts(csa_hashlock(k, [key0, key1, ...], hash, time))`\n",
" * Witness: `[signature], [signature], ..., [32B pre-image]`\n",
" * Hashlock: `hash160(32B-preimage)`\n",
"\n",
"\n",
"\n",
"* `ts(csahasholder(k, [key0, key1, ...], hash, time))`\n",
" * Witness: `[signature], [signature], ..., [32B pre-image]`"
"* `ts(csa_delay(k, [key0, key1, ...], hash, time))`\n",
" * Witness: `[signature], [signature], ...`\n",
" * Spendable after delay (with `nSequence > delay`)\n",
"\n",
"\n",
"* `ts(csa_hashlock_delay(k, [key0, key1, ...], hash, time))`\n",
" * Witness: `[signature], [signature], ..., [32B pre-image]`\n",
" * Hashlock: `hash160(32B-preimage)`\n",
" * Spendable after delay (with `nSequence > delay`)"
]
},
{
Expand All @@ -385,9 +408,9 @@
"We also provide checksigadd tapscript constructors for for the `TapLeaf` class. \n",
"\n",
"* `TapLeaf.construct_csa(k, [ECPubKey, ECPubKey, ...])`\n",
"* `TapLeaf.construct_csahash(k, [ECPubKey, ECPubKey, ...], 20B-hash-digest)`\n",
"* `TapLeaf.construct_csaolder(k, [ECPubKey, ECPubKey, ...], delay)`\n",
"* `TapLeaf.construct_csahasholder(k, [ECPubKey, ECPubKey, ...], 20B-hash-digest, delay)`\n",
"* `TapLeaf.construct_csa_hashlock(k, [ECPubKey, ECPubKey, ...], 20B-hash-digest)`\n",
"* `TapLeaf.construct_csa_delay(k, [ECPubKey, ECPubKey, ...], delay)`\n",
"* `TapLeaf.construct_csa_hashlock_delay(k, [ECPubKey, ECPubKey, ...], 20B-hash-digest, delay)`\n",
"\n",
"**Note:** Any single public key in CSA tapscripts can be generated with multi-party schemes such as MuSig."
]
Expand All @@ -396,12 +419,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2.3.5 _Programming Exercise:_ Generate a 2-of-2 `csahasholder` tapscript\n",
"#### 2.3.5 _Programming Exercise:_ Generate a 2-of-2 `csa_hashlock_delay` tapscript\n",
"\n",
"Construct a `csahasholder` tapscript with the following locking conditions:\n",
"Construct a `csa_hashlock_delay` tapscript with the following locking conditions:\n",
"\n",
"* 2-of-2 public keys\n",
"* Hashlock with the preimage `sha256(b'secret')`\n",
"* `OP_HASH160` hashlock with the preimage `sha256(b'secret')`\n",
" * `OP_HASH160` is equivalent to `ripemd160(sha256(preimage))`\n",
"* Delay of 20 blocks"
]
},
Expand All @@ -419,22 +443,22 @@
"print(\"pubkey2: {}\\n\".format(pubkey2.get_bytes().hex()))\n",
"\n",
"# Method: 32B preimage - sha256(bytes)\n",
"# Method: 20B digest - hashlib.new('ripemd160', bytes).digest()\n",
"# Method: 20B digest - hash160(bytes)\n",
"secret = b'secret'\n",
"preimage = # TODO: implement\n",
"digest = # TODO: implement\n",
"delay = # TODO: implement\n",
"\n",
"# Construct tapscript\n",
"csahasholder_tapscript = # TODO: implement\n",
"print(\"Descriptor:\", csahasholder_tapscript.desc, \"\\n\")\n",
"csa_hashlock_delay_tapscript = # TODO: implement\n",
"print(\"Descriptor:\", csa_hashlock_delay_tapscript.desc, \"\\n\")\n",
"\n",
"print(\"Tapscript operations:\")\n",
"for op in csahasholder_tapscript.script:\n",
"for op in csa_hashlock_delay_tapscript.script:\n",
" print(op.hex()) if isinstance(op, bytes) else print(op)\n",
"\n",
"print(\"\\nSatisfying witness elements:\")\n",
"for element, value in csahasholder_tapscript.sat:\n",
"for element, value in csa_hashlock_delay_tapscript.sat:\n",
" print(\"{}, {}\".format(element, value.hex()))"
]
},
Expand Down Expand Up @@ -478,7 +502,7 @@
"\n",
"* Use the `tagged_hash()` function to compute a tagged hash.\n",
"* Generate an internal public key.\n",
"* Compute the taptweak from a single `csahasholder_tapscript` commitment."
"* Compute the taptweak from a single `csa_hashlock_delay_tapscript` commitment."
]
},
{
Expand Down Expand Up @@ -513,7 +537,7 @@
"metadata": {},
"outputs": [],
"source": [
"taptree = TapTree(key=pubkey_internal, root=csahasholder_tapscript)\n",
"taptree = TapTree(key=pubkey_internal, root=csa_hashlock_delay_tapscript)\n",
"segwit_v1_script, tap_tweak_constructed, control_map = taptree.construct()\n",
"\n",
"assert taptweak == tap_tweak_constructed\n",
Expand Down Expand Up @@ -547,7 +571,7 @@
"source": [
"#### Example 2.3.8: Generate a single tapscript segwit v1 address\n",
"\n",
"In this example, we construct segwit v1 output for spending along the single script path. We will reuse the previously generated segwit v1 witness program which has the `csahasholder` tapscript committed to it, and encode it to a bech32 address."
"In this example, we construct segwit v1 output for spending along the single script path. We will reuse the previously generated segwit v1 witness program which has the `csa_hashlock_delay` tapscript committed to it, and encode it to a bech32 address."
]
},
{
Expand Down Expand Up @@ -657,7 +681,7 @@
"source": [
"#### _Programming Exercise 2.3.12:_ Add the witness and test acceptance of the transaction\n",
"\n",
"Remember to revisit the satisfying witness elements for `csahasholder_tapscript` constructed in exercise 2.3.5:\n",
"Remember to revisit the satisfying witness elements for `csa_hashlock_delay_tapscript` constructed in exercise 2.3.5:\n",
"* Preimage\n",
"* Signature for pubkey2\n",
"* Signature for pubkey1\n",
Expand Down
10 changes: 3 additions & 7 deletions 2.4-taptree.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,10 @@
"metadata": {},
"outputs": [],
"source": [
"tapleafA = TapLeaf()\n",
"tapleafB = TapLeaf()\n",
"tapleafC = TapLeaf()\n",
"\n",
"# Construct tapleafs\n",
"tapleafA.construct_pk(pubkeyA)\n",
"tapleafB.construct_pk(pubkeyB)\n",
"tapleafC.construct_pk(pubkeyC)\n",
"tapleafA = TapLeaf().construct_pk(pubkeyA)\n",
"tapleafB = TapLeaf().construct_pk(pubkeyB)\n",
"tapleafC = TapLeaf().construct_pk(pubkeyC)\n",
"\n",
"# Construct taptree nodes.\n",
"tapbranchAB = Tapbranch(tapleafA, tapleafB)\n",
Expand Down
12 changes: 5 additions & 7 deletions 3.1-degrading-multisig-case-study.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,16 @@
"outputs": [],
"source": [
"# Tapscripts - 2 main keys & 1 backup key\n",
"# Use construct_csaolder() to construct the tapscript\n",
"tapscript_2a, tapscript_2b, ... = # TODO: implement\n",
"# Use construct_csa_delay() to construct the tapscript\n",
"delay = # TODO: implement\n",
"tapscript_2a.construct_csaolder(... # TODO: implement\n",
"tapscript_2b.construct_csaolder(... # TODO: implement\n",
"tapscript_2a = # TODO: implement\n",
"tapscript_2b = # TODO: implement\n",
"... # TODO: implement\n",
"\n",
"# Tapscripts - 1 main keys & 2 backup keys\n",
"tapscript_3a, tapscript_3b, ... # TODO: implement\n",
"long_delay = # TODO: implement\n",
"tapscript_3a.construct_csaolder(... # TODO: implement\n",
"tapscript_3b.construct_csaolder(... # TODO: implement\n",
"tapscript_3a = # TODO: implement\n",
"tapscript_3b = # TODO: implement\n",
"... # TODO: implement\n",
"\n",
"# Set list of backup tapscripts\n",
Expand Down
21 changes: 10 additions & 11 deletions solutions/2.3-tapscript-solutions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2.3.5 _Programming Exercise:_ Generate a 2-of-2 `csahasholder` tapscript"
"#### 2.3.5 _Programming Exercise:_ Generate a 2-of-2 `csa_hashlock_delay` tapscript"
]
},
{
Expand All @@ -21,23 +21,22 @@
"print(\"pubkey2: {}\\n\".format(pubkey1.get_bytes().hex()))\n",
"\n",
"# Method: 32B preimage - sha256(bytes)\n",
"# Method: 20B digest - hashlib.new('ripemd160', bytes).digest()\n",
"# Method: 20B digest - hash160(bytes)\n",
"secret = b'secret'\n",
"preimage = sha256(secret)\n",
"digest = hashlib.new('ripemd160', preimage).digest()\n",
"digest = hash160(preimage)\n",
"delay = 20\n",
"\n",
"# Construct tapscript\n",
"csahasholder_tapscript = TapLeaf()\n",
"csahasholder_tapscript.construct_csahasholder(2, [pubkey1, pubkey2], digest, delay)\n",
"print(\"Descriptor:\", csahasholder_tapscript.desc, \"\\n\")\n",
"csa_hashlock_delay_tapscript = TapLeaf().construct_csa_hashlock_delay(2, [pubkey1, pubkey2], digest, delay)\n",
"print(\"Descriptor:\", csa_hashlock_delay_tapscript.desc, \"\\n\")\n",
"\n",
"print(\"Tapscript operations:\")\n",
"for op in csahasholder_tapscript.script:\n",
"for op in csa_hashlock_delay_tapscript.script:\n",
" print(op.hex()) if isinstance(op, bytes) else print(op)\n",
"\n",
"print(\"\\nSatisfying witness elements:\")\n",
"for element, value in csahasholder_tapscript.sat:\n",
"for element, value in csa_hashlock_delay_tapscript.sat:\n",
" print(\"{}, {}\".format(element, value.hex()))"
]
},
Expand All @@ -58,7 +57,7 @@
"\n",
"# Method: ser_string(Cscript) prepends compact size.\n",
"TAPSCRIPT_VER = bytes([0xc0])\n",
"tapleaf = tagged_hash(\"TapLeaf\", TAPSCRIPT_VER + ser_string(csahasholder_tapscript.script))\n",
"tapleaf = tagged_hash(\"TapLeaf\", TAPSCRIPT_VER + ser_string(csa_hashlock_delay_tapscript.script))\n",
"taptweak = tagged_hash(\"TapTweak\", pubkey_internal.get_bytes() + tapleaf)\n",
"print(\"Your constructed taptweak is: {}.\".format(taptweak.hex()))"
]
Expand All @@ -82,7 +81,7 @@
" SIGHASH_ALL_TAPROOT,\n",
" input_index=0,\n",
" scriptpath=True,\n",
" tapscript=csahasholder_tapscript.script)\n",
" tapscript=csa_hashlock_delay_tapscript.script)\n",
"\n",
"# Sign with both privkeys\n",
"signature1 = privkey1.sign_schnorr(sighash)\n",
Expand All @@ -108,7 +107,7 @@
"# Add witness to transaction\n",
"# Tip: Witness stack for script path - [satisfying elements for tapscript] [TapLeaf.script] [controlblock]\n",
"# Tip: Controlblock for a tapscript in control_map[TapLeaf.script]\n",
"witness_elements = [preimage, signature2, signature1, csahasholder_tapscript.script, control_map[csahasholder_tapscript.script]]\n",
"witness_elements = [preimage, signature2, signature1, csa_hashlock_delay_tapscript.script, control_map[csa_hashlock_delay_tapscript.script]]\n",
"spending_tx.wit.vtxinwit.append(CTxInWitness(witness_elements))\n",
"\n",
"print(\"Spending transaction:\\n{}\\n\".format(spending_tx))\n",
Expand Down
12 changes: 4 additions & 8 deletions solutions/2.4-taptree-solutions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,10 @@
"privkeyD, pubkeyD = generate_key_pair()\n",
"\n",
"# Construct Pay-to-Pubkey TapLeafs and Taptree.\n",
"TapLeafA = TapLeaf()\n",
"TapLeafB = TapLeaf()\n",
"TapLeafC = TapLeaf()\n",
"TapLeafD = TapLeaf()\n",
"TapLeafA.construct_pk(pubkeyA)\n",
"TapLeafB.construct_pk(pubkeyB)\n",
"TapLeafC.construct_pk(pubkeyC)\n",
"TapLeafD.construct_pk(pubkeyD)\n",
"TapLeafA = TapLeaf().construct_pk(pubkeyA)\n",
"TapLeafB = TapLeaf().construct_pk(pubkeyB)\n",
"TapLeafC = TapLeaf().construct_pk(pubkeyC)\n",
"TapLeafD = TapLeaf().construct_pk(pubkeyD)\n",
"\n",
"# Create a Taptree with tapleafs and huffman constructor.\n",
"# Method: TapTree.huffman_constructor(tuple_list)\n",
Expand Down
Loading