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 |
#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:
| 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 |
#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