Ledger Model

Note

Canton enables multi-party workflows by providing parties with a virtual global ledger (VGL), which encodes the current state of their shared contracts written in a smart contract language. At a high level, the interactions are visualized below: Three parties Alice, Bob, and Charlie connect independently to a virtual global ledger, depicted as a large cloud. They hold different views of that ledger in their local databases shown as blue icons. The global ledger is virtual in the sense that no single entity typically sees this global ledger in its entirety; it is an imaginary database that represents the union of all parties’ databases.

A conceptual diagram of the Virtual Global Ledger.

The Ledger Model defines:

  1. What the ledger looks like - the structure of the Canton Ledger

  2. Who sees which changes and data - the privacy model for the Canton Ledger

  3. What changes to the ledger are allowed and who can request them - the integrity model for the Canton Ledger

The sections below review these concepts of the Ledger Model in turn and how they relate to Daml smart contracts.

Running example

The running example for the Ledger Model is a simple multi-party interaction of two parties atomically swapping their digital assets. A digital asset is modeled as a SimpleAsset with an issuer, an owner, and the asset description. The owner can transfer such an asset to a new owner with the Transfer choice.

Note

These Daml templates are for illustration purposes only. They are not meant for real-world usage as they are heavily simplified.

template SimpleAsset with
    issuer : Party
    owner : Party
    asset : Text
  where
    signatory issuer
    observer owner

    choice Transfer : ContractId SimpleAsset
      with
        newOwner : Party
      controller owner
      do
        create this with owner = newOwner

An atomic swap, also known as delivery versus payment (DvP), combines two asset transfers between the parties in a single transaction. The SimpleDvP template below captures the agreement between two parties partyA and partyB to swap ownership of the two allocated assets. Either party to the DvP can execute the swap by exercising the Settle choice.

template SimpleDvP with
    party1 : Party
    party2 : Party
    asset1 : ContractId SimpleAsset
    asset2 : ContractId SimpleAsset
  where
    signatory party1
    signatory party2

    choice Settle : (ContractId SimpleAsset, ContractId SimpleAsset)
      with
        actor : Party
      controller actor
      do
        assert $ actor == party1 || actor == party2
        new1 <- exercise asset1 Transfer with newOwner = party2
        new2 <- exercise asset2 Transfer with newOwner = party1
        pure (new1, new2)

To create a DvP contract instance, the parties go through the usual propose-accept workflow pattern shown next. Party proposer creates a proposal for the party counterparty with their allocated asset and the description of the asset they expect to swap with. The counterparty can accept the proposal with the Accept choice to create a SimpleDvP contract, or can accept and immediately settle the swap with the AcceptAndSettle choice.

template ProposeSimpleDvP with
    proposer : Party
    counterparty : Party
    allocated : ContractId SimpleAsset
    expected : SimpleAsset
  where
    signatory proposer
    observer counterparty

    choice Accept : ContractId SimpleDvP
      with
        toBeAllocated : ContractId SimpleAsset
      controller counterparty
      do
        fetchedAsset <- fetch toBeAllocated
        assert $ fetchedAsset == expected
        create $ SimpleDvP with
          party1 = proposer
          party2 = counterparty
          asset1 = allocated
          asset2 = toBeAllocated

    nonconsuming choice AcceptAndSettle : (ContractId SimpleAsset, ContractId SimpleAsset)
      with
        toBeAllocated: ContractId SimpleAsset
      controller counterparty
      do
        dvp <- exercise self $ Accept with ..
        exercise dvp $ Settle with actor = counterparty