Created
February 16, 2022 13:48
-
-
Save shesek/ede9ca921a394580b23d301b8d84deea to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Witness stack elements | |
// Extra params for call type | |
// Buy token (0x00) | |
// New owner key | |
//<0x5bbab142b0bcf49860583797c35052007310fc4c2e7c78993e60f1af9007a394> $N_owner | |
// Matching desk SPK: 51201d7ec5f54f094c698c1689755672c1612df511f4b3db36d4774f9467fcefb23b (prefix 0x02) | |
// Update price (0x01) | |
// New price | |
<0x8400000000000000> $N_price | |
// Signature by current owner | |
<0x14b15710815ec7ac6576101a0ada6b4f1a33cca39308b3a6b27c94d1cb4722bc5200228e18f3349112bf07a5e500dc7543c5c25bf7d1aa3739c7f593bcdafa50> $sig | |
// Matching dest SPK: 6d769f7349af3796d0ad28a77112b3b58c15061b8a45a7f92d77805d942261ad (prefix 0x02) | |
// Destroy (0x02) | |
// Signature by creator | |
//<0x59bd7c18e985c23b75582f246c53d1e75bcff07df3e42014c7acb11cb43855f70126740203d1424772e588e42b8502109facef4eabbfc4b3076dee2be43106c5> $sig | |
// Tweaked output key prefix flag (0x02/0x03) | |
<0x02> $prefix | |
// Call type - 0x00 to buy, 0x01 to update price (owner only), 0x02 to destroy (creator only) | |
<0x01> $call | |
// State | |
// Current price in satoshis | |
<0x6400000000000000> $S_price // 100 | |
// Current owner key | |
<0x9a8049130e1651fed4ba129960222064cce479c24c552fe7a9e68f91154c1d3c> $S_owner | |
// Private key: 21e7f0c48e712c772f51576c0cc1421aee06c2b3e67f1368ff3dc07b50bf3949 | |
// Constants (sent to altstack) | |
// Contract own tapscript for recursive enforcment (excluding state and this constant -- only ops from this point onwards, starting at the push for the next constant) | |
<0x20499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14201efc611e1e6feffd661894690e1354ac6269215bd370e9f8f73ed80ea14497050114e0201dae61a4a8f841952be3a511502d4f56e889ffa0685aa0098773ea2d4309f624547a7c6b6b6b6b6bcdcb04FFFFFFFD88527a6351ce696c766b8851d169887651cf698852ce696c8852d1696c886cda697c7552cf69887c08ffffffffffffffff6776547a7c6d7c75527a6c6c6c6d75688258887c8201208800ce69cdc8698800cf69cdc9698801207c7e7e587c7e6c7682014d7c7e7c7e7c7e7e11546170547765616b2f656c656d656e7473a8767e6c766b7e105461704c6561662f656c656d656e7473a8767e01c47e527a8201FD7c7e7c7e7ea87ea87c00d1697e7c6ce4> $C_script | |
// tL-BTC asset id | |
<0x499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c14> $LBTC | |
// Creator pubkey for royalty payments | |
<0x1efc611e1e6feffd661894690e1354ac6269215bd370e9f8f73ed80ea1449705> $creator | |
// Royalty amount denominator (royalty = price/N) | |
<20> $royalty // 5% | |
// A point with unknown discrete logarithm (script-path-only) | |
<0x1dae61a4a8f841952be3a511502d4f56e889ffa0685aa0098773ea2d4309f624> $H | |
<4> OP_ROLL OP_SWAP // re order constants for more convinient access | |
OP_TOALTSTACK OP_TOALTSTACK OP_TOALTSTACK OP_TOALTSTACK OP_TOALTSTACK | |
// Alt stack: <script-path-only point> <own tapscript> <royalty> <creator pubkey> <LBTC-id> | |
// Stack: [call type params] <output key prefix> <call type> -- <current price> <current owner key> | |
// Sanity checks | |
// Require RBF to be enabled | |
OP_PUSHCURRENTINPUTINDEX | |
OP_INSPECTINPUTSEQUENCE | |
<0xFFFFFFFD> | |
OP_EQUALVERIFY | |
// Enforce CSV to prevent unconfirmed chain? | |
// TODO Check tx fee | |
// Bring call type onto the top of the stack | |
<2> OP_ROLL | |
// Main call flow | |
// 0x00: Buy token | |
OP_DUP <0x00> OP_EQUAL | |
OP_IF OP_DROP | |
// Stack: <new owner key> <output key prefix> -- <current price> <current owner key> | |
// Verify payment output to current owner at index #1 | |
// Is tL-BTC | |
<1> OP_INSPECTOUTPUTASSET | |
OP_VERIFY // check explicit asset id prefix | |
// Get LBTC asset id | |
OP_FROMALTSTACK OP_DUP OP_TOALTSTACK | |
OP_EQUALVERIFY | |
// Pays to current owner key (pops the owner key off the stack) | |
<1> OP_INSPECTOUTPUTSCRIPTPUBKEY | |
OP_VERIFY // check witness version 1 (taproot) | |
OP_EQUALVERIFY | |
// Duplicate the current price for later use | |
OP_DUP | |
// Amount paid matches the current price | |
<1> OP_INSPECTOUTPUTVALUE | |
OP_VERIFY // check explicit prefix | |
OP_EQUALVERIFY | |
// Verify royalty payment output to creator at index #2 | |
// Is tL-BTC | |
<2> OP_INSPECTOUTPUTASSET | |
OP_VERIFY // check explicit asset id prefix | |
// Get LBTC asset id constant (removing it from the altstack) | |
OP_FROMALTSTACK | |
OP_EQUALVERIFY | |
// Pays to creator key | |
<2> OP_INSPECTOUTPUTSCRIPTPUBKEY | |
OP_VERIFY // check witness version 1 (taproot) | |
// Get creator key constant (removing it from the altstack) | |
OP_FROMALTSTACK | |
OP_EQUALVERIFY | |
// Amount paid matches the <royalty> setting | |
// Calculate royalty amount (price/<royalty>) | |
// Get royalty constant (removing it from the altstack) | |
OP_FROMALTSTACK | |
OP_SCRIPTNUMTOLE64 | |
OP_DIV64 | |
OP_VERIFY // verify DIV didn't overflow | |
OP_SWAP OP_DROP // ignore the remainder | |
// TODO minimum dust amount | |
<2> OP_INSPECTOUTPUTVALUE | |
OP_VERIFY // check explicit prefix | |
OP_EQUALVERIFY | |
// Bring new owner to top of stack | |
OP_SWAP | |
// Set price to MAX (that is, disallow sales until a new price is set by the new owner) | |
<0xffffffffffffffff> $N_price | |
// Stack: <output key prefix> <owner key (key)> <price (max)> | |
// Alt stack: <script-path-only point> <own tapscript> | |
// 0x01: Update price | |
OP_ELSE <0x01> OP_EQUAL OP_IF | |
// Stack: <new price> <owner sig> <output key prefix> -- <current price> <current owner key> | |
// Keep owner key for later use | |
OP_DUP | |
// Bring the sig before the key | |
<4> OP_ROLL OP_SWAP | |
// Verify owner signature | |
OP_2DROP // FIXME OP_CHECKSIGVERIFY -- scriptwiz doesn't support CHECKSIG in Liquid Taproot | |
// Drop old price | |
OP_SWAP OP_DROP | |
// Bring new price to front | |
<2> OP_ROLL | |
// Stack: <output key prefix> <owner key (unchanged)> <price (new)> | |
// Drop unnecessary altstack constants (royalty-related, only needed for Buy calls) | |
OP_FROMALTSTACK OP_FROMALTSTACK OP_FROMALTSTACK OP_2DROP OP_DROP | |
// Alt stack: <script-path-only point> <own tapscript> | |
// 0x02: Destroy token (creator 'backdoor') | |
OP_ELSE | |
// Stack: <creator sig> <output key prefix> -- <current price> <current owner key> | |
// Drop current state, we don't need it | |
OP_2DROP | |
// Get creator key constant (removing it and the royalty ratio from the altstack) | |
OP_FROMALTSTACK OP_DROP OP_FROMALTSTACK | |
// Bring sig before key | |
<2> OP_ROLL OP_SWAP | |
// Verify creator signature | |
OP_2DROP // FIXME OP_CHECKSIGVERIFY | |
// Drop unnecessary altstack constant | |
OP_FROMALTSTACK OP_DROP | |
// Transition state into an impossible price and impossible pubkey, | |
// effectively taking the token out of circulation | |
// Use point with unknown discrete logarithm as the new owner pubkey | |
OP_FROMALTSTACK OP_FROMALTSTACK OP_DUP OP_TOALTSTACK OP_SWAP OP_TOALTSTACK $N_owner | |
// Use MAX as the new price (more than all bitcoins to be created) | |
<0xffffffffffffffff> $N_price | |
// Stack: <output key prefix> <owner key (unknown discrete logarithm)> <price (max)> | |
OP_ENDIF OP_ENDIF | |
// Verify new owner key and price are of right size | |
OP_SIZE <8> OP_EQUALVERIFY | |
OP_SWAP | |
OP_SIZE <32> OP_EQUALVERIFY | |
// Stack: <output key prefix> <price> <owner key> | |
// Enforce recursive covenant at output #0 | |
// Matches the input's asset id | |
<0> | |
OP_INSPECTOUTPUTASSET | |
OP_VERIFY // check explicit asset prefix | |
OP_PUSHCURRENTINPUTINDEX | |
OP_INSPECTINPUTASSET | |
OP_VERIFY // check explicit asset prefix | |
OP_EQUALVERIFY // check the asset id matches | |
// Same amount as the input (typically 1 token) | |
<0> | |
OP_INSPECTOUTPUTVALUE | |
OP_VERIFY // check explicit amount prefix | |
OP_PUSHCURRENTINPUTINDEX | |
OP_INSPECTINPUTVALUE | |
OP_VERIFY // check explicit amount prefix | |
OP_EQUALVERIFY | |
// Verify the output tapscript matches this the contract's tapscript with the updated state | |
// Build the state blob for the new state - <0x08><price><0x20><owner pubkey> | |
<0x20> // size of <owner pubkey> | |
OP_SWAP OP_CAT | |
OP_CAT | |
<0x08> // size of <price> | |
OP_SWAP | |
OP_CAT $N_state | |
// Stack: <output key prefix> <new state blob> | |
// Build the expected destination tapscript (<new state> || <push(own tapscript)> || <own tapscript>) | |
OP_FROMALTSTACK | |
OP_DUP | |
// PUSH for <own tapscript> (without state) | |
OP_SIZE | |
<0x4d> // PUSH with two bytes for the size (0x4c for shorter scripts with a size of 1 byte) | |
OP_SWAP | |
OP_CAT | |
OP_SWAP | |
OP_CAT | |
// Append <own tapscript> code (actual opcodes, not a data push) | |
OP_SWAP OP_CAT | |
// Prepend <new state> blob | |
OP_CAT $N_script | |
// Stack: <output key prefix> <expected tapscript> | |
// Push the taproot tweak key for the expected script into the stack | |
// TapTweak tag prefix | |
<'TapTweak/elements'> | |
OP_SHA256 OP_DUP OP_CAT | |
// Add internal pubkey to tweak (point with unknown discrete logarithm constant) | |
OP_FROMALTSTACK OP_DUP OP_TOALTSTACK | |
OP_CAT | |
// Add tap leaf to tweak | |
// TapLeaf tag prefix | |
<'TapLeaf/elements'> | |
OP_SHA256 OP_DUP OP_CAT | |
// Leaf version | |
<0xc4> | |
OP_CAT | |
// Move expected tapscript (including state) to top | |
<2> | |
OP_ROLL | |
// Prefix tapscript with size | |
OP_SIZE | |
// Add varint prefix | |
<0xFD> OP_SWAP OP_CAT | |
OP_SWAP OP_CAT | |
// Add size+tapscript to leaf | |
OP_CAT | |
OP_SHA256 | |
OP_CAT | |
OP_SHA256 $N_tweak | |
// Stack: <output key prefix> <tweak key for expected new tapscript> | |
// Push the actual tweaked script used by the destination output (prefixed with the 0x02/0x03 prefix) | |
// Move <output key prefix> to the top | |
OP_SWAP | |
// Get the SPK of the destination output | |
<0> OP_INSPECTOUTPUTSCRIPTPUBKEY | |
OP_VERIFY // check witness version is 1 (tapscript) | |
OP_CAT $A_dest | |
OP_SWAP | |
// Stack: <actual tweaked destination output script> <tweak key for expected new tapscript> | |
// Get point with unknown discrete logarithm constant (script-path-only) | |
OP_FROMALTSTACK | |
// Verify that the taproot tweak for the destination output matches the expected tapscript | |
OP_TWEAKVERIFY |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment