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.

An inconsistent ledger where Alice double-spends her asset

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

A non-conformant ledger where one leg of the DvP settlement is missing

Authorization violation examples

Next, we give three examples that show different kinds of authorization violations.

Unauthorized transfer

First, Alice attempts to steal Bob’s asset by requesting a transfer in his name. This results in an authorization failure because for TX 1 the actor of the exercise root action differs from the requester.

      let usdAsset = SimpleAsset with
            issuer = bank
            owner = bob
            asset = "1 USD"
      bobUsd <- submit bank $ do createCmd usdAsset

      aliceUsd <- submit alice $ do
        exerciseCmd bobUsd $ Transfer with
            newOwner = alice
A ledger where Alice submits a transaction where Bob exercises the transfer choice on his asset

Skip the propose-accept workflow

Next, Bob wants to skip the propose-accept workflow for creating the SimpleDvP contract and instead creates it out of nowhere and immediately settles it. This must be treated as an authorization failure, as Alice did not consent to swapping her EUR asset against Bob’s USD asset.

      let eurAsset = SimpleAsset with
            issuer = bank1
            owner = alice
            asset = "1 EUR"
      eur <- submit bank1 do createCmd eurAsset
    
      let usdAsset = SimpleAsset with
            issuer = bank2
            owner = bob
            asset = "1 USD"
      usd <- submit bank2 do createCmd usdAsset

      let dvp = SimpleDvP with
            party1 = alice
            party2 = bob
            asset1 = eur
            asset2 = usd
      (newUsd, newEur) <- submit bob $ do
         createAndExerciseCmd dvp $ Settle with actor = bob

On the ledger, the first root action of TX 2 is not properly authorized because Alice is a signatory of the contract #3 created in the first root action even though she did not request the update.

A ledger with an authorization violation on the creation of the DvP contract

Allocate someone else’s asset

The final example shows that authorization failures may not only happen at root actions. Here, Alice allocates Carol’s CHF asset in the DvP proposal. When Bob tries to settle the DvP, the Exercise to transfer Carol’s asset in the first leg is not properly authorized because Carol did not agree to have her asset transferred away.

      let chfAsset = SimpleAsset with
            issuer = bank1
            owner = carol
            asset = "1 CHF"
      chf <- submit bank1 do createCmd chfAsset
      disclosedChf <- fromSome <$> queryDisclosure carol chf
    
      let usdAsset = SimpleAsset with
            issuer = bank2
            owner = bob
            asset = "1 USD"
      usd <- submit bank2 do createCmd usdAsset

      proposeDvP <- submit alice $ do
        createCmd ProposeSimpleDvP with
           proposer = alice
           counterparty = bob
           allocated = chf
           expected = usdAsset
           
      (newUsd, newEur) <- submit (actAs bob <> disclose disclosedChf) $ do
          exerciseCmd proposeDvP $ AcceptAndSettle with toBeAllocated = usd

The ledger produced by this script has an authorization failure for the Exercise node on contract #1: The transaction structure provides no evidence that the actor Carol has agreed to exercising the Transfer choice on her asset.

A ledger with an authorization failure where Alice allocates Carol's asset to her DvP with Bob

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 of the DvP ledger

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 3 is 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 ④.

The execution order of the ledger where Alice double-spends her asset

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

An inconsistent ledger where Alice's projection is consistent

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

Bob's transaction accepting and settling the DvP via the helper contract and Alice's projection thereof

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.

Authorization

The last validity condition ensures that only the indended parties can request a change, and thereby rules out the authorization violation examples. Authorization requirements are expressed in Daml using the signatories and observers of a contract and the controllers of choices.

This section introduces the notions to formalize this:

  • Required authorizers define the set of parties who must have consented to an action.

  • The authorization context captures the parties who have actually authorized an action.

  • Well-authorization :ref:<da-ledgers-authorization-rules> demands that the authorization context includes all the required authorizers.

The running example of Bob skipping the propose-accept workflow will be used to show how node ③ requires more authorizers than its authorization context provides, and is thus not well auhtorized. For ease of reference, the ledger diagram is repeated below.

A ledger with an authorization violation on the creation of the DvP contract

Required authorizers

Every node defines a non-empty set of parties who must have consented to the action of this node. This set is called the required authorizers of the node and defined as follows: For Create nodes, the required authorizers are the signatories of the contract, and for Exercise and Fetch nodes, the required authorizers are the actors of the node.

For the running example where Bob skips the propose-accept workflow, the following table lists for each party the nodes for which they are a required authorizer. For example, node ③ has the required authorizers Alice and Bob because they are the signatories of contract #3.

Required authorizers in the example where Bob tries to skip the propose-accept workflow

Node

Bank1

Bank2

Alice

Bob

signatory

signatory

signatory

signatory

actor

actor

signatory

actor

signatory

Authorization context

In a Canton ledger, a party can authorize a subaction of a commit in two ways:

  • The requesters of a commit authorize every top-level action of the commit.

  • For an Exercise action, the signatories of the input contract and the actors of the action jointly authorize every consequence of the action.

The set of authorizing parties for a given action is called the authorization context. Continuing the example of the required authorizers, the following table shows the authorization context for each node. For instance, the authorization context for nodes ③ and ④’s authorization context consists of Bob because Bob is the sole requester of the commit. For node ⑥, the authorization context contains two parties:

  • Bank1 because Bank1 is the signatory of the input contract of the parent node ⑤.

  • Alice because Alice is the actor on the parent node ⑤.

Similarly, the authorization context for nodes ⑤ and ⑦ contains both Alice and Bob: Alice is a signatory on the input contract #3 of the parent node ④, and Bob is both a signatory on #3 and the actor of ④.

Authorization contexts in the example where Bob tries to skip the propose-accept workflow

Node

Bank1

Bank2

Alice

Bob

requester of TX 0

requester of TX 1

requester of TX 2

requester of TX 2

signatory on #3

signatory on #3 + actor of ④

signatory on #1

actor of ⑤

signatory on #3

signatory on #3 + actor of ④

signatory on #2

actor of ⑦

Important

The authorization context summarizes the context (parent action or commit) in which an action happens on the Ledger. It cannot be derived from the action itself.

Well-authorization

Well-authorization ensures that the authorizing parties and the required authorizers fit together.

Definition: Well-authorization

An action is internally well-authorized if for every proper subaction, the authorization context contains all the required authorizers of the subaction.

An action is well-authorized if it is internally well-authorized and the authorization context of the action contains all the required authorizers of the action.

A commit is well-authorized if every root action is well-authorized.

In the running example, well-authorization requires that every non-empty cell in the required authorizers table is also non-empty in the authorization context table. For example, the commit TX 0 is well-authorized because it contains only one subaction ① and the required authorizer Bank1 is also the requester of the commit. Conversely, the commit TX 2 is not well-authorized because ③’s required authorizers include Alice who is not in ③’s authorization context. This authorization failure captures the problem with this commit TX 2: The Ledger does not contain any record of Alice consenting to the DvP.

In contrast, the Exercise action at ④ is well-authorized. This illustrates how authorization flows from the signatories of a contract to the consequences of the choices. Assuming that the signatories Alice and Bob entered the SimpleDvP contract #3, the authorization rules allow Bob, the one controller of the Settle choice, to swap the two assets even though Bob does not own one of the assets (#1). In other words, Alice delegates via the SimpleDvp contract #3 to Bob the right to transfer her asset #1.

A similar flow of authorization also happens in the propose-accept workflow for the SimpleDvP contract in the correct workflow: In TX 2, Alice proposes the ProposeSimpleDvP contract #3 as a signatory. When Bob accepts the proposal with the Accept choice, Alice’s authority flows to the creation of the SimpleDvP contract #4, where both Alice and Bob are signatories.

Well-authorization with projection

The example of the wrongly allocated asset illustrates the difference between well-authorization and internal well-authorization. The action rooted at node ⑨ is internally well-authorized because it has only one proper subaction with node ⑩ whose authorization context includes the required authorizer Bank1. Yet, the action itself is not well-authorized because the required authorizers of ⑨ include Carol, but its authorization context contains only Alice and Bob, as they are signatories of the input contract #4 of node ⑧.

The authorization failure disappears in the projection to Bank1 though, because the projection of a ledger forgets the requesters of the commits. So from Bank1’s perspective, the asset transfer looks fine.

Bank1's projection of the DvP with Carol's asset

This example reiterates that well-authorization of an action cannot be determined solely from the action alone, and projections do not retain the context for root actions of the projection.

In contrast, internal well-authorization is a property of an action in isolation, independent of a context. For example, the actions rooted at ⑧ and ④ are not internally well-authorized because they contain the action at ⑨ as a sub-action and they define the authorization context for ⑨. Accordingly, internal well-authorization is common knowledge and therefore interacts with projection similar to conformance: projections preserve internal well-authorization; and reflection of internal well-authorization is irrelevant because only internally well-authorized actions can be part of the Ledger by construction.

In contrast, well-authorization is not common knowledge and does not behave well under projections. The validity definition below therefore deals explicitly with it.

Authorization vs. conformance

Well-authorization and conformance are both necessary to ensure that the Ledger contains only the intended changes. To illustrate this, we modify the example of the wrongly allocated asset such that node ⑨ specifies Alice as the actor instead of Carol. Then, the action (and the ledger as a whole) is well-authorized. Yet, it no longer conforms to the Daml model, because the Transfer choice defines the controller to be the owner of the asset #1, which is Carol in this case.

This conformance failure does show up in Bank 1’s projection, unlike corresponding the well-authorization failure from the previous section.

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.