-
Notifications
You must be signed in to change notification settings - Fork 367
CIP-0160? | Receiving Script Purpose and ProtectedAddress #1063
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
base: master
Are you sure you want to change the base?
Conversation
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.
It is hard for me to justify this CIP since, from my perspective, most Cardano DeFi dApps are not implemented with a good understanding of economic incentives. We don't need to block 100% of misconfigured UTxOs - we just need to make it costly to create them.
I can't speak for L2s like Midgard (which isn't done yet); but for DeFi dApps, keeping track of state tokens really isn't that hard. If a dApp has a convoluted threading of tokens, I would argue that is "bad code smell" and a sign that the dApp is trying to do too many things at once - possibly relying too much on global state.
I think the eUTxO model is still too young to say "this is a problem with Cardano" instead of "developers still don't know how to properly use it".
|
|
||
| - **Off-chain filtering**: While indexers and DApp backends can attempt to filter out junk UTxOs, they provide no on-chain security and cannot be relied on in adversarial environments or in composable settings. | ||
|
|
||
| - **Multivalidator pattern**: While technically feasible, this couples minting and spending logic into a single script, constrained by the 16KB script size limit. Furthermore, this introduces a huge layer of complexity and an associated attack surface. Managing the lifecycle of minted state tokens to prevent smuggling is extremely difficult, and becomes more impractical as the complexity of the dApp increases (ie. The attack surface for state token smuggling in a protocol that has 12 different validator scripts is nearly impossible to secure). In practice, this constraints the realm of what types of dApps are feasible on Cardano, you cannot build a cutting-edge financial instrument like AAVE, Balancer, or MakerDAO on Cardano because managing the lifecycle of dozens of state tokens across dozens of scripts while preventing smuggling is infeasible. |
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.
... you cannot build a cutting-edge financial instrument like AAVE, Balancer, or MakerDAO on Cardano ...
People say the same about DeFi order books and I think they are wrong. I'm sure this might upset some people, but I'm really not convinced this isn't just a eUTxO model skill issue...
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.
You certainly can build orderbooks on Cardano, and certainly there are many cutting-edge financial instruments that are built on orderbooks in general, in-fact, I would argue that the vast majority of financial instruments are built on orderbooks. Even in the DeFi space, the single largest financial instrument innovation in recent times is HyperLiquid, an onchain orderbook.
If we look at Cardano dApps relative to their competition on other smart contract L1s, there is clear proof that we are far behind, whether we are looking at orderbooks, or pooled protocols or anything in-between. Sure we can call it a skill issue, as there is nothing that explicitly prevents us from creating competitive dApps with robust feature sets, but we can't just say "it's possible so it will happen". It's also possible on those chains, and it's much easier. We need to improve the developer experience, especially the DeFi developer experience if we want to be the go-to chain for DeFi applications.
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.
Note that one of the big benefits of Leios is that we can/could significantly weaken the script execution and size limits of transactions and blocks. So the size arguments might not be so relevant anymore in the future.
|
|
||
| - **Status quo**: Relying on auth tokens, thread tokens, and registry patterns introduces complexity, performance bottlenecks, and cyclic dependencies that are fragile and hard to audit. It has also led to serious exploits in real-world protocols due to misused or mishandled tokens. | ||
|
|
||
| - **Off-chain filtering**: While indexers and DApp backends can attempt to filter out junk UTxOs, they provide no on-chain security and cannot be relied on in adversarial environments or in composable settings. |
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.
Off-chain filtering: ...
Actually it can! That was the whole point of CIP-89's beacon tokens. The beacon tokens enable filtering out junk UTxOs fully trustless and p2p, and the dApp can rely on them internally as "stamps of validity". CIP-89 is meant for both "adversarial environments" and "composable settings".
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.
CIP-89 isn't about offchain sanity checks, it's about onchain use of state / auth tokens that can be easily filtered offchain. The actual security which guarantees validity still must be implemented in the onchain code and the token minting logic. The point about offchain filtering logic is to say that you cannot simply filter out the malicious UTxOs in your offchain code as your mechanism for security, because other users can run their own offchain code and use them to interact maliciously with your protocol. To prevent this, you must use one of the other solutions (ie. state tokens), which of-course you can filter on in your offchain code for simplicity.
|
|
||
| ### Alternatives considered | ||
|
|
||
| - **Status quo**: Relying on auth tokens, thread tokens, and registry patterns introduces complexity, performance bottlenecks, and cyclic dependencies that are fragile and hard to audit. It has also led to serious exploits in real-world protocols due to misused or mishandled tokens. |
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 complexity could just be a sign that the developers are misusing the eUTxO model and they need to consider economic incentives more.
Sure, you don't need to. It is theoretically possible to write AAVE on Cardano with thread tokens, using 30 16kb validators and fully secure the lifecycle of thread tokens. However, this is not feasible for regular engineers. We want to lower the barrier to entry, not keep an ivory tower. Also out of the gate, this vastly enhances protocol efficiency, because you get rid of the need to mint and burn state tokens, and all the execution overhead that brings.
It can have nothing to do with global state, if you have a very complex cutting-edge financial instrument, even a peer-to-peer one, you can easily hit huge complexity limits, this is one of the biggest reasons why dApps on Cardano are so far behind their counterparts in the rest of the crypto space.
"Anything that is possible with this feature can be done without this feature -> skill issue", while technically true, this line of thinking is extremely harmful for the ecosystem, and the same can be said about any improvement to dev ex, This line of thinking can be expanded to: "Anything you can write in a programming language you can write in assembly so we don't need Haskell or Rust it's just a developer skill issue". The goal is to make Cardano more developer friendly, reduce the attack surface against the DeFi ecosystem, and lower the ivory tower. For you managing complex systems and state token lifecycle management is not difficult, because you are one of the small handful of developers in the ecosystem with extremely intimate understanding of our smart contract platform and a great deal of experience utilizing it. I am also one such developer, and I can count the other developers capable of doing this reliably on my hands. I know how much of an issue this is for ecosystem security because I spend roughly a dozen hours each week going through open-source codebases and identifying and reporting vulnerabilities, |
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 seems like a helpful extension of the current Cardano smart contract model to allow for only validated/approved deposits to an address. I'm not particularly interested in some of the comments up above about whether or not this is "best practice" but instead view this as value-add benefit for those that desire it and, in that framing, this extends the functionality of the Cardano ecosystem positively without having a large detriment on any one or thing that wants to maintain the "status quo" as it's put in the CIP body.
CIP-GuardScripts/README.md
Outdated
|
|
||
| ## Abstract | ||
|
|
||
| This CIP proposes the introduction of a new script credential type, `GuardScriptCredential`, and a corresponding `Guard` script purpose. This enhancement allows a smart contract to validate not only UTxO spends from its address, but also UTxO creations to its address. The lack of such a mechanism today forces developers to implement complex workarounds involving authentication tokens, threaded NFTs, and registry UTxOs to guard against unauthorized or malformed deposits. This CIP aims to provide a native mechanism to guard script addresses against incoming UTxOs, thereby improving protocol safety, reducing engineering overhead, and eliminating a wide class of vulnerabilities in the Cardano smart contract ecosystem. |
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 not certain that we need the GuardScriptCredential in the ledger as this is still, ultimately, just a ScriptCredential but whether or not it has IsGuard in the address type flag is the real question? Would it be possible to have or need a Guard script for staking credentials out of curiosity? Maybe this is part of the validation for something like #1061 extending the rewards account model?
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, the intention was that you would be able to have a GuardScriptCredential as either a payment or staking credential.
If it is used as a payment credential it must be executed to send a UTxO to that payment credential. If it is used as a staking credential, it must be executed to deposit funds into the associated reward account. I should probably document this, however, the mechanism for transferring between reward accounts isn't concrete yet.
CIP-GuardScripts/README.md
Outdated
| data Credential = | ||
| KeyCredential PubKeyHash | ||
| | ScriptCredential ScriptHash | ||
| | GuardScriptCredential ScriptHash -- new |
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.
| | GuardScriptCredential ScriptHash -- new |
I don't think we can define this as a new type of Credential because the ledger ultimately will not know or see the difference fundamentally except for whether or not your are depositing to an address that has the isGuard address type flag. Requesting the advice of @lehins on this one to correct my knowledge if I'm mistaken.
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 ledger is able to differentiate between scripthash and pubkeyhash based on the address bytes right? That's how we are able to match on PubKeyCredential and ScriptCredential. The idea is that the address bytes would also be able to flag if it is a GuardScriptCredential, ie. via the isGuard flag you discuss.
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.
ie if flag byte 0 represents pub key credential, and 1 represents script credential, then 2 could represent guard script credential and if desired even, 3 could represent guard pubkey credential.
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 not a potential design choice that we can make, because it will have an enormous knock off effect. In other words it cannot be implemented this way:
| | GuardScriptCredential ScriptHash -- new |
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.
Just so you don't think that I am blindly dismissing this idea, think about what are the semantics of this change for all other credentials: reward accounts, DReps & CC members.
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 semantics are the same. If it is used for a reward account, then the witness is required to deposit funds into that reward account (ie. a random user cannot submit a gov proposal and set my reward account as the refund credential and deposit 100k ada into my account without my consent, the transaction to submit the GA would have to have the witness for the guarding credential if a guarding credential is used as the refund address.).
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.
@lehins I'm not particularly tied to the implementation at all, I don't care how it is implemented, at the end of the day, what we need is a way to have addresses where a witness is required to sent funds to that address, and not just from that address. However we accomplish that doesn't matter to me. But we cannot continue in an architecture where a terrorist can just send me stolen funds and get my address OFAC sanctioned, and I have no way to prevent this.
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.
@colll78 I hear you. I am only saying that changing semantics of credentials is not the right solution. There is a simpler and better approach of changing only the aspects that are affected: utxo and accounts, since those are the only two that can receive funds.
We can solve this with special certificate for accounts and a special flag for addresses. In both cases we can use the same credential, be it a script hash or a key hash.
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.
But we cannot continue in an architecture where a terrorist can just send me stolen funds and get my address OFAC sanctioned, and I have no way to prevent this.
Then you'd also want a similar mechanism for pub key address - so I agree with Alexey that another approach would work better.
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.
Agreed, I'm completely removing any mention of this from the CIP, as the primary purpose is to reduce the complexity of smart contract protocols on Cardano and get rid of the need to treat all UTxOs as adversarial or use state tokens.
CIP-GuardScripts/README.md
Outdated
| ``` | ||
|
|
||
| **Guard validation rule:** | ||
| During phase-2 script validation, for each transaction output to a `GuardScriptCredential`, the associated script must be executed using the `Guarding` purpose, where `ScriptHash` is the hash of the guarding script and the first `Integer` is the index of the output sent to the guarding script that is being validated, the `Maybe Integer` an optional type that may contain the index of an input from the same script. There is one important exception to this rule, if the `Maybe Integer` argument is `Just idx` and the credential of the input at index `idx` in the transaction inputs set corresponds to the `GuardScriptCredential` associated with the Guarding script then the script succeeds by default and does not need to be executed. This rule is very important to avoid redundant scirpt executions, as if the script is already invoked with a `Spending` purpose then it can already reject unauthorized `UTxOs. Without this rule, the same script would need to be evaluated twice for all state transitions with continuing outputs (extremely common) and this would simply be too inefficient for production use. |
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.
Ugh, are we still reduced to having to manually sort through our inputs/outputs to identify the index of which one(s) trigger the guard? Is it not enough for the one or more isGuard scripts in the transaction to check against all outputs? Wouldn't they need to in order to ensure there's not double-satisfaction happening?
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.
Agreed. Since the output execution doesn't have a datum, I would rather it only be executed once. The redeemer can specify the indices of the required outputs, if necessary.
Also, I'd rather the guard script still be executed even if there is both an input and an output because it helps simplify the code. The input execution can just care about the inputs, and the output execution can just care about the outputs. If the output execution was dropped, the input execution would need to check the outputs too. But this means the input script is checking the outputs in every execution just in case...
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 you don't need to sort anything or do anything onchain. When you are constructing the transaction offchain, the index is provided, and in phase 1 validation we check whether an input index is provided and if it is, and the indexed input has a payment credential which matches the GuardScript, then the GuardScript is not executed at all.
Also, the ledger team is changing it so that users can control the order of inputs when constructing transactions, so you won't have to reorder them onchain, you can just construct the transaction such that they are in the desired order and position and then validate it when you traverse them onchain.
Cardano already forces you to provide indices when building the tx anyway, so you as a developer should never have to do any sorting or anything to identify what triggers the guard.
In onchain code, to know what triggers the guard you just do
GuardingPurpose ownScriptHash guardIdx _ ->
let guardedOutput = elemAt guardIdx txInfoOutputs
...Or do you mean that you would prefer to have this script not execute once for each output, but instead execute once per transaction as long as there is at-least one output to the script? If that is your suggestion, I'm not opposed to it at all. However, note that achieving that was the purpose of the Observers CIP, and that with this CIP in combination with the observers CIP you could achieve the exact same thing.
IE. If you want to validate all outputs with a single script execution you can make your GuardScriptCredential a native script defined as:
{
"type": "observer",
"keyHash": "OUR_OBSERVATION_SCRIPT_HASH"
}
where OUR_OBSERVATION_SCRIPT_HASH is the hash associated with your script that will validate all the outputs in a single execution.
Again, I'm not opposed to changing this to remove the indices entirely and just make this the default behavior, so that you don't even need to use it in combination with the Observe script type to achieve that. Perhaps there is a reason where someone would want to execute scripts on each and every input / output from the same script individually, however, I personally have never found that to make sense in production smart contracts. Some people have argued very strongly about retaining the ability to do that, which is why I created the Observe script, to allow those that want to have the redundant executions per each input, to retain that ability, and to allow serious dApp developers to validate the state transition of a smart contract in a single script execution.
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.
Also, I'd rather the guard script still be executed even if there is both an input and an output because it helps simplify the code. The input execution can just care about the inputs, and the output execution can just care about the outputs. If the output execution was dropped, the input execution would need to check the outputs too. But this means the input script is checking the outputs in every execution just in case...
Redundant script executions are a massive burden on the ledger. The vast majority of ex-budget consumed onchain up to this point is entirely from completely redundant validation. We need to limit this to preserve room for meaningful computation.
To address your concern of an input script needing to redundantly check for outputs, we can introduce two purposes for Guarding scripts. The GuardingPurpose will be invoked if there is a UTxO output to the script and no UTxO is spent from the script. The GuardingContinuingPurpose ScriptHash will be invoked if there is both a UTxO spent from the script, and a UTxO output to the script. That way if you are simply spending a UTxO from the script you can safely avoid checking for any outputs to 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.
You know what I had almost completely forgotten about the Observe script type that was proposed a while ago... We need to kick the Plutus team and see if that might get included in v4 because yes, I believe that's what I was thinking. This would allow a single input from my wallet to create multiple deposits into the same guarded address with only a single walk through each UTxO of the transaction, check to see if it matches the GuardScript payment or staking hash, and then validate as necessary. With the explicit declaration of GuardPurpose idx we would need to redeclare and re-run the same script if a single transaction generated multiple UTxO at the guarded address as currently defined?
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 Plutus team and ledger team are indeed already implementing it! Once it is implemented, it could be used in conjunction with this to do exactly what you describe. IE you can validate all the UTxOs in a single script execution.
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 Plutus team and ledger team are indeed already implementing it!
Yes this will be in V4.
This is a huge assumption and one that I do not share. I think it can be done with a fraction of the complexity.
Again, I do not share this assumption. By "skill issue", I do not mean ivory tower experience; I just mean the right mental model.
I disagree. My contracts are extremely simple (I still consider myself an amateur...). It is the difference between essential complexity and accidental complexity. When you use the wrong mental models, you experience an explosion of accidental complexity. I believe the complexity you are describing can almost entirely be attributed to accidental complexity from dApp developers using the wrong mental models. To be clear, I am not entirely against this CIP. If it is trivial to implement, then I'm fine with it. But since I believe most dev ex pain-points are actually self-inflicted, I'm against this CIP if it turns out to be complex to implement. EDIT: I apologize if my original wording offended anyone. That was not my intention... |
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 may be personally interested in the issue of "order book" design vs. UTxO-based trading models, though would stay out of the technical discussion due to lack of deep practical expertise.
As an editor I feel obligated to open the door wider for standards supporting more finely distributed systems: but that's mainly because such systems represent untapped potential and the "order book" designs have already received heavy (almost exclusive) support from Cardano's existing enterprises & most ambitious commercial developers.
Objectively & editorially I think we should avoid language in CIPs on either "side" of the issue characterising the other model as (for example) either obsolete (for order books) or incurably inefficient / error-prone (for tagged UTxOs).
Therefore editors would try to weed out statements of generalisation & prejudice if written directly into the CIP, mainly to ensure the playing field remains level: not only for one CIP vs. another with an opposite aim, but also to preserve the idea space for CIPs & methods that haven't been developed yet.
I'm in favour of this method as long as reviewers believe it to be technically sound — especially with Ledger (cc @lehins @WhatisRT) team members having no arguments against its feasibility — and with further confirmation that it will simplify commercially beneficial development as already suggested.
We do need a non-redundant title that suits both the CIP and PR so I'm going to suggest something convergent below & tentatively retitle the PR as such:
|
This CIP would enhance Cardano’s appeal at the enterprise level and to users who prefer not to accept arbitrary tokens. It would also simplify reasoning about dApp architecture and improve overall security. |
Co-authored-by: Robert Phair <[email protected]>
This CIP has nothing to do with order books. It will improve order books just as much as it will improve AMMs or pooled lending platforms or p2p lending platforms, or any smart contract platform.
Again, this is just a general improvement to the smart contract platform of Cardano. It doesn't have any specific benefits for any type of dApp, it just improves the security and performance of the entire dApp ecosystem. What it will do, that may upset some, is lower the barrier for entry for new developers to enter the ecosystem. |
|
This CIP is logical and a highly valuable feature to end blockchain users. We were having discussions with non-technical users just yesterday in a public forum and they expressed overwhelming interest in supporting this feature. There is absolutely no reason not to include this in Cardano and it would actually make us stand out in an extremely unique way. Overall this CIP is:
Arguments on any other grounds are injection off absurd personal tastes. So far all Dapp builders I have talked to have supported this. And I mean real Dapp builders with real users, not fantasy Dapps that don't exist or have no one users. |
|
Comments like the following lead me to believe people are misunderstanding my perspective 🫤
Given the |
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 will take a look at this CIP in more detail in subsequent week or two, just because I want to understand what exactly the problem it is trying to solve. I am sure we would be able to come up with a solution that has a much smaller development and complexity impact on ledger. But, as it is described right now, I do not see this as a viable solution.
I also get a feeling that much of the stuff that is desired in this will be possible with CIP-112
The problem this is trying to solve, is that right now, there is no way to prevent someone from sending a UTxO to an address. Smart contracts can be used to guard the spending of UTxOs, they cannot be used to guard against receiving UTxOs. This is one of the reasons Charles Hoskinson and many other high-profile individuals don't have a public wallet, because someone could send them a million meme-coins and they would now have tax obligations for something they never even consented to receive. Also, an OFAC sanctioned terrorist could send them stolen assets (this has happened on Ethereum), that they of course did not consent to receive, which in turn will put their addresses on the OFAC blacklist as-well.
No, it is impossible to use CIP-112, or any existing functionality of the ledger, to prevent someone from sending a UTxO to any address with any datum they want. As the author of CIP-112, I am certain of this. |
|
@colll78 I don't see how this CIP addresses either of these problems.
So the problem is high-profile people aren't sharing their addresses publicly? The main reasons people hide their addresses are for privacy of balances and transactions. This CIP does not address either of these issues so people will still choose to keep their addresses private. And again, the
Given the transaction-level view that smart contracts have, I don't see how it is possible for a smart contract to prevent this. All ADA is fungible which means scripts need another way to detect stolen ADA. You could blacklist addresses but what is stopping a malicious person from creating a new address, sending the stolen ADA to that address, and then passing it on to recipient? The recipient still got the stolen ADA and will still get added to the OFAC blacklist. Am I missing something? How can Cardano smart contracts guarantee detection of terrorist money? |
You are missing something. If I have a
I cannot speak to the main reason; I can only speak to what high-profile individuals have stated as their explicit reasoning for not having public addresses. Charles himself stated that he personally does not have a public address because if he did, there is nothing he could do to prevent people sending him memecoins or OFAC sanctioned funds. I have heard similar reasoning from many other high-profile individuals. In traditional finance, someone cannot just walk up to you and forcefully give you money / assets against your will. The same should be true in web3. You should always be able to accept or reject funds. The same logic follows for scripts, if anyone can send any UTxO to a script with any arbitrary datum, then by default, smart contract developers cannot trust any UTxOs at their contracts. If a datum claims the UTxO was submitted by some POSIXTime 4 months ago, you cannot trust that, unless there is a state token, and your contract explicitly knows that the minting policy of said state token validates that particular field in the datum AND you are certain that the state token can never leave the confines of your dApp protocol (ie. cannot be smuggled) because if it can then a malicious user could use it to trick your protocol into trusting data that is unvalidated. |
This and also integrations into existing languages like Aiken would be easy. I would be happy to have this CIP integrated asap. The dev complexity reduction is not just in contract writing but also reducing offchain complexity. |
|
I would like this functionality. It seems important to have the choice for regulation. |
I already understood this part. I meant more specifically how to use the @colll78 I appreciate you taking the time to explain things. |
|
This CIP requires new addresses, here's why: For backwards compatibility reasons, any transaction that is valid today should be valid in the future. Since I can make arbitrary outputs at any address (given I have the funds) we cannot break this property. Technically, you can make new addresses by extending the set of possible scripts or public keys, but that doesn't help: hashes are completely opaque when creating outputs, and having to provide pre-images again breaks backwards compatibility. The current version of the CIP does exactly that of course. So there's a very clear downside to this CIP that needs to be mentioned in the text: every downstream tool needs to implement the new address format, whatever it may end up being. So everyone supporting this should weigh the benefits against this cost. I agree with @lehins, changing the The issue is that you can always change this flag and make an output without restrictions. So for a Also, note that the issue I just described is also present in the design that's currently provided in this CIP. I also thought about adjusting the hashes (e.g. by prepending a zero if the flag is set) as a way to solve this problem, but it doesn't work: when spending the output you have to reveal the preimage, and at that point the protection provided by this scheme is lost. In conclusion, evaluated on a purely technical basis the benefits don't seem very strong (only works for scripts, all scripts interacting with these addresses need to do extra validation) while the ecosystem cost (implement new addresses) seems fairly high. Bonus suggestion: it's possible to make a 'wallet script' that requires signatures to accept funds without any ledger changes (and thus without the ecosystem cost). Make a script with a thread token that stores a list of numbers as the state. When spending an output, check the transaction is signed by the private key of the wallet owner and that the datum of the output is of the form With that script, anyone can publicly verify what outputs at that script are spendable, so any adversarially donated funds are publicly known to be entirely unspendable. |
No one will ever use that script you are suggesting, there are also a number of security issues with the script you suggested and practical limitations (datum size limits, concurrency issues, so on). This is a great example of why this feature is so desperately needed. The barrier for entry is so high, that you, a core developer, cannot design a script that is even remotely close to being acceptable in production. That's a problem, a problem that this CIP addresses. Every smart contract developer in the ecosystem is explaining how proudly this will benefit them. The effect on downstream tooling does not matter, how much time it takes doesn't matter, this change is the single biggest improvement to core Cardano smart contract security and dev ex. It does not matter how this feature is implemented. New address type? Totally fine. Whatever needs to be done. |
Fixing the datum size issue is a relatively simple engineering challenge, I wanted to give a conceptual solution rather than a full-blown production ready script. If you think that's required, here you go: Just store another number,
Maybe I should have said this more clearly, I'm not opposed to this feature. If it improves dev ex that's great, go ahead! All I'm saying is we need to be very clear about the downsides and limitations. Maybe you're fine getting this at all cost, but others may not be. Also, maybe there are other solutions to the problems this CIP attempts to solve? I feel like a CPS might be appropriate here. Especially if, and I quote again, 'every smart contract developer in the ecosystem' benefits from this. Well, then we better be sure we find the best solution for this issue! |
I agree that downsides should be transparently conveyed, but I haven't heard a downside outside of "it will take a while to implement this, and downstream tooling will need to update to be able to interact with the new address types" which are immaterial to the actual impact of the change.
Again, the concrete problem is there is no way to require a witness for transfers to an address, The concrete implementation or design of any solution that allows us to have addresses that require witnesses to transfer to doesn't matter to me, so long as the end result gives us that ability.
My point wasn't that you cannot ever develop such an app, my point is that in the absence of this CIP, the complexity required to develop any dApp on Cardano, even the EXTREMELY trivial one that we are talking about, is ridiculously high. Even in the extremely simple example you gave, there is so much to consider that datum size limitation fell through the cracks, and we had to iterate. Now imagine trying to build a cutting-edge financial instrument with a massive business requirement spec like AAVE. It's just not feasible. There is too much to consider when by default every script must treat all state information as untrusted, and the only mitigation for this is state tokens which require: solving cyclic dependencies, managing their lifecycles against smuggling, making sure they are burned when no longer needed. The insane amount of complexity of this system is transparent in the fact that even after multiple audits, I have gone through hundreds of open-source dApps and I cannot even count the number times I have identified reported and resolved state token smuggling attacks or the many other attacks related to the fact that datums and UTxOs at a script must be considered adversarial by default. I understand that everything is fixable, there are more concrete criticisms I could have, and eventually we would wind up with an app that works, and then we would have to update wallets to support this account abstraction wallet smart contract with all the offchain code etc, every single interaction with this would require a smart contract transaction and clog the chain's ex-unit budget, and eventually if we invested enough money, and engineering hours we would be able to have this in production. Anything that can be developed with this CIP can be developed without it; the devil is in the details. This would reduce the lines of code and complexity of every single dApp on Cardano by a huge margin, similarly it would reduce the attack surface by the same margin. |
|
Also to be clear it certainly can be implemented exactly as proposed. Right now in the definition of address the first bit of the header is completely unused. It could be used as a 'isGuarding' flag, if the bit is set to one, then the addresses are guarding, if it is set to 0 then they are not. If you reference CIP 19 this information is transparently available. If anyone on the ledger team or otherwise has an alternative implementation that is better / has lower impact on the ledger I am totally happy to update the CIP accordingly as long as the end result is we get smart contract addresses that can require witness to receive assets. |
While being kind of neutral on the rest of this argument for the time being (with some sympathies for the viewpoint of @fallen-icarus that UTxO is used horribly wrong in large parts of the Cardano space), this is just wrong: SEPA bank transfers do not need consent of the receiver! I can send anybody money and they cannot do anything against it. |
While it’s technically true that SEPA and similar systems allow pushing funds to an account without the recipient’s explicit consent, this misses the broader point: in traditional finance, bank account information is not the primary interface for interacting with services or transferring funds. People don’t casually share their IBANs or account numbers for day-to-day financial interaction. Instead, we use controlled instruments like credit/debit cards, and invoicing systems. If you have my debit card number you cannot send funds to my account without my consent, the same is true for the rest of the above. So while “forced deposits” are technically possible in traditional finance, they’re extremely rare as all interfaces to deposits are heavily regulated, and gated by social, legal, and institutional regulation; this does not hold true for blockchain protocols, which are fully permissionless by default. More importantly, this type of attack doesn’t even work in TradFi. An OFAC-sanctioned entity cannot wire you money in the first place, as regulated financial institutions block such transfers preemptively. Even in the rare case where tainted funds are sent before sanctions, banks can reclaim or reverse the transaction unilaterally without burdening the innocent recipient. On-chain, this is impossible. There is no reversal mechanism, no recourse, and no way to reject malicious deposits. No bank there to handle this on your behalf to prevent you from receiving the funds (in a sense that they can actually be spent), no one can prevent an OFAC sanctioned party from sending you funds, and you as a user will have to deal with the cost burden of paying for a transaction to move those funds out of your wallet and even that will not absolve you. The proof of this is clear, there has never been a successful instance of a blacklisting attack in tradfi (where stolen / malicious funds were sent to innocent people with the intention of having those people's accounts unbanked and having them blacklisted from financial institutions). There have been many successful cases of blacklisting attacks in web3. Tons of innocent users were completely banned from every major off-ramp and centralized exchange during the Tornado Cash "tainting" attacks, and to this day, the majority of these accounts are still marked as illicit by compliance tools like Chainalysis and TRM Labs which prevents these users from interacting with almost any regulated product. And again, to reiterate the primary purpose of this CIP has nothing to do with user wallets, it is for smart contracts to be able to sanitize UTxOs. The rest is just a nice side-effect. |
|
Let's assume CIP-112 (Observer scripts) are implemented, then you really wouldn't want to have this single script execution pattern as it's quite inconsistent with how inputs are validated. It also saves us from having the A script making use of this CIP would generally be of this form: We can transform this into the script that has essentially the same behaviour but works today: Note that What I'm trying to understand is why this is not considered viable. Of course this is clearly worse (more complexity, higher fees, higher min ada) but not by much. Is there some other downside to this? |
|
@colll78 I just had an idea that could make this CIP almost redundant and allow us to utilize most of CIP-112. What if we add a flag to the address, which is when set, and the output that is being created is locked by a script, we simply require in ledger rules for the same script to be also present in the list of observers? Do you think that could work? |
|
@colll78 Ok, I understand now that we're in a death by 1000 cuts situation. However, I still think the impact of this CIP is somewhat overstated: As @lehins and I have pointed out, any proposal so far still requires every script that interacts with such an address to correctly check that the address actually has the correct flag set - a single screwup here can potentially invalidate the security of the entire DApp.
Same here (at least one of the primary goals), that's the only reason I even wrote a suggestion of how to do it with a script today.
I don't think that solves the issues we have either. Someone can nefariously register the address without the flag, and then scripts that don't check whether this flag is set can still be tricked. I guess we could only allow registering one of the two, but that means that it's now part of your security assumptions that the address cannot be deregistered. I'm not sure requiring that pattern is better. |
|
Actually, I've just realized that there's a bigger issue with accounts: Accounts are fundamentally non-concurrent (at least with deterministic script execution) and concurrency was listed as a requirement earlier in the conversation. Accounts basically behave like UTxO addresses where the ledger guarantees that exactly one output is present, which seems like a restriction that is too strong. You couldn't even have two transactions depositing money into the account that don't know about each other in one way. |
@lehins This perfectly achieves the goal of being able to easily check the integrity of UTxOs at smart contracts!
I don't think that's an issue at all. If you are writing a guarding script, you just don't use the spending purpose (ie. It always fails), for someone to mess this up, they would have to deliberately add a path for the spending script purpose on top of their guarding path, which would be a hard mistake to make by accident. |
I feel like you don't listen to me Alexey :D #1063 (comment) |
@WhatisRT I totally agree. I was suggesting it as a drastic simplification to this CIP. As far as ledger is concerned we'd only guarantee that the witness is required for creating outputs, the rest would be still up to the script. 🤷♂️
I am not sure I understand why depositing assets into the same account has issues with concurrency. If scripts do not care about the final balance then two separate transactions can safely put assets into the same account regardless of the order they are added to the block. Which is a nice property that other blockchains that are based account model do not have, due to their replay protection getting in a way of concurrency. |
@Crypto2099 Sorry, Adam, I guess I did not understand your suggestion the first time I read it. Now that I re-read it again, it does sound very much like what I suggested too. |
I think it's fundamentally the same here too. There is no such thing as a GuardingScriptHash. There is just ScriptHash, a contract can determine whether that scripthash is guarding or not either from the Address or from the Credential. It doesn't matter where we put the flag in Plutus world. I would prefer we put it in Credential because Address is not a sum type, whereas credential already is, and thus developers are already familiar with matching on it, and developers are all used to abusing the fact that address only has one constructor to avoid checking the constructor tag and extract the fields. However, I also see the argument for making Address a sum type, because we only have 1 bit we can use for this flag, you can never have an address where one credential (ie. Payment credential or staking credential) is a guarding credential and the other credential is the regular variant, either both credentials must be guarding or both must be the normal variant. I'll update the CIP accordingly to use Address instead of Credential. |
|
@colll78 Giving it some more though, I don't think we want to use CIP-112 for this purpose, since then users would not be able to pass an argument that is related to a specific output that is being created. Here is what I suggest:
The rest can be done on the side of a plutus script. Namely any script can fail whenever it is attempted to be used with an address that is marked as One note I would also like to make is that I don't think we need to disambiguate whether address is locked by a plutus script, native script or public key with regards to this new flag:
I like this approach the best, since it is fairly simple to implement, it is consistent with other aspects of transactions that require witnesses and it seems to cover all the desired use cases. |
This makes sense.
This is great. How about we call the new script purpose 'Receiving'? This is inline with 'Spending'. |
@lehins Ah, I saw now that there's an actual CIP with a proposal that doesn't impact determinism. Asserting a range on the account balance works here, so I guess this isn't a concern then. Also, why did nobody ping me on the account enhancement CIP? |
|
@WhatisRT if you are asking why "editors" didn't ping you... I can only speak for myself, but I didn't tag you in addition to @lehins (after the original round of tags in #1061 (comment)) because the proposal has only been in Ordinarily I would leave tagging to the author (@fallen-icarus) under those conditions, and then only tag all stakeholders once emerging from |
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 love the idea of removing the need for authentication tokens. It will simplify code and logic significantly.
CIP-GuardScripts/README.md
Outdated
| | Rewarding StakingCredential | ||
| | Voting Vote | ||
| | Proposing Proposal | ||
| | Guarding ScriptHash Integer (Maybe Integer) -- new |
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.
Why do we need the ScriptHash? None of the other purposes includes ScriptHash.
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.
Every other purpose includes script hash except Spending and Proposing, and there is a huge amount of frustration that is a result of the fact that Spending doesn't include script hash. Minting includes script hash, the currency symbol of the token being minted, Rewarding Credential includes script hash in the credential, same with certifying (it is inside the in the tx cert credential).
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.
For completeness, can you include a short explanation of when and why ScriptHash is useful for Spending and Guarding?
CIP-GuardScripts/README.md
Outdated
| ``` | ||
|
|
||
| **Guard validation rule:** | ||
| During phase-2 script validation, for each transaction output to a `GuardScriptCredential`, the associated script must be executed using the `Guarding` purpose, where `ScriptHash` is the hash of the guarding script and the first `Integer` is the index of the output sent to the guarding script that is being validated, the `Maybe Integer` an optional type that may contain the index of an input from the same script. There is one important exception to this rule, if the `Maybe Integer` argument is `Just idx` and the credential of the input at index `idx` in the transaction inputs set corresponds to the `GuardScriptCredential` associated with the Guarding script then the script succeeds by default and does not need to be executed. This rule is very important to avoid redundant scirpt executions, as if the script is already invoked with a `Spending` purpose then it can already reject unauthorized `UTxOs. Without this rule, the same script would need to be evaluated twice for all state transitions with continuing outputs (extremely common) and this would simply be too inefficient for production use. |
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 Plutus team and ledger team are indeed already implementing it!
Yes this will be in V4.
CIP-GuardScripts/README.md
Outdated
| data Credential = | ||
| KeyCredential PubKeyHash | ||
| | ScriptCredential ScriptHash | ||
| | GuardScriptCredential ScriptHash -- new |
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.
But we cannot continue in an architecture where a terrorist can just send me stolen funds and get my address OFAC sanctioned, and I have no way to prevent this.
Then you'd also want a similar mechanism for pub key address - so I agree with Alexey that another approach would work better.
|
@lehins
|
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.
@colll78 we had a good presentation of this at the CIP meeting yesterday where those familiar with the proposal including @fallen-icarus @GeorgeFlerovsky @Crypto2099 presented more context in the process of enthusiastically confirming it as a CIP candidate. The editors look forward to:
- @lehins recording the high-level recommendations for this proposal & confirmation about what part(s) the Ledger would be ready to implement;
- @colll78 likely refactoring the proposal based on the Ledger assessment: particularly its applicability to payment addresses but not (yet / necessarily) stake addresses.
Please rename this directory to CIP-0160 and update the link to "rendered version" in your OP 🎉
In general this CIP needs an overhaul, since during Ledger Working Group Meeting #21 we've agreed that:
Benefits are that Plutus scripts would be able to see easily whether TxOut is protected or not and from ledger perspective it would be fairly straightforward to implement. |
CIP-GuardScripts/README.md
Outdated
| @@ -0,0 +1,146 @@ | |||
| --- | |||
| CIP: 160 | |||
| Title: Guard Script Purpose and Credential | |||
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.
As I've mentioned earlier Guard name is already taken by CIP-112, at least in Ledger implementation. Unless you want to infuse more confusion I'd suggest changing it, since again in Ledger implementation it will not be called "Guard Script Purpose"
I suggest name "Protected Output", but this is naturally opened up to a discussion. IMHO naming is important, since it can either describe the concept clearly or just confuse everyone.
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'll rename to Protection Addresses and Protection Scripts is that okay?
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.
Maybe Protected Addresses instead?
I don't think "Scripts" is a good suggestion, since this will apply to public key hashes as well. We will require witnesses irrespective of whether TxOut that is being created is locked by a script hash or a key hash.
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.
"protection", "validation", "guard" are all really similar words. How about the opposite of "Spending" - say "Depositing" or "Earning"?
Btw I'm really in favor of this CIP - state tokens are an abomination.
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.
"Depositing" or "Earning"?
These are bad names, since:
- depositing makes more sense for an account instead of an output. In fact, we will probably need to add a script purpose with such name for accounts (see CIP-159)
- earning implies you are making money, which is not necessarily the case, you might be sending it to your self. Earning would make sense for depositing staking rewards, but we don't run any scripts when we do that 😄
The reason why I like "protected" address, because it is a flag on the address that prevents unauthorized access. That being said, for the script purpose "creating" or "receiving" would be a fine name
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.
Then I'd try a different opposite word of "spending". Yes that could potentially be confused with "depositing" or "earning" (once they are introduced), but the confusion between "protect", "validate" and "guard" will be worse since these are more general words. But anyway, it's just a name 🤷♂️
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.
"Spending" name makes sense, because we are spending the unspent output.
However, int his context the opposite "spending" is "creating" the unspent output. For this reason, I am definitely leaning towards "creating". This is when it comes to the script purpose (i.e. the verb)
With respect to addresses that participate in creation and spending those outputs, I think the term "protected address" suits them very well.
In other words only a "protected" address could trigger the "creating" purpose, while "spending" purpose will be applicable to both "protected" and "unprotected" addresses, with script being able to access that flag and make any necessary decisions about it.
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.
it's just a name
I don't like to waste too much time on naming things, since bikeshedding is a real thing, but putting some effort into naming can and often does simplify understanding of concepts by many people learning theses things down the road.
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.
How about this:
"Receiving" script is evaluated when an address receives funds.
"Protected" address has a receiving script to protect it from receiving undesirable funds.
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.
"Receiving" sounds like a good name for the concept.
Update to replace `GuardingCredential` (a new credential type) with `ProtectedAddress` (a new address type), and replace `Guarding` script purpose with `Receiving` script purpose.
|
Updated with respect to the discussion above, please feel free to re-review at your leisure @lehins |
Introduces GuardScriptCredential, a new credential type that allows smart contracts to validate incoming UTxOs, not just spent ones.
This closes one of the most dangerous gaps in Cardano's smart contract model: today, anyone can send arbitrary UTxOs with spoofed datums to any script address — and the script can't stop it.
Guard scripts eliminate this vulnerability, removing the need for auth tokens, thread tokens, registry hacks, and complex off-chain filtering.
It’s a simple, native fix to a systemic security and UX problem. Huge reduction in attack surface and dApp complexity.
Let's make Cardano best-in-class for DeFi applications.
(latest rendered version)