Ledger Model¶
Note
Update examples and notation in the style of the Canton polyglott whitepaper https://www.canton.network/hubfs/Canton%20Network%20Files/whitepapers/Polyglot_Canton_Whitepaper_11_02_25.pdf
Interleave definitions and diagrams with concrete examples from Daml; link to Daml source files that are checked by CI.
Remove exceptions
Extend with time, upgrading (via packages), requester of a commit, explicit disclosure
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.
The Ledger Model defines:
What the ledger looks like - the structure of the Canton Ledger
Who sees which changes and data - the privacy model for the Canton Ledger
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