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:
Method
Description
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.
Method
Description
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.
Method
Description
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.
Method
Description
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.
Method
Description
PutDelegation(addr, rep)
Set or update a delegation
DeleteDelegation(addr)
Remove a delegation
DelegationIterator
Method
Description
IterateDelegations(fn)
Iterate over all delegations with a callback
FrontierLister
Method
Description
AllFrontiers()
Return all account frontiers as a map
LeaseStore
Manages compute lease records.
Method
Description
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.
Method
Description
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:
Field
Type
Description
Block
Block
The block to store
Account
string
Account address
Chain
[]string
Updated chain hash list
FrontierHash
string
New frontier hash
AddPending
*PendingSend
Pending send to create (for send blocks)
DeletePendingID
string
Pending send to remove (for receive blocks)
PutLease
*Lease
Lease to create or update
SettleLeaseHash
string
Lease to mark as settled
[!TIP] Why atomic writes matter Without
CommitBlock, a crash between writing the block and updating the frontier could leave the store in an inconsistent state. The atomic commit ensures all-or-nothing semantics.
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
[!NOTE] Note
MemStoreimplements all optional interfaces (ConflictStore, VoteStore, QuorumStore, DelegationStore, DelegationIterator, FrontierLister, LeaseStore, AtomicBlockStore).
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:
Prefix
Content
0x01
Block
0x02
Account chain (hash list)
0x03
Pending send
0x04
Pending by destination
0x05
Frontier
0x06
Delegation
0x07
Weight
0x08
Conflict
0x09
Staged block
0x0a
Vote
0x0b
Block status
0x0c
Confirmation height
0x0d
Lease
0x0e
State chain block
[!EXAMPLE] Key construction A block with hash
abc123...is stored at key0x01+abc123...(prefix byte concatenated with the hash string). Account chains use0x02+ account address, and so on.
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