For situations where a multisig is appropriate
Could you give any example, where it is not?
For situations where a multisig is appropriate
Could you give any example, where it is not?
1. Password Recovery
Let's say I want to crack a sha256 password hash (your own personal password and not someone else's, of course).
If know the hash is 5bf01f36fa15d5ccf2dea7a53c777426d559a9a8d1384e58bc8d3dc423cd2f12, I could offer a reward in bitcoin for anyone who can provide the preimage to the password with the following script:
OP_SHA256
<5bf01f36fa15d5ccf2dea7a53c777426d559a9a8d1384e58bc8d3dc423cd2f12>
OP_EQUAL
You can try it out if you like on testnet (the password is
supersecretpassword)
2.
Recursive Covenants + Trustless peg-in/peg-out for layer 2s
Imagine we have a layer 2 which posts a withdrawals tree of the chain to bitcoin, and we want to allow users to trustlessly withdraw their bitcoins from layer 2.
When block producers submit a block they spend and existing block script which has two outputs:
1. an output to a withdrawal UTXO script which can be spent by the users who withdrew funds from layer 2 back to layer 1 in the most recent block (includes a withdrawal tree merkle root)
2. a new block UTXO for the next block (recurse)
The withdrawal script can only be spent by spending a recursive covenant which includes a delta merkle proof proving the validity of the spend of one of the withdrawals and an additional recursive covenant that creates a new withdrawal script with the updated withdrawal tree root (the withdrawal made is set to the null value so you can't spend a withdrawal more than once).
The locked BTC in the layer 2 block UTXO can only be sent to a script which publishes a new block (1 output) or a withdrawal script which allows users to withdraw a portion of the BTC and sends the rest to a new script with the updated merkle root (2 outputs).
In our withdrawal script, we verify a merkle proof which links the previously submitted state root/withdrawals tree root with a spender:
<0x8fe1e5f7bc9333914565f011a63942d988c88b00547b35af859635b00cbbc64c> // old root, must be this value as enforced by the block UTXO covenant
<0x7163998f26fec2e57db7413d706d30e7b9f14e13b416217160221ff05690fb65> // new root after spending the withdrawal and removing it from the withdrawals tree
// merkle proof siblings
<0x2f0935c2963ead8b3be0ee5d4d4dcb3af23646c6bbfc330e41e90a47ee8cf7f7> // sibling at level 14
<0xeac71276dd976b550cace2fb1097ad5d296193fea0359202ca4899b398ab2436> // sibling at level 13
<0x2c7011733c31c9c58e0d5cbe40be77c714a4b77b281ffd72155276af21452e24> // sibling at level 12
<0x5cc411ccc9b0ce4704798316824afffbbeb724d7fb06701bf7c57f1fa122102f> // sibling at level 11
<0x485a020e9ce6f948b88528bc38ac34838283bd4a001db1f99b84cbe87ebe3e99> // sibling at level 10
<0x885fb789bdd39eee523ade57bedba8fa308b9014c910d6db04b779732139c5b7> // sibling at level 9
<0x8b4578b4daaa30f78eaa2f5537142c9fe277a87fc4e8c5dd872ab95524d34c48> // sibling at level 8
<0xdcd5818eaddf2de1a4fd8792664403a1b1e24779baf3bfac02f169ae63b46d95> // sibling at level 7
<0x7900bfb52e43f1c0b93a87e4b42bc652a1de0c6e72becc37a4f1d26a3eac42a6> // sibling at level 6
<0x2013541c218b99def8301bb9b283f3696605e39a267e757e61692e47623bebdb> // sibling at level 5
<0x9e5a86f32a7231309e3f4bc44473a181b3482352acc8e332d10b493a8acca77c> // sibling at level 4
<0xca67e1b7055afe377bd9fb285335a3685472b1c08f94cf133481320b0fe63653> // sibling at level 3
<0xf464a8d91ca7029a4b88483b7d778403c310c2eaabf46a905874343f975b49a0> // sibling at level 2
<0xaa6ef54b7988700bbcffcaf327be3ade37a68573e9fd6a498a00600059644a28> //sibling at leaf level
<999> // index of the withdrawal leaf we are spending with in the withdrawals tree
// recursive covenant inputs...
<0x3905000000000000> // 8 byte spend amount (1337)
<0xb4a2ef4141dfdedc3b548222b11fcf90bf81782ed6bf998b6552ab08978b2ce6> // spender
OP_2DUP
OP_CAT
OP_SHA256 // compute the withdrawal hash -> 0xeac7f4e4689862aa7baf72c1673a48a66561ab222eb6a6d8af38c32393265d90
OP_TOALTSTACK // move the withdrawal hash to the alt stack for later
// the rest of the recursive covenant code
// now only the merkle roots + siblings + index remains in the stack
OP_FROMALTSTACK // old leaf value computed earlier (withdrawal hash)
<0x0000000000000000000000000000000000000000000000000000000000000000>// new value
OP_ROT // move the index to the top of the stack
// start decompose the index into bits
// we decompose the number into bits which are stored on the alt stack
<8192>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<4096>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<2048>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<1024>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<512>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<256>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<128>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<64>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
<32>
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
OP_16
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
OP_8
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
OP_4
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
OP_2
OP_2DUP
OP_GREATERTHANOREQUAL
OP_DUP
OP_TOALTSTACK
OP_DUP
OP_TOALTSTACK
OP_IF
OP_SUB
OP_ELSE
OP_DROP
OP_ENDIF
OP_DUP
OP_TOALTSTACK
OP_TOALTSTACK
// end decompose the index into bits
// now the lowest bit of the index is on the alt stack
// compute old and new merkle roots
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
OP_ROT
OP_ROT
OP_OVER
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute old merkle path at level
OP_ROT
OP_ROT
OP_FROMALTSTACK
OP_ROLL
OP_CAT
OP_SHA256 // compute new merkle path at level
// end compute old and new merkle roots
Now we just check the old and new roots from the inputs to make sure the match the computed ones (ie. proof that the withdrawal exists in the tree, and that the merkle tree with merkle root new root has all the same leaves as old tree, except for the leaf at the specified index that has just been spent (and has been nulled out):
/*
now the stack is as follows:
<old merkle root> // guarenteed correct via the covenant
<new merkle root> // attested by the spender
<computed old merkle root> // computed from the index/siblings/withdrawal hash value
<computed new merkle root> // computed from the index/siblings/nulled out value
*/
OP_ROT
OP_EQUALVERIFY // check that the attested new merkle root is a copy of the old tree with the spent leaf nulled out