Ledger

Note

See the Ledger and Snapshots section to find out more about how ledger files are generated by CCF nodes and how they should be managed by operators.

The entire service and application state is contained in the ledger (including both governance and application transactions). The ledger contains regular signature transactions (public:ccf.internal.signatures map) which sign the root of the Merkle Tree at the time the signature transaction is emitted.

Tip

Use the read_ledger.py utility available as part of the Python Library to read the public content of a CCF ledger.

Ledger Format

The ledger is split into multiple files (or chunks). Each ledger file starts with an 8-byte offset pointing to the end of file where a vector of positions is stored for fast fetching of transactions (only set when a file is complete, internal use only).

The serialised transactions follow the 8-byte offset, as described in Transaction Format.

Note

A complete ledger file always ends on a signature transaction.

Transaction Format

The following table describes the structure of a serialised transaction as it is stored in the ledger.

Note

These serialised transactions in CCF currently describes only the final write set rather than the internal execution path of a transaction; writes are reordered, only the final writes are present, and reads are omitted (read count will be 0).

Field Type

Description

Header

Transaction Header

- Version (1 byte)
- Flags (1 byte)
- Entry size (6 bytes)

AES GCM Header

IV and tag fields required to decrypt and verify integrity

uint64_t

Length of serialised public domain

Public
Domain

kv::EntryType

Snapshot, or a WriteSet variant

kv::Version

Transaction version

crypto::Sha256Hash

User-defined claims digest, when entry type is WriteSetWith.*Claims

crypto::Sha256Hash

Commit evidence digest, when entry type is WriteSetWithCommitEvidence.*

kv::Version

Unused, reserved for compatibility

Repeating [0..n]

With n the number of maps in the transaction

std::string

Name of the serialised kv::Map

kv::Version

Read version

uint64_t

Read count

Repeating [0..read count]

uint64_t
K
Ver
Key length
Key
Version

uint64_t

Write count

Repeating [0..write count]

uint64_t
K
uint64_t
V
Key length
Key
Value length
Value
uint64_t

Remove count

Repeating [0..remove count]

uint64_t
K
Key length
Key
Private
Domain

Optional | Encrypted serialised private domain blob.

Transaction Encryption

Each entry in the ledger corresponds to a transaction committed by the primary node.

When a transaction is committed, each Store::Map containing writes is serialised in different security domains (i.e. public or private), based on the name of the Map when it was created (default is private). A public Store::Map (i.e. one whose name starts with “public:”) is serialised and stored in the ledger as plaintext while a private Store::Map is serialised and encrypted before being stored.

Ledger entries are integrity-protected and encrypted using a symmetric key shared by all trusted nodes (see Cryptography). This key is kept secure inside each enclave. See Rekeying Ledger for details on how members can rotate the ledger encryption key.

Note that even if a transaction only writes to a private Store::Map, unencrypted information such as the sequence number is always present in the serialised entry.