The Registrar Should Not Know the Price
Alice buys 100 bonds from Bob for €100k. A registrar keeps the bond register. A bank issues the tokenized cash. Both legs have to settle together, because whoever delivers first carries the full value of the trade as risk until the other side moves. That is delivery versus payment, and a shared ledger makes the atomic part easy.
The harder part is who learns what. The bank has no business knowing which bond Alice bought. The registrar has no business knowing the price. A competitor that runs a node on the same network should learn nothing. Two stacks that banks use for tokenization answer this in opposite ways.
On a permissioned EVM network, every node reads the trade
On Hyperledger Besu with ordinary transactions, a DvP is one contract call. It moves the cash token one way and the bond token the other, and the EVM guarantees that both transfers happen or the call reverts. Atomicity costs nothing.
The same mechanism publishes the trade. Every node on the network executes that call and stores the result. The bank's node holds the bond transfer. The registrar's node holds the cash transfer, and with it the price. So does the node of every other member, including the ones that had nothing to do with the trade.
Besu's answer used to be private transactions. Tessera distributed the payload to a privacy group, and only the nodes in that group executed it. Two things limit that design for settlement. Besu deprecated Tessera-based privacy in version 24.12, as part of a wider simplification of the client. And a private transaction lives inside one privacy group. Put the bank and the registrar in the same group and both see both legs. Put them in separate groups and you no longer have one atomic transaction across cash and bonds.
On Canton the transaction is a tree
A Daml transaction is a tree of actions, and privacy is decided per action. The
settlement has a root, where the parties exercise Settle on their DvP
contract, and two children: one transfer of cash and one transfer of bonds.
Each action has informees, the parties entitled to see it. A party sees an action when it is an informee of that action or of an action above it in the tree. Canton cuts everything else out of that party's copy of the transaction.
Alice and Bob signed the DvP contract, so they are informees of the root and see the whole tree. The bank signed the cash holding, so it sees that holding consumed and a new one created for Bob. It learns that Alice paid Bob €100k. It does not learn why, and the bond leg is absent from its view. The registrar gets the mirror image. The Canton ledger model documentation works through the same swap with the same result: each bank sees the transfer of its own asset and nothing about the reason for it.
Three rules produce those views
The informees of an action follow from the contract it touches:
- Create: the signatories and observers of the new contract.
- Consuming exercise: the signatories and observers of the consumed contract, the actors who exercise the choice, and any choice observers.
- Non-consuming exercise: the signatories, the actors and any choice observers. Contract observers are not told.
A toy model small enough to check by hand:
template CashHolding
with
bank : Party
owner : Party
amount : Decimal
where
signatory bank
observer owner
choice Transfer : ContractId CashHolding
with
newOwner : Party
controller owner
do
create this with owner = newOwner
template DvP
with
buyer : Party
seller : Party
cashCid : ContractId CashHolding
bondCid : ContractId BondHolding
where
signatory buyer, seller
choice Settle : (ContractId CashHolding, ContractId BondHolding)
controller buyer
do
cash <- exercise cashCid Transfer with newOwner = seller
bond <- exercise bondCid Transfer with newOwner = buyer
return (cash, bond)
BondHolding has the same shape, with the registrar as signatory and a
quantity instead of an amount.
Walk the tree with the three rules. Settle is a consuming exercise on a
contract signed by the buyer and the seller. Informees: Alice and Bob. The first child
consumes Alice's CashHolding. Its signatory is the bank, its observer is
Alice, and the actor is Alice. Informees: the bank and Alice. That child creates a holding
with the bank as signatory and Bob as observer. Informees: the bank and Bob. The registrar
appears nowhere on that branch.
What the synchronizer learns
Canton splits the tree into views and encrypts each view for the participants that host its informees. The synchronizer has two roles in the protocol. The sequencer orders the encrypted messages and delivers each one to its recipients. The mediator collects the confirmations and declares the result, so the transaction commits for all participants or for none. Neither reads the payload.
They do see metadata. The sequencer knows which participants received a message and how large it was. For most settlement designs that is acceptable. If your threat model includes someone who watches which institutions trade with each other and when, decide who operates the synchronizer before you decide anything else.
Two ways to get it wrong
One observer too many. Someone asks for the registrar to "have
visibility of settlements", and the quickest change is one line on
CashHolding: observer owner, registrar. The registrar is now a
stakeholder of every cash holding. It is an informee of each transfer that consumes one,
and it reads the amount. The price leak that the tree removed is back, and no test fails,
because nothing is broken. Scope the disclosure instead. Give the registrar a separate
report contract that carries only the fields it needs, or make it a choice observer on
the one choice it should witness.
Authority that does not reach. A tempting shortcut is to let
Settle archive Alice's holding and create Bob's directly. Settle
runs with the authority of its signatories and its actor, which means Alice and Bob. A
CashHolding needs the bank's signature. The bank never signed the DvP, so
the transaction is rejected as not well-authorized, and the bond leg goes down with it.
Routing the payment through Transfer works because the signatories of a
contract authorize the consequences of the choices exercised on it. The bank signed the
holding, so its authority covers the new one.
What the toy model leaves out
- Checks.
Settletrusts the two contract IDs it was given. A real choice fetches both holdings and asserts owner, instrument, quantity and amount against the agreed terms before it moves anything. - Consent to hold. With the owner as an observer, anyone can push an asset to anyone. Production models make the owner a signatory or add a propose and accept step. The authority question from the previous section then returns for the receiving side.
- Submitter visibility. Whoever submits
Settlemust be able to read both holdings, and Alice cannot see Bob's bonds before the trade. Real designs lock each side's asset into an allocation that the settling party can see. The Canton Network token standard, CIP-56, defines allocations and allocation requests for this multi-leg case.
Before the first template
An order of work that keeps the privacy property through later changes:
- List every party and, next to each one, what it must not learn.
- Assign signatories, observers and controllers per template to satisfy that list. Treat each added observer as a disclosure decision.
- Write the expected view of each party for each workflow, as a matrix like the one above.
- Test it per party. Run the workflow, then query as the bank and as the registrar and assert what is absent.
- Review every later change to an observer or a controller as a change to who sees client data.