Multi-byte opcodes
6 messages, 16 to 19 Nov 2024. Started by Weikeng Chen.
Brandon Black (2), Weikeng Chen, Garlo Nicon, Ethan Heilman, moonsettler
I think we need a way to allow more opcodes without taking up the rest of the NOPs.
This is related to a point from Murch (https://groups.google.com/g/bitcoindev/c/usHmnXDuJQc/m/hhtvAjSdCgAJ) that the reasoning of "its' compatible, why not" for adding CHECKSIGFROMSTACK(VERIFY/ADD) is not solid because when we add a new opcode, we usually have to give up a NOP. We do not have many NOPs left.
We can, however, solve that by allowing multi-byte opcodes.
Say, for example, we can have:
OP_OP { 0x1521 }
which will set the current opcode to be the one with the assigned number
0x1521.
Another idea is maybe OP_OP takes a stack element as the opcode.
{ 0x1521 } OP_OP
We can enforce some sort of minimal rule, or not do so, to allow more flexible use of existing opcodes.
This, of course, runs at a cost as this opcode needs three bytes in total to represent, but since the existing opcodes already take care of most of the basic functionalities that we expect users to use very frequently, the new opcodes that we want to add are likely those that complete something important and are going to be used only a few times in a script.
Similarly, we can require that multi-byte opcodes that have not been enabled my result in OP_SUCCESS.
OP_OP is not the best name as it could be confusing. OP_SETOP, OP_NEXT, etc could be taken into consideration.
The result of this is that we can worry less about whether it is worthy of a NOP to do an opcode, but focus on if the opcode has enough use cases to support it.
I feel that someone must have brought this up before (but it is a little bit hard to find the history in this mailing list at this moment).
What do people think?
Thanks, Weikeng
Quoted text (1 line)
> I think we need a way to allow more opcodes without taking up the rest of
the NOPs.
It is already possible since Taproot. For example: OP_CHECKSIGADD was added, without replacing any OP_NOP.
Quoted text (1 line)
> I feel that someone must have brought this up before (but it is a little
bit hard to find the history in this mailing list at this moment).
Satoshi added OP_SINGLEBYTE_END, set to 0xf0, and OP_DOUBLEBYTE_BEGIN, set to 0xf000. It was removed later, but it can be reintroduced in a similar way, if needed. See source code for version 0.1.0 for more details.
sobota, 16 listopada 2024 o 03:00:53 UTC+1 Weikeng Chen napisał(a):
Quoted text (46 lines)
> I think we need a way to allow more opcodes without taking up the rest of > the NOPs. > > This is related to a point from Murch ( > https://groups.google.com/g/bitcoindev/c/usHmnXDuJQc/m/hhtvAjSdCgAJ) that > the reasoning of "its' compatible, why not" for adding > CHECKSIGFROMSTACK(VERIFY/ADD) is not solid because when we add a new > opcode, we usually have to give up a NOP. We do not have many NOPs left. > > We can, however, solve that by allowing multi-byte opcodes. > > Say, for example, we can have: > OP_OP { 0x1521 } > which will set the current opcode to be the one with the assigned number > 0x1521. > > Another idea is maybe OP_OP takes a stack element as the opcode. > { 0x1521 } OP_OP > > We can enforce some sort of minimal rule, or not do so, to allow more > flexible use of existing opcodes. > > This, of course, runs at a cost as this opcode needs three bytes in total > to represent, but since the existing opcodes already take care of most of > the basic functionalities that we expect users to use very frequently, the > new opcodes that we want to add are likely those that complete something > important and are going to be used only a few times in a script. > > Similarly, we can require that multi-byte opcodes that have not been > enabled my result in OP_SUCCESS. > > OP_OP is not the best name as it could be confusing. OP_SETOP, OP_NEXT, > etc could be taken into consideration. > > The result of this is that we can worry less about whether it is worthy of > a NOP to do an opcode, but focus on if the opcode has enough use cases to > support it. > > I feel that someone must have brought this up before (but it is a little > bit hard to find the history in this mailing list at this moment). > > What do people think? > > Thanks, > Weikeng >
Hi Weikeng, thanks for your thoughts on this!
Quoted text (9 lines)
> We can, however, solve that by allowing multi-byte opcodes.
>
> Say, for example, we can have:
> OP_OP { 0x1521 }
> which will set the current opcode to be the one with the assigned number
> 0x1521.
>
> Another idea is maybe OP_OP takes a stack element as the opcode.
> { 0x1521 } OP_OPAnother option that works for many cases is to have opcode families where an argument is augmented with flags to determine the behavior. We can consider this to already be the case for OP_CHECKSIG* where the signature determines the behavior of the hashing portion of the opcode.
This is also how OP_CHECKTEMPLATEVERIFY is designed, and how OP_CHECKSIGFROMSTACKVERIFY as currently spec'd in the PR is designed. CTV and CSFSV only constrain 32-byte first arguments, but not other lengths leaving open extensions using any other length, including using other lengths of either opcode as OP_OP, or as variants on CTV and CSFSV respectively.
The benefit of this approach is that it doesn't "waste" the length byte only to specify the opcode behavior, but enables it to do double duty as specifying the total length of the first argument including both flags and data.
Best,
--Brandon
Couldn't we add opcodes contexts via the script version?
By context I mean an opcode that loads a 1 to N byte map rewriting the mapping from opcode number to what instruction that number signifies. This should let you have an infinite number of instructions. You could change the context multiple times in a script as a cost of only two or three bytes.
On Mon, Nov 18, 2024, 1:36 PM Brandon Black <freedom@reardencode•com> wrote:
Quoted text (41 lines)
> Hi Weikeng, thanks for your thoughts on this!
>
> > We can, however, solve that by allowing multi-byte opcodes.
> >
> > Say, for example, we can have:
> > OP_OP { 0x1521 }
> > which will set the current opcode to be the one with the assigned number
> > 0x1521.
> >
> > Another idea is maybe OP_OP takes a stack element as the opcode.
> > { 0x1521 } OP_OP
>
> Another option that works for many cases is to have opcode families
> where an argument is augmented with flags to determine the behavior. We
> can consider this to already be the case for OP_CHECKSIG* where the
> signature determines the behavior of the hashing portion of the opcode.
>
> This is also how OP_CHECKTEMPLATEVERIFY is designed, and how
> OP_CHECKSIGFROMSTACKVERIFY as currently spec'd in the PR is designed.
> CTV and CSFSV only constrain 32-byte first arguments, but not other
> lengths leaving open extensions using any other length, including using
> other lengths of either opcode as OP_OP, or as variants on CTV and CSFSV
> respectively.
>
> The benefit of this approach is that it doesn't "waste" the length byte
> only to specify the opcode behavior, but enables it to do double duty as
> specifying the total length of the first argument including both flags
> and data.
>
> Best,
>
> --Brandon
>
> --
> You received this message because you are subscribed to the Google Groups
> "Bitcoin Development Mailing List" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to bitcoindev+unsubscribe@googlegroups•com.
> To view this discussion visit
> https://groups.google.com/d/msgid/bitcoindev/Zzt2OCE6Aj9H3DiY%40console.
>Hi All,
I think we should discuss back-porting tapscript instead of coming up with a further divergent set of opcodes.
To sum up earlier discussion with Brandon, we could:
* Use a single upgradeable NOP for OP_TAPSCRIPTVERIFY * <s1> .. <sn> | <faux-control-block> <tapscript> CTAPV * Isolated execution environment * Gets the entire stack below the top 2 elements * Fails if it fails, does nothing if internal script succeeds. * Require the last opcode executed by the script interpreter to be a push opcode, fails otherwise
The faux control block can be just a few bytes mainly for tapscript version.
BR, moonsettler
Sent with Proton Mail secure email.
On Monday, November 18th, 2024 at 6:15 PM, Brandon Black <freedom@reardencode•com> wrote:
Quoted text (38 lines)
> Hi Weikeng, thanks for your thoughts on this!
>
> > We can, however, solve that by allowing multi-byte opcodes.
> >
> > Say, for example, we can have:
> > OP_OP { 0x1521 }
> > which will set the current opcode to be the one with the assigned number
> > 0x1521.
> >
> > Another idea is maybe OP_OP takes a stack element as the opcode.
> > { 0x1521 } OP_OP
>
>
> Another option that works for many cases is to have opcode families
> where an argument is augmented with flags to determine the behavior. We
> can consider this to already be the case for OP_CHECKSIG* where the
> signature determines the behavior of the hashing portion of the opcode.
>
> This is also how OP_CHECKTEMPLATEVERIFY is designed, and how
> OP_CHECKSIGFROMSTACKVERIFY as currently spec'd in the PR is designed.
> CTV and CSFSV only constrain 32-byte first arguments, but not other
> lengths leaving open extensions using any other length, including using
> other lengths of either opcode as OP_OP, or as variants on CTV and CSFSV
> respectively.
>
> The benefit of this approach is that it doesn't "waste" the length byte
> only to specify the opcode behavior, but enables it to do double duty as
> specifying the total length of the first argument including both flags
> and data.
>
> Best,
>
> --Brandon
>
> --
> You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups•com.
> To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/Zzt2OCE6Aj9H3DiY%40console.On 2024-11-19 (Tue) at 16:38:21 +0000, moonsettler wrote:
Quoted text (12 lines)
> I think we should discuss back-porting tapscript instead of coming up with a further divergent set of opcodes. > > To sum up earlier discussion with Brandon, we could: > > * Use a single upgradeable NOP for OP_TAPSCRIPTVERIFY > * <s1> .. <sn> | <faux-control-block> <tapscript> CTAPV > * Isolated execution environment > * Gets the entire stack below the top 2 elements > * Fails if it fails, does nothing if internal script succeeds. > * Require the last opcode executed by the script interpreter to be a push opcode, fails otherwise > > The faux control block can be just a few bytes mainly for tapscript version.
Few additional points that came up in discussing this with moon earlier:
* Compared to enabling tapscript without a key spend path (i.e. potential for quantum resistance) via a new witnessv1 program length or a new witness version, this approach will be similar in weight. * The biggest challenge with this approach that I came up with is the need to clean up the stack after execution (to satisfy witness v0 clean stack). * To keep it simple, it probably makes sense to make it a 1-primary argument opcode where the argument is `<tapscript||version>` to prevent the version from coming from spend-time witness elements.
Best,
--Brandon