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

CIP-0130? | Transaction Pieces #873

Conversation

fallen-icarus
Copy link

@fallen-icarus fallen-icarus commented Aug 5, 2024

This CIP suggests enabling users to create and sign pieces of transactions that can then be communicated and batched off-chain. This batch can then be submitted as a completed transaction without requiring any coordination among users. Due to the way it works, this CIP would make Cardano's basic transaction a natural intent settlement system which means it would enable direct support for things like Babel Fees and off-chain trading. Thanks to the eUTxO model, it is able to achieve this without sacrificing any of Cardano's determinism. All that is really required for the implementation is a re-organization of the cddl.

This CIP is a potential solution to CPS-0015, and an alternative to Validation Zones and Smart Transactions.


rendered proposal

@fallen-icarus
Copy link
Author

@Quantumplation @michaelpj @polinavino This CIP elaborates on what I was talking about in the Validation Zones PR discussion. I think it covers the same use cases with a fraction of the complexity.

I'm going to tag other people that have expressed interest in intents:
@colll78 @AndrewWestberg @vlasin

@rphair rphair added the Category: Ledger Proposals belonging to the 'Ledger' category. label Aug 6, 2024
Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

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

@fallen-icarus thanks for continuing that premise. I've added this PR for introduction to tomorrow's CIP meeting agenda: https://hackmd.io/@cip-editors/94

I would be really happy to keep this evolving along with Validation Zones and any other approaches to intent-based transactions. At some point soon we would need the Ledger team's opinion about feasibility so also tagging them (@lehins @WhatisRT).

This is not a detailed review, since the point at which I could assess all the technical propositions and/or compare/contrast with Validation Zones would be a long time coming: this is just a skim for format consistency, etc., while we get this into the pipeline.

CIP-????/README.md Outdated Show resolved Hide resolved
CIP-????/README.md Show resolved Hide resolved
CIP-????/README.md Outdated Show resolved Hide resolved
CIP-????/README.md Outdated Show resolved Hide resolved
@vlasin
Copy link

vlasin commented Aug 6, 2024

I support this solution as a significantly less complex alternative.

A suggestion: smart contracts could get some extra efficiency from this CIP if there is a way to access the current transaction piece inside the script. Oftentimes, a script needs to find a specific input satisfying some conditions. Since transaction inputs are not sorted by the signers, the signers don't have control over the input order. So, in transactions with many inputs, a lot of ExUnits may be spent just on finding the right input, which is usually from the same transaction piece as the script itself.

@gitmachtl
Copy link
Contributor

gitmachtl commented Aug 6, 2024

I guess this is currently not hw wallet compatible, because hw wallets need a full transaction from a security pov. Its not possible to just sign "anything" with a hw wallet. Arbitrary data can be signed now with hw wallets, but its embedded in the CIP-8 COSE_Sign1 structure to avoid users falling into the trap that they think they sign just some data, but in reality they are signing a transaction with a given hash.

Or do i understand this CIP wrong? The hw wallet does not care if a transaction is balanced or not. But without the whole transaction in place the total hash of the transaction would not be correct and so the hw wallet signing would not work.

Its already possible - and we do this all the time - to collect multiple witnesses. And a hw wallet can of course provide a witness to a transaction, which is than later assembled into a fully signed transaction.

Copy link
Contributor

@WhatisRT WhatisRT left a comment

Choose a reason for hiding this comment

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

This appears to me to be mostly a simplified version of the Validation Zones CIP that has a bunch of benefits because of its simplicity, but also some drawbacks. Maybe there is a 'best of both worlds' design that lies somewhere in the middle?

Comment on lines +290 to +291
While the cryptographic keys only need to sign the relevant transaction pieces, the smart contracts
would be executed against the full transaction. This requirement enables the current smart contract
Copy link
Contributor

Choose a reason for hiding this comment

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

This goes against our current philosophy of preserving the submitted transaction as verbatim as possible. Without a good reason for this, it's probably better to properly reflect this structure in TxInfo.

Copy link
Author

Choose a reason for hiding this comment

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

Without a good reason for this, it's probably better to properly reflect this structure in TxInfo.

Personally, whichever option minimizes added complexity is my preferred choice. If we preserve the transaction as you are suggesting, the smart contract interface would need a pretty large change and DApps would need to be re-architected. Perhaps keeping the structure in TxInfo would be better in the long run, but I'm not sure. I do not know what changes would be required on the ledger/plutus side to actually preserve the structure in TxInfo.

@vlasin If we did preserve the structure, I believe this would address your suggestion.

be able to deterministically derive the full transaction, this instance must be standardized. The
monoid instance requires answering two questions:

1. What is an empty transaction piece?
Copy link
Contributor

Choose a reason for hiding this comment

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

You cannot submit an empty transaction anyway, so we don't necessarily need to define this. There is no reason why it needs to be a Monoid.

Comment on lines +469 to +475
Imagine if Alice wants to swap 100 ADA for 50 DJED; this says nothing about how the counter-party
will satisfy her swap. Perhaps Bob (the counter-party) wants to provide the DJED, but it is
currently broken up across three UTxOs. And perhaps he wants the 100 ADA he receives to be stored in
two separate outputs. If Alice specifies only one input placeholder and one output placeholder in
her intent transaction, Bob might be unable to act as her counter-party. If Bob really wanted to
still trade with Alice, he would have to find a way to merge his inputs and outputs into one input
and one output. This may not always be possible and Bob may have to give up on being Alice's
Copy link
Contributor

Choose a reason for hiding this comment

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

This is actually quite easy to do with validation zones. Bob just needs to spend his three outputs and Alice's Ada output and negative Djed output, and make two new outputs containing the Ada. The only extra effort is to spend Alice's two outputs.

Copy link
Author

Choose a reason for hiding this comment

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

See my other comment here.

placeholders and extra transactions just to satisfy these placeholders, the impact Validation Zones
would have on the average block size is likely equivalent to, if not larger than, the impact this
CIP would introduce.

Copy link
Contributor

Choose a reason for hiding this comment

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

Compared to the validation zones approach, this approach appears to be somewhat simpler indeed, but it also has some restrictions. For example, this approach doesn't allow for atomic batch transactions in general. A transaction piece cannot spend an output generated by another transaction piece in the same transaction. I see this as a major drawback, and it should be discussed here.

Copy link
Author

Choose a reason for hiding this comment

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

Atomic batch transactions seems orthogonal to intents (it isn't mentioned in the CPS), but I can still discuss it in the CIP.

@polinavino Perhaps there is a way to use transaction pieces as the foundation for zones?

Comment on lines +482 to +484
having enough liquidity. The Validation Zones CIP argues that DApps can just add support for
breaking-off/merging UTxOs so that the liquidity source UTxOs can be forced into the proper forms
for the required placeholders. However, this would dramatically increase the complexity and attack
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see where this is discussed in the Validation Zones CIP. But I also don't see a big problem here, just by going on with the same logic from the last paragraph. A matching transaction can always spend the 'problematic' outputs and then construct the remainder of the transaction as it would construct the transaction piece. I don't see where this impacts 'dramatically increases complexity'.

Copy link
Author

@fallen-icarus fallen-icarus Aug 6, 2024

Choose a reason for hiding this comment

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

A matching transaction can always spend the 'problematic' outputs and then construct the remainder of the transaction as it would construct the transaction piece. I don't see where this impacts 'dramatically increases complexity'.

I guess I'm just biased. Given there is a path that doesn't require these extra steps, why would you ever take the path of more resistance?

EDIT: Actually, I think the Validation Zones' approach is still an issue. Being able to break-up/merge DApp UTxOs is most likely orthogonal to what the DApp is trying to do. I strongly believe DApps should use the bare minimum level of complexity to accomplish their task since people's livelihoods are literally at stake. Perhaps "dramatically" is an exaggeration in most cases, but "increases complexity" is still accurate and I still think it is bad. Especially since there is an alternative method that doesn't require the extra complexity.

@fallen-icarus
Copy link
Author

@gitmachtl A transaction piece is literally just a full transaction body that can be unbalanced. Therefore, hw wallets should already be (mostly) capable of signing pieces. If hw wallets can hash/verify/sign a transaction body, then it can hash/verify/sign a piece since they are the same thing.

But without the whole transaction in place the total hash of the transaction would not be correct.

When signing the piece, the hardware wallet would hash the piece itself. This is all the signer cares about anyway. Since the piece is included in its entirety on chain, users can easily verify their transaction piece was properly included using the hash of the piece. The off-chain tooling would definitely need to be updated to enable this verification, but I don't see anything that would make this difficult/impossible.

In this new paradigm, hw wallets only need to be able to sign transaction pieces. You can still have a piece be a fully balanced transaction so there is no loss of functionality with this change.

@fallen-icarus
Copy link
Author

A suggestion: smart contracts could get some extra efficiency from this CIP if there is a way to access the current transaction piece inside the script. Oftentimes, a script needs to find a specific input satisfying some conditions. Since transaction inputs are not sorted by the signers, the signers don't have control over the input order. So, in transactions with many inputs, a lot of ExUnits may be spent just on finding the right input, which is usually from the same transaction piece as the script itself.

@vlasin I'd have to think about this. This would definitely add a lot of complexity and actually require a change to the smart contract interface. I agree there would be some efficiency gains, but since the aggregator can control the order of the pieces themselves, the ExUnits spent can already be minimized quite a bit. I'm not sure if the rest of the gains would be worth the extra complexity.

@gitmachtl
Copy link
Contributor

gitmachtl commented Aug 6, 2024

A transaction piece is literally just a full transaction body that can be unbalanced. Therefore, hw wallets should already be (mostly) capable of signing pieces. If hw wallets can hash/verify/sign a transaction body, then it can hash/verify/sign a piece since they are the same thing.

maybe i am just confused, but how would an unbalanced transaction be ready for signing? if you change the lovelace/asset value of an output, the hash of the tx would not be the same again. or is it output to a fixed "pseudo" address, and later those transactions are somehow put together?

@fallen-icarus
Copy link
Author

if you change the lovelace/asset value of an output, the hash of the tx would not be the same again

You wouldn't change an output after signing. The transaction piece stays unbalanced. The aggregator can then fit unbalanced transaction pieces together, just like lego toys. The unbalanced transaction pieces end up balancing each other; no outputs need to be changed.

@gitmachtl
Copy link
Contributor

Got it, thx

Co-authored-by: Robert Phair <rphair@cosd.com>
be built up as well. Duplicate executions should be removed. For example, if two transaction pieces
look to spend the same smart contract input using the same redeemer, there is no reason to evaluate
this scenario twice since they are guaranteed to have the same result.

Choose a reason for hiding this comment

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

This looks like a very promising design indeed! I am curious to understand how minting NFTs that require the spending of a specific UTxO entry would work, if two pieces are trying to mint the same NFT? Would you be able to locally test if your NFT contract validates?

Copy link
Author

Choose a reason for hiding this comment

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

I am curious to understand how minting NFTs that require the spending of a specific UTxO entry would work, if two pieces are trying to mint the same NFT? Would you be able to locally test if your NFT contract validates?

Hmm, I hadn't thought about testing just a piece, but it should be possible since a transaction piece is just a transaction body. This would just require a smart contract to be able to run against an unbalanced transaction body. Whether the smart contract would still succeed when aggregated with other pieces depends on the smart contract logic. So Alice can verify her piece will successfully validate before she signs, and the aggregator can verify it will still validate after adding it to the batch.

Copy link

@polinavino polinavino Aug 6, 2024

Choose a reason for hiding this comment

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

In general, it opens up the possibility of an attack if the outcome of contract validation can be changed after you submit your transaction or transaction piece. I see two ways to deal with this :

  1. restrict your design to only allow combining pieces that contain no Plutus smart contracts whatsoever

  2. simplify Validation Zones to be more in line with what you are suggesting/be more implicit with value flow :

  • revert changes to the UTxO rule, UTxO State, and Transaction data structures (leave them the same as in the current non-zone ledger design)
  • remove new Plutus version requirement
  • move the produced = consumed computation to the ZONE rule, and aggregate across all transactions in the zone, that is Sum_{tx \in zone} consumed tx = Sum_{tx \in zone} produced tx
  • replace the cycle detection in the ZONE rule by in independency check, which checks that "for all inputs (txid, ix) in all transactions in a zone, txid is not the ID of another transaction in the zone
  • only require the per-transaction component of the transaction fee to be paid once per zone

The validation zones simplification will all remove the need for the weird requireTxs song and dance, and an aggregator would be able to spend their liquidity pool once per zone to provide any missing value and claim any extra value within a single transaction. Scripts would operate without any changes. Flash loan attacks would be prevented by the independency check.

I believe this achieves what you are trying to do, @fallen-icarus , without compromising contract validation, by retaining the property that you cant tamper with transactions (and therefore their contract outcomes) without invalidating them

Copy link
Author

Choose a reason for hiding this comment

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

it opens up the possibility of an attack if the outcome of contract validation can be changed after you submit your transaction or transaction piece.

Only an individual piece is susceptible to this. The transaction batch is set upon submission to the blockchain and the determinism of the eUTxO model guarantees this can't happen to submitted transactions.

There is also another choice: if the user is looking to use a smart contract in a transaction piece and the user does not control the final batch, the user should accept the risk that the smart contract validation will change.

I do not believe all risks need to be eliminated. I think only the non-obvious ones need to be eliminated. The risk you bring up is a very obvious risk. When users place market orders, they accept the risk of slippage due to not controlling what the actual trade will be. How is this any different?

If the user actually wants to guarantee the smart contract validation will be a certain way, they can either:

  1. Write the smart contract to be agnostic to other pieces - DApp developers already do something like this to securely support DApp compositions and prevent double satsifaction.
  2. Create the batch themselves.

Both of your suggestions have trade-offs that I think are too extreme:

  1. Not allowing smart contracts whatsoever would make it impossible to balance these pieces against DApp liquidity sources. This would cut these batches off from a huge amount of liquidity.
  2. AFAIU the changes to Validation Zones would still not be able to properly model unbalanced intents which means all of the counter-party problems (and extra complexity) still exist. (Please correct me if I am wrong; it is sometimes hard to follow the jargon.)

I'm still open to ideas, but I really don't think people need this much hand-holding.

Copy link

@polinavino polinavino Aug 6, 2024

Choose a reason for hiding this comment

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

  1. The difference between an account-based dApp doing things and the Boolean outcome of an EUTxO contract is that the dApp can (and has to) be written in a way that accommodates any possible account state to which it can be applied, eg price changes etc. An EUTxO contract fee cannot even be calculated without the complete fixed transaction data.  You can make any contract fail (with no negative consequences for yourself) by bulking up the tx size. If you make a transaction that is valid, someone can always make your scripts fail by submitting it batched with whatever nonsense. To avoid this, you have to be able to prevent your transaction from being modified (this is currently possible only by signing it with a key that is required to sign it). Note that even if you yourself didn’t include collateral, and are relying on someone else to include it, the transaction batch they construct can also be messed up. This brings me to the Validation Zones modification - 

  2. The change I propose actually allows you to model unbalanced intents by allowing whole transactions that are unbalanced, while the zones containing transactions must be balanced. It is somewhere in between the original design and what you propose. Instead of requests and fulfills, it simply allows assets to be missing from or be extra in a transaction. The preservation of value at the zone level ensures that regardless of who provides the missing assets and claims the extra ones , things balance out. Consider zone [Tx1; Tx2]

Tx1 -
(Resolved) inputs: txIn -> (10 Ada, …)
Outputs:(5PolinaCoin, …)

Tx2 -
(Resolved) inputs :txIn’ -> (5PolinaCoin, …)
Outputs :(10Ada, …)

The important thing is that txIn’ is not = (hash (Tx1), _), because of flash loans. No need to request 5 PolinaCoin or make an unlocked offer of 10Ada.

Copy link

@polinavino polinavino Aug 7, 2024

Choose a reason for hiding this comment

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

  • i think it will not be possible to specify a fee of 0 (this is because the amount of fee specified in each transaction must go to the fee pot, and that's how the system knows how much to add), but it does not mean you have to provide the fee. Your transaction can simply be short that amount, and someone else's transaction needs to have that amount extra to make a valid zone

  • the flash loan problem here really has to do with smart contracts : you should not be able to use a thread token or a role token in your transaction unless you spend an actual output containing one. If we allow transactions in a zone to be chained in this way, you dont actually need to spend a real output with a role token to use one, flash-loaning yourself one :

Tx1 -
(Resolved) inputs : txIn -> (out1)
Outputs: (1RoleToken, …) , (out2)

where out1 and out2 are some outputs containing the state of some contract which can only be updated (from out1 to out2) if the transaction contains a RoleToken that allows such an update to be made. We are assuming here that out1 and out2 both have no assets in them for simplicity of example

Tx2 -
(Resolved) inputs : (hash (Tx1), 1) -> (1RoleToken, …)

Copy link
Author

Choose a reason for hiding this comment

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

you should not be able to use a thread token or a role token in your transaction unless you spend an actual output containing one.

Perhaps I misunderstood what you meant by (hash (Tx1), _). I may also be misunderstanding what you mean by

(Resolved) inputs: txIn -> (out1)`.

The way I am thinking about it, the role token requirement is guaranteed to be true; there is no way to NOT spend the role token. No "resolving" is required.

A zone is just a list of transaction: [tx1, tx2, tx3]. But we can force a dimension of time on the list:

---- Time ---->
[tx1, tx2, tx3]

If we do this, then the above is just a mini-blockchain, and we can treat it as such. The zone can build up its own mini UTxO set, and transactions in the zone have access to the current state of real UTxO set + zone UTxO set at the time they are being processed. The zone can keep track of both the UTxOs spent from the real UTxO set and the UTxOs created/spent from the zone UTxO set. When all transactions in the zone have been fully validated, the net affect of the real UTxO set + the zone UTxO set is what actually gets applied to the blockchain.

In the above zone, tx1 gets processed first. Imagine if tx1 spends a real output r1 and produces an output z1. The zone would log that r1 has been spent from the real UTxO set and z1 has been added to the zone UTxO set. tx2 comes after tx1 and can spend any remaining UTxO in the real UTxO set and the zone UTxO set. So tx2 cannot spend r1 because it was already spent by tx1, but it can spend both r2 (another input from the real set) and z1 to produce z2. tx2 would call z1 explicitly by output reference: (hash(tx1), z1_index). z1_index is its index in tx1's output list.

Now the new state is the real UTxO set minus r1 and r2, plus the zone UTxO set of z2. tx3 comes after tx2 so it must use this new state. If it wants to spend z2, it must call it explicitly by output reference: (hash(tx2), z2_index). z2_index is its index in tx2's output list. Let's say tx3 spends z2 and produces z3. tx3 cannot spend z1 because it was already spent by tx2.

The net effect of this zone is r1 and r2 are both spent to produce z3. This is the change that would actually be recorded on the blockchain.

With this method, if the role token was in r1, tx1 moved it to z1. If tx2 wants to execute the smart contract that requires the role token, it must now spend z1 because that is where the token is at the time tx2 is being processed. If tx3 wants to use the role token as well, it must spend z2 since that is where tx2 moved the token to. Because time is enforced on the zone, the zone can know where the role token is at all times. When z3 is actually recorded on-chain, it is the result of r1 -> z1 -> z2 -> z3. The eUTxO model guarantees the result is the same as doing the updates separately (ie, in separate zones).

Important

With this approach, building a transaction chain requires coordination between the transactions in the chain. However, I am making the assertion that: coordination is always possible whenever transaction chaining is useful. If we accept this assertion, there is no need for any "resolving", and flash loans are impossible.

Strangers can't build up chains this way since they don't know each others' transactions (and therefore each others' output references), but why would strangers be chaining their transactions anyway? This is mainly useful for account style DApps that are controlled by batchers.

A batcher can personally build up the chain when trying to fulfill the user orders. They can create tx1 first and use its hash in tx2, and then use tx2's hash in tx3. Furthermore, since the batcher knows the order of the outputs in each transaction, they know what the resulting output references will be in advance. The batcher can even turn the user orders into chains (if the user transactions allow for it). If batchers need to build up a chain together, they can coordinate this off-chain.

Because the outputs are being called explicitly by output reference, it is trivial to check if the role token is actually being spent. The smart contracts don't even need to know they are being executed in a zone.

Copy link
Author

Choose a reason for hiding this comment

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

i think it will not be possible to specify a fee of 0 (this is because the amount of fee specified in each transaction must go to the fee pot, and that's how the system knows how much to add)

I don't see why it is possible for the zone to know if it is balanced, but not know if it has enough ADA provided for the fee. The zone checks should have everything they need to calculate the total required fee for the zone, and it can compare this against the sum of the fee specified in each transaction within the zone. This would enable some transactions to have a fee of zero while still being part of a valid zone.

Copy link

@polinavino polinavino Aug 7, 2024

Choose a reason for hiding this comment

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

I don't see why it is possible for the zone to know if it is balanced, but not know if it has enough ADA provided for the fee.

This is maybe possible? Removing the per-transaction fee check (in favour of a per-zone check) is likely possible, and if we do, entering a paid fee of 0 for a transaction would also be possible. I need to think more about this, though, and would be happy to discuss in CIP meeting.

I think i need a diagram for your previous comment ...

Here is a draft of the "Implicit Value Flow" validation zones version, it is much simpler than the original

1fd1d6d#diff-560051716ec259fa49b3ef402bb6c1f4b7517d1bf400db3e735996986398bf96

ps do not follow the links to the prototype or Agda spec (they're not done)

Choose a reason for hiding this comment

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

@AndrewWestberg @WhatisRT if you feel like having a look also

Discussions:
- https://github.com/cardano-foundation/CIPs/pull/862#discussion_r1700138228
- https://github.com/cardano-foundation/CIPs/pull/862#issuecomment-2266689423
Solution-To:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Solution-To:

adding to the header will break downstream tools

@rphair rphair changed the title CIP-???? | Transaction Pieces CIP-0130? | Transaction Pieces Aug 6, 2024
Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

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

Agreed to assign number in today's CIP meeting mainly due to the good documentation & the specific, practical nature of the proposal.

@@ -0,0 +1,530 @@
---
CIP: ????
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
CIP: ????
CIP: 130

Please also retitle containing directory to CIP-0130 and update the rendered proposal link in your OP 🎉

@AndrewWestberg
Copy link
Contributor

I like this concept very much. Very elegant.

@vlasin
Copy link

vlasin commented Aug 6, 2024

@vlasin I'd have to think about this. This would definitely add a lot of complexity and actually require a change to the smart contract interface. I agree there would be some efficiency gains, but since the aggregator can control the order of the pieces themselves, the ExUnits spent can already be minimized quite a bit. I'm not sure if the rest of the gains would be worth the extra complexity.

Right, I think by controlling the order of pieces, we already get most of the efficiency gain I was talking about!

Copy link
Contributor

@lehins lehins left a comment

Choose a reason for hiding this comment

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

Unfortunately I see way too many problems with this proposal:

  • change of transaction structure that would require a rewrite of half of the ledger implementation
  • Idea of combining transactions (pieces) into one is just not sound. The common argument that is given in the CIP of rejecting any transactions that are incompatible in some way is way too dangerous and very hard to implement and foresee all the incompatibilities. Most importantly by changing the transaction structure through this combination process you'd be changing the content that users have signed. Which can't be allowed. In other words, transactions do not have a lawful Semigroup instance 😉
  • No protection against transaction reordering

It might sound like I am bashing this CIP, but I am definitely not. I am pointing out the problems. There are few ideas I really like. For example the idea of having a list of transaction that are individually signed, but then combined together by a third part is pretty cool.

So, I've tried to understand this CIP as well as @polinavino's Validation Zones | CIP-118 as much as I could. And here is my attempt at an alternative solution: Transaction Swaps It is a vastly different solution, but it has the same spirit, so hopefully you'll find it satisfactory.

transaction =
[ transaction_pieces : [* transaction_piece]
, bool
, auxiliary_data / null
Copy link
Contributor

Choose a reason for hiding this comment

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

Which transaction_piece would contain the auxDataHash?

Copy link
Author

Choose a reason for hiding this comment

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

I thought I mentioned that, but I guess I accidentally deleted that part while editing. Each piece can specify it, but if multiple do, they must all agree. I wasn't anticipating users to use pieces for storing metadata on-chain. I thought they would build a personal transaction for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

So it is the "must agree" thing sprinkled throughout the CIP that I really have a problem with. Because of those agreements would have to be enforced by the ledger rules. Which increases complexity of already very complex rules.

Comment on lines +272 to +273
- If two transaction pieces require the same witness (ie, both pieces require a signature from
Alice), this witness would now effectively appear twice.
Copy link
Contributor

Choose a reason for hiding this comment

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

Public keys would appear twice, but signatures would be different since they would be signing different content.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, that is what I meant by "effectively", but I could have been more explicit.

credential, etc), so no one can control the order of them.

> [!IMPORTANT]
> This would required [CIP-128](https://github.com/cardano-foundation/CIPs/pull/758) since the order
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
> This would required [CIP-128](https://github.com/cardano-foundation/CIPs/pull/758) since the order
> This would require [CIP-128](https://github.com/cardano-foundation/CIPs/pull/758) since the order


### Phase 1 Validation

Phase 1 validation must be slightly changed to check:
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a severe understatement. Many of the ledger rules would have to be changed and restructured to accommodate validation of these new type of transactions.

So "slightly changed" does not even begin to describe the amount of work that will be needed.

}

transaction =
[ transaction_pieces : [* transaction_piece]
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a backwards incompatible change to a transaction.

Comment on lines +298 to +301
When combining the transaction pieces, the required executions (ie, script + datum + redeemer) can
be built up as well. Duplicate executions should be removed. For example, if two transaction pieces
look to spend the same smart contract input using the same redeemer, there is no reason to evaluate
this scenario twice since they are guaranteed to have the same result.
Copy link
Contributor

Choose a reason for hiding this comment

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

How can you spend the same input twice? That would go totally against all guarantees we provide in ledger 😄

Now, if you meant two different inputs that are locked by the same script and they expect the same datums, then you are totally wrong that they are guaranteed to have the same outcome. Each script gets the script purpose that uniquely identifies which input being spent and that could easily affect the outcome of the script.

Copy link
Author

Choose a reason for hiding this comment

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

How can you spend the same input twice?

You wouldn't. You would remove duplicates when merging the pieces. If you have a list [1,2,3,4] and another [2,5,6] merging them (and removing duplicates) would give [1,2,3,4,5,6]. So input 2 is listed twice, but only spent once.

Smart contracts are a definite weak-point of this CIP, but the new Validation Zones revision seems to address this weak-point while still incorporating the main ideas from this CIP. I personally prefer the new Validation Zones CIP over my own for this reason.

Copy link
Contributor

Choose a reason for hiding this comment

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

You would remove duplicates when merging the pieces

So, as I said you can't remove duplicates because of smart contracts.

And with validation zones you can't have duplicates.

would be executed against the full transaction. This requirement enables the current smart contract
interface to remain mostly untouched. The flow is essentially:

1. Deterministically derive the whole transaction from the list of pieces.
Copy link
Contributor

Choose a reason for hiding this comment

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

You can't deterministically derive a new transaction out of many separate ones. I'll just give you couple of counterexamples:

  • two transactions that withdraw from the same reward account. It is irrelevant that one of them will be zero, it will change semantics. Moreover if a stake credential is a scritp, then how can you ignore running one of them.
  • A DRep casting a yes vote on a proposal in one transaction and then a No on the same proposal in a separate transaction. You might think that we could then ignore the first vote, since the second one replaces the first. But, what if the DRep is backed by a script and it would actually fail if we didn't ignore it.
  • Two transactions minting the same asset. How do you run the script twice? You'll be forced to put squash those two mints together into one, which could affect the execution of the script.

I mean, this is just the tip of the iceberg. So, it is not just about philosophy, as @WhatisRT put it, it is a really bad idea to even attempt it.

Copy link
Author

Choose a reason for hiding this comment

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

  • two transactions that withdraw from the same reward account. It is irrelevant that one of them will be zero, it will change semantics. Moreover if a stake credential is a scritp, then how can you ignore running one of them.

I would argue this transaction should be rejected. I think you are over-complicating things. IMO things should either exact match or be rejected. The only exceptions are when there is a very obvious way to merge them, like with mints and validity intervals.

  • A DRep casting a yes vote on a proposal in one transaction and then a No on the same proposal in a separate transaction. You might think that we could then ignore the first vote, since the second one replaces the first. But, what if the DRep is backed by a script and it would actually fail if we didn't ignore it.

This transaction would be rejected as well because the proposal vote in one piece does not exact match the proposal vote in the other. The pieces are not applied to the ledger sequentially. They are folded into a single transaction and applied as a unit.

  • Two transactions minting the same asset. How do you run the script twice? You'll be forced to put squash those two mints together into one, which could affect the execution of the script.

The mints would be merged upon folding the pieces and the script would only be executed once. As I said before, smart contracts are a weak-point with this CIP. AFAIU, the new Validation Zone revision addresses this limitation by actually having smart contracts validate against the piece they are used in.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not over-complicating! Those are all of the cases that would have to be addressed in the ledger rules. If we are to reject them, we need to implement specific predicate failures for each one of them that do the rejection. I can see this CIP adding more predicate failures than the total number of predicate failures that we already have today.

Validation Zone revision addresses this limitation

I am really confused here. Validation Zones is a totally different CIP. Are you trying to combine the two together somehow?

The only way I currently see combining multiple transactions into one for validation to work is if we restrict all those pieces to be completely disjoined, i.e. no common credentials.

Copy link
Author

@fallen-icarus fallen-icarus Aug 9, 2024

Choose a reason for hiding this comment

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

I am not over-complicating!

Sorry, my confusion came from not realizing the redeemers included the execution budgets and therefore you can't do an exact match. I was under-complicating it 😅

I am really confused here. Validation Zones is a totally different CIP. Are you trying to combine the two together somehow?

The new Validation Zone revision (the README-implicit.md) is trying to incorporate the unbalanced transaction idea from this CIP. It uses them in an entirely different way than how this CIP used them.

Comment on lines +337 to +339
The second question requires more consideration, but as a general rule, as long as there are no
incompatibilities between transaction pieces, they can be combined into the full transaction. Any
transaction that contains incompatible pieces should be rejected.
Copy link
Contributor

Choose a reason for hiding this comment

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

Any two transactions that have plutus scripts would be immediately incompatible.

Imagine a script that relies for there to be only a single input being spent or a single output being produced. As soon as you combine two transactions together they no longer validate. However, you can't know that until you try to combine them and run the scripts

There is another fundamental problem here. Imagine those inputs in two separate transactions are both locked by scripts. Those two transactions both provide script integrity hash that have potentially been signed. But whenever you combine those two transactions together you must change their redeemers and pass those redeemer modified in the script context to plutus scripts, which is totally screwing with the data that have been signed.

So, combining multiple pieces into one transaction idea is definitely out of the window

Comment on lines +367 to +368
It is valid for two separate transaction pieces in a batch to spend the same UTxOs (eg, a liquidity
source UTxO). This UTxO can still only be spent once and the batch must still be fully balanced
Copy link
Contributor

Choose a reason for hiding this comment

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

This not valid. Their position in the input set can affect the execution outcome of a plutus script that locks them

Comment on lines +222 to +224
The order of the transaction pieces list is significant since it represents the order in which the
pieces must be combined, and ultimately determines the order of the inputs and outputs. How to
combine the pieces will be discussed later in the spec.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is very true. However, I can't find any security mechanism that would preserve the order of transaction pieces. There is no guarantee that man in the middle or a nefarious pool will not reorder the transaction pieces in their favor

@fallen-icarus
Copy link
Author

fallen-icarus commented Aug 9, 2024

@rphair @Ryun1 I would like to withdraw this CIP in favor of Transaction swaps. @lehins correctly pointed out fatal flaws with this design:

  • Potential man in the middle attack - since the list of transaction pieces itself is not signed, it can be reordered by a malicious party. This was an oversight on my part.
  • Impossible to pair up pieces using smart contracts - I was not aware that the redeemer in the cddl actually includes the execution units. Then, the script_data_hash in the transaction body is calculated with these budgets. This means it is fundamentally impossible to use a transaction piece with a smart contract since the budgets are based on the whole transaction, and the intent creator must sign this budget. Thus, aggregating the piece with other pieces is guaranteed to change the required budgets and invalidate the signature.

In my view, lehins' CIP is what I intended this one to be.

@rphair
Copy link
Collaborator

rphair commented Aug 9, 2024

@fallen-icarus thank you as always for your brilliant work, your insight, and your humility. I'll mark this as Likely Deprecated and will confirm at next CIP meeting for closure if that still looks appropriate (you can also close it yourself if you wish).

@rphair rphair added the State: Likely Deprecated Close if confirmed deprecated (or long waiting). label Aug 9, 2024
@lehins
Copy link
Contributor

lehins commented Aug 9, 2024

@fallen-icarus I am planning on holding a Ledger working group meeting next Monday. So please join it if you can, because we will be discussing CPS-15 and the suggested approaches at tackling it:

Monday, August 12th 2024 [5pm - 6pm UTC]
Room https://meet.google.com/jer-vaqm-jbu

Anyone else interested in this topic is free to join.

@fallen-icarus
Copy link
Author

@lehins That link isn't working for me. It says: "Could not find the requested event."

@lehins
Copy link
Contributor

lehins commented Aug 9, 2024 via email

@fallen-icarus
Copy link
Author

The link still isn't working for me 😅 You may need to also make the calendar itself public (source).

@lehins
Copy link
Contributor

lehins commented Aug 10, 2024

You may need to also make the calendar itself public

That sucks. Google could have made this UX a bit better. Unfortunately, I can't make my calendar public, but it should not be a problem. Here is the location and time of the event:

Monday, August 12th 2024 [5pm - 6pm UTC]
Room https://meet.google.com/jer-vaqm-jbu

@rphair
Copy link
Collaborator

rphair commented Aug 21, 2024

@fallen-icarus @lehins I'm just assigning CIP # 131 to #880 = Transaction swaps. In my gut feeling for future scenarios I can imagine some circumstances in which this PR might be revived if a re-thinking of the security issue leads to a better evolved design... so I'm not giving the old number to the superseding proposal so the two could in fact exist literally side-by-side someday if they both came somehow to be equally considerable.

@rphair rphair closed this Aug 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: Ledger Proposals belonging to the 'Ledger' category. State: Likely Deprecated Close if confirmed deprecated (or long waiting).
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants