-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add bip-csfs OP_CHECKSIGFROMSTACK(VERIFY) #1535
base: master
Are you sure you want to change the base?
Conversation
bip-csfs.mediawiki
Outdated
==Summary== | ||
|
||
We propose replacing OP_NOP5 (0xb4) in bitcoin script with | ||
<code>OP_CHECKSIGFROMSTACKVERIFY</code>. When verifying taproot script spends having |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why split across two extension mechanisms instead of just focusing on the modern path (op success)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checking signatures from the stack is something that can be supported on all script types (i.e. is compatible with NOP upgrade), and there are use cases where segwit v0 scripts still make the most sense vs. tapscripts.
If supporting legacy and segwitv0 where possible for new opcodes is not something that others value, switching CSFSV to an OP_SUCCESS upgrade would make sense.
bip-csfs.mediawiki
Outdated
** If the signature is not the empty vector, the signature is validated against the public key and message according to BIP340. Validation failure in this case immediately terminates script execution with failure. | ||
* For legacy and segwit v0 scripts, if the public key size is 33 bytes and its first byte is 0x02 or 0x03, it is considered a compressed public key as described in BIP137: | ||
** If the signature is not the empty vector, the signature is validated against the public key and message using ECDSA. Validation failure in this case immediately terminates script execution with failure. | ||
* If the public key size is not zero, and it is not a BIP340 public key, nor a BIP137 compressed public key; the public key is of an unknown public key type, and no actual signature verification is applied. During script execution of signature opcodes they behave exactly as known public key types except that signature validation is considered to be successful. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public key types were only introduced in segwit v1 (BIP 342), so this wouldn't apply to a version of this op code that also exists under the segwit v0 paradigm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signature verification for CHECKSIGFROMSTACK is done the same regardless of the script context.
bip-csfs.mediawiki
Outdated
*** For <code>OP_CHECKSIGFROMSTACK</code>, an empty vector is pushed onto the stack, and execution continues with the next opcode. | ||
** If the signature is not the empty vector: | ||
*** For tapscript 0xc0, the opcode is counted towards the sigops budget as described in BIP341. | ||
*** For legacy and segwit v0, the opcode is counted towards the sigops limit, as described in BIP141 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sig ops budget is described in BIP 342: https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#resource-limits
bip-csfs.mediawiki
Outdated
*** For tapscript 0xc0, the opcode is counted towards the sigops budget as described in BIP341. | ||
*** For legacy and segwit v0, the opcode is counted towards the sigops limit, as described in BIP141 | ||
*** For <code>OP_CHECKSIGFROMSTACKVERIFY</code>, execution continues without any further changes to the stack. | ||
*** For <code>OP_CHECKSIGFROMSTACK</code>, a 1-byte value 0x01 is pushed onto the stack. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if there's a use for a OP_CHECKSIGFROMSTACKADD
variant...
So something that would be compatible with usage of OP_CHECKSIGADD
. On the other hand, it is possible to replicate such behavior with OP_CHECKSIGFROMSTACK
in combination with other existing op codes, eg:
OP_TOALTSTACK (assuming the number N is on the top of the stack)
<pubkey> OP_CHECKSIGFROMSTACK
OP_IF
OP_DROP (drop the 0x01 pushed in the success case)
OP_FROMALTSACK (bring back the accumulator)
1 OP_ADD (acc += 1)
OP_ELSE
OP_DROP (drop the empty vector)
OP_FROMALTSACK (put unchanged accumulator back on the stack)
OP_ENDIF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep. I had the same internal conversation and concluded that the rare cases where CSFSA behavior is needed using the altstack is an acceptable alternative.
Compared to standard CHECKSIG where CHECKMULTISIG was in active use on legacy/segwitv0 wallets, new wallets or protocols built on CSFS are much more likely to use MuSig2 or FROST when multiple signers are insolved, or (as BitGo's 2-of-3 MuSig2/tapscript wallet does) to have the different signing combinations split between tapscript paths where CSFSV+CSFS are more efficient than CSFSA.
b1cb659
to
f6e50d1
Compare
Updated to match the BIN, and with @Roasbeef 's comments. TYVM! |
ea7b96b
to
0267f3f
Compare
Fixed preamble. There's still 2 open conversations around this one:
I'll post on the mailing list for additional opinions on these topics, but not sure whether it makes sense for the BIP to get numbered and merged as is while still potentially subject to those revisions. |
@reardencode: If you feel that your proposal is not ready to be merged, please convert the pull request to a Draft. |
## Specification | ||
|
||
* If fewer than 3 elements are on the stack, the script MUST fail and terminate immediately. | ||
* The public key (top element), message (second to top element), and signature (third from top element) are read from the stack. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd like to offer the suggestion that the push order goes:
<msg> <sig> <key>
instead of <sig> <msg> <key>
. The current CHECKSIG
ordering is <sig> <key>
and it seems odd to me that we would make the CSFS opcode put the msg between these other pushes. Alternatively you could go <sig> <key> <msg>
and it would seem to me as equally intuitive. As is though it feels like a gotcha.
Is there a specific reason this choice was made that I hadn't considered?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything that comes from the input stack should be before things that are provided by the locking script, so sig
definitely comes first. Depending on particulars msg
might come from the input stack or the locking script, and key
always comes from the locking script. Examples:
<delegate-sig> <sig> <delegate-key> || DUP TOALT <key> CSFS VERIFY FROMALT CHECKSIG
<sig> <hash> || CTV <key> CSFS
<sig> || <flags> TXHASH <key> CSFS
<sig> <settlement-hash> <update-hash> || CTV SHA256 CAT <key> CSFS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retracted.
I understand the general direction of what you're getting at but I have to admit that I'm not following the specific use cases where <msg>
might come from the witness instead of the script. It makes sense to me why you'd want <msg>
in the script, what's lest clear is under what conditions it is useful to let the witness define it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any kind of delegation (see the first example above) the message is provided by the witness (i.e. the thing you're delegating to by signing). Here's a powerful example:
<delegate_sig> <sig> <delegate_pubkey> <locktime> || CLTV SHA256 CAT <pubkey> CSFS VERIFY CHECKSIG
Delegate to a key but only after <locktime>
, with different locktimes per delegate key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgive me but I have to expand this out because it doesn't make sense to me:
Witness:
- delegate signature is pushed onto the stack:
<delegate_sig>
- the signature authorizing the delegate is pushed:
<delegate_sig> <authorizing_sig>
- the delegate pubkey is pushed:
<delegate_sig> <authorizing_sig> <delegate_pubkey>
- locktime is pushed:
<delegate_sig> <authorizing_sig> <delegate_pubkey> <locktime>
Script:
5. locktime is checked: <delegate_sig> <authorizing_sig> <delegate_pubkey> <locktime>
6. locktime is hashed: <delegate_sig> <authorizing_sig> <delegate_pubkey> <locktime_hash>
7. locktime hash combined with delegate pubkey: <delegate_sig> <authorizing_sig> <delegate_pubkey || locktime_hash>
8. authorizing pubkey is pushed: <delegate_sig> <authorizing_sig> <delegate_pubkey || locktime_hash> <authorizing_pubkey>
9. CSFS(V) operation(s) checks that the delegate pubkey is authorized after the locktime by checking the key || locktime
message was signed by authorizing_sig
: <delegate_sig>
10. CHECKSIG operation fails because we don't have a key? Maybe you forgot a dup somewhere? Maybe I don't understand script as well as I thought? idk.
Assuming this is roughly what you meant, then I'd imagine in this case the authorizing signature would be pre-signed and distributed. Then at transaction time the delegate presents the authorizing signature along with their key and assigned locktime.
Assuming all of this is a fair reading, then I think I get what you mean with <msg>
having an interesting use case wherein it could be:
- provided by the script
- provided by the witness
- collaboratively derived from witness and script participation.
... which seems pretty cool. I'd love if you can correct either my understanding or your example for completeness, but otherwise I'm satisfied with this and this thread can be resolved.
This BIP is based on the BCH OP_CHECKDATASIG work, as well as postings from the bitcoin dev mailing list in this thread. Some differences appear due to the activation of bips 340, 341, and 342 (taproot) since those were developed.
OP_CHECKSIGFROMSTACKVERIFY is a NOP upgrade available in all script types.
OP_CHECKSIGFROMSTACK is an OP_SUCCESS upgrade available only in BIP342 tapscript.