I read on bitcoin-dev that CHECKSIGFROMSTACKVERIFY (CHECKDATASIG in BCash) can be used to emulate new sighash flags. How would that work, concretely? As an example, please provide an example update transaction based on figure 4 of the Eltoo paper, or aj's simplified lightning (that doesn't work with watchtowers).
Eltoo relies on a primitive
SIGHASH_NOINPUT (hereafter referred as APO) BPI118, constructing Eltoo from APO would be replacing the
CHECKSIG figure 4 of Eltoo paper with a modified script.
Most of this answer is based on a blog post by Russell O'Connor from this link(which goes into other covenant constructions too). For completeness, let us look at the basics of transaction introspection and later hop into how to construct
APO. The answer is for Segwit Signature Hash and the ECDSA signature algorithm. Unfortunately, extending this to taproot requires additional opcodes/other changes which I will explain at the end of the answer.
CSFS) There is no formal proposal for this opcode yet, but most likely this would work as follows: pop 1) a pubkey
pk, 2) a message
m, and a 3) digital signature
sig. The operation would perform the verify the sig as ECSDA.Verify(pk, sha256(m), sig) and push the result onto the stack top.
- OP_CAT: Pop two items from stack
next_top || toponto the stack where
||denotes the byte string concatenation. I will be assuming a
SIGHASH_ALLfor the below construction, but the construction can be used for sighash type.
First we will look at basic transaction introspection using
Consider the following script:
1. OP_OVER OP_SHA256 <pubKey> 2. 2 OP_PICK 1 OP_CAT OP_OVER 3. OP_CHECKSIGVERIFY 4. OP_CHECKSIGFROMSTACKVERIFY
with the witness stack
<fixed_pk> is public whose secret key is known to everyone. Say, the generator
G whose secret key is
<sig_msg> denotes transition digest being signed.
At the end of Step 1, the stack is
<pubKey> <sha256(<sig_msg>)> <signature> <<sig_msg>>
At the end of step 2, the stack contents are
<pubKey> <sig;SIGHASH_ALL> <pubKey> <sha256(sig_msg)> <sig> <sig_msg>
1 is translated as
SIGHASH_ALL and catted to
sig. This creates a regular bitcoin signature which we can verify using regular
OP_CHECKSIG. After Step 3, the stack contents are
<pubKey> <sha256(sig_msg)> <sig> <sig_msg>
Which would call
OP_CHECKSIGFROMSTACKVERIFY to leave only
<sig_msg> onto the stack. The OP_CHECKSIGFROMSTACKVERIFY of step 4 uses the same pubKey and signature. This operation performs another SHA-256(double sha256 is required for segwit sighash algorithm) and does a digital signature validation on the doubly hashed <sig_msg>. Note that because we are using exactly the same
<sig> that we used for the previous
OP_CHECKSIGVERIFY operation, the current
OP_CHECKSIGFROMSTACKVERIFY can only succeed if <sig_msg> is identical to the message that was checked during step 3’s OP_CHECKSIGVERIFY operation. At this point, we are certain that the
<sig_msg> on the stack top is the same as the signed transaction message.
Note that since the private key to
<fixed_pk> is known, anyone can create this signature, but this script would only succeed if the
<sig_msg> is correctly specified as mentioned above.
Anyprevout essentially is substituting some things from the regular signature hash messages and replacing those with 0's. In detail, we can now split our
sig_msg into multiple parts as defined in BIP143 using
OP_SUBSTR. It is also possible to use
OP_CAT to initially supply the split up the message and then CAT it together.
Now that we have signature hash message as per BIP143 on the stack, we manipulate it to make certain fields 0's. In particular, we make items 2-4 0.
2. hashPrevouts (32-byte hash) 3. hashSequence (32-byte hash) 4. outpoint (32-byte hash + 4-byte little endian)
We take a
OP_DUP OP_SUSTR 0 <4> to get item 1, version. onto the stack. and similarly, obtain a concatenation of items 5-10 onto the stack. Finally, we CAT them together by
<ver> <[0; (32+ 32 + (32 +4))]> <items 5.10>` to get a new sighash which we use for Eltoo update key.
OP_OVER OP_SHA256 <pubKey> 2 OP_PICK 1 OP_CAT OP_OVER OP_CHECKSIGVERIFY OP_CHECKSIGFROMSTACKVERIFY
as we discussed above. And let
manipulate_txdata_script be the script to modify the stack contents from BIP143 sighash message to get a new message which sets the respective items to
0s. Then our final substitute for
OP_CHECKSIGANYPREVOUT would be the following script.
final_script(eltoo_pk) = <introspect_script> <manipulate_txdata_script> <eltoo_pk> OP_CSFS
Satisfying this would require the following onto the initial witness stack.
<sig_from_fixed_key> <sig_msg> <sig_from_eltoo_update_priv_key>
Lastly, the eltoo script would now look like:
OP_IF 10 OP_CSV 2 A(s,i) B(s,i) 2 OP_CHECKMULTISIGVERIFY OP_ELSE <Si + 1> OP_CHECKLOCKTIMEVERIFY final_script(Au) final_script(Bu) OP_ENDIF
Note how the two of two multi-sig is split into two parts.
Taproot Signature Hash:
The same technique does not work for Taproot Signature Hash algorithm(see BIP341) and Schnorr signatures (see BIP340) because of tagged hashes. At a high level, because of tags, the size of these messages is more than 520 bytes which is currently the maximum supported by bitcoin network. It is possible to overcome this limitation by adding 1)streaming
SHA256 opcodes, 2) by raising the per stack element limit, Or 3) adding support for tagged hashes by new opcodes.