Storage Layer

Storage Layer

The XE node uses a pluggable storage layer built around a core Store interface with optional capability interfaces composed on top. This design lets tests use a fast in-memory implementation while production nodes use BadgerDB for persistence.

#Core Store interface

Every storage backend must implement the base Store interface:

MethodDescription
GetBlock(hash)Retrieve a block by its hash
PutBlock(block)Store a block by its hash
GetAccountChain(addr)Get the ordered list of block hashes for an account
PutAccountChain(addr, hashes)Store the block hash list for an account
GetPendingSend(hash)Retrieve a pending send by its block hash
PutPendingSend(send)Store a pending send
DeletePendingSend(hash)Remove a pending send after it has been received
GetPendingByDest(addr)Get all pending sends addressed to an account
GetAllPending()List all pending sends across all accounts
PutFrontier(addr, hash)Set the frontier (latest block hash) for an account
GetFrontier(addr)Get the frontier hash for an account
IsEmpty()Check whether the store contains any data
Close()Shut down the store and release resources

#Optional interfaces

Additional capabilities are exposed through separate interfaces. The node checks at runtime whether the store implements each one (Go interface assertion).

#ConflictStore

Manages conflict detection state during consensus.

MethodDescription
SaveConflict(conflict)Persist a detected conflict
GetConflict(id)Retrieve a conflict by ID
DeleteConflict(id)Remove a resolved conflict
GetConflictsForAccount(addr)List conflicts involving an account
GetAllConflicts()List all active conflicts
SaveStagedBlock(block)Stage a block pending conflict resolution
GetStagedBlock(hash)Retrieve a staged block
DeleteStagedBlock(hash)Remove a staged block

#VoteStore

Stores representative votes during conflict resolution.

MethodDescription
PutVote(vote)Store a vote
GetVotesByConflict(id)Get all votes for a conflict
HasVoted(repAddr, conflictID)Check if a representative already voted

#QuorumStore

Tracks quorum outcomes and confirmation heights.

MethodDescription
SetBlockStatus(hash, status)Mark a block as confirmed or rejected
GetBlockStatus(hash)Get the confirmation status of a block
SetConfirmationHeight(addr, height)Set the confirmed chain height for an account
GetConfirmationHeight(addr)Get the confirmed chain height
DeleteVotesForConflict(id)Clean up votes after quorum is reached

#DelegationStore

Manages voting weight delegation.

MethodDescription
PutDelegation(addr, rep)Set or update a delegation
DeleteDelegation(addr)Remove a delegation

#DelegationIterator

MethodDescription
IterateDelegations(fn)Iterate over all delegations with a callback

#FrontierLister

MethodDescription
AllFrontiers()Return all account frontiers as a map

#LeaseStore

Manages compute lease records.

MethodDescription
PutLease(lease)Store a lease
GetLease(hash)Retrieve a lease by hash
GetLeasesByProvider(addr)List leases for a provider
GetAllLeases()List all leases

#AtomicBlockStore

Provides atomic multi-part writes for block processing.

MethodDescription
CommitBlock(BlockCommit)Atomically write a block and all side effects

#BlockCommit

The BlockCommit struct bundles all state changes that must be applied atomically when processing a block:

FieldTypeDescription
BlockBlockThe block to store
AccountstringAccount address
Chain[]stringUpdated chain hash list
FrontierHashstringNew frontier hash
AddPending*PendingSendPending send to create (for send blocks)
DeletePendingIDstringPending send to remove (for receive blocks)
PutLease*LeaseLease to create or update
SettleLeaseHashstringLease to mark as settled

#Implementations

#MemStore

In-memory implementation using Go maps protected by sync.RWMutex. All reads return deep copies to prevent mutation of internal state.

  • Use case: Tests and short-lived nodes
  • Durability: None -- data is lost on process exit
  • Thread safety: Full read/write mutex protection
  • Copy semantics: Deep copies on both read and write

#BadgerStore

Persistent implementation backed by BadgerDB. Blocks and metadata are serialized as JSON. A background goroutine runs garbage collection every 5 minutes.

  • Use case: Production nodes
  • Durability: Full disk persistence with WAL
  • Serialization: JSON
  • GC: Background value log GC every 5 minutes
  • Shutdown: Close() stops GC and closes the database

#Key prefix scheme

All keys in BadgerDB are prefixed with a single byte to partition the keyspace:

PrefixContent
0x01Block
0x02Account chain (hash list)
0x03Pending send
0x04Pending by destination
0x05Frontier
0x06Delegation
0x07Weight
0x08Conflict
0x09Staged block
0x0aVote
0x0bBlock status
0x0cConfirmation height
0x0dLease
0x0eState chain block

#See also

  • Binary Encoding -- how blocks are serialized to bytes
  • Consensus -- conflict detection and voting that use ConflictStore, VoteStore, QuorumStore
  • Compute Leasing -- lease lifecycle that uses LeaseStore
  • State Chain -- DAO state stored under prefix 0x0e