Skip to content
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

[Doc] New Opcodes for tapscript #949

Merged
merged 2 commits into from
Jul 6, 2021

Conversation

sanket1729
Copy link
Member

This PR adds a doc describing suggested new opcodes for better script programming with elements. The purpose of these opcodes to aid easier writing of complex scripts like covenants in elements.

This introduces 1) Opcodes for Streaming hashes, 2) Transaction introspection opcodes 3) 64 byte athematic opcodes 4) Some utility conversion opcodes 5) Crypto opcodes for low-level operations and tweaking 6) A cleaner way to write For loops and 7) Changes to existing opcodes.

The doc also describes a brief rationale for choice. Feedback welcome :)

doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
@dgpv
Copy link
Contributor

dgpv commented Dec 10, 2020

Seems like a big effort that would increase the ergonomics of using Script, but will also make it more complex due to increased number of opcodes. I thought that Simplicity was a path to more expressive contracts. This seems to be a parallel effort to have another system that is more expressive than current Script (effectively, due to added ergonomics)

At the same time, propping up script with more power means that people's focus will be diluted - they will need to choose between studying Simplicity and this extended script.

The amount of new opcodes is huge, and implementation of each opcode is a huge undertaking. Even if it may seem to be simple to implement the opcodes, the amount of what can go wrong is usually very big. And there's no way to formally verify these implementations, as is the case with Simplicity. It seems to me that such effort, if applied towards Simplicity, would made it real-world-usable much faster

Is Simplicity not expected to be usage-ready for extended amount of time, so a decision to prop up Script is made instead ?

@sanket1729 sanket1729 changed the title Update testframework sighash to inculde issuance [Doc] New Opcodes for tapscript Dec 10, 2020
@apoelstra
Copy link
Member

@dgpv The timeline for Simplicity is (hopefully) not that much longer than the timeline for this. We certainly don't mean this as a replacement for Simplicity or as some sort of crutch because we foresee long delays. The goal is rather to improve the ergonomics of the existing script and give people a sandbox to play with where they can continue to work in the "I can hex-edit a script for an imperative stack machine" model which is easy to experiment with.

So there are sorta two goals here, which I think explain how large this an effort this appears to be:

  1. Let us eliminate some limits and simplify the existing covenant code that you and I have been working on. For this we basically need the streaming hashes or a couple of the tx introspection opcodes and the FOR loop. The streaming hashes are pretty-much trivial to think about and would get rid of the 520-byte limits. Replacing CHECKSIGFROMSTACK with introspection and FOR is harder, but I suspect you'll have a much easier time getting other people to review your stuff if it's structured this way.
  2. Add a giant ad-hoc pile of "experimental" opcodes which are designed to be easy to implement, grant a lot of power "in principle", but which we otherwise don't plan to spend a lot of time thinking about. This is just continuing the spirit of Elements. Last time we did this was back in Elements Alpha, and some opcodes (e.g. CSV) wound up in Bitcoin, others proved powerful but too scary for Bitcoin (CHECKSIGFROMSTACK) and others AFAIK nobody ever found a use for (DETERMINISTICRANDOM).

So I dispute your charactization of "huge" for both the number of new opcodes and the size of the undertaking :). I think in practice, for you and I (and everyone else who isn't at the "basic experimentation" phase of development), the bulk of the complexity will be in understanding and reasoning about FOR. Then most of the other opcodes have simple/redundant descriptions "init/write/finalize hash X" or "fetch transaction data Y", and the rest we're basically just throwing over the fence to see what people do with them.

Then as for the relationship with Simplicity -- our first goals with Simplicity are to implement Miniscript fragments (you can see some early work in this repo), with the idea that much of the "standard script operations" you can write Simplicty for by just taking a Miniscript and translating it directly into Simplicity, and then the difficult parts of actually writing Simplicity will be limited to the cool stuff that actually makes use of Simplicity's extra power. (And the way to do this would be either fork elements-miniscript to add some new crazy script fragment that could only be compiled to Simplicity, or to use some sort of rawsimplicity fragment that we'd add directly to elements-miniscript.)

We'd like to extend Miniscript on Elements to include a bit more than just sig/hash/time checks, e.g. to do some basic forms of transaction introspection and oracle signature checks, to make this progarmming model even easier to get started with. But to do that, we need FOR and transaction introspection.

I hope this clarifies our goals and the scope of this project.

@dgpv
Copy link
Contributor

dgpv commented Dec 10, 2020

So I dispute your charactization of "huge"

This is subjective, of course. It is just that implementing/reviewing such critical code parts is always a bit anxiety-inducing (at least for me), and thus the effort expended seem extra huge relative to the size of the code. Each implemented opcode is in consensus-critical path, and any bug might be consensus bug. It is not on Bitcoin, so maybe the potential consequences of a bug are not that bad, but at the same time - less eyes for review before merge, less people trying to break it after merge, the bug might lurk for a long time.

It seemed to me that goal of the new opcodes is to make it easier to build more expressive miniscript, and this is what most of the people would use, not raw script. And the miniscript -> simplicity link seemed more logical. But if the goal of these is first give the tools to raw script writers, that's fine by me. Given these more specialized opcodes, maybe raw scripts will be smaller an simpler, that's a plus.

@apoelstra
Copy link
Member

apoelstra commented Dec 10, 2020

It is definitely anxiety-inducing! But hopefully by only working with fixed-length hashes and EC points, and fixed-width numbers, we can eliminate the majority of the consensus risk, and reduce the review burden of these opcodes.

It may be that when we actually start writing code, we find that some of them are excessively branch-heavy or do things where we fear we may touch resource limits or allow division by zero or somehow depend on endianness, and then we'll just drop them. But we did consider this when choosing which opcodes to include. Personally for me, the multi-byte opcode stuff is more worrisome than any individual proposed opcode (but still manageable, to be clear).

doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
@JeremyRubin
Copy link
Contributor

It might be helpful to either add CTV explicitly, or a modified version with whatever elements extensions are needed, bitcoin/bitcoin#21702, or to ensure that the transaction data inspection opcodes are sufficient to construct the template hash on the stack. Currently I think some fields (like scriptsigs) are missing, for legacy address support.

@apoelstra
Copy link
Member

As a MVP I think we should reduce scope to:

  • streaming hash opcodes
  • introspection opcodes
  • 64-bit arithmetic opcodes (consistent with endianness/formatting of introspection ops)
  • op_taptweak

@brqgoo-zz
Copy link

Would it be possible to add OP_INSPECTTXSIZE as an extension to OP_INSPECTTX? Advanced covenants in some cases need to enforce fee policies and it’s required to know transaction size in vBytes. Also, OP_INSPECTTXFEE should be very useful since a tx can have multiple fee outputs and it’s very tricky to inspect total fees paid.

@apoelstra
Copy link
Member

For now, to keep implementation scope down, I'm gonna say no ... you can use INSPECTX SIZE NIP to simulate this. (Unlike pre-taproot, there is no 201-opcode limit (see BIP 342) so the cost of this is "only" the two extra bytes for opcodes.

If this turns out to be a common pattern we'd be happy to add it in a future tapscript version (alongside the other "experimental" opcodes that are listed in this doc but which we are cutting from the initial implementation).

@sanket1729
Copy link
Member Author

@apoelstra, there is no OP_INSPECTTX. So far, we only have things that inspect stuff covered in sighash. The transaction is not covered in sighash, so it is not immediately clear how to evaluate OP_INSPECTTXSIZE as it depends on witness size(not covered in sighash)

@Kukks
Copy link

Kukks commented Jun 10, 2021

It might be helpful to either add CTV explicitly, or a modified version with whatever elements extensions are needed, bitcoin/bitcoin#21702, or to ensure that the transaction data inspection opcodes are sufficient to construct the template hash on the stack. Currently I think some fields (like scriptsigs) are missing, for legacy address support.

I'd like to jump in to also suggest including BIP-119 CheckTemplateVerify, perhaps even instead of the dozen introspection opcodes (I think CTV can fulfill most use-cases these target?), for the sake of providing smart contracts that can be potentially replicated both on main chain and elements in the future.

@apoelstra
Copy link
Member

apoelstra commented Jun 10, 2021

Lol, we are definitley not replacing introspection opcodes with CTV. We already have CHECKSIGFROMSTACK+CAT if you want to do covenants in indirect/general ways. We could plausibly introduce CTV in the future as a way to onboard developers who are excited about it, but it's not the direction that we're focusing our script development on.

@sanket1729 oh! Lol, I misread @brqgoo as writing "inspect X" i.e. "inspect arbitrary parts of the transaction" rather than "inspect TX".

I definitely agree that INSPECTTXSIZE would be useful. And probably more useful than INSPECTTX since it would not limit the total transaction size to fit into stack elements.

@apoelstra
Copy link
Member

Another thing we might want to bring into scope is widening the CSV timeout. See #961. It'd be worth doing this now because it'll be hard to do later.

@Kukks
Copy link

Kukks commented Jun 10, 2021

Lol, we are definitley not replacing introspection opcodes with CTV. We already have CHECKSIGFROMSTACK+CAT if you want to do covenants in indirect/general ways. We could plausibly introduce CTV in the future as a way to onboard developers who are excited about it, but it's not the direction that we're focusing our script development on.

Fair enough, I can live with either solution for my Liquid projects but would have preferred something that's more likely to find its way to BTC.

@apoelstra
Copy link
Member

apoelstra commented Jun 25, 2021

Certainly if/when it looked likely that CTV would get into BTC then we'd pull it into Elements.

doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
- Define `OP_SUCCESS207` as `OP_MUL64`: pop the first number(8 byte LE) as `b` followed another pop for `a`(8 byte LE). Push `a*b` onto the stack. Push 1 `CScriptNum` if there is no overflow. Overflow behavior defined above.
- Define `OP_SUCCESS208` as `OP_DIV64`: pop the first number(8 byte LE) as `b` followed another pop for `a`(8 byte LE). First push remainder `a%b`(must be non-negative and less than |b|) onto the stack followed by quotient(`a//b`) onto the stack. Abort if `b=0`. Push 1 `CScriptNum` if there is no overflow. Overflow behavior defined above.
- Define `OP_SUCCESS209` as `OP_LESSTHAN64`(cannot fail!): pop the first number(8 byte LE) as `b` followed another pop for `a`(8 byte LE). Push ` a < b`.
- Define `OP_SUCCESS210` as `OP_LESSTHANOREQUAL64`(cannot fail!): pop the first number(8 byte LE) as `b` followed another pop for `a`(8 byte LE). Push ` a <= b`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm torn between having the "less than or equal" opcodes and requiring the user do GREATERTHAN NOT. What do others think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a decent amount of value in just having GT in terms of minimalism, but in practice having both GT/LT should be able to yield more efficient scripts...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern here is that we are consuming opcodes(already up to 222/254) and we don't want to run out of them in future upgrades.

The previous version of this doc had multi-byte opcodes(opcodes for ec operations, scalar exps etc) which were removed in favor of implementation simplicity.

- Define `OP_SUCCESS222` as `OP_LE32TOLE64`: pop the stack as a 4 byte signed LE. Push the corresponding 8 byte LE number. Cannot fail, useful for conversion of version/sequence.

6. **Changes to existing Opcodes**:
- Add `OP_CHECKSIGFROMSTACK` and `OP_CHECKSIGFROMSTACKVERIFY` to follow the semantics from bip340-342 when witness program is v1.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we remove CHECKSIGFROMSTACK and leave only the VERIFY variant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about it. It's not additional code complexity so might as well have it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that both seems reasonable -- although I thought CHECKSIGFROMSTACK was already part of elements?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are part of elements, but we need to modify them to use BIP340-342 type schnorr signatures. Currently, this uses ECDSA sigs

@apoelstra
Copy link
Member

FYI to readers, Sanket's recent force-push actually rewrites the document to reduce its scope. The new document lists everything we actually plan to deploy on Liquid alongside Taproot. The other opcodes will have to wait for another softfork.

Copy link
Member Author

@sanket1729 sanket1729 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed another commit addressing @apoelstra's suggestions

doc/tapscript_opcodes.md Outdated Show resolved Hide resolved
- Define `OP_SUCCESS222` as `OP_LE32TOLE64`: pop the stack as a 4 byte signed LE. Push the corresponding 8 byte LE number. Cannot fail, useful for conversion of version/sequence.

6. **Changes to existing Opcodes**:
- Add `OP_CHECKSIGFROMSTACK` and `OP_CHECKSIGFROMSTACKVERIFY` to follow the semantics from bip340-342 when witness program is v1.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about it. It's not additional code complexity so might as well have it?

@JeremyRubin
Copy link
Contributor

somewhat of a concept NACK towards inspect annex, given that there are no defined uses for annex yet -- when one is soft forked in, it should be enabled then?

i think there are some circumstances where being able to put the annex on the stack might be counter to the purpose of the annex itself.

@JeremyRubin
Copy link
Contributor

Certainly if/when it looked likely that CTV would get into BTC then we'd pull it into Elements.

Is that normative for Elements? I.e., do the other upgrades planned in this release pass such a test?

@apoelstra
Copy link
Member

Yes, if it looked likely that anything in this planned release were to get into Bitcoin, we would still include it in the planned release.

@apoelstra
Copy link
Member

apoelstra commented Jun 28, 2021

I don't feel strongly about it. It's not additional code complexity so might as well have it?

@sanket1729 the non-VERIFY version of CSFS has no benefit over the VERIFY version but cannot be batch-verified.

A similar argument applies to TAPTWEAK, although in this case (theoretically) the non-VERIFY version is more efficient because it does not require the user to include the result on the witness stack. Though we'd have to think a bit about whether this is a real benefit in practice.

@JeremyRubin
Copy link
Contributor

Yes, if it looked likely that anything in this planned release were to get into Bitcoin, we would still include it in the planned release.

I mean that is the inclusion of features in this upgrade to Elements an indication that the house view is these are going to be included in Bitcoin?

Or would it being clear a feature would not go into Bitcoin be a reason to disinclude from elements?

@sanket1729
Copy link
Member Author

sanket1729 commented Jun 29, 2021

Hi @JeremyRubin, I spent some time trying to read BIP119 and figuring out how it might adapt to elements. Some comments in no particular order of importance:

  • Directly migrating OP_CTV is tricky because fees in elements are output UTXO. The value cannot be known statically while receiving money in CTV controlled output. We would have to modify the template w.r.t to not include fee outputs.
  • It might require more thinking about how the templating would interact with confidential assets. Just directly using the same template from bitcoin BIP 119 might not be the best way.
  • All the functionality which OP_CTV allows is permitted by the current design. You can directly fix the transaction structure by looking at the outputs themselves. The solution is more generic and flexible than a fixed template. You can emulate any specific template that you want here.
  • The best selling point about BIP119 is IMO the tooling around it(Sapio). But I don't think we have the bandwidth to extend/maintain Sapio for elements.

These things might require more discussion, opening a new issue for this

@sanket1729
Copy link
Member Author

Opened an issue #1015 for more discussion.

@sanket1729
Copy link
Member Author

somewhat of a concept NACK towards inspect annex, given that there are no defined uses for annex yet -- when one is soft forked in, it should be enabled then?

It is already possible to inspect annex using the CSFS trick. So, having an additional opcode to inspect it does not any new functionality that elements codebase currently does not have.

@JeremyRubin
Copy link
Contributor

somewhat of a concept NACK towards inspect annex, given that there are no defined uses for annex yet -- when one is soft forked in, it should be enabled then?

It is already possible to inspect annex using the CSFS trick. So, having an additional opcode to inspect it does not any new functionality that elements codebase currently does not have.

right... that's an interesting observation!

@apoelstra
Copy link
Member

@sanket1729 the non-VERIFY version of CSFS has no benefit over the VERIFY version but cannot be batch-verified.

Sanket pointed out to me that we already have a consensus rule for CHECKSIG that any invalid signature needs to be the null signature. If we extend this rule to CSFS then there is no issue wit h batch verification.

@apoelstra apoelstra merged commit 0355e30 into ElementsProject:master Jul 6, 2021
@apoelstra
Copy link
Member

Hmm, crap, I meant to merge the other doc PR #933.

Since this is documentation only I think it's OK to just leave it be, and to make any additional changes in a future PR.

gwillen pushed a commit that referenced this pull request Jun 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants