How to check a contract has been archived?

Is there a way I can check this from within daml?
I was thinking of trying to fetch a contract, but I don’t see an DA.Exception that is specific enough for this. I suppose I could pattern match against the error message (otherwise I may get an error due to e.g. an authorization failure). Is that a terrible idea? Are error messages guaranteed to be stable from release to release?

Contracts are immutable, including their permissions, so if you were able to fetch a given contract ID in the past and can’t anymore, the only possible explanation is that it’s been archived.

But how would you implement this in daml code? Specifically,

Is not something you can express in a single Update.

Whitin the language, there is no direct way to check that an arbitrary contract has been archived. This cannot neihter be done indirectly by using fetch (as described by Gary) as failing fecthes raise non catchable exceptions. Those are deliberate design choices, that would require substantial work on both the language and ledger sides to be changed.

The only workarounds I can come with are:

  • using a contract key and lookup by key
  • moving the logic off ledger, in daml-script for instance.

Doesn’t an archived contract raise a GeneralError with message saying that it’s bene archived? :thinking:

There are actually two cases for failing fetches,

  • If the contract is archived in the same transactions, the engine raises a ContractNotActive error
  • If the contract was archived in a previous transaction, the engine raises a ContractNotFound error

Both errors are uncatchable and exposed through the ledger API.

I am not sure where the GeneralError, you are referring to, comes from. I suspect the message you see is generated by the daml-script runner within the IDE, when you hit one of the cases.

1 Like

It’s not quite the same question, but as further food for thought when querying historical data has been brought up in the past, the recommendation that was generally given was that Daml code should focus on active contracts, and if your Daml code needs to reference some notion of “history”, then that history is part of your data modal and should be reflected in Daml logic.

In this case, this could mean, for example, that you assign a UUID as the key to a template, and add a boolean attribute archived. That way, your logic can fetchByKey and check the attribute.

You obviously need to trust the key maintainers not to attribute the same UUID twice, but at least that trust is made explicit.

Gary to give you an idea of the use-case, we’re looking at archived tokens (assets). A token represents state, so if I transfer some bitcoin to you, then the original token would be archived and replaced by a new one with owner = gary. In this case keeping the old state (tokens) would be really messy.

As @Remy already pointed out you cannot do that in Daml but let me try expanding on the reasons a bit:

First, there are two failures here: There never was a contract with the given id or the contract with the given id has been archived. To differentiate between the two you need to keep a list of all contracts that have ever been created which is incompatible with pruning. So if you want to do this at all, you would need to treat both errors the same (or only catch this within a pruning window which is just very confusing semantics).

Next, Daml interpretation happens fully on the participant and is then later only validated after sequencing. That means that you will never be able to catch a race where the contract is archived between interpretation on the participant and validation. Not necessarily a deal breaker but somewhat confusing that for races you will still get an uncatchable error.

Now we get to the dealbreaker: All nodes in a Daml transaction are validated. In your setting you would need a node for a failed fetch or something along those lines. However, there is noone that can validate this: Given a contract id, which parties should validate that the contract is not active? For contract keys that is solved by having the maintainers be a function of the key so even if no contract exists we know who the maintainers are and they can validate it. We’re cannot compute the signatories purely based on a contract id. So there is not really any way to make this work in a validated fashion.

I think you could potentially make this a non-validated node that is evaluated purely on the participant node and then just trusted by everyone else. However, that would be a huge departure from the current Daml ledger model and leads to very different trust properties and at the moment, I don’t think this is a route we want to go down atm.

2 Likes

One technique that I’ve used in the past is to (echo @Gary_Verhaegen 's point) create contracts specifically for this purpose. A contract that just claims that there was a previous owner (ex. PreviousOwnersAsset ) which has different rights and observers from the current Asset.