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
|
Snapshot, or a WriteSet variant |
|||
Transaction version |
||||
|
User-defined claims digest, when entry type is WriteSetWith.*Claims |
|||
|
Commit evidence digest, when entry type is WriteSetWithCommitEvidence.* |
|||
Unused, reserved for compatibility |
||||
Repeating [0..n] |
With |
|||
std::string |
Name of the serialised |
|||
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.