-
Notifications
You must be signed in to change notification settings - Fork 319
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
CIP-0038? | Arbitrary Script as Native Script spending conditions #309
Changes from 2 commits
2a7790b
fcbdbd7
33b47ac
5f52150
96a3e85
3202ace
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,84 @@ | ||||||||||||||||||||
--- | ||||||||||||||||||||
CIP: XXXX | ||||||||||||||||||||
Title: Arbitrary Script as Native Script spending conditions | ||||||||||||||||||||
Authors: Sebastien Guillemot <seba@dcspark.io> | ||||||||||||||||||||
Comments-URI: TBD | ||||||||||||||||||||
Status: Draft | ||||||||||||||||||||
Type: Standards | ||||||||||||||||||||
Created: 2022-07-27 | ||||||||||||||||||||
License: CC-BY-4.0 | ||||||||||||||||||||
--- | ||||||||||||||||||||
|
||||||||||||||||||||
# Abstract | ||||||||||||||||||||
|
||||||||||||||||||||
Native scripts are often easier to work with as any application that allows users to enter an ADA address to receive funds supports native scripts (the same is not true for Plutus scripts as the app would need to know how to structure the datum). However, limited composability between native scripts and Plutus scripts limits leveraging this fact. | ||||||||||||||||||||
|
||||||||||||||||||||
This CIP introduces a way to use native scripts as a starting point for more complex interactions which helps unlock use cases such as simple proxy contracts. | ||||||||||||||||||||
|
||||||||||||||||||||
# Motivation | ||||||||||||||||||||
|
||||||||||||||||||||
Suppose that you are part of a DAO whose funds are managed by a Plutus contract. Your DAO, in order to receive payments, would like receive ADA or tokens to its script address directly. However, this is non-trivial because applications cannot sent to arbitrary Plutus scripts as they do not know how to structure the datum or what other kind of restrictions may exist for this contract. | ||||||||||||||||||||
|
||||||||||||||||||||
To solve this, one way would be to instead have a proxy contract that receives funds and forwards them to your DAO with the proper structure. Native scripts at the moment can play this role by creating a native script multisig where some set of DAO members have their public keys specified in the spending condition of the multisig. However, this approach has the following problems: | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe such a proxy contract could be written in Plutus today. For example with plutus-tx syntax:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a Plutus script to solve this problem won't work unless a PlutusV3 is made that accept no-datum inputs as mentioned in the thread above. I also go more into detail about this in a video (I forgot to add it to the original PR description) Additionally, native scripts also have the benefit that they don't need collateral so they behave slightly differently that script kinds that require a phase-2 validation. Even if we enable datum-less execution of Plutus scripts (which I think we should), this change to native scripts may still be useful to some -- especially if we end up adding other languages in the future that also don't require phase-2 validation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I also think we should make this possible! Even if we do disagree on the amount of work needed to extend simple vs. plutus scripts, it still seems to me that this would be the minimum viable feature the motivation of this CIP requires. Would datum-less execution of Plutus scripts allow the use cases you have in mind? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you have datum-less support for Plutus you still would need 2 hops to achieve this kind of proxy contract because you would need a datum-less templated Plutus script where the template hard-codes the datum content into the contract which then is used to forward the datum to the final contract That is to say, I think datum-less contracts also enable the same proxy infrastructure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, the basic structure for the "proxy contract" use case is the same. But the plutus script would also allow to encode other things than just an embedded datum, i.e. whatever you intend to be putting in that side-car plutus script referred by Are you planning to create a CIP for that or shall I create one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. feel free to create one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we solve this problem with a convention for an "empty datum"? Perhaps the cbor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm much more in favour of something like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As another argument in favor of this adding this native script solution as an alternative to any Plutus improvements, this native script version combined with #310 makes it easier for wallets to know it's safe for a user to send to an address. If they are sending to a Plutus script, there is no guarantee the Plutus script is handling the empty datum script so a warning would still have to be shown to the user |
||||||||||||||||||||
|
||||||||||||||||||||
- There is no way to guarantee that these members will actual forward the funds to the DAO instead of pocketing the funds | ||||||||||||||||||||
- DAO membership may not easily be representable by a small set of fixed public keys | ||||||||||||||||||||
|
||||||||||||||||||||
These problems could be solved by adding a many new native script conditions such as enforcing that the transaction that spends it needs to have a certain NFT as part of its inputs or that the transaction contains a specific output. However, there is no guarantee this is flexible enough for all use-cases and these may bring unnecessary feature creep to the native script feature. | ||||||||||||||||||||
|
||||||||||||||||||||
Instead, the most generic solution is to allow a new condition where a native scripts can only be spent if a specific Plutus script is also part of the transaction input. This allows the multisig to simply handle receiving the funds and having all the complex logic (DAO membership checks, output checks, etc.) to be added to the Plutus script. | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am worried that this design opens up the door to the "double satisfaction problem". In other words, someone might place a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm true, it's possible for somebody to write a smart contract such that using it as a native script condition could lead to unintended exploits. You could try and tackle some of the common solutions to double satisfaction (predicate on the datum, NFT to indicate real version, etc.) inside native scripts but this also complicated the native script language. I feel it would be better to tackle and double satisfaction problem by having the native script require the presence of 2 Plutus scripts -- one is the real script you need and the second is just a script that checks for any double satisfaction issues There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I definitely understand why folks would want this feature. The beauty of the native scripts, as I see it, is their simplicity. Maybe we could have this CIP be dependent of having a good solution to the double satisfaction problem? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is the "double satisfaction problem"? @JaredCorduan's question doesn't specify how the unintended consequences can arise (and of what nature?). Aren't Plutus scripts supposed to be pure functions? So what if a Plutus script runs twice or more? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JaredCorduan Thanks for the link to an excellent explainer of the problem! I'd say, if the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indeed, I just wanted to make sure that we would be consistent with however Plutus solves the problem, and I wanted to point out that the situation is at least slightly more complicated than folks might realize at first glance. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The "double satisfaction problem" seems related to the redundant executions of spending scripts (see here). Right now, spending scripts are executed once for every UTxO spent from the script's address and their is no way around this. In my Maybe Datum PR, I try to address this issue by allowing tx-level spending scripts (i.e., spending scripts that are only executed once per tx). With this change, your concern can be addressed by only using Thanks to the eUTxO model, you can already design validators that check ALL the inputs from the script's address and then check that the tx outputs satisfy the conditions for the leaving balance. So to use your link's example, since B is trying to trick the validator by using one tx for both UTxOs, the validator can see that T1 and T2 are leaving the address in that tx. Therefore, it can easily verify if the proper amount (20 ADA in this case) goes to A's address. I created a DEX proof-of-concept that uses composable atomic swaps which do exactly this (here). The issue with these tx-level validator designs is that, even though they validate based off the tx context as a whole, they are still forced to be executed once per UTxO which makes them inefficient to use due to redundant executions. So while my DEX does not need to be worried about the "double satisfaction problem", it does suffer from the redundant executions. The Maybe Datum PR seeks to address this in plutusV3. Edit: Fixed saying "20 ADA" is leaving instead of "T1 and T2" in link's example. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
yep, absolutely. if you are aware of the problem, you can guard against it. the problem is that if you are not aware of the potential problem, it's an easy trap to fall into. I haven't grokked your Maybe Datum CIP yet, but thanks for the link. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think I may have misunderstood this condition. In fact, I don't know what it means at all. Perhaps you could add as part of the specification an explanation of how the new script clause is actually validated (which you don't say anywhere!). My original reading was that this meant that there must be some other input which is locked with the given script, but it could also be read as saying that the given script must be run as if it was the validator script for the current input and pass in that case. These are quite different. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This was the meaning I intended |
||||||||||||||||||||
|
||||||||||||||||||||
# Specification | ||||||||||||||||||||
|
||||||||||||||||||||
The current definition of native scripts uses the following BNF notation | ||||||||||||||||||||
|
||||||||||||||||||||
```BNF | ||||||||||||||||||||
<native_script> ::= | ||||||||||||||||||||
<RequireSignature> <vkeyhash> | ||||||||||||||||||||
| <RequireTimeBefore> <slotno> | ||||||||||||||||||||
| <RequireTimeAfter> <slotno> | ||||||||||||||||||||
|
||||||||||||||||||||
| <RequireAllOf> <native_script>* | ||||||||||||||||||||
| <RequireAnyOf> <native_script>* | ||||||||||||||||||||
| <RequireMOf> <num> <native_script>* | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
Importantly, note that native scripts can only require other native scripts and not plutus scripts or any other future script type introduced. | ||||||||||||||||||||
|
||||||||||||||||||||
Therefore, this proposal suggests the definition be changed to | ||||||||||||||||||||
|
||||||||||||||||||||
```BNF | ||||||||||||||||||||
<native_script> ::= | ||||||||||||||||||||
<RequireSignature> <vkeyhash> | ||||||||||||||||||||
| <RequireScript> <scripthash> | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Summary of a conversation with Las on this: Although require script is sufficient to do everything one would need, it's kind of tedious because there is no way to specify a datum or a tag (script, mint) in the native script, so it means you need to inline checks in the Plutus script. We could extend this CIP to enable adding other restrictions to the script (ex: passed with a given datum or tag) if that's acceptable to people If we do this, we also have to consider how to handle inline datums and reference scripts as well |
||||||||||||||||||||
| <RequireTimeBefore> <slotno> | ||||||||||||||||||||
| <RequireTimeAfter> <slotno> | ||||||||||||||||||||
|
||||||||||||||||||||
| <RequireAllOf> <native_script>* | ||||||||||||||||||||
| <RequireAnyOf> <native_script>* | ||||||||||||||||||||
| <RequireMOf> <num> <native_script>* | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
Which we propose uses the `type: script` when used in JSON notation such as in the following example: | ||||||||||||||||||||
|
||||||||||||||||||||
```json | ||||||||||||||||||||
{ | ||||||||||||||||||||
"type": "all", | ||||||||||||||||||||
"scripts": | ||||||||||||||||||||
[ | ||||||||||||||||||||
{ | ||||||||||||||||||||
"type": "script", | ||||||||||||||||||||
"scriptHash": "b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398538" | ||||||||||||||||||||
}, | ||||||||||||||||||||
] | ||||||||||||||||||||
} | ||||||||||||||||||||
``` | ||||||||||||||||||||
|
||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wouldn't hurt to add a section on how exactly the referred scripts are going to be interpreted. Here's what I would add.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean by this. No additional processing is done, since the script referenced in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps, it is bad wording. Would it be clearer if we remove the phrase |
||||||||||||||||||||
# Backwards compatibility | ||||||||||||||||||||
|
||||||||||||||||||||
Currently there are two versions of native scripts: | ||||||||||||||||||||
|
||||||||||||||||||||
- V1 starting in Shelley that had `RequireAllOf`, `RequireAnyOf`, `RequireMOf`, `RequireSignature` | ||||||||||||||||||||
- V2 starting in Allegra that added `RequireTimeBefore`, `RequireTimeAfter` | ||||||||||||||||||||
|
||||||||||||||||||||
Note that all that was required to add functionality in Allegra was to create a new native script language internally (`SimpleScriptV1` vs `SimpleScriptV2`) inside the Cardano codebase and did not require a new hash namespace or a new address for existing scripts. The same should be true for this proposal. |
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.
Are you sure that a UTxO with
DatumNone
paid to a plutus script is unspendable? I have not tried this yet, but it should be possible if the script does not look for a datum? If not, then this might be the solution you are looking for instead?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.
Should this not be possible today, this would likely need to go into a
PlutusV3
, but changing the native scripts syntax is about the same size of change (IIRC there is evenSimpleScriptV2
as a language and this would then beSimpleScriptV3
).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.
This is unfortunately the case. All spending scripts require datum (it is the 2-argument scripts that do not, such as for minting)
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.
@james-iohk is indeed correct. note that you cannot tell if a script hash inside of a transaction output is a native script or a plutus script without having the script.
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 disagree with this because any change to the Plutus context breaks same-transaction composability (i.e. you wouldn't be able to spend a PlutusV2 tx in the same tx as a PlutusV3 tx)
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 think the answer is yes, but let me try to spell it out. An inline datum in a transaction output can only be used for the script (hash) inside the given output. You cannot just use a reference input whose corresponding output has an inline datum, and have that datum be totted around to all the places it is needed (like how reference scripts work).
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'm also not sure what you mean by this. The PlutusContext contains all the reference inputs of the transaction. These outputs all contain the datum which may be available inlined.
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.
Yes, it is true that inline datums attached to reference inputs end up in the script context, but there is a requirement that each datum hash must have its preimage in the transaction witnesses. What I am saying as that reference inputs (which point to inline datums) cannot substitute for this requirement.
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 am thinking of something like how URLs work. We are quite familiar with the idea of passing additional information in URLs via e.g. query parameters. That gives you the "one link to click" UX but with the ability to include extra payload to be sent. You could imagine an address format like
address?datum=<cbor hex>
. Maybe this is bad UX for other reasons, I just wanted to float it as an idea.This seems pretty bad. I would think that on the wallet side you want to be pretty careful with letting people send money to script addresses, but I'm not sure what the ideal UX is.
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.
Exactly; some wallets are starting to warn about that, but the main problem is that the wallets can't tell the difference between a native script and a Plutus script without seeing the script bytes somewhere on-chain. It's totally safe to send funds to a multi-sig address without a datum. They both start 0x70. Hence, I think, #310