Documentation
¶
Overview ¶
Package state manages persistent state for the S-Chain — the Lux storage VM.
M0 persists exactly two things over the chain's database namespace:
- MANIFESTS — the (bucket, object) -> {fileIds, size, etag} mapping that records which content blobs make up an object.
- LAST-BLOCK pointer — the last-accepted block id + height, the recovery anchor.
Every accessor reads/writes through the supplied database.Database, which the VM hands in as a *versiondb.Database (the per-block in-memory version layer). A Put therefore stages into the version layer during block processing and becomes durable ONLY when the VM commits the batch at Accept — the exact commit discipline dexvm/state/state.go follows. This package never imports the zapdb engine directly; it speaks only the luxfi/database interface, so the VM is free to swap the backing store.
Index ¶
- Variables
- type Manifest
- type State
- func (s *State) GetAlloc(rng string) (uint64, error)
- func (s *State) GetLastBlock() (ids.ID, uint64)
- func (s *State) GetManifest(bucket, object string) (m Manifest, found bool, err error)
- func (s *State) Initialize() error
- func (s *State) PutManifest(bucket, object string, m Manifest) error
- func (s *State) Root() (ids.ID, error)
- func (s *State) SetAlloc(rng string, n uint64) error
- func (s *State) SetLastBlock(blockID ids.ID, height uint64) error
Constants ¶
This section is empty.
Variables ¶
var ( // ErrStateCorrupted is returned when a stored value cannot be decoded into // its expected shape — a corrupt record must never be silently reinterpreted. ErrStateCorrupted = errors.New("state corrupted") )
Functions ¶
This section is empty.
Types ¶
type Manifest ¶
type Manifest struct {
FileIDs []string `json:"fileIds"`
Size int64 `json:"size"`
ETag string `json:"etag"`
}
Manifest is the committed content manifest for one object: the file blobs that compose it plus the object-level metadata an S3 HEAD returns. It is encoded as a deterministic JSON body (no maps, declaration-order fields), the same self-describing, protobuf-free style dexvm uses for its structured values.
type State ¶
type State struct {
// contains filtered or unexported fields
}
State manages the S-Chain's persistent state over the version layer `db`.
func New ¶
New creates a state manager over db — the per-block version layer the VM commits atomically at Accept. Mirrors dexstate.New (dexvm/state/state.go:96), minus the durable receipt side-channel the storage VM does not need in M0.
func (*State) GetAlloc ¶
GetAlloc returns the current allocator counter for range — the next id that will be handed out. An absent range reads as 0 (its first allocation starts at id 0). Reads through the version layer, so it observes a counter staged in this block before commit and the durable value after commit.
func (*State) GetLastBlock ¶
GetLastBlock returns the last accepted block id and height.
func (*State) GetManifest ¶
GetManifest returns the manifest for (bucket, object). found is false when no manifest exists. Reads through the version layer, so it observes a manifest staged in this block before commit (the in-flight view) and the durable store after commit — identical to dexvm's versiondb-backed reads.
func (*State) Initialize ¶
Initialize loads the last-accepted block pointer from the database.
func (*State) PutManifest ¶
PutManifest stages a manifest into the version layer. It becomes durable only when the VM commits the block's batch at Accept — before that, a GetManifest on a freshly-constructed reader over the base DB does not see it. This is the versiondb/CommitBatch discipline the M0 proof asserts.
func (*State) Root ¶
Root returns a deterministic SHA-256 commitment over the COMMITTED object keyspaces: every manifest/<bucket>/<object> -> {fileIds,size,etag} entry AND every alloc/<range> -> counter entry the chain holds after this block's writes are staged. It is the value the block header binds, so two validators whose committed state actually diverges — a different object set, a changed etag/size/fileIds for an object, OR a different allocator counter for a range — ALWAYS produce different roots. Divergence can never hide behind a matching block hash. This is the manifest-VM analog of dexvm's State.StateHash (dexvm/state/state.go:395), covering the storage VM's two committed keyspaces.
The walk iterates each rootPrefix in turn via the zapdb prefix iterator NewIteratorWithStartAndPrefix(nil, prefix), so it reads through the versiondb (this block's staged in-memory writes merged over the durable base, deleted keys dropped). Only the manifest + alloc keyspaces are folded — the last-block pointer is consensus binding folded separately into the header via blockHash/height, NOT object state, so it is excluded here exactly as dexvm excludes its proposer-local receipt prefix.
The prefix order (rootPrefixes) is fixed and the keys within each prefix come out in lexicographic order, so the digest is independent of write history. Each field (domain, key, value) is framed with SP 800-185 left_encode of its BIT length before it is absorbed, so no two distinct (key,value) splits can collide by concatenation — the same canonicalization the validator NodeID scheme uses (ids/node_id_scheme.go). And because every key is absorbed WITH its full prefix, a manifest key and an alloc key can never alias even if their suffixes coincide. The hash is SHAKE256 (SHA-3): not length-extendable (unlike SHA-256), domain-separated by stateRootDomain, and PQ-strength (256-bit output → 128-bit collision/preimage even under Grover).
func (*State) SetAlloc ¶
SetAlloc stages the allocator counter for range into the version layer. It becomes durable only when the VM commits the block's batch at Accept — the same versiondb/CommitBatch discipline as PutManifest. The value is a fixed 8-byte big-endian uint64 so the stored bytes are canonical (GetAlloc rejects any other width as corruption).