Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Difficulty in emulating "weaker" OP_SUCCESS and why it should be a real opcode

178 views
Skip to first unread message

Weikeng Chen

unread,
Dec 9, 2024, 8:37:33 AM12/9/24
to Bitcoin Development Mailing List
When I am implementing fraud proofs in Bitcoin script, I find it useful to have an opcode "OP_SUCCESS" that will mark the execution to be successful without running the rest of the script, if this opcode is being executed. This is useful for writing code for fraud proofs such as BitVM, where the verifier wins if it finds one mismatch, and the verifier does not need to show the other mismatches.

This OP_SUCCESS is weaker version of the OP_SUCCESSx in the Taproot upgrade, which marks the execution as successful for the mere presence of OP_SUCCESSx anywhere in the script. Rusty Russell in a 2023 article, "Covenants: Examining ScriptPubkeys in Bitcoin Script", also mentioned about the usefulness of such an opcode. 

Of course, this opcode can be emulated, and one can rewrite the existing script in a way to realize the same functionality without adding a new opcode to Bitcoin.

The problem is that such rewriting is indeed fairly complicated. For example, say that we have the following program.

```
OP_NOP1
OP_IF
    OP_NOP2
    OP_IF_SUCCESS
    OP_NOP3
    OP_IF_SUCCESS
    OP_NOP4
    OP_IF_SUCCESS
    OP_NOP5
OP_ENDIF
OP_NOP6
```
with OP_IF_SUCCESS short for "OP_IF OP_SUCCESS OP_ENDIF"

The equivalent version without using new opcode is as follows (generated by computer, using a rewriting tool: https://github.com/Bitcoin-Wildlife-Sanctuary/fraud-proof-compiler)

```
OP_NOP1
OP_IF
    OP_NOP2
    OP_IF
        1
        0
    OP_ELSE
        OP_NOP3
        OP_IF
            1
            0
        OP_ELSE
            OP_NOP4
            OP_IF
                1
            OP_ELSE
                OP_NOP5
                0
                0
                0
            OP_ENDIF
        OP_ENDIF
        OP_IF 1 OP_ENDIF
    OP_ENDIF
    OP_IF 1 OP_ENDIF
OP_ELSE
    0
OP_ENDIF
OP_IF
    1
OP_ELSE
    OP_NOP6
    0
OP_ENDIF
OP_IF
    OP_DEPTH 512 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (256 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 256 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (128 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 128 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (64 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 64 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (32 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 32 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (16 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 16 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (8 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 8 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (4 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 4 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP ... OP_2DROP (2 OP_2DROP)
    OP_ENDIF
    OP_DEPTH 2 OP_GREATERTHANOREQUAL OP_IF
        OP_2DROP
    OP_ENDIF
    OP_DEPTH OP_IF
        OP_DROP
    OP_ENDIF
    OP_TRUE
OP_ENDIF
```

The second part of the code is mainly a general-purpose and stack-size-independent method to remove all the stack elements in order to be standard. 

For this reason, it seems better to avoid the need for developers to "emulate" such a weak version of OP_SUCCESS but to actually implement an opcode for that.

What do you think?

Andrew Poelstra

unread,
Dec 9, 2024, 2:10:02 PM12/9/24
to Weikeng Chen, Bitcoin Development Mailing List
On Mon, Dec 09, 2024 at 05:27:54AM -0800, Weikeng Chen wrote:
> When I am implementing fraud proofs in Bitcoin script, I find it useful to
> have an opcode "OP_SUCCESS" that will mark the execution to be successful
> without running the rest of the script, if this opcode is being executed.
> This is useful for writing code for fraud proofs such as BitVM, where the
> verifier wins if it finds one mismatch, and the verifier does not need to
> show the other mismatches.
>
> This OP_SUCCESS is weaker version of the OP_SUCCESSx in the Taproot
> upgrade, which marks the execution as successful for the mere presence of
> OP_SUCCESSx anywhere in the script. Rusty Russell in a 2023 article,
> "Covenants: Examining ScriptPubkeys in Bitcoin Script", also mentioned
> about the usefulness of such an opcode.
>
> Of course, this opcode can be emulated, and one can rewrite the existing
> script in a way to realize the same functionality without adding a new
> opcode to Bitcoin.
>
> The problem is that such rewriting is indeed fairly complicated. For
> example, say that we have the following program.
>
> <snip>

I raised the question on IRC about why the existing SUCCESSx codes work
the way they do, and got several good anwsers that you can read here

https://gnusha.org/bitcoin-wizards/2024-12-09.log

In short, for purpose of softforking upgrade mechanism, the existing
SUCCESS codes give us way more freedom of action.

But it sounds like you want a "weak SUCCESS" opcode in order to use the
success semantics, not as an upgrade mechanism. Maybe it makes sense to
propose that one of the existing OP_SUCCESSx opcodes should be
softforked to become OP_WEAK_SUCCESS?

--
Andrew Poelstra
Director, Blockstream Research
Email: apoelstra at wpsoftware.net
Web: https://www.wpsoftware.net/andrew

The sun is always shining in space
-Justin Lewis-Webster

signature.asc

Brandon Black

unread,
Dec 9, 2024, 5:16:27 PM12/9/24
to Andrew Poelstra, Weikeng Chen, Bitcoin Development Mailing List, Rusty Russell
Hey list,

On 2024-12-09 (Mon) at 19:08:51 +0000, Andrew Poelstra wrote:
> On Mon, Dec 09, 2024 at 05:27:54AM -0800, Weikeng Chen wrote:
> > When I am implementing fraud proofs in Bitcoin script, I find it useful to
> > have an opcode "OP_SUCCESS" that will mark the execution to be successful
> > without running the rest of the script, if this opcode is being executed.
> > This is useful for writing code for fraud proofs such as BitVM, where the
> > verifier wins if it finds one mismatch, and the verifier does not need to
> > show the other mismatches.
> >
> > This OP_SUCCESS is weaker version of the OP_SUCCESSx in the Taproot
> > upgrade, which marks the execution as successful for the mere presence of
> > OP_SUCCESSx anywhere in the script. Rusty Russell in a 2023 article,
> > "Covenants: Examining ScriptPubkeys in Bitcoin Script", also mentioned
> > about the usefulness of such an opcode.
> >
> > <snip>
>
> In short, for purpose of softforking upgrade mechanism, the existing
> SUCCESS codes give us way more freedom of action.
>
> But it sounds like you want a "weak SUCCESS" opcode in order to use the
> success semantics, not as an upgrade mechanism. Maybe it makes sense to
> propose that one of the existing OP_SUCCESSx opcodes should be
> softforked to become OP_WEAK_SUCCESS?

An alternative that Rusty Russel has discussed wanting as part of his
script restoration work is "OP_SEGMENT" which would split the script
execution for purposes of SUCCESS checking, allowing (for example) a
prefix to be required to execute before an arbitrary user provided
script that might contain an OP_SUCCESS.

It occurred to me today when thinking about Weikeng's post that we can
slightly weaken the existing OP_SUCCESS behavior while retaining
essentially all of its benefits in practice without introducing
OP_SEGMENT by leveraging OP_CODESEPARATOR. Redefine OP_SUCCESS with a
soft fork from "make the script unconditionally valid" to "make the
script segment unconditionally valid", and define a script segment as
"each lexicographic section of the script containing no
OP_CODESEPARATOR".

The script interpreter can perform SUCCESS checking as it currently does
until it encounters an OP_CODESEPARATOR. Each OP_CODESEPARATOR gets a
"SUCCESS" flag defaulted to false and SUCCESS checking now sets that
flag to true on the most recently encountered OP_CODESEPARATOR.

During script execution, whenever an OP_CODESEPARATOR is popped (not
executed) its "SUCCESS" flag value is copied to the interpreter state.
After this state setting conditional, if the interpreter "SUCCESS" flag
is true, and fExec is true, the script immediately succeeds.

For Weikeng's scripts, this would require 2 OP_CODESEPARATORS for each
OP_SUCCESS (or some really weird logic about what's executed in what
SUCCESS state).

Thoughts on this approach?

--Brandon

Weikeng Chen

unread,
Dec 12, 2024, 1:15:33 AM12/12/24
to Rusty Russell, Brandon Black, Andrew Poelstra, Bitcoin Development Mailing List
I was mostly thinking about the names atm. OP_RETURN_TRUE may be a name less confusing than OP_SUCCESS. It helps if one day we nickname/alias OP_RETURN as OP_RETURN_FALSE.

This could eventually become an opcode BIP proposal that is pretty causal---if a major soft fork happens (like the one that adds tapscript), it could be piggybacked into it, otherwise, it would just stay as a proposal as there is no urgency since it doesn't enhance the ability of Bitcoin script (you can emulate it), but more to avoid bugs in code and for clarity.

On Thu, Dec 12, 2024 at 10:54 AM Rusty Russell <ru...@rustcorp.com.au> wrote:
Beware success inside branches?  This is why I preferred to segment the
script and scan for OP_SUCCESS and evaluate each part in order (if you
have part of an if statement inside one segment, you fail as expected).
This is actually not that different inside Bitcoin's script.cpp.

But that's kind of a detail.  IMHO there's nothing fundamentally wrong
with runtime success opcodes, in fact several proposals work better if
you allow them (e.g. "undefined bit patterns in operand to OP_TXHASH
cause immediate success" lets you reserve some bits for future
extension).

(Also: OP_CODESEPARATOR is cursed, so I chose a different name :)

Cheers,
Rusty.

Brandon Black

unread,
Dec 12, 2024, 1:15:49 AM12/12/24
to Rusty Russell, Andrew Poelstra, Weikeng Chen, Bitcoin Development Mailing List
On 2024-12-10 (Tue) at 10:51:38 +1030, Rusty Russell wrote:
> Brandon Black <fre...@reardencode.com> writes:
> > It occurred to me today when thinking about Weikeng's post that we can
> > slightly weaken the existing OP_SUCCESS behavior while retaining
> > essentially all of its benefits in practice without introducing
> > OP_SEGMENT by leveraging OP_CODESEPARATOR. Redefine OP_SUCCESS with a
> > soft fork from "make the script unconditionally valid" to "make the
> > script segment unconditionally valid", and define a script segment as
> > "each lexicographic section of the script containing no
> > OP_CODESEPARATOR".
> >
> > The script interpreter can perform SUCCESS checking as it currently does
> > until it encounters an OP_CODESEPARATOR. Each OP_CODESEPARATOR gets a
> > "SUCCESS" flag defaulted to false and SUCCESS checking now sets that
> > flag to true on the most recently encountered OP_CODESEPARATOR.
> >
> > During script execution, whenever an OP_CODESEPARATOR is popped (not
> > executed) its "SUCCESS" flag value is copied to the interpreter state.
> > After this state setting conditional, if the interpreter "SUCCESS" flag
> > is true, and fExec is true, the script immediately succeeds.
>
> Beware success inside branches? This is why I preferred to segment the
> script and scan for OP_SUCCESS and evaluate each part in order (if you
> have part of an if statement inside one segment, you fail as expected).
> This is actually not that different inside Bitcoin's script.cpp.

I believe my description of CODESEPARATOR segments has a predictable but
different handling of SUCCESS with conditionals. As long as interpreter
state change are handled in order:

* update success mode if opcode is CODESEPARATOR
* if execution enabled by flow control and success mode is true, succeed
* execute operation if enabled or operation is flow control

That said, I've just reread [BIP342] and realized that the designers of
tapscript had greater things in mind for SUCCESS than I had remembered
(e.g. the existence of an SUCCESS can be used to change the behavior of
other opcodes or even stack limits) which would not work at all well
with segmented SUCCESS.

Some of these be possible with segmented execution, but not all.

Based on this, I'm now in favor of Weikeng's idea for a runtime success
operator possibly akin to the original RETURN. If it were equivalent to
the original RETURN, it would be a useful alternative to VERIFY in
certain cases.

> (Also: CODESEPARATOR is cursed, so I chose a different name :)

Isn't tapscript CODESEPARATOR uncursed? :)

Aside: Because SUCCESS allows for changing resource limits and more,
restored script can mostly be implemented in tapscript 0xc0. If any of
the new opcodes are present, 1NEGATE is redefined to TX (or something),
numbers are bigwholes, limits are varops, etc. Gets a little weird if
someone wants to write a restored script without any of the new ops, so
maybe you also provide an RESTORE that does nothing other than trigger
the mode change.

All the best,

--Brandon

[BIP342]: https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki
Reply all
Reply to author
Forward
0 new messages