This proposal is an alternative to bip119 and bip118, providing the functionality of both proposals with minimal additional overhead in many cases, while clearing certain objections to both, and opening clear upgrade paths.
This is, in essence, an initially constrained version of Russel O'Connor's OP_TXHASH+OP_CSFS proposal.
We define three new Tapscript-only opcodes. Replacing OP_SUCCESS80
,
OP_SUCCESS187
, and OP_SUCCESS188
with OP_TXHASH
, OP_CHECKSIGFROMSTACK
,
and OP_CHECKSIGFROMSTACKVERIFY
respectively.
For OP_TXHASH
, we define exactly 5 methods of hashing the transaction depending
on a minimally encoded numeric argument popped from the stack:
argument | behavior |
---|---|
0 | as in bip119 |
1 | as in bip118 with sighash flag 0x41 |
2 | as in bip118 with sighash flag 0xc1 |
3 | as in bip118 with sighash flag 0x43 |
4 | as in bip118 with sighash flag 0xc3 |
OP_CHECKSIGFROMSTACK(VERIFY)
is defined similarly to the implementation in
the Elements project, but does not
internally SHA256 hash the data argument. As bip340 defines signatures on
arbitrary length messages, and these OP_CHECKSIGFROMSTACK(VERIFY)
are
defined only in Tapscript, the internal hashing is unnecessarily restrictive.
Users may wish to use pre-hashed values as in this proposal, or non-SHA256
hashes available in script. OP_CHECKSIGFROMSTACK(VERIFY)
also inherit the
ability to check signatures against the taproot internal key from bip118
(using OP_0
instead of OP_1
as the reasons for using 1 do not apply in
this context).
Much ink has been spilled on the discussion of what is next for bitcoin scipt development. The two proposals nearest to consensus are bip118 and bip119, but the proponents of each disagree about the relative priority and the merits of the other. Here, we'll briefly outline some of the objections to each and demonstrate how this proposal reduces those objections. We will not discuss the concerns about the introduction of covenants or recursive covenants generally.
CTV Objections
- Not general enough
- Inefficient when otherwise validating the hash (e.g. when combined with
OP_CHECKSIGFROMSTACK
) - Uses
OP_NOPx
extension semantics even thoughOP_SUCCESSx
is available
APO Objections
- Not general enough
- Accidentally enables inefficient, hard to use covenants
- Uses new Tapscript key version to avoid accidents
- By providing the behavior of both bip118 and bip119, this proposal is more general than either of those proposals. It also provides explicit upgrade hooks for further generality (e.g. to full OP_TXHASH).
- By splitting the hashing from the validation of bip119, the hash can be
used in ways other than
OP_EQUALVERIFY
. - We use
OP_SUCCESSx
upgrade semantics. - We explicitly enable some of the sighash-based covenants accidentally enabled by bip118.
- By using new signature checking opcodes, we do not require the safety of a new Tapscript key version.
When validating Tapscript, the behavior of OP_SUCCESS80
is modified as
follows:
- If there is not at least one item on the stack, fail1.
- If the top item on the stack is not a minimally encoded
OP_0
,OP_1
,OP_2
,OP_3
, orOP_4
; succeed immediately2. - Pop the top item from the stack, and name it
hash_mode
- If
hash_mode
is 0:- Hash the transaction as defined in bip119
- Push the resulting hash to the stack
- If
hash_mode
is 1:- Hash the transaction as defined in bip118 using
sighash_type=0x41
- Push the resulting hash to the stack
- Hash the transaction as defined in bip118 using
- If
hash_mode
is 2:- Hash the transaction as defined in bip118 using
sighash_type=0xc1
- Push the resulting hash to the stack
- Hash the transaction as defined in bip118 using
- If
hash_mode
is 3:- Hash the transaction as defined in bip118 using
sighash_type=0x43
- Push the resulting hash to the stack
- Hash the transaction as defined in bip118 using
- If
hash_mode
is 4:- Hash the transaction as defined in bip118 using
sighash_type=0xc3
- Push the resulting hash to the stack
- Hash the transaction as defined in bip118 using
When validating Tapscript, the behavior of OP_SUCCESS187
and OP_SUCCESS188
are modified as follows:
- If there are not at least 3 items on the stack, fail1.
- If the top-minus-0 stack item is exactly equal to OP_0
- Pop the top item from the stack
- Push the taproot internal key to the stack
- If the top-minus-0 stack item's length is not 32, succeed immediately2.
- If the top-minus-2 stack item's length is not 64, fail3.
- Pop the top 3 stack items as
pubkey
,msg
, andsig
respectively. - Let
result
equal the result of verifyingsig
againstmsg
andpubkey
according to bip340. - Push
true
ifresult
otherwisefalse
to the stack. - If validating
OP_CHECKSIGFROMSTACKVERIFY
- Pop the top item from the stack as
check
. - If
check
is nottrue
, fail.
- Pop the top item from the stack as
How does the efficiency compare to bip118?
SIGHASH_ANYPREVOUT
:
<64-byte signature>||<1-byte sighash type> <33-byte pubkey> OP_CHECKSIG(VERIFY)
with pushes: 64+1+1 + 33+1 + 1 = 101 witness bytes (25.25vBytes)
This proposal:
<64-byte signature> <1-byte argument> OP_TXHASH <32-byte pubkey> OP_CHECKSIGFROMSTACK(VERIFY)
with pushes: 64+1 + 1 + 1 + 32+1 + 1 = 101 witness bytes (25.25vBytes)
How does the efficiency compare to bip119?
OP_CHECKTEMPLATEVERIFY
alone4:
<32-byte hash> OP_CHECKTEMPLATEVERIFY
with pushes: 32+1 + 1 = 34 witness bytes (8.5vBytes)
OP_CHECKTEMPLATEVERIFY
with a subsequent check:
<32-byte hash> OP_CHECKTEMPLATEVERIFY OP_DROP <...>
with pushes: 32+1 + 1 + 1 = 35 witness bytes (8.75vBytes)
This proposal:
<1-byte argument> OP_TXHASH <32-byte hash> OP_EQUAL(VERIFY)
with pushes: 1 + 1 + 32+1 + 1 = 36 witness bytes (9 vBytes)
Bare OP_CHECKTEMPLATEVERIFY
4:
Lock: <32-byte hash> OP_CHECKTEMPLATEVERIFY
with pushes: 32+1 + 1 = 34 bytes (34vBytes)
Unlock: <empty>
Total: 34 + 0 = 34vBytes
Witness v0 CTV:
Lock: OP_0 <32-byte hash>
with pushes: 1 + 32+1 = 34 bytes (34 vBytes)
Unlock:
<34-byte witness script>
with sizes: 34+1 = 35 witness bytes (8.75vBytes)
Total: 34 + 8.75 = 42.75vBytes
This proposal:
Lock: OP_1 <32-byte pubkey>
with pushes: 1 + 32+1 = 34 bytes (34 vBytes)
Unlock:
<36-byte leaf script> <33-byte control block>
with sizes: 36+1 + 33+1 = 71 witness bytes (17.75vBytes)
Total: 34 + 17.75 = 51.75vBytes
Compared to bare CTV, this proposal is 17.75vBytes more costly. If CTV use cases gain popularity, a separate upgrade for bare CTV may be warranted, either as specified in bip119, as its own witness version, or some alternative.
Bearing in mind the risks mentioned in bip119, fee sensitive users can add
OP_RIPEMD160
to save 2.75 vBytes when using OP_TXHASH
with
OP_EQUAL(VERIFY)
. This brings the excess cost relative to bare CTV down to
15vBytes.
Possibly due to a lack of imagination, we are unable to see a use for signing none of the inputs and none of the outputs (or a single input script and none of the outputs).
Can this be used in ln-symmetry?
Yes, this is fully compatible with ln-symmetry. It uses a different script, but has the same size and behavior as bip118 for this purpose.
Yes, this is fully compatible with PTLCs. It uses a different script, but has the same size and behavior as bip118 for this purpose.
Yes, this is fully compatible with OP_VAULT. It uses a different script, but has the same size and behavior as bip119 for this purpose.
Yes. The argument to OP_TXHASH
is taken from the stack, so it can be
specified by the spender, however the script would need to do a range check to
prevent OP_TXHASH
being turned into OP_SUCCESS
. This would only be
advisable with a signature check to authorize the spend.
OP_0 OP_5 OP_WITHIN OP_VERIFY OP_TXHASH <pubkey> OP_CHECKSIGFROMSTACK
It is also possible to construct a script which allows spending with
OP_TXHASH OP_CHECKSIGFROMSTACK
or OP_CHECKSIG
with the same pubkey
depending on the spend stack.
<pubkey> OP_TOALTSTACK OP_SIZE OP_1SUB OP_IF OP_FROMALTSTACK OP_CHECKSIG OP_ELSE OP_0 OP_5 OP_WITHIN OP_VERIFY OP_TXHASH OP_FROMALTSTACK OP_CHECKSIGFROMSTACK OP_ENDIF
field \ mode | CTV(0) | APO/ALL(1) | APOAS/ALL(2) | APO/SINGLE(3) | APOAS/SINGLE(4) | ARK6 |
---|---|---|---|---|---|---|
hash type | x | x | x | x | x | |
version/locktime | x | x | x | x | x | x |
this input UTXO | ||||||
other input UTXOs | x | |||||
this script pubkey/amount | x | x | x | |||
other script pubkeys/amounts | x | |||||
this script sig | x | x | ||||
other script sig | x | x | ||||
the number of inputs | x | x | ||||
this input sequence | x | x | x | x | x | x |
other input sequences | x | x | ||||
spend type/annex | x | x | x | x | x | |
leaf script | x | x | x | |||
key version/codesep pos | x | x | x | x | x | |
tap merkle path7 | x | |||||
corresponding output script/amount | x | x | x | x | x | x |
other output scripts/amounts | x | x | x | x | ||
the number of outputs | x | x |
If this proposal gains traction, we'll implement it similarly to bip119, updated to be more in line with bip341/bip342 hashing. This gives us an opportunity to consider additional fields for hashing. Specifically:
- all modes will get an updated hash type specific to this application
- modes 0, 1, and 3 include the tap merkle path
- modes 1 and 2 include the number of outputs
- modes 1, 2, 3, and 4 drop the (now meaningless) key version
- mode 0 includes the spend type, annex, and codesep pos
field \ mode | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
hash type | x | x | x | x | x |
version/locktime | x | x | x | x | x |
this input UTXO | |||||
other input UTXOs | |||||
this script pubkey/amount | x | x | |||
other script pubkeys/amounts | |||||
this script sig | x | ||||
other script sig | x | ||||
the number of inputs | x | ||||
this input sequence | x | x | x | x | x |
other input sequences | x | ||||
spend type/annex | x | x | x | x | x |
leaf script | x | x | |||
codesep pos | x | x | x | x | x |
tap merkle path7 | x | x | x | ||
corresponding output script/amount | x | x | x | x | x |
other output scripts/amounts | x | x | x | ||
the number of outputs | x | x | x |
Footnotes
-
We fail on invalid stack lengths to ensure that attackers cannot skip validation. ↩ ↩2
-
We succeed on unspecified txhash modes or pubkey lengths to allow future extensions. ↩ ↩2
-
We fail on invalid signature lengths after the pubkey length check, thus allowing only 64-byte signatures for 32-byte keys, but allowing future key types to potentially also have different signature lengths. ↩
-
OP_CHECKTEMPLATEVERIFY
leaves the checked hash on the stack, which evaluates to true via bitcoin's CastToBool function, so this satisfies both legacy and segwit script success criteria without any additional ops (e.g. OP_DROP OP_TRUE). ↩ ↩2 -
As far as we know there is no use for sighash types other than those defined in bip118 with this proposal, as the other types either reduce to
OP_CHECKSIG(VERIFY)
or create infinite hash loops. ↩ -
Ark could benefit from a hash that includes other UTXOs but not this UTXO. This would introduce uncacheable per-input hashing. ↩
-
Some or all of these probably should: https://github.com/bitcoin-inquisition/bitcoin/issues/19 ↩ ↩2