Why are Fetch and NoSuchKey considered ledger Actions?

In the DAML ledger model there are four actions defined: Create, Exercise, Fetch, and NoSuchKey.

Only Create and Exercise can have consequences which cause Events on the ledger API’s flat transaction stream. Similarly, the only commands available in the ledger API are variants of Create and Exercise.

If Fetch and NoSuchKey are not available on the DAML Ledger API and have no visible effect on the ledger transaction stream, why are they considered ledger Actions?

4 Likes

The fact that Fetch and NoSuchKey are not available over the LedgerAPI is an oversight in my opinion. The Ledger API should give you everything you need to validate a DAML Ledger.

And that idea is also the reason why Fetch, nonconsuming Exercise and NoSuchKey are actions. The transaction graph (Ie the graph of Actions) should contain the information you need to decompose a DAML Ledger into individual party views (aka projections) and validate those. You need the Fetch and NoSuchKey nodes to be able to decompose into projections, and you need the information on those nodes to validate those projections.

Let’s just imagine a simple example where you sent me an instance of the DisclosureRequest below:

template DisclosedAsset
  with
    assetCid : ContractId Asset
    asset : Asset
    disclosee : Party
  where
    signatory [asset.owner, disclosee]

template DisclosureRequest
  with
    requester : Party
    requestee : Party
  where
    signatory requester
    controller requestee can
      Disclose : ContractId DisclosedAsset
        with
          assetCid : ContractId Asset
        do
          asset <- fetch assetCid
          create DisclosedAsset with disclosee = requestee; ..

When I exercise Disclose, the current Ledger Model says that there are three nodes:

Exercise  (assetCid)
| - Fetch (assetCid -> asset)
| - Create (DisclosedAsset assetCid asset requester)

You and I validate the whole thing. The Fetch node contains the contract arguments of the Asset contract which allow you to execute the create statement.
The asset issuer only gets to see and validate the Fetch node.

This model is safe:

  • I can’t fake the contents of assetCid because the Fetch node is validated by the issuer.
  • I can’t create DisclosedAsset contents by any other means because they need your signature.

So you have the guarantee that unless the issuer collaborates with me, the asset in DisclosedAsset will always match assetCid, and the contract is active at the time of the transaction.

Now let’s imagine we only had Create and Exercise nodes in the Ledger Model. Now the resulting transaction looks like this:

Exercise  (assetCid)
| - Create (DisclosedAsset assetCid asset requester)

What does the issuer get to see here? If nothing, you are at my mercy when it comes to saying what asset contains. You lose the guarantee that assetCid and asset match.

But let’s say we have a magic wand which given an Asset and a ContractId Asset allows us to check that they match and that the contract is active. You as a human reading this DAML code can infer that if you see the transaction above, the asset and assetCid must match, and you can verify that using the magic wand. Thus, you can verify the whole transaction. But you can’t always do that. Imagine I added a second choice to DisclosureRequest:

    controller requestee can
      DiscloseHigher : ContractId DisclosedAsset
        with
          assetCid1 : ContractId Asset
          assetCid2 : ContractId Asset
        do
          asset1 <- fetch assetCid1
          asset2 <- fetch assetCid2
          let
            (assetCid, asset) = if asset1.quantity >= asset2.quantity
              then (assetCid1, asset1)
              else (assetCid2, asset2)
          create DisclosedAsset with disclosee = requestee; ..

If I exercised that, you’d see this:

Exercise  (assetCid1, assetCid2)
| - Create (DisclosedAsset assetCid asset requester)

What can you infer now? With your magic wand you can still check that assetCid and asset match, and that the contract was active, but you have no chance to check that I evaluated the if..else statement correctly. The only way you could validate that is to know what the fetches result in. Ie you need to know the Fetch nodes.

With NoSuchKey it’s more about the validation than sharing the needed information. You need a node that you can send to the maintainers of the key to confirm the non-existence.

3 Likes

Could someone shed some more light on the NoSuchKey node? I found the documentation on it rather sparse.