Integrity¶
The section on the ledger structure section answered the question “What does the Ledger looks like?” by introducing a hierarchical format to record the party interactions as changes to the Ledger. The section on privacy answered the question “Who sees which changes and data?” by introducing projections. This section addresses the question “Who can request which changes?” by defining which ledgers are valid.
Overview¶
At the core is the concept of a valid ledger: a change is permissible if adding the corresponding commit to the ledger results in a valid ledger. Valid ledgers are those that fulfill three conditions, which are introduced formally below:
Consistency: A consistent Ledger does not allow exercises and fetches on inactive contracts; that is, they cannot act on contracts that have not yet been created or that have already been consumed by an exercise.
Conformance: A conformant Ledger contains only actions that are allowed by the smart contract logic of the created or used contract. In Daml, templates define this smart contract logic.
Authorization: In a well-authorized Ledger, the requesters of a change encompass the required authorizers as defined via the controllers and signatories.
Validity is defined as the conjunction of these three conditions. Later sections add further validity conditions as they increase the expressivity of the Ledger Model.
For example, the running example of the DvP workflow is a good example for a non-trivial Ledger that satisfies all validity conditions. However, it is instructive to look at examples that violate some validity condition, to gain intuition for why they are defined as they are.
Consistency violation example¶
In this example, Alice tries to transfer her asset twice (“double spend”): once to Bob and once to Charlie, as shown in the following Daml script excerpt. This script is expected to fail at runtime, because it violates consistency.
let eurAsset = SimpleAsset with
issuer = bank
owner = alice
asset = "1 EUR"
aliceEur <- submit bank do createCmd eurAsset
bobEur <- submit alice $ do
exerciseCmd aliceEur $ Transfer with
newOwner = bob
carolEur <- submit alice $ do
exerciseCmd aliceEur $ Transfer with
newOwner = carol
pure ()
The corresponding Canton ledger looks as shown below.
This ledger violates the consistency condition because contract #1 is the input to two consuming exercise nodes,
one in TX 1 and one in TX 2.
Conformance violation example¶
In the example below, the last transaction TX 4 omits one leg of the DvP workflow:
Bob exercises the Settle choice, but it has only one subaction, namely Alice transferring her IOU.
This violates conformance because the Settle choice body of a SimpleDvP specifies via the two exercise calls that there are always two consequences.
(This situation cannot be expressed as a Daml script scenario
because Daml script ensures that all generated transactions conform to the Daml code.)
Interaction with projection¶
Apart from introducing the validity notion, this page also discusses how validity interacts with privacy, which is defined via projection. To that end, the sections on the different validity conditions analyse the prerequisites under what the following two properties hold:
Preservation: If the Ledger adheres to a condition, then so do the projections. This property ensures that a valid Ledger does not appear as invalid to individual parties, just because they are not privy to all actions on the Ledger.
Reflection: If the projections adhere to a condition, then so does the Ledger from which these projections were obtained. This property ensures that the condition can be implemented by the distributed Canton protocol where nobody sees the Ledger as a whole.
Consistency¶
Consistency can be summarized in one sentence: Contracts must be created before they are used, and they cannot be used after they are consumed. This section introduces the notions that are needed to make this precise:
The execution order defines the notions of “before” and “after”.
Internal consistency ensures that all the operations on a contract happen in the expected order of creation, usage, archival, but does not require that all contracts are created; they may be merely referenced as inputs.
(Contract) Consistency strengthens internal consistency in that all used contracts must also have been created.
Execution order¶
The meaning of “before” and “after” is given by establishing an execution order on the nodes of a ledger.
The ledger’s graph structure already defines a happens-before order on ledger commits.
The execution order extends this happens-before order to all the nodes within the commits’ transactions
so that “before” and “after” are also defined for the nodes of a single transaction.
This is necessary because a contract can be created and used multiple times within a transaction.
In the AcceptAndSettle action of the DvP example, for example,
contract #3 is used twice (once in the non-consuming exercise at the root and once consumingly in the first consequence)
and contract #4 is created and consumed in the same action.
Definiton: execution order
For two distinct nodes n1 and n2 within the same action or transaction, n1 executes before n2 if n1 appears before n2 in the preorder traversal of the (trans)action, noting that the transaction is an ordered forest. For a ledger, every node in commit c1 executes before every node in commit c2 if the commit c1 happens before c2.
Diagrammatically, the execution order is given by traversing the trees from root to leaf and left to right: the node of a parent action executes before the nodes in the subactions, and otherwise the nodes on the left precede the nodes on the right. For example, the following diagram shows the execution order with bold green arrows for the running DvP example. So a node n1 executes before n2 if and only if there is a non-empty path of green arrows from n1 to n2. The diagram grays out the parent-child arrows for clarity.
The execution order is always a strict partial order. That is, no node executes before itself (irreflexivity) and whenever node n1 executes before n2 and n2 executes before n3, then n1 also executes before n3 (transitivity). This property follows from the ledger being a directed acyclic graph of commits.
The execution order extends naturally to actions on the ledger by looking at how the action’s root nodes are ordered. Accordingly, an action always executes before its subactions.
Internal consistency¶
Internal consistency ensures that if several nodes act on a contract within an action, transaction, or ledger, then those nodes execute in an appropriate order, namely creation, usage, archival. Internal contract consistency does not require Create nodes for all contracts that are used. This way, internal contract consistency is meaningful for pieces of a ledger such as individual transactions or actions, which may use as inputs the contracts created outside of the piece.
Definition: internal consistency
An action, transaction, or ledger is internally consistent for a contract c if for any two distinct nodes n1 and n2 on c in the action, transaction, or ledger, all of the following hold:
If n1 is a Create node, n1 executes before n2.
If n2 is a consuming Exercise node, then n1 executes before n2.
The action, transaction or ledger is internally consistent for a set of contracts if it is internally consistent for each contract in the set. It is internally consistent if it is internally consistent for all contracts.
For example, the whole ledger shown above in the execution order example is internally consistent.
Hint
To see this, we have to check for pairs of nodes acting on the same contract.
This hint performs this tedious analysis for the transaction TX 3;
a similar analysis can be done for the other transaction on the Ledger.
You may want to skip this analysis on a first read.
The nodes in the transaction involve six contracts #1 to #6.
Contracts #1, #5, and #6 appear only in one node each, namely ⑨, ⑩, and ⑫, respectively.
TX 3is therefore trivially consistent for these contracts.Contract #2 appears in the Fetch node ⑥ and the Exercise node ⑪. So internal consistency holds for #2 because the first condition does not apply and the second one is satisfied as ⑪ is consuming and ⑥ executes before ⑪.
Contract #3 appears in the two Exercise nodes ④ and ⑤. Since the consuming ⑤ executes after the non-consuming ④, internal consistency holds also for #3.
Contract #4 is created in ⑦ and consumed in ⑧. So both conditions require that ⑦ executes before ⑧, which is the case here.
In contrast, the next diagram shows that the ledger in the consistency violation example is not internally consistent for contract #1. This contract appears in nodes ①, ②, and ④. The second condition is violated but violated for n1 = ④ and n2 = ② as ④ does not execute before ②. Note that the second condition is satisfied for n1 = ② and n2 = ④, but the definition quantifies over both pairs (②, ④) and (④, ②). The first condition is also satisfied because the Create node ① executes before both other nodes ② and ④.
Note
Internal consistency constrains the order of the commits in a Ledger via the execution order.
In the running DvP example, TX 0, TX 1, and TX 2 all create contracts that TX 3 uses.
Internal consistency therefore demands that these create nodes execute before the usage nodes in TX 3.
So by the definition of the execution order, TX 0, TX 1, and TX 2 all must happen before TX 3
(although internal consistency does not impose any particular order among TX 0, TX 1, and TX 2).
Definition¶
Consistency strengthens internal consistency in that used contracts actually have been created within the action, transaction, or ledger.
Definition: consistency
An action, transaction, or ledger is consistent for a contract if all of the following hold:
It is internally consistent for the contract.
If a node uses the contract, then there is also a node that creates the contract.
It is consistent for a set of contracts if it is consistent for all contracts in the set. It is consistent if it is consistent for all contracts.
For example, the DvP ledger is consistent because it is internally consistent and all used contracts are created.
In contrast, if the DvP ledger omitted the first commit TX 0 and thus contains only commits TX 1 to TX 3, it is still internally consistent, but not consistent,
because TX 3 uses the contract #1, but there is no create node for #1 in TX 1 to TX 3.
Consistency and projection¶
This section looks at the conditions under which projections preserve and reflect (internal) consistency.
Projections preserve consistency for stakeholders¶
For preservation, projections retain the execution order and preserve internal consistency.
Yet, consistency itself is preserved in general only for contract stakeholders.
For example, Alice’s projection of the DvP workflow is not consistent
because it lacks TX 1 and therefore the creation of contract #2 used in TX 3.
Fortunately, consistency behaves well under projections if we look only at contracts the parties are stakeholders of. In detail, if an action, transaction, or ledger is (internally) consistent for a set of contracts C and P is a set of parties such that every contract in C has at least one stakeholder in P, then the projection to P is also (internally) consistent for C.
To see this, note that the execution order of the projection of an action or transaction to P is the restriction of the execution order of the unprojected action or transaction to the projection. That is, if n1 and n2 are two nodes in the projection, then n1 executes before n2 in the projection if and only if n1 executes before n2 in the original (trans)action. Accordingly, projections preserve internal consistency of an action or transaction too. Moreover, the projection to P never removes a Create node if one of the stakeholders is in P. Therefore, consistency is preserved too. For ledgers, the same argument applies with the current simplification of totally ordered ledgers. The causality section relaxes the ordering requirement, but makes sure that projections continue to preserve (internal) consistency for the parties’ contracts.
Signatories check consistency on projections¶
From Canton’s perspective, the reflection property is at least as important: If the projection of a (trans)action or ledger to a set of parties P is (internally) consistent for a set of contracts C where each contract has at least one signatory in P, then so is the (trans)action or ledger itself. This statement can be shown with a similar argument.
Importantly, reflection requires a signatory of the contracts in P, not just a stakeholder.
The following example shows that the propery does not hold if P contains a stakeholder, but no signatory.
To that end, we extend the SimpleAsset template with a non-consuming Present choice
so that the issuer and owner can show the asset to a choice observer viewer:
nonconsuming choice Present : SimpleAsset
with
actor : Party
viewer : Party
observer viewer
controller actor
do
assert $ actor == issuer || actor == owner
pure this
In the following script, Alice transfers her EUR asset to Bob and then later the Bank wants to show Alice’s EUR asset to Vivian.
Such a workflow can happen naturally when Alice submits her transfer concurrently with the Bank submitting the Present command,
and the Synchronizer happens to order Alice’s submission first.
let eurAsset = SimpleAsset with
issuer = bank
owner = alice
asset = "1 EUR"
aliceEur <- submit bank $ do createCmd eurAsset
bobEur <- submit alice $ do
exerciseCmd aliceEur $ Transfer with
newOwner = bob
submit bank $ do
exerciseCmd aliceEur $ Present with
actor = bank
viewer = vivian
The next diagram shows the corresponding ledger and Alice’s projection thereof. The projection does not include the non-consuming Exercise ④ because Alice is not a signatory of the EUR asset #1 and therefore not an informee of ④. Alice’s projection is therefore consistent for contract #1. In contrast, the original ledger violates internal consistency for #1, namely the second condition: for n2 as ② and n1 as ④, the consuming exercise ② does not execute after ④.
With signatories instead of stakeholders, this problem does not appear: A signatory is an informee of all nodes on the contract and therefore any node relevant for consistency for the contract is present in the signatory’s projection.
Conformance¶
The conformance condition constrains the actions that may occur on the ledger. The definitions in this section assume a given contract model (or a model for short) that specifies the set of all possible actions. In practice, Daml templates define such a model as follows:
Choices declare the controller and the choice observers and constrain via their body the valid values in the exercised contract and choice arguments. Their body defines the subactions (by creating, fetching or exercising contracts) and the Exercise result.
The
ensureclause on the template constrains the valid arguments of a Create node.
With smart-contract upgrading, the templates applicable for a given contract may change over time. For simplicity, the Ledger Model assumes that it is always clear (to all involved parties) what template defines the set of possible actions for a given contract.
Definition: conformance
An action conforms to a model if the model contains it. A transaction conforms to a model if all the actions of the transaction conform to the model. A ledger conforms to a model if all top-level transactions of the ledger conform to the model.
The above example of conformance violation shows this definition in action.
The choice implementation of SimpleDvP.Settle exercises Transfer on two contracts and therefore requires that there are two subactions.
The action on the ledger however has only one of the two subactions and therefore violates conformance.
This example demonstrates why the contract model specifies actions instead of nodes:
a set of acceptable nodes cannot catch when a consequence is missing from an action,
because nodes ignore the tree structure.
Conformance and projection¶
Like consistency, conformance to a Daml model behaves well under projections. If an action, transaction or ledger conforms to a Daml model, then all their projections also conform to the same Daml model.
In fact, Daml models enjoy the stronger property that every subaction of a Daml-conformant action conforms itself. This essentially follows from two observations:
The controllers of any choice may jointly exercise it on any contract, and the signatories of a contract may jointly create the contract, without going through some predefined workflow. So contract creations and choices are essentially public.
The Daml language is referentially transparent. That is, all inputs and outputs of a transaction are explicitly captured in contracts, choice arguments and exercise results.
Not every such projection can be expressed as a set of commands on the Ledger API, though.
The Ledger Model considers this lack of expressivity artificial, because future versions of the Ledger API may remove such restrictions.
There are two kinds of cases where ledger API commads are less expressive than the ledger model defined here.
First, a projection may contain a Fetch node at the root, like the projection of the DvP AcceptAndSettle choice for Bank 2.
Yet, there is no Ledger API command to fetch a contract, as there are only commands for creating and exercising contracts.
Second, the Ledger API command language does not support feeding the result of an Exercise as an argument to a subsequent command.
For example, suppose that the AcceptAndSettle choice of ProposeSimpleDvP was actually implemented on a helper template AcceptAndSettleDvP as shown below.
template AcceptAndSettleDvP with
counterparty : Party
where
signatory counterparty
choice Execute : (ContractId SimpleAsset, ContractId SimpleAsset)
with
proposal : ContractId ProposeSimpleDvP
toBeAllocated: ContractId SimpleAsset
controller counterparty
do
dvp <- exercise proposal $ Accept with ..
exercise dvp $ Settle with actor = counterparty
Bob can then execute accept and settle the DvP in one transaction by creating a helper contract and immediately exercising the Execute choice.
(newUsd, newEur) <- submit (actAs bob <> disclose disclosedEur) $ do
createAndExerciseCmd (AcceptAndSettleDvP with counterparty = bob) $
Execute with
proposal = proposeDvP
toBeAllocated = usd
The difference to the running example is that Bob is the only stakeholder of this helper contract.
Accordingly, Alice’s projection of this TX 3 consists of two root actions, where the second exercises a choice on a contract created in a consequence of the first.
Even though such transactions cannot be currently expressed in the language of Ledger API commands, they are considered conformant Daml transactions according to the Ledger Model. In other words, conformance does not look at how values flow across actions, and this is what makes conformance behave well under projections.
Important
A Daml model can restrict the flow of information only within an action. Across actions, it is at the discretion of the submitters to ensure the desired flow. The Ledger does not validate this.
Conformance of an action or transaction depends only on the Daml model of interest, which is unambiguously referenced via the package IDs. Therefore, witnesses, informees, and third parties can independently check conformance of an action. So conformance is common knowledge. This makes the reflection property irrelevant for a distributed implementation, as non-conformant actions simply can not occur on the Ledger by construction.
Validity¶
Having formalized the three conditions consistency, conformance and well-authorization, we can now formally define validity.
Definition: Valid Ledger
A Canton Ledger is valid for a set of parties `P` if all of the following hold:
The Ledger is consistent for contracts whose signatories include one of the parties in P.
The Ledger conforms to the Daml templates.
Every root action on the Ledger is internally well-authorized and its required authorizers in P are requesters of the commit.
A Ledger is valid if it is valid for all parties.
The restriction to a set of parties P comes from privacy. As discussed above, consistency and well-authorization are not common knowledge. The Canton protocol therefore relies on the relevant parties to check these conditions. Accordingly, the protocol only ensures these properties for the parties that follow the protocol.
Virtual Global Ledger¶
The Canton protocol creates a Virtual Global Ledger (VGL) that is valid for the honest parties and such that each of these parties sees their projection of VGL. Honesty here means that the parties and the nodes they are using correctly follow the Canton protocol subject to the Byzantine fault tolerance configured in the topology.
This Virtual Global Ledger is not materialized anywhere due to privacy: in general, no node knows the entirety of the ledger. In the DvP ledger, for example, if the Banks, Alice, and Bob are hosted on different systems, only the projections to the Banks, to Alice, and to Bob materialize on these systems, but none of them sees the unprojected Ledger as a whole.
Accordingly, the Canton protocol cannot ensure the validity of the Virtual Global Ledger as a whole. For example, if a group of signatories decides to commit a double spend of a contract, then this is their decision. Since each spend may be witnessed by a different honest party, the VGL contains both spends and is therefore inconsistent for this contract.
Interaction with projection¶
Preservation and reflection for validity is difficult to formalize because projections discard the requesters of a commit. Therefore, we analyze these two properties for a weak validity notion, namely validity without the constraint on the requesters of the commit. Then, projection preserves weak validity in the following sense: If a Ledger is weakly valid for a set of parties P, then its projection to a set of parties Q is weakly valid for the parties in both P and Q. The restriction of the parties to the intersection of P and Q takes care of the problem of the projected-away contract creations discussed in the consistency section.
Reflection does not hold for weak validity in general when we look only at projections to sets of honest parties. For example, consider a Ledger with a root action that no honest party is allowed to see. So none of the projections contains this root action and therefore the projections cannot talk about its conformance or internal well-authorization. Fortunately, this is not necessary either, because we care only about the pieces of the Ledger that are visible to some honest party.
More formally, two Ledgers are said to be equivalent for a set of parties Q if the projections of the two Ledgers to Q are the same. Then reflection holds in the sense that there is an equivalent weakly valid Ledger. Let F be a set of sets of parties whose union contains the set of parties Q. If for every set P in F, the projection of a Ledger L to P is weakly valid for P insterected with Q, then the projection of L to Q is weakly valid. Note that this projection of L to Q is equivalent to L for Q due to the absorbtion property of projection.