-
Notifications
You must be signed in to change notification settings - Fork 322
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-0131? | Transaction swaps #880
base: master
Are you sure you want to change the base?
Conversation
CC @polinavino, @fallen-icarus and @WhatisRT |
thanks; a timely submission @lehins... So we can link to a readable draft, please rename your document to |
fe3b943
to
5f370fd
Compare
There was a lot of discussion on the mentioned CIPs and it was easier for me to write a CIP of my own that describes of how I would solve this problem, rather participate in lengthy discussion on existing CIPs trying to explain my point of view.
Done. Sorry, my first CIP here 😁 |
Since this is not marked as a draft on GitHub, and because it's being referenced in other actively reviewed CIP pull requests, I think it's appropriate to "triage" it at the next CIP meeting even though you |
@rphair I've changed the wording to "attempt", which actually better reflects my intent than a "draft" |
I like this approach. It seems simple and straightforward. AFAICT this doesn't support transaction batching, but I think it covers enough use cases to still be worth it. |
Interesting idea! I would be very curious to understand the changes to the UTXO rule that this approach requires. Also, the updated approach to Validation Zones does not require changes to the ledger state because there are no more requests and fulfills. There are also no changes to the transaction structure at all, so it is compatible with existing Plutus contracts (no new version needed). The main change is moving the |
Co-authored-by: Adam Dean <[email protected]>
We'd first need to define what transaction batching would actually mean for Cardano. What does it mean in the context of plutus scripts, etc. This approach allows you to batch multiple swaps, which themselves can be thought of miniture transactions with limited functionality. So in some sense it is batching of transactions.
We would need to validate each individual swap (eg. verify signatures, ensure outputs satisfy minutxo requirement, etc.) We would be able to reuse various portions of existing rules to create this rule for swap validation. We would then need to enforce that that there are no duplicate inputs. Then for the purpose of balancing consumed and produced in the UTXO rule we could pretend that all pieces of swaps are defined in the transaction itself, while enforcing there are no duplicate inputs.
I am not to worried about changing the ledger state, wed do it all the time.
This is not possible. Any existing script that for example expects a fee to never be |
As far a rule changes, that is significantly more complicated than the (implicit/updated) validation zones proposal, but could possibly work. I still do not understand if this is built on top of the zones concept or transactions can be validated individually (without the need for zones)?
Fair, a better approach would be - add a new field of type Value to the transaction that is computed at the time of deserialisation - the extra or missing value would be in it, and only let old version scripts run on a transaction if that field is 0. |
With the way I am thinking about it, and the way @WhatisRT described it, perhaps transaction "batching" is not appropriate. Transaction "chaining" may be more accurate. Ergo defines transaction chaining as: the sequential use of outputs from off-chain transactions. I personally don't have a use case for it, but a CIP was opened a while ago about it. I like the simplicity of this CIP so unless there is also a simple extension to enable chaining, I think I'd rather leave it as future work. IMO babel fees and off-chain trading are more critical than chaining. |
A couple of other thoughts -
another example of the sort of problem I am getting at in (2) : you can mint an NFT whose script requires the signature of a swap-builder, and the spending of some input in the swap, without them actually signing off on this minting. |
CIP-xxxx/README.md
Outdated
swap_body = { 0 : oset<transaction_input> ;; Regular inputs | ||
, 1 : [+ transaction_output] ;; Outputs, that will have to be satisfied | ||
, ? 3 : slot_no ;; Validity interval start slot number | ||
, ? 5 : withdrawals ;; Regular withdrawals | ||
, ? 8 : slot_no ;; Validity interval end slot number | ||
, ? 9 : mint ;; Mint field | ||
, ? 14 : required_signers ;; Required signers | ||
, ? 15 : network_id ;; Network Id | ||
, ? 22 : positive_coin ;; Treasury donation | ||
} |
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 this still needs something like the original script_data_hash
to guarantee the proper plutus_data
is used as the redeemer for each smart contract. Otherwise, there is a possible man-in-the-middle attack where a malicious party can change the supplied redeemer to one that benefits them. Wouldn't the following work?
swap_redeemer = [ tag: swap_redeemer_tag, index: uint, data: plutus_data ] ; no ex_units
swap_redeemer_tag =
0 ; inputTag "Spend"
/ 1 ; mintTag "Mint"
/ 3 ; wdrlTag "Reward"
swap_script_data_hash = $hash32
; The derivation is the same as script_data_hash except it uses
; swap_redeemer instead of the original redeemer so that the execution units
; can be given later.
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 this still needs something like the original script_data_hash to guarantee the proper plutus_data is used as the redeemer for each smart contract.
The transaction swap is signed, so no-one can mess with the datum that will be passed to the swap scripts. Transaction builder will not have the capability to supply plutus datum's, it will only be the execution units that are supplied:
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.
Datums and redeemers are separate things. Datums are part of the UTxO set so the input itself has everything needed to verify the proper datum is being used. The same is not true for redeemers. The above swap_body
does not contain the plutus_data
(or hash of it) for the redeemers anywhere. AFAIU that was the point of the script_data_hash
being in the transaction_body
.
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.
Datums and redeemers are separate things.
Datums was a terrible terminology. The way we use it internally in Ledger (not sure of that is the same outside) is there are spending datums and datums that are supplied in the redeemers. Both are just arguments that are supplied to plutus scripts through plutus context.
You are right. We'll need to move swap_redeemers
into swap_body
, so it can be signed. That is because there is no scriptIntegrityHash in the swap body.
swap_redeemers = { + [ tag : swap_redeemer_tag
, index : uint .size 4
] => plutus_data
}
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.
Datums was a terrible terminology. The way we use it internally in Ledger (not sure of that is the same outside) is there are spending datums and datums that are supplied in the redeemers.
It seems everyone uses the terms differently. The redeemer for a smart contract developer doesn't contain execution units, it is just the plutus data. The fact that the same term is used with a different definition by the cddl is really confusing... The terms should be standardized.
We'll need to move swap_redeemers into swap_body, so it can be signed.
I don't think this is enough. If we have a list of signed redeemers, how do we know they are being paired up with the right scripts? I think we really need a script integrity check for the transaction swaps; this version just wouldn't care about the execution units. The swap_redeemers
can remain outside the swap_body
while the swap_script_data_hash
encapsulates the required script+datum+redeemer pairing.
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.
The swap_redeemer_tag
is also relevant for the integrity check since some scripts can be executed as spending, minting, or withdrawals. How do we know the script is being executed as the right type without the script integrity check?
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.
If we have a list of signed redeemers, how do we know they are being paired up with the right scripts?
Ledger will take of this part in the same way we take care of it right now.
The point of script integrity hash is that it takes pieces outside of the transaction body and through a cryptographic hash it places it into the body, so it can be signed. In other words it is like placing redeemers, datums and current costmodels directly into the transaction body.
So, if we place redeemers (without execution units) into the swap body, then the user by signing the body prevents anyone messing with the arguments of all of the plutus scripts in the swap. Same applies to the purpose (redeemer tag), since that is the key in the redeemer map.
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.
We could of course employ the same process and include script integrity hash in the swap transactions. Maybe that would be even better for consistency with regular transactions, albeit at the cost of slightly higher complexity
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.
No please, calculating and working with script integrity hash is the worst part of offchain library development on Cardano. Frankly redeemers should have just been directly in the body originally.
The work of a transaction builder happens off chain, which is the work that no-one has to pay for. Even the largest and the most expensive of scripts are really cheap to run locally on a single computer. We only need to worry about cases like that for cardano-node that does validations of transaction. Maybe I misunderstood your concern, so if you can elaborate on it that will be great.
No they can't. This is not a problem for swap transactions at all. When you have a script that locks something in a swap and that swap also contains a role token. That script will only succeed if that role token is present in that exact swap. Tokens from swaps are not placed in some pool that makes all of them equivalent. So, the fact that scripts from other swaps can see such tokens in other swaps, does not mean anything for the safety of the swap. It is up to the script writer to enforce where the role token is expected to be for the script to succeed. So, using such script that expects a token in the same swap will make it fail if it is missing, regardless if that token is present in other swaps in the transaction. You gotta remember that scripts will see the full transaction and they can make any decision the please on that transaction.
There is no expected trust in this model between swap builders and the transaction builder. swap builders have full control of their portion of the transaction through signing the swaps and providing all the necessary data for execution of their scripts. So they DO NOT NEED to sign the transaction in which their token is used. In the matter of fact this is a feature that Validation Zones does not have: a script writer can look into other swaps and see who provided such tokens or make their own decision on such information. Hell, they even could even make the transaction to fail if it contains swaps with undesired tokens.
So, this is totally incorrect. No one is surrendering anything. In the matter of fact they are receiving more powers when comparing to Validation Zones: the see the origin of funds and everything that took place for those funds to balance the whole transaction. And they can make any decision they like on that information
Plutus contracts will have to change, since the context will change. I would like to bring an important argument to this point. Formal verification is just a tool that we use. It should never get in a way of new features. If some feature requires more work on the formal methods side we will just have to add more people to solve it. Vast majority of work always happens on the implementation side and I don't see any blockers here. |
The point of the two-phase validation mechanism is to make sure that when a node executes a script from an incoming (phase-1 valid) transaction, and the script fails (this is still off-chain/in the mempool), the node is still able to put that transaction in a block for the purpose of collecting its collateral, thereby getting paid for the work of running of the scripts in that transaction. The scripts are later also run as part of block validation, to make sure that they indeed fail, and collateral should be collected. This feature is a way to prevent DDoS attacks. Because swaps are not transactions, they force the node to run their scripts to determine if they can go into blocks, but do not provide any collateral. The two-phase validation guarantee that nodes will be compensated for running mempools is then void. Part (2) of my earlier comment is not so much an issue to solve, but rather I am just pointing out that script logic with swaps will have to be significantly more complicated, with more cases/attacks to consider. |
@polinavino I know exactly how phase1/2 validation works and how, why and when collateral is collected. You are just missing my point. Transaction builder has to supply the collateral for all of the scripts in all of the swaps. The person or application building the final transaction that contains all the swaps is ultimately responsible for its validity. They know ahead of time before submitting transaction will it result in a phase2 validationfailure or not. We are not dealing with Etherium here. It is the same as with ValidationZones, the last transaction must provide collateral for all prior transactions in the zone. |
CIP-xxxx/README.md
Outdated
#### Plutus Context | ||
|
||
This proposal has one huge difference from the Validation Zones proposal, namely all of the scripts in a transaction, uncluding the ones in the swaps will see all of the transaction swaps in their context, because they get access to the full transaction. This comes with a benefit of allowing plutus scripts to make decisions on all of the individually unbalanced pieces. That being said it would come at a higher cost for scripts, unless we would also implement [cardano-ledger#3124](https://github.com/IntersectMBO/cardano-ledger/issues/3124), which we have plans on doing anyways. The biggest cost is extra complexity for script writers, since now inputs and withdrawals and minting scritps could now appear in two different places: in regular transactions and in swaps. |
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 way the plutus context would work with this CIP needs to be explicitly stated in the specification section. I'm imagining the TxInfo
would have a new field:
data TxInfo = TxInfo
{ ...
, swapTransactions :: [SwapTxInfo]
}
All of the information related to the transaction swaps would only appear in their respective SwapTxInfo
(eg, the signatures for a swap transaction are only found in that swap transaction's SwapTxInfo
).
Smart contracts also need a quick way to know which context is theirs. The ScriptPurpose
would need to specify which SwapTxInfo
this execution is for in addition to the rest of the information in the purpose. An index into the [SwapTxInfo]
would likely be fine.
CIP-xxxx/README.md
Outdated
|
||
#### Dependencies of transactions and collateral | ||
|
||
Another major difference is that swaps are constructed completely independently and it is only the top level transaction that combines them all together. This allows for an unlimited number of swaps to be constructed concurrently, while Validation zones have inherent dependency in their design: every transaction depends on all of the preceding transactions in the zone. From my understanding this dependency comes from the design of how colateral is specified in the zones. |
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.
With the new update to Validation Zones (implicit) , this dependency only exists for transactions that specify requiredTxs
. That is, transactions that are performing exchanges without caring who the counterparty is are now not required to provide collateral for any other transactions, only themselves.
For requiredTxs
, any DAG of dependencies can be formed. As soon as a failing script is encountered, only the failing transaction + the ones it depends on is entered into the block/zone. For this reason, each transaction is phase-1 checked to cover the size-based fee of transactions it depends on so that they can be included in the zone, and shown to Plutus scripts when needed. Note that this is actually not the collateral for running scripts, but only the size-based fee portion. This is cheaper than having to supply collateral for all scripts.
CIP-xxxx/README.md
Outdated
|
||
The decision of who pays for the collateral in Validation Zones comes with a natural benefit of deterring users from constructing transactions with phase2 validation, since the first transaction that fails phase2 validation is the one that pays for all scripts in all of the preceding transactions in the zone. | ||
|
||
In case of swaps it is up to the transaction builder to figure out which swaps together make up a phase2 valid transaction, because ultimately they will be paying for the collateral if any of the scripts do not succeed. In my personal opinion it is totally reasonable to put this responsibilty on the transaction builder, since ultimately that is the entity that will be making the money in this process. |
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.
After some consideration, it became clear to me that in Validation Zones implicit, transactions do not need to provide collateral for preceding ones (except in requiredTx
cases discussed above). So, everyone provides their own collateral and it still functions for its intended purpose - to prevent DDoS attacks on nodes by forcing them to run arbitrary amounts of failing scripts.
In VZ design, is possible to allow for the special cases where some nodes would be willing to cover collateral of other users by signing users transactions that include the use of their collateral UTxOs, taking on the risk. This should be the exception and not the rule, however.
CIP-xxxx/README.md
Outdated
|
||
#### Full transaction vs a subset of features | ||
|
||
Validation Zones allow for full blown transactions that allow usage of features that are not relevant for the goal of solving unbalanced transactions and the feature of Babel Fees, for example voting, proposing, certificates etc. It makes no sense to include them in the swaps, since that would unnecessarily complicate the logic, while in Validation zones it would not make sense to exclude any of them for the same reason. This could be viewed as a benefit or a drawback, depending on one's point of view. One imporant thing to remember when concidering this point is that both of the approaches respet the same transaction size limit. |
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 guess my overall comparison would be something like :
VZ and Swaps do :
- reduce duplication of witnesses
- support exchanges with counterparty irrelevance
- allow scripts to see other Txs
- allow others to pay your fees
- require some sophisticated-ish off-chain infrastructure
VZ negatives :
- need to construct zones
- mempool needs to deal with zones
- more fiddly
VZ positives :
- does not impose transaction dependencies unless specified by the user
- any DAG dependency graph is allowed rather than *-dependency , which is more versatile
- collateral mechanism still works for DDoS
- collateral required for only your own scripts (unless dependencies specified)
- individual collateral slightly cheaper because only the scripts in one transaction with failing scripts ever need to be run/covered by collateral
- Plutus scripts will be slightly cheaper to run, because other transactions' data is only shown to them when required by user
Co-authored-by: Ryan <[email protected]>
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.
Thanks @lehins for the groundbreaking work so far on this proposal and vital review on other proposals within the same scope. CIP meeting today acknowledged this & was keen to assign a number so the community can start referring to the proposal more concretely.
Please also change your directory in this branch to CIP-0131
and update the link to (rendered proposal) in your OP. 🎉
@lehins it would be quite nice to mesh this with CIP-112, and provide a "required observers" field on each transaction swap, which specifies observer scripts that must be executed on the full transaction. That would allow me, for example, to add additional requirements that must be true of the whole transaction in order for the piece I'm signing to be valid. |
This CIP is a solution to Intents for Cardano | CPS-15
After reading Transaction pieces - CIP-0130 and Validation Zones I realized there are too many downsides in both of those proposals fro my liking. Almost all of the downsides come out of introduction these strange new relationships between transactions. In order to retain sanity for all of the ledger team developers including myself, I would like to reduce the complexity and propose a different approach that hopefully satisfies all of the needs that other competing proposals would solve.
This is an initial attempt. Before I spend more time on polishing it up, I would like to get some feedback from the community.
(rendered proposal)