Skip to content

Instantly share code, notes, and snippets.

@kallewoof
Last active January 28, 2020 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kallewoof/89f9386ad31005eb1bae313390598adc to your computer and use it in GitHub Desktop.
Save kallewoof/89f9386ad31005eb1bae313390598adc to your computer and use it in GitHub Desktop.
Tapscript notes
Initial script:
OP_IF
144
OP_CHECKSEQUENCEVERIFY
<pubkey_alice>
OP_ELSE
OP_SHA256
preimage_hash
OP_EQUALVERIFY
<pubkey_bob>
OP_ENDIF
OP_CHECKSIG
Resulting paths:
1. Alice and Bob both sign: taproot spend.
2. Alice spends: 144 OP_CSV <pubkey_alice> OP_CHECKSIG =
3. Bob spends: OP_SHA256 preimage_hash OP_EQUALVERIFY <pubkey_bob> OP_CHECKSIG
With below:
Owner: Key: Pubkey:
alice 2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 9997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be
bob 81b637d8fcd2c6da6359e6963113a1170de795e4b725b84d1e0b4cfd9ec58ce9 4edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10
intrnl 3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f 5bf08d58a430f8c222bffaf9127249c5cdff70a2d68b2b45637eb662b6b88eb5
And preimage 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f with sha256(preimage) = 6c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd5333
Note that we generated a third private key (securely generated by Alice and Bob somehow out of the scope of this text) that we refer to as the internal key. If Alice and Bob so desires, they can spend directly using this key. This is path 1 above. Note that neither Alice nor Bob has direct knowledge of the private key, but both of them know about the public key.
Resulting paths become:
1. ?
2. 029000b2209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803beac
3. a8206c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd533388204edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10ac
Taproot's tree helper gives:
(
[
(
(0xc0, 0x029000b2209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803beac),
0x632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42
),
(
(0xc0, 0xa8206c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd533388204edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10ac),
0xcc74d6dd5756ed0d5966f51407e72dbeb433249a58befbb05dacbee3cecabc22
)
],
0x9a05b8336dd928a8781171e2f0308b7c8a8f453df908e34ea2d189c5eaff8ad9
)
We have:
- (bottom) TapBranch_hash(path 2 h || path 3 h) = TapBranch(
TapLeaf(v, s2len, s2),
TapLeaf(v, s3len, s3)
)
- path 2 script with h=TapLeaf_hash(version || script2_compact_len || script2)
- path 3 script with h=TapLeaf_hash(version || script3_compact_len || script3)
The taproot output script:
51204ab602e6b0d282d4437a2f47148c98ae6136d2a0f64497ee153e4d1cbcc59a0f
i.e.
OP_1 4ab602e6b0d282d4437a2f47148c98ae6136d2a0f64497ee153e4d1cbcc59a0f
the segwit witness v1 program 4ab60....a0f.
This is derived as intrnl_pubkey + TapTweak_hash(intrnl_pubkey || h)*G, which means the private key for key spending can be derived as intrnl_privkey + TapTweak_hash(intrnl_pubkey || h). Using this as the private key, path 1 is achievable directly.
Control object:
There are two possible paths. The control object describes which one is taken.
Spend 2: <control byte with leaf version and negation bit> <intrnl_pubkey> <path 3 h>
Spend 3: <control byte with leaf version and negation bit> <intrnl_pubkey> <path 2 h>
I.e.
Spend 3: 0xc0 b58eb8b662b67e63452b8bd6a270ffcdc5497212f9fabf22c2f830a4588df05b 632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42
Script = 0x45 a8206c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd533388204edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10ac
./bitcoin-cli -datadir=$HOME/signet-taproot sendrawtransaction 02000000000101672432e02acf9e6b109ecca197b86caed043de3604ad365a99da25be60392a300000000000ffffffff01905f01000000000016001444e18f60837d890bece55799fbbf21c478e6fc4f\ 04\ 40\ 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f\ 20\ 107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f\ 45\ a8206c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd533388204edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10ac\ 41c0\ 5bf08d58a430f8c222bffaf9127249c5cdff70a2d68b2b45637eb662b6b88eb5\ cc74d6dd5756ed0d5966f51407e72dbeb433249a58befbb05dacbee3cecabc22\ 00000000
D = tagged_hash("TapLeaf", bytes([leaf_version]) + ser_script(script3)) = 632c8632b4f29c6291416e23135cf78ecb82e525788ea5ed6483e3c6ce943b42
C = tagged_hash("TapLeaf", bytes([leaf_version]) + ser_script(script2)) = cc74d6dd5756ed0d5966f51407e72dbeb433249a58befbb05dacbee3cecabc22
CD = tagged_hash("TapBranch", C + D) = e68b96c607eae01aa6b446cf76673b94d961500975029cd73f326f551c4c2772
Execution:
One stack item = key path spending; SigVersion = TAPROOT (path 1; analysis ends here)
Else script path spending, SigVersion = TAPSCRIPT:
1. Pop last entry off stack. This is the control object.
2. Pop last entry off stack. This is the scriptPubKey.
3. Verify taproot commitment, and produce tapleaf hash.
1. Let path_len = (|control| - base_size) / control node size, where base size = 33, control node size = 32.
2. Let p = an x only pubkey from control[1..33].
3. Let q = uint256(the program, which is guaranteed to be 32 b; the program is the equivalent of a P2WSH program in segwit0)
4. Let k = TapLeaf-tagged hash of control[0] & leaf mask, followed by the script, where leaf mask = 0xfe
5. Set the outgoing tap leaf hash to k
6. Loop i from 0 to path_len-1
1. Let ss_branch be the TapBranch-tagged hash
2. Let node = control[base + node size * i .. + node size]
3. If k < node, append k || node to ss_branch
4. Else append node || k instead
5. Set k = ss_branch.GetSHA256()
7. Let k = TapTweak-tagged-hasher's sha256 of p || k (MakeSpan(p))
8. Return true if q.CheckPayToContract(p, k, control[0] & 1)
4. If control[0] & leaf mask == tapscript leaf (0xc0), this is a current version Tapscript; fail otherwise.
5. Evaluate script
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment