Documentation
¶
Overview ¶
Package ledger is an authenticated key-value store: a versioned sparse Merkle tree (16-ary storage, binary hashing) over a pluggable KV backend.
One 32-byte root attests the entire logical data state. The root is encryption-independent: it commits to keyed plaintext hashes (HMAC-SHA-256 under a commitment key ck), never to the bytes at rest. At-rest confidentiality is the volume's job by default (a LUKS data partition inside a confidential VM, typically); an optional per-machine storage key (WithStorageKey) adds AES-256-GCM value encryption as a second layer. Replicas sharing ck compare state as (version, root) whatever each one does at rest.
This package is the Go port of the enclave-os-mini `enclave-os-merkle` crate. The commitment scheme, node hashing, record encodings and proof format are byte-identical: a Go store and a Rust store sharing ck produce the same root for the same logical data, and proofs verify across implementations.
Reads fail closed: any record that does not verify against the in-memory root (node hashes, GCM tags, value commitments) is an error, never data.
Index ¶
- Constants
- Variables
- type Backend
- type BatchOp
- type Change
- type Error
- type Fork
- type Hash
- type KV
- type Kind
- type Leaf
- type MemBackend
- func (b *MemBackend) Get(key []byte) ([]byte, bool, error)
- func (b *MemBackend) Keys() [][]byte
- func (b *MemBackend) Len() int
- func (b *MemBackend) Reads() int
- func (b *MemBackend) Remove(key []byte) bool
- func (b *MemBackend) ResetReads()
- func (b *MemBackend) Scan(start, end []byte, limit uint32) ([]KV, error)
- func (b *MemBackend) Tamper(key []byte) bool
- func (b *MemBackend) WriteBatch(ops []BatchOp) error
- type Op
- type Option
- type Proof
- type ProofLeaf
- type PruneStats
- type SealedFork
- type Store
- func Create(backend Backend, commitmentKey [KeySize]byte, opts ...Option) (*Store, error)
- func Open(backend Backend, commitmentKey [KeySize]byte, root Hash, version uint64, ...) (*Store, error)
- func OpenLatest(backend Backend, commitmentKey [KeySize]byte, opts ...Option) (*Store, error)
- func OpenOrCreate(backend Backend, commitmentKey [KeySize]byte, opts ...Option) (*Store, error)
- func (s *Store) Backend() Backend
- func (s *Store) ChangesAt(version uint64) ([]Change, error)
- func (s *Store) Get(key []byte) (value []byte, ok bool, err error)
- func (s *Store) GetAt(version uint64, key []byte) (value []byte, ok bool, err error)
- func (s *Store) HistoryEnabled() bool
- func (s *Store) HistoryHead() (Hash, uint64, error)
- func (s *Store) HistoryHeadAt(version uint64) (Hash, error)
- func (s *Store) PinVersion(version *uint64)
- func (s *Store) PreviewBatch(ops []Op) (Hash, uint64, error)
- func (s *Store) Prove(key []byte) (*Proof, error)
- func (s *Store) ProveAt(version uint64, key []byte) (*Proof, error)
- func (s *Store) Prune(beforeVersion uint64) (PruneStats, error)
- func (s *Store) PutBatch(ops []Op) (Hash, uint64, error)
- func (s *Store) RestoreLeaves(leaves []Leaf) (Hash, uint64, error)
- func (s *Store) RetainRecent(window uint64) (PruneStats, error)
- func (s *Store) Root() (Hash, uint64)
- func (s *Store) RootAt(version uint64) (Hash, error)
- func (s *Store) SetCacheCapacity(capacity int)
- func (s *Store) SnapshotLeaves(version uint64, startAfter *Hash, max int) (leaves []Leaf, done bool, err error)
- func (s *Store) StampVersion(version uint64) error
- func (s *Store) VerifyAbsent(root *Hash, key []byte, proof *Proof) (bool, error)
- func (s *Store) VerifyHistory(fromVersion uint64, fromHead Hash) error
- func (s *Store) VerifyValue(root *Hash, key, value []byte, proof *Proof) (bool, error)
- type Verified
Constants ¶
const HashSize = 32
HashSize is the size of every hash in the tree.
const KeySize = 32
KeySize is the size of both the commitment key and the storage key.
Variables ¶
var HistoryKey = []byte("\x00immutable-ledger:history-head")
HistoryKey is the reserved logical key of the history-chain head leaf. It lives in the reserved system namespace: user batches cannot write it; reads return the current head.
Functions ¶
This section is empty.
Types ¶
type Backend ¶
type Backend interface {
// Get returns the record at key, or (nil, false, nil) when absent.
Get(key []byte) (value []byte, ok bool, err error)
// WriteBatch applies all ops atomically: either every op lands or
// none does. Atomicity is what makes commits crash-safe.
WriteBatch(ops []BatchOp) error
// Scan returns records in [start, end) in ascending key order, at
// most limit entries (0 = unlimited). An empty end is unbounded.
Scan(start, end []byte, limit uint32) ([]KV, error)
}
Backend is the storage surface the tree requires.
type BatchOp ¶
BatchOp is one operation of an atomic write batch. A nil Value with Delete=false stores an empty record; set Delete for removals.
type Change ¶
Change is one leaf-level difference between a version and its predecessor. Path is the tree position (the keyed hash of the logical key — logical keys are not recoverable); Value is the plaintext for puts and nil for deletes.
type Error ¶
type Error struct {
Kind Kind
Msg string
// Err carries the underlying backend error for KindBackend.
Err error
}
Error is the ledger error type. Use errors.As / Error.Kind to classify.
type Fork ¶
type Fork struct {
// contains filtered or unexported fields
}
Fork is a pending, uncommitted transaction over a Store.
func (*Fork) Get ¶
Get reads through the overlay, then the underlying store. ok reports whether the key is present.
func (*Fork) PendingOps ¶
PendingOps returns the number of buffered operations.
func (*Fork) RootBefore ¶
RootBefore returns the state this fork is based on.
func (*Fork) Seal ¶
func (f *Fork) Seal() (*SealedFork, error)
Seal computes (rootAfter, versionAfter) without committing, and hands back the deterministic write-set. Fails closed if the store moved underneath the fork.
type Hash ¶
Hash is a 32-byte SHA-256 output.
func HistoryLink ¶
HistoryLink computes the chain head written by the commit that produced version, from the previous head and previous root. Pure function; head_0 is all zeros.
func Placeholder ¶
func Placeholder() Hash
Placeholder is the hash standing in for any empty subtree, at every height. It is also the root of an empty store.
type Kind ¶
type Kind int
Kind classifies a ledger error. Every integrity failure is Corrupted and fails closed: the store never silently returns data that did not verify against the in-memory root.
const ( // KindBackend: the storage backend returned an error. KindBackend Kind = iota + 1 // KindCorrupted: a record failed hash verification, decoding or // decryption. Either storage was tampered with or it is damaged. KindCorrupted // KindMissing: a record that must exist is missing (e.g. a node // referenced by its parent, or a root record for a requested version). KindMissing // KindInvalid: invalid input from the caller. KindInvalid )
type MemBackend ¶
type MemBackend struct {
// contains filtered or unexported fields
}
MemBackend is an in-memory Backend for tests: it counts reads (I/O-complexity assertions) and can tamper with stored records (fail-closed assertions).
func NewMemBackend ¶
func NewMemBackend() *MemBackend
NewMemBackend returns an empty in-memory backend.
func (*MemBackend) Get ¶
func (b *MemBackend) Get(key []byte) ([]byte, bool, error)
Get implements Backend.
func (*MemBackend) Keys ¶
func (b *MemBackend) Keys() [][]byte
Keys returns all record keys (for tamper sweeps).
func (*MemBackend) Reads ¶
func (b *MemBackend) Reads() int
Reads returns the number of point reads served since construction or the last ResetReads.
func (*MemBackend) Remove ¶
func (b *MemBackend) Remove(key []byte) bool
Remove deletes a record outright (storage "loses" data).
func (*MemBackend) ResetReads ¶
func (b *MemBackend) ResetReads()
ResetReads zeroes the read counter.
func (*MemBackend) Scan ¶
func (b *MemBackend) Scan(start, end []byte, limit uint32) ([]KV, error)
Scan implements Backend.
func (*MemBackend) Tamper ¶
func (b *MemBackend) Tamper(key []byte) bool
Tamper flips one bit of the record at key. Returns false if absent.
func (*MemBackend) WriteBatch ¶
func (b *MemBackend) WriteBatch(ops []BatchOp) error
WriteBatch implements Backend.
type Op ¶
Op is one operation of a PutBatch: a put (Value, possibly empty) or a delete. Use Put and Del to construct.
type Option ¶
type Option func(*storeOptions)
Option configures a store at construction.
func WithHistoryChain ¶
func WithHistoryChain() Option
WithHistoryChain makes every commit extend a hash chain over the root lineage, stored in a reserved leaf so each root commits to the entire sequence of roots before it (see audit.go for the audit workflow). Choose at Create — the setting is persisted and the chain changes what roots a given history produces, so it cannot be toggled later. Passing it to Open/OpenLatest asserts the store was created with the chain (open fails otherwise).
func WithStorageKey ¶
WithStorageKey enables at-rest encryption of value records and the checkpoint under a per-machine storage key (AES-256-GCM), as a second layer on top of whatever encrypts the volume. Without it, value bytes are stored plaintext in the backend and confidentiality at rest is the volume's job. The root is a pure function of the logical data and the commitment key in BOTH modes, so replicas and proofs are unaffected by this choice — but the two modes write different value-record bytes, so one store's backend must always be opened in the mode that wrote it.
type Proof ¶
type Proof struct {
// Leaf is the terminal leaf found by the descent, if any.
Leaf *ProofLeaf
// Siblings holds sibling hashes, bottom-up (deepest first).
Siblings []Hash
}
Proof proves presence or absence of one path at one root.
func DecodeProof ¶
DecodeProof is the strict decoder of Encode's format.
type PruneStats ¶
type PruneStats struct {
// StaleEntries is the count of stale-index entries processed (and removed).
StaleEntries int
// RecordsDeleted is the count of node + value records deleted.
RecordsDeleted int
// RootRecordsDeleted is the count of root records deleted.
RootRecordsDeleted int
}
PruneStats reports what a Prune call removed.
type SealedFork ¶
type SealedFork struct {
RootBefore Hash
VersionBefore uint64
RootAfter Hash
VersionAfter uint64
// Ops is the deterministic write-set (key-ordered).
Ops []Op
}
SealedFork is the state transition a transaction proposes.
func (*SealedFork) IsNoop ¶
func (sf *SealedFork) IsNoop() bool
IsNoop reports whether this transaction is a no-op (empty or ineffective write-set).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the authenticated KV store. Single writer: it is not safe for concurrent use; wrap it in a mutex at the application layer.
func Open ¶
func Open(backend Backend, commitmentKey [KeySize]byte, root Hash, version uint64, opts ...Option) (*Store, error)
Open opens an existing store at a trusted (root, version) checkpoint (e.g. one anchored externally). It verifies the backend actually holds that root before returning; fails closed otherwise.
func OpenLatest ¶
OpenLatest opens at the checkpoint record the store itself maintains, written atomically inside every commit batch and authenticated: AES-256-GCM under the storage key when one is configured, otherwise an HMAC under a key derived from the commitment key. Storage cannot forge it — at worst it can replay an old checkpoint together with a matching old store, the documented restart-replay residual. Prefer anchoring Root() externally when an extra anchor is available.
func OpenOrCreate ¶
OpenOrCreate opens an existing store, or creates a fresh one if no checkpoint exists yet.
func (*Store) ChangesAt ¶
ChangesAt extracts what the commit producing version changed, by structural diff against the predecessor: subtrees with equal hashes are skipped, so the cost is proportional to the change, not the store. The reserved history leaf is omitted (verify it with VerifyHistory instead). Works on any store, chained or not, for any unpruned version.
func (*Store) Get ¶
Get returns the value for key at the current version. ok reports whether the key is present (an empty value is a value).
func (*Store) GetAt ¶
GetAt returns the value for key at a historical version.
Content is authenticated against the stored root record for that version; the version→root binding for history is backend-held, so history is strongest for roots the caller pinned externally.
func (*Store) HistoryEnabled ¶
HistoryEnabled reports whether this store maintains the chain.
func (*Store) HistoryHead ¶
HistoryHead returns the current chain head and the version it covers (zeros before the first chained commit). Anchor all three of (root, version, head) at each audit.
func (*Store) HistoryHeadAt ¶
HistoryHeadAt returns the chain head recorded at a historical version (the same freshness caveat as GetAt; version 0 is the all-zero genesis head).
func (*Store) PinVersion ¶
PinVersion pins a version against pruning (a snapshot is being served from it). Pass nil to release the pin.
func (*Store) PreviewBatch ¶
PreviewBatch computes the (root, version) this batch WOULD produce, without committing anything. The tree math is pure, so a later PutBatch with the same ops from the same state produces exactly this result. This is what a transaction Fork uses to seal rootAfter.
func (*Store) ProveAt ¶
ProveAt proves presence or absence of key at a historical version (same freshness caveat as GetAt).
func (*Store) Prune ¶
func (s *Store) Prune(beforeVersion uint64) (PruneStats, error)
Prune deletes storage needed only by versions strictly before beforeVersion: stale records that died at or before it, and root records below it. Afterwards GetAt/ProveAt keep working for every version >= beforeVersion and fail with a KindMissing error below it. The live tree is never touched — both node and value records are versioned and never rewritten, so stale targets are deleted blindly.
Costs are proportional to accumulated garbage, not store size. Idempotent; safe to re-run after a partial failure.
func (*Store) PutBatch ¶
PutBatch applies a batch of operations as one commit. Later ops win over earlier ops on the same key. Returns the new (root, version).
If the batch changes nothing (deletes of absent keys, overwrites with identical values), no commit happens and the current (root, version) is returned unchanged.
func (*Store) RestoreLeaves ¶
RestoreLeaves inserts plaintext values addressed by PATH (not logical key) — the snapshot-restore path. One atomic commit; returns the new (root, version).
func (*Store) RetainRecent ¶
func (s *Store) RetainRecent(window uint64) (PruneStats, error)
RetainRecent prunes so that (at least) the last window versions stay readable: Prune(version - window), clamped at zero.
func (*Store) Root ¶
Root returns the current (root, version). Anchor this pair externally after every commit when an anchor is available.
func (*Store) RootAt ¶
RootAt returns the root hash recorded for a historical version (same freshness caveat as GetAt).
func (*Store) SetCacheCapacity ¶
SetCacheCapacity resizes the node cache (0 disables). Clears current contents.
func (*Store) SnapshotLeaves ¶
func (s *Store) SnapshotLeaves(version uint64, startAfter *Hash, max int) (leaves []Leaf, done bool, err error)
SnapshotLeaves collects up to max leaves of the tree at version, in path order, starting strictly after startAfter (nil = from the start). Each value is read, decrypted and commitment-verified (fail-closed). done is false when more leaves remain.
Chunked iteration re-descends only the resume path (subtrees entirely at or below startAfter are skipped by nibble comparison), so a full scan costs O(n) total.
func (*Store) StampVersion ¶
StampVersion is the snapshot-restore epilogue: stamp the store at version (>= the current build version), writing the root record and checkpoint for it, and duplicating the root NODE record at the stamped version so a later Open(root, version) resolves. Interior node records keep their (smaller) build versions, which child references tolerate (each carries its own version).
func (*Store) VerifyAbsent ¶
VerifyAbsent checks a proof claiming key is absent at root.
func (*Store) VerifyHistory ¶
VerifyHistory confirms that the recorded root sequence from (fromVersion, fromHead) to the live state is the unique lineage the current root commits to: it recomputes the chain over the stored root records and compares the result with the live, root-bound head. fromVersion 0 with a zero fromHead verifies from genesis; otherwise pass a previously anchored (version, head) pair. Fails if any root record in the range was pruned — audit before pruning.