Documentation Index
Fetch the complete documentation index at: https://docs.canton.network/llms.txt
Use this file to discover all available pages before exploring further.
Pruning
As a synchronizer orchestrates the execution of commands, the sequencers, mediators and participant persist records of the data/changes that they process:- For participants, the stored data represents the activated and deactivated contracts, and their associated transactions and events
- For sequencers, the stored data represents the messages they process and the changes in participants’ subscription status
- For mediators, the stored data represents the verdicts they send to participants, based on the confirmation responses that the participants sent to the mediator
- In addition, all nodes store both the current and the past topology data for the synchronizers they are connected to
- First, nodes have a limited amount of storage that they will eventually exhaust if the stored data keeps growing
- Second, regulation concerning privacy may require removing Personally Identifiable Information (PII) upon request. For example, to comply with the General Data Protection Regulation (GDPR) in Europe, Canton operators must declare reasonable retention periods and make sure that the data is deleted afterwards. Another example, in the context of health information, the Health Insurance Portability and Accountability Act (HIPAA) aims to protect sensitive patient data, deciding how long the data should be kept and when it should be deleted.
Pruning means that nodes can forget a prefix of the history, and instead store a state snapshot that summarizes the history.
History vs. State Snapshot
History represents the list of all changes up to a particular point in time, whereas state snapshot (or snapshot for brevity) represents the summary of all changes at a particular point in time. One analogy to think about history vs snapshot is imagining an operator that wants to bootstrap a node in a working system at time t. For example, the operator wants to add a fresh participant node and host some parties on it, or may want to restore an existing participant from backup. The snapshot represents all the data that the participant needs to be operational in the system at the time it joins, which includes but is not limited to the active contracts that have as stakeholders the parties hosted on the participant at time t. In contrast, history represents all events changing the stakeholder contracts, e.g., creations, archivals, reassignments, since the system’s inception and up to time t. The operator could simply bootstrap from the snapshot at time t. Alternatively, if that is not available for some reason, the operator could bootstrap from the snapshot at a prior time t-delta, and then replay the history between timestamps t-delta and t. The figure below illustrates the contrast between history and snapshot in an example concerning the active contracts.The participant retains some of its historical data in a long-term store, called the Participant Query Store (PQS)_. The PQS is a store separate from the Canton internal stores. The PQS stores a distilled version of history and its role is to answer queries about the past, for example for regulatory purposes.
Pruning a node at time t deletes the history stored on that node up to time t, but retains the node’s snapshot at time t. A node’s snapshot depends on the type of node: participant, synchronizer, mediator. Once a node has been pruned up to time t, it will not be able to serve events that precede t.
- The set of active contracts at time t that have stakeholders hosted on that participant. This set is fundamental for the participant node in order to participate correctly in Canton’s transaction processing, e.g., the participant safeguards double spending by rejecting such transactions.
- The in-flight reassignments at time t. A participant that is a reassigning participant for a contract must retain reassignment data while the reassignment is in-flight, i.e., while either the unassign or the assign commands did not complete. Otherwise, the participant would not be able to approve or reject reassignments correctly. For more details on reassignments, refer to the reassignments section. For now, participants do not prune their reassignment data, however, this is planned for the future.
- The participant’s private keys. For now, the participant requires all key material, not just the keys valid at time t. The participant uses these keys to authenticate the messages it sends to other nodes by signing these messages.
- All past topology snapshots up to and including t. The past topology snapshots are necessary because the participant may need to serve them in order to validate future requests, e.g., validating the target timestamp in an unassignment request. The topology snapshot at t is necessary because it informs the participant of which nodes are present (mediators, sequencers, participants), which restrictions each synchronizer imposes on participants (e.g. the maximum number of parties a participant may host), which parties are hosted on which participants, etc. Thus, all nodes participating in the Canton transaction protocol know which participants are involved in a transaction at time t, which keys they use, etc.
-
Command deduplication data, in some cases. Each participant accumulates the commands submitted through that participant, which it uses as deduplication information to not submit a transaction multiple times. The participant retains this deduplication information for a window of time ending at t_end. If the configured deduplication window ending at t_end is large enough to contain t, then the snapshot at t contains deduplication data for a window ending at t of duration
deduplication window - (t_end - t). Otherwise, the snapshot at t does not contain any deduplication data.
- The topology state at time t. The topology information enables the mediator to know which participants are involved in a transaction and should thus send a confirmation responses or rejecting the transaction.
- The deduplication information, in some cases (similar to the participant command deduplication data). The deduplication information contains the transactions that the mediator saw for a recent window of time, which is necessary so that the mediator can prevent multiple executions of the same transaction.
- The mediator’s private keys valid at time t, needed for authentication purposes.
- All past topology snapshots up to and including t. The sequencer needs past topology snapshots because participants may specify a topology timestamp on their submission requests that affects how the sequencer resolves group addresses. Moreover, a sequencer is supposed to serve as the ground truth for all topology transactions, because participants download their onboarding topology data from the sequencer, which needs to include all the past topology transactions, as we explained above. The sequencer requires the topology information at time t to learn about synchronizer members (participants, sequencers, mediators) at time t.
- The previous timestamp for every member registered on the synchronizer. The sequencer provides a global total-order multicast, where events are uniquely timestamped and members derive global ordering from these timestamps. By storing with each event the timestamp of the previous event for the same member, the sequencer enables each member to detect gaps in the stream of events for that member. The previous timestamps are essential for participant auditing, because they enable a participant to prove to an auditor that the participant revealed the complete sequence of messages it received from the sequencer.
- Data items pertaining to the ordering layer when using the BFT Orderer. The BFT Orderer organizes transactions in a sequence of ordered blocks, and it needs to retain these blocks in order to catch up and onboard other BFT nodes, and for retransmitting blocks to nodes that missed recent messages in order to get them unstuck. (Note: for ordering layers other than the BFT orderer, such as those based on external drivers, pruning is handled separately.)
- The sequencer’s private keys valid at time t, needed for authentication purposes.
Pruning Safeguards
Pruning needs to ensure that we do not delete data that is still needed for the node’s operation. There are three considerations when deciding what data to prune: snapshots for operational continuity, crash recovery data, and non-repudiation guarantees.Snapshots for operational continuity
In order to operate properly, each node needs its snapshot at time t, described above. Therefore: A participant:- Never deletes contracts that are active.
- Never deletes its most recently observed transaction in the deduplication window, which has a configurable length
ledger-api.max-deduplication-duration. This means that the minimal retention period for pruning is at least the configured maximum deduplication period. - Never deletes its in-flight reassignments. In fact, participants do not yet prune reassignment data, although this is planned for the future.
- Never deletes data in the deduplication window, which has a configurable length.
- Never deletes data, e.g., member’s previous timestamp, unless the member acknowledges that it no longer needs it. However, as a safeguard to unresponsive members, a sequencer operator can specify a data retention window and prune without acknowledgements from members. If members do need older data, they will not be able to recover it. Please refer to the global synchronizer documentation and the how-to guide on sequencer pruning for details.
- Never deletes ordering layer data (when using the BFT Orderer) in the retention period, specified in time. For retransmissions, when using the BFT Orderer, sequencers need to keep ordering layer data for a few epochs. For helping other sequencers catch up, they need to keep the data for as long as at least one node in the network still needs help catching up. However, as a safeguard to unresponsive nodes, a sequencer operator prunes once the data retention window elapses, by default 30 days. If nodes do need older data, they need to leave the network and join fresh. Moreover, if the entire system is down for a period of time longer than the pruning retention period, we do not want that the sequencers prune everything when the come back up. To protect against such cases, the operator can also specify a minimum amount of blocks to keep.
- Nodes do not yet implement pruning of topology state. Although topology state does not grow too quickly for system where the topology changes infrequently, it is a limitation of the system that Canton does not prune topology state. Pruning of topology is planned as future work.
Crash recovery data
Nodes do not delete data relevant for crash recovery. The precise data depends on the participant/mediator/sequencer processing pipelines.Non-repudiation guarantees
A participant never prunes history older than time t unless it has an undeniable proof of agreement with other participants on their set of shared active contracts at time t. The participant can use such a proof in legal disputes with other participants. To this extent, participants exchange ACS (active contract set) commitments, which are undeniable summaries of their shared active contracts at specific points in time. ACS commitments are described in detail in the non-repudiation section below. A participant prunes its history at time t only if it has received a matching pairwise commitment message from every counter-participant at time t. The participant also prunes all commitments for timestamps before the pruning timestamp.Manual pruning
There are also data items that the operator must manage manually instead of via a pruning schedule.- The participant node operator manually manages the pruning of its DAR (Daml Archive Repository) and package store, which contain the contracts that the participant vetted. These stores can become sizeable in a CI/CD model with long-running participants. The operator needs to decide when it no longer needs this data, and manually prune these stores via console commands.
- Nodes operators manually manage the pruning of cryptographic material, and in particular private keys. Operators can manually delete keys via console commands.
- Node operators manually manage the pruning configuration data such as sequencer connections, domain aliases, static domain parameters, etc. However, configuration data should normally be small.
Non-repudiation
A fork represents a situation where two or more participant nodes in the network diverge in their view of their shared state. Forks can happen due to misconfiguration, e.g., when changing contract stores via the repair service, bugs, or malicious behavior.
Commitments enable participant nodes to verify agreement on their shared state, so that they can prune history.
Two participant nodes that have no shared active contracts do not exchange commitments.
Commitment exchange
Pairs of participant nodes connected to a synchronizer exchange commitments for shared active (previously activated, not yet deactivated) contracts. Two or more participant nodes share a contract if each participant node hosts some stakeholder of the contract (not necessarily the same stakeholder) on the synchronizer where the contract is active. Periodically, each participant node computes and sends a commitment to each counter-participant node that it knows of, according to the topology at the timestamp of the commitment. Participant nodes exchange commitments over the synchronizer’s sequencer. If a participant node’s locally computed commitment matches the received commitment, then the participant node knows it agrees with the counter-participant. Both participant nodes store the received signed commitment, which they can later use as a cryptographic proof for non-repudiation. How often participant nodes exchange commitments is a synchronizer parameter and affects the trade-off between performance and guarantees. A smaller period enables timely fork detection, at the cost of more computational overhead on the participant node and more bandwidth overhead on the synchronizer. The synchronizer operator can adjust the period length in the dynamic synchronizer parameters as a duration (referred to as reconciliation interval).The reconciliation interval can be adjusted on a running synchronizer. The period length change takes effect immediately.
The format of a commitment
For a meaningful agreement, participant nodes should generate commitments for their shared state taken at the same point in time. Each participant node’s ACS state can only evolve with events the participant node receives from the sequencer. Thus, it makes sense that we also use the sequencer time for commitment periods. Participant nodes receive several event types from the sequencer, such as topology changes, ACS changes, etc., which are timestamped with the sequencer time. All these events advance the participant node’s knowledge of the sequencer time, and trigger commitment generation if the time crosses a period boundary. Participant nodes capture in a commitment those ACS contract activations with a timestamp lower or equal to the period end of the commitment, and that are not deactivated before the period end.Sequencer time advancements that a participant node observers are called ticks, and are said to tick the ACS commitment processor.
Each commitment contains the cryptographic hash summarizing the ACS state, as well as metadata about the period and sender.
Hash generation
Participant nodes need to efficiently summarize the ACS state into commitments, so that commitment generation does not become a bottleneck. An inefficient but simple approach would be to send the whole shared ACS as part of the commitment, rather than a hash. This would be computationally cheap, but prohibitively expensive in terms of network bandwidth, as the ACS can be large. A more bandwidth-friendly but computationally-inefficient approach would be for each participant node to traverse its ACS at each period end and, per counter-participant, summarize the shared active contracts in a hash. This approach would overload the participant node at each epoch end. Instead, each participant node summarizes ACS changes as they happen during the interval, maintains a running hash of the ACS per stakeholder group. The grouping per stakeholders ensures that each ACS change is processed only once. Specifically, per ACS change, the hash adds or removes the contract id, which authenticates the contents, and the contract’s reassignment counter, which identifies the exact (de)activation of this contract: either creation, archival, or the reassignment instance for a contract that was reassigned between synchronizers. The hash function is homomorphic in the sense that contract deactivations cancel out activations, thus the running hash can be updated incrementally. The final commitment computation at the end of a period is as follows. For each counter-participant, the participant node collects all non-empty running hashes of stakeholder groups it shares with the counter-participant, and adds them into a homomorphic hash. The commitment essentially contains a two-level homomorphic hashing: first the hashing of ACS changes into stakeholder group hashes, and then the hashing of stakeholder group hashes into the commitment hash. The reason for this two-level hashing is that it enables optimizations for updating commitments and investigating mismatches, which we describe later. Afterwards, the participant node significantly reduces the size of the homomorphic hash by applying a cryptographic hash for compression purposes. Commitments are cryptographic hashes that are signed by the participant node, with the following properties:- Collision resistance: Because commitments are hashes, it is unlikely that two participant nodes with distinct ACS states will calculate the same commitment.
- Preimage resistance: Given a commitment, it is difficult for anyone (in particular a counter-participant) to create an ACS state that fits the provided commitment.
- Second preimage resistance: Once a commitment has been transmitted, and implicitly after the participant node pruned its state, a counter-participant cannot later claim a different ACS state that corresponds to the same commitment. Collision resistance implies second preimage resistance.
- Efficient verification: Given an ACS state, anyone can easily check whether the state matches a specific commitment. In particular, the originator of the commitment can determine if a certain ACS state is valid in relation to a commitment issued by the originator itself.
- Non-repudiation: Because commitments are signed, anyone may determine who the originator is, and the originator cannot deny issuing that commitment.
Consequences of a fork
The Canton protocol ensures agreement between honest participant node nodes, i.e., a mismatch should happen only due to misconfiguration or bugs. If the commitments between two participant nodes do not match, then the participant nodes’ operators should investigate the mismatch, verify, and potentially amend the states of the participant nodes. If a period has resulted in a mismatch between two participant nodes, then we have a fork between the participant nodes’ states. The fork might automatically resolve itself, otherwise the participant node operators need to intervene. This intervention would require all involved participant node operators to look at their shared active contracts and find the cause of the mismatch. We provide tooling for mismatch inspection, which allows participant nodes to identify which contracts cause the mismatch. However, the tooling does not automatically resolve the mismatch, as the participant node operators need to decide, perhaps through out-of-band communication, what the correct end state is, and then apply the changes. As long as a mismatch is ongoing then the affected participant nodes would not be able to prune data prior to the initial mismatch. Once a period is matched with all counter-participant nodes then all previous periods are implicitly safe to prune: Even though initially the states showed a mismatch, a fork no longer exists.Optimizations
Computing a large number of hashes for commitments can be performance-heavy, and exchanging commitments consumes network bandwidth. Therefore, we have implemented several optimizations in order to reduce the amount of computation and bandwidth required. First, participant nodes that do not share active contracts neither compute, nor send commitments or expect to receive commitments. If one of the participant nodes thinks otherwise, however, the participant node sends a commitments, which causes the other participant node to output a warningACS_MISMATCH_NO_SHARED_CONTRACTS: Received a commitment where we have no shared contract with the sender. The participant node then replies with an empty commitment, which triggers mismatch inspection based on the non-empty state of the counter-participant. This optimization produces significant performance improvements for a participant node that has many counter-participant nodes without shared active contracts. Moreover, if a participant node believes its shared state with a counter-participant to be empty, it can prune without establishing that the counter-participant agrees that the state is empty.
Second, regarding bandwidth, participant nodes only exchange commitments periodically, which bounds the amount of bandwidth consumed regardless of the contract activity within a period. Also, periods are aggregated when there is no activity. Instead of constantly sending the same commitment, each participant node simply aggregates periods and send a commitment once it notices activity. Still, aggregation does not happen if the participants observe any possibly unrelated synchronizer activity, because activity triggers ticks, and in that case the participant nodes do exchange the same commitment hashes repeatedly, albeit with different timestamps. Moreover, there is a lower bound for exchanging commitments even when there is no synchronizer activity, by default every 24 hours.
Additionally, to avoid a spike in network activity at the end of every period, each participant node sends commitments with jittered delay. The delay is a random amount uniformly distributed between 0 and 2/3 of the period length, therefore the sequencer is not getting flooded with commitments.
Commitments are sent best-effort, without a guaranteed delivery, which saves network resources. This means that a participant node might miss commitments from a counter-participant. However, this is not a problem because each commitment is stand-alone with respect to semantics, thus missing a commitment has no effect on the usefulness of future incoming commitments. A participant nodes might miss some commitments for some timestamps in the past, but as long as it agrees with its counter-participants on the present state, it can prune history. In other words, even though the participant node may not know whether a prior fork existed, it can still prune history up to timestamps where certainly no forks exist.
Third, regarding commitment computation, the two-level homomorphic hash structure allows for efficient commitment updates. participant nodes perform incremental updates to per-stakeholder running hashes, which are grouped per participant node at the end of each period. This means that participant nodes do not have to recompute commitments constantly and the complexity increases linearly with the amount of activations and deactivations.
Moreover, this structure allows for efficient updates when few stakeholder groups see contract changes, whereas many remain unchanged. In this case, grouping all stakeholder hashes and hashing them again per participant node is inefficient, because it is proportional to the number of stakeholder groups. Instead, we cache the stakeholder hashes, and update in the commitment only those that have changed. Thanks to the homomorphic hash properties, participant nodes can subtract cached stakeholder caches from the commitment and add the new stakeholder hashes, which is proportional to the number of stakeholder groups that saw contract updates.
Also, the two-level homomorphic hash structure allows for efficient mismatch inspection. A participant node inspecting a mismatch does not require the entire set of shared contracts ids: The participant node can perform matching per level, keeps blinded those stakeholder commitments that match, and only requires the set of shared contracts ids of the stakeholder hashes that do not match.
Fourth, in periods with high load, a participant node that lags behind with transaction processing reduces the overhead caused by commitments. A participant node lags behind when its observed sequencer time is behind some of its counter-participants; the participant can infer the sequencer time of a counter-participant from the periods in the received commitments. A lagging participant node automatically triggers a so-called catch-up mode, where it skips computing and sending commitments for some periods. The synchronizer operator can control the catch-up behavior through a synchronizer configuration. The catch-up mode brings a participant node up to date with transaction throughput, while still detecting forks.
Fifth, for multi-hosted parties, a participant node only requires threshold-many counter-participants to send matching commitments, instead of all counter-participants. Such an optimization is natural, given that we only need threshold-many counter-participants to approve a transaction, and therefore deem a contract active. This allows for pruning even if some commitments are outstanding, as long as each multi-hosted party is hosted on at least threshold-many counter-participants that send matching commitments. This is especially relevant for decentralized parties, some of whose participant nodes may be malicious / faulty.