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.