Skip to content

Conversation

@colll78
Copy link
Contributor

@colll78 colll78 commented Jul 25, 2025

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)

Copy link
Contributor

@fallen-icarus fallen-icarus left a 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.
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 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...

Copy link
Contributor Author

@colll78 colll78 Jul 25, 2025

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.

Copy link
Contributor

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.
Copy link
Contributor

@fallen-icarus fallen-icarus Jul 25, 2025

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".

Copy link
Contributor Author

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.
Copy link
Contributor

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.

@colll78
Copy link
Contributor Author

colll78 commented Jul 25, 2025

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.

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.

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.

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.

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".

"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,

Copy link
Collaborator

@Crypto2099 Crypto2099 left a 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.


## 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.
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

data Credential =
KeyCredential PubKeyHash
| ScriptCredential ScriptHash
| GuardScriptCredential ScriptHash -- new
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
| 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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

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 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:

Suggested change
| GuardScriptCredential ScriptHash -- new

Copy link
Contributor

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.

Copy link
Contributor Author

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.).

Copy link
Contributor Author

@colll78 colll78 Jul 27, 2025

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

```

**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.
Copy link
Collaborator

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?

Copy link
Contributor

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...

Copy link
Contributor Author

@colll78 colll78 Jul 25, 2025

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.

Copy link
Contributor Author

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.

Copy link
Collaborator

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?

Copy link
Contributor Author

@colll78 colll78 Jul 26, 2025

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.

Copy link
Contributor

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.

@fallen-icarus
Copy link
Contributor

fallen-icarus commented Jul 25, 2025

It is theoretically possible to write AAVE on Cardano with thread tokens, using 30 16kb validators and fully secure the lifecycle of thread tokens.

This is a huge assumption and one that I do not share. I think it can be done with a fraction of the complexity.

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.

Again, I do not share this assumption. By "skill issue", I do not mean ivory tower experience; I just mean the right mental model. Why was I the first person to suggest a fee market to handle UTxO contention? Why am I still the only one trying to incorporate it into my dApps? This isn't hard; it is just a different way of thinking. I would argue that Cardano is so far behind because its dApp developers are using the account style mental models when they design eUTxO dApps. The batcher ecosystem is a direct result of this mismatch!

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 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...

@rphair rphair changed the title Introduce GuardScripts CIP CIP-???? | Guard Script Purpose and Credential Jul 25, 2025
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.

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:

@rphair rphair added Category: Ledger Proposals belonging to the 'Ledger' category. State: Triage Applied to new PR afer editor cleanup on GitHub, pending CIP meeting introduction. labels Jul 25, 2025
@Mercurial
Copy link
Contributor

Mercurial commented Jul 26, 2025

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.

@colll78
Copy link
Contributor Author

colll78 commented Jul 26, 2025

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.

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.

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.

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.

@rvcas
Copy link

rvcas commented Jul 26, 2025

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:

  1. fully backwards compatible
  2. Composable with existing primitives
  3. yields extreme value for tax and regulatory purposes

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.

@fallen-icarus
Copy link
Contributor

Comments like the following lead me to believe people are misunderstanding my perspective 🫤

What it will do, that may upset some, is lower the barrier for entry for new developers to enter the ecosystem.

Arguments on any other grounds are injection off absurd personal tastes.

Given the minUTxOValue requirement with UTxOs, I have always viewed misconfigured UTxOs as free money. So by guarding against creation of UTxOs, it seems to me like all we are really doing is guarding against free money... Why should the ledger defend against something that is already defended against by economic incentives? What am I missing? I can see some arguments for using it with account addresses, but I'm at a loss for how this is required for UTxOs. For the sake of keeping the ledger simple, why isn't it better to rely on economic incentives?

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.

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

@colll78
Copy link
Contributor Author

colll78 commented Jul 26, 2025

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.

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.

I also get a feeling that much of the stuff that is desired in this will be possible with CIP-112

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.

@fallen-icarus
Copy link
Contributor

@colll78 I don't see how this CIP addresses either of these problems.

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.

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 minUTxOValue requirement is already a deterrent against meme-coin airdrops - if your address is private, they need to send the meme-coins to everyone which is prohibitively expensive. Why do we need a complex and large ledger change to solve a problem that the protocol already disincentivizes economically?

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.

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?

@colll78
Copy link
Contributor Author

colll78 commented Jul 27, 2025

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 GuardCredential, the witness (whether it is a smart contract credential or a payment credential) is required not just to spend from that address, but to send to what address. With a GuardCredential you can have an address where you cannot be sent assets without your consent, if you don't sign, you will never receive it. In the case of a GuardScriptCredential the witness is a script that must be executed in the transaction, in the case of a GuardPublicKeyCredential the witness is a pubkeyhash that must sign the transaction. In either case, if the witness is not provided, the transaction will fail, and thus the address cannot be sent assets without consent.

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.

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.

@MicroProofs
Copy link
Contributor

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:

  1. fully backwards compatible
  2. Composable with existing primitives
  3. yields extreme value for tax and regulatory purposes

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.

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.

@bytewizard42i
Copy link

I would like this functionality. It seems important to have the choice for regulation.

@fallen-icarus
Copy link
Contributor

fallen-icarus commented Jul 27, 2025

You are missing something. If I have a GuardCredential, the witness (whether it is a smart contract credential or a payment credential) is required not just to spend from that address, but to send to what address. ... and thus the address cannot be sent assets without consent.

I already understood this part. I meant more specifically how to use the GuardCredential for OFAC compliance. Personally vetting and signing every incoming transaction does not seem practical. But after thinking about it more, I can imagine a company being set up with a honeypot address. All high-profile individuals could whitelist only payments from this company. The company is responsible for auditing for terrorist money. If it is clean, the company forwards the payment on to the recipient. It is like a Cloudflare, but for payments.

@colll78 I appreciate you taking the time to explain things.

@WhatisRT
Copy link
Contributor

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 Credential type is not the way to go. The most obvious thing to me would be to change the BaseAddr type - include a flag that indicates whether this address needs to witness outputs being created at that address. However, this only works for scripts, and only correctly written ones specifically.

The issue is that you can always change this flag and make an output without restrictions. So for a KeyHash credential this flag has zero impact on spendability and so any unwanted funds can still be donated even if it's technically a different address. I'm not sure if people would legally or socially agree that these are different addresses, so it doesn't sound useful. For a ScriptHash credential, this problem can be solved by the script inspecting the address of the output being spent and verifying that the flag is in fact set correctly. However, this does still not fully solve the problem since such an invalid output could also be referenced to a different script. So all scripts that rely on referencing also need to check that this flag is set correctly.

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 (n, sig) where sig signs n with the same private key (otherwise fail). If n is already in the state, the script fails, otherwise it adds n to the state.

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.

@colll78
Copy link
Contributor Author

colll78 commented Jul 28, 2025

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 Credential type is not the way to go. The most obvious thing to me would be to change the BaseAddr type - include a flag that indicates whether this address needs to witness outputs being created at that address. However, this only works for scripts, and only correctly written ones specifically.

The issue is that you can always change this flag and make an output without restrictions. So for a KeyHash credential this flag has zero impact on spendability and so any unwanted funds can still be donated even if it's technically a different address. I'm not sure if people would legally or socially agree that these are different addresses, so it doesn't sound useful. For a ScriptHash credential, this problem can be solved by the script inspecting the address of the output being spent and verifying that the flag is in fact set correctly. However, this does still not fully solve the problem since such an invalid output could also be referenced to a different script. So all scripts that rely on referencing also need to check that this flag is set correctly.

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 (n, sig) where sig signs n with the same private key (otherwise fail). If n is already in the state, the script fails, otherwise it adds n to the state.

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.

@WhatisRT
Copy link
Contributor

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).

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, m, with the added requirement that all entries in the list are at least m. Then add a step to the state machine that can step from (m, l) to (max l, []). Now you can receive an exponential amount of outputs before your address runs out of capacity. This was also specifically for the (single-user) wallet use case, so no concurrency requirements here. If you have other concrete criticisms I'm sure they're fixable as well.

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.

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!

@colll78
Copy link
Contributor Author

colll78 commented Jul 28, 2025

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.

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.

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!

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.

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, m, with the added requirement that all entries in the list are at least m. Then add a step to the state machine that can step from (m, l) to (max l, []). Now you can receive an exponential amount of outputs before your address runs out of capacity. This was also specifically for the (single-user) wallet use case, so no concurrency requirements here. If you have other concrete criticisms I'm sure they're fixable as well.

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.

@colll78
Copy link
Contributor Author

colll78 commented Jul 29, 2025

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.

@HeptaSean
Copy link

In traditional finance, someone cannot just walk up to you and forcefully give you money / assets against your will.

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.

@colll78
Copy link
Contributor Author

colll78 commented Jul 29, 2025

In traditional finance, someone cannot just walk up to you and forcefully give you money / assets against your will.

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.

@WhatisRT
Copy link
Contributor

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 GuardingContinuing script purpose. There are really just simplifying assumptions for the following:

A script making use of this CIP would generally be of this form:

case scriptPurpose of
  (Spending x) -> p1
  (Guarding x) -> p2
  _ -> error

We can transform this into the script that has essentially the same behaviour but works today:

case scriptPurpose of
  (Spending x) -> p1 ∧ containsToken x ∧ noOutputHasTokens (filter (isNotAtAddr x) outs)
  (Minting (x, _)) -> p2 ∧ everyOutputHasOneToken (filter (isAtAddr x) outs)
  _ -> error

Note that x always contains the hash of the script being executed, and the token the helper function checks for is any token with the AssetID of this script.

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?

@lehins
Copy link
Contributor

lehins commented Jul 31, 2025

@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?

@WhatisRT
Copy link
Contributor

WhatisRT commented Aug 1, 2025

@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.

The OFAC stuff and this CIPs impact on user wallets is an afterthought.

Your #1063 (comment) and a whole bunch of other arguments in comments on this CIP about it made me wrongfully believe that was the primary goal.

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.

@lehins

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?

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.

@WhatisRT
Copy link
Contributor

WhatisRT commented Aug 1, 2025

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.

@colll78
Copy link
Contributor Author

colll78 commented Aug 1, 2025

@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?

@lehins
Yes this is perfect! Basically the flag states "the creation of this output was validated by the script".

This perfectly achieves the goal of being able to easily check the integrity of UTxOs at smart contracts!

My point is, that from the ledger perspective, when a user sends to GuardScriptHash fooHash, nothing prevents a nefarious user from sending assets to the same script hash fooHash, but as ScriptHash fooHash, which would require for the script itself to be checking whether it is allowed to run only under this new script purpose or not, because otherwise nothing is preventing those funds to be successfully spend as a regular Spending script.
It might not necessarily be a problem, but it is worth pointing out that there will be responsibility on the DApp developer to implement the script correctly.

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.

@Crypto2099
Copy link
Collaborator

@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?

I feel like you don't listen to me Alexey :D #1063 (comment)

@lehins
Copy link
Contributor

lehins commented Aug 1, 2025

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.

@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. 🤷‍♂️

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.

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.

@lehins
Copy link
Contributor

lehins commented Aug 1, 2025

I feel like you don't listen to me Alexey :D #1063 (comment)

@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.

@colll78
Copy link
Contributor Author

colll78 commented Aug 1, 2025

I feel like you don't listen to me Alexey :D #1063 (comment)

@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.

#1063 (comment)

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.

@lehins
Copy link
Contributor

lehins commented Aug 1, 2025

@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:

  1. We add a flag to an Address, let's call it IsProtected or something along these lines. This will result in utilization of a single bit out of a few that we have available in the header of an address that is not used today anyways.
  2. Add one new Script Purpose: Creating (name is opened up to debate, but it can't be "guarding", since that is what we will call scripts from CIP-112)
  3. Ledger will require a witness for creating an output to an address with IsProtected set to True.

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 IsProtected set to False, regardless whether an output is being created or being spent.

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:

  1. This will make the implementation simpler.
  2. There is no danger in doing that as far as I can see.
  3. Most importantly, there might be some use cases that we didn't think of, like a user simply wanting to show to others or to some script that the output that is being created is controlled by the named user.

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.

@colll78
Copy link
Contributor Author

colll78 commented Aug 1, 2025

@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:

  1. We add a flag to an Address, let's call it IsProtected or something along these lines. This will result in utilization of a single bit out of a few that we have available in the header of an address that is not used today anyways.

  2. Add one new Script Purpose: Creating (name is opened up to debate, but it can't be "guarding", since that is what we will call scripts from CIP-112)

  3. Ledger will require a witness for creating an output to an address with IsProtected set to True.

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 IsProtected set to False, regardless whether an output is being created or being spent.

This makes sense.

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:

  1. This will make the implementation simpler.

  2. There is no danger in doing that as far as I can see.

  3. Most importantly, there might be some use cases that we didn't think of, like a user simply wanting to show to others or to some script that the output that is being created is controlled by the named user.

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 is great. How about we call the new script purpose 'Receiving'? This is inline with 'Spending'.

@WhatisRT
Copy link
Contributor

WhatisRT commented Aug 4, 2025

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.

@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?

@rphair
Copy link
Collaborator

rphair commented Aug 4, 2025

@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 Draft review status ever since.

Ordinarily I would leave tagging to the author (@fallen-icarus) under those conditions, and then only tag all stakeholders once emerging from Draft into "Ready for review" — but if you would like to be tagged more readily then please say so and I'll try to keep you covered.

Copy link
Contributor

@zliu41 zliu41 left a 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.

| Rewarding StakingCredential
| Voting Vote
| Proposing Proposal
| Guarding ScriptHash Integer (Maybe Integer) -- new
Copy link
Contributor

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.

Copy link
Contributor Author

@colll78 colll78 Aug 4, 2025

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).

Copy link
Contributor

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?

```

**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.
Copy link
Contributor

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.

data Credential =
KeyCredential PubKeyHash
| ScriptCredential ScriptHash
| GuardScriptCredential ScriptHash -- new
Copy link
Contributor

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.

@GeorgeFlerovsky
Copy link
Contributor

GeorgeFlerovsky commented Aug 19, 2025

@lehins
For the CIP Editors' benefit, could you please post a few high-level bullet points relevant to this CIP from our Ledger WG discussion?
(The one that was recorded but not yet made public. You can omit any security-sensitive info)

  • using an address flag for guarded addresses
  • why guarded staking addresses should be deferred for a separate future CIP
  • feasibility of implementing the CIP in Cardano ledger
  • ledger team's general positive/negative view of the CIP and remaining issues we need to address in discussion

@rphair rphair changed the title CIP-???? | Guard Script Purpose and Credential CIP-0160? | Guard Script Purpose and Credential Aug 20, 2025
@rphair rphair added State: Confirmed Candiate with CIP number (new PR) or update under review. and removed State: Triage Applied to new PR afer editor cleanup on GitHub, pending CIP meeting introduction. labels Aug 20, 2025
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.

@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 🎉

@lehins
Copy link
Contributor

lehins commented Aug 26, 2025

recording the high-level recommendations for this proposal & confirmation about what part(s) the Ledger would be ready to implement;

could you please post a few high-level bullet points relevant to this CIP from our Ledger WG discussion?

In general this CIP needs an overhaul, since during Ledger Working Group Meeting #21 we've agreed that:

  • Credential type will stay unchanged.
  • We only need to add one script purpose, instead of two. I'll call it "Protecting" for now, but if my memory serves me right the suggested name was "Creating"
  • We add a isProtected tag to Address, which means every Address today will become by default unprotected. So, it would be a backwards compatible and an opt-in feature.
  • We will resolve TxOut and add it to the script purpose, which was also suggested in CIP-???? | More Descriptive Script Purposes #1072 for orthogonal reasons.

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.

@@ -0,0 +1,146 @@
---
CIP: 160
Title: Guard Script Purpose and Credential
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor

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 🤷‍♂️

Copy link
Contributor

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.

Copy link
Contributor

@lehins lehins Aug 29, 2025

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.

Copy link
Contributor

@GeorgeFlerovsky GeorgeFlerovsky Sep 6, 2025

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.

Copy link
Contributor

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.

@cardano-foundation cardano-foundation deleted a comment Sep 17, 2025
Update to replace `GuardingCredential` (a new credential type) with `ProtectedAddress` (a new address type), and replace `Guarding` script purpose with `Receiving` script purpose.
@colll78
Copy link
Contributor Author

colll78 commented Oct 4, 2025

Updated with respect to the discussion above, please feel free to re-review at your leisure @lehins

@colll78 colll78 changed the title CIP-0160? | Guard Script Purpose and Credential CIP-0160? | Receiving Script Purpose and ProtectedAddress Oct 5, 2025
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: Confirmed Candiate with CIP number (new PR) or update under review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.