state

package
v1.3.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2026 License: BSD-3-Clause Imports: 8 Imported by: 0

Documentation

Overview

Package state manages persistent state for the DEX VM proxy.

PROXY STATELESSNESS INVARIANT: this holds ZERO canonical DEX state. There are NO order / pool / position / tick / feeGrowth keys — matching + DEX state live ONLY on the d-chain. The proxy persists exactly four things, all proper to an atomic transport layer:

  1. NONCES — per-account replay protection for proxy txs.
  2. RELAY RECEIPTS — in-flight clob_* relays bound to (blockHash, txIndex), so a re-execution / reorg / retry maps to exactly one d-chain match (replay-idempotency).
  3. CONSUMED UTXOs — the atomic-UTXO consumption set: source-chain UTXO ids already claimed by an Import, so the same exported value can never be imported twice.
  4. COLLATERAL ESCROW — the locked-collateral ledger: per collateral ref, the (asset, amount) an Import locked into the proxy. It is the value-conservation witness: a settle credits the realized proceeds and REFUNDS the unfilled remainder of this locked amount, so value_in == value_out exactly. This is NOT canonical DEX state — it is the transport layer's record of value in flight, the exact analogue of the consumed-UTXO set for the return leg.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrStateCorrupted   = errors.New("state corrupted")
	ErrUTXOAlreadySpent = errors.New("atomic UTXO already consumed")

	// ErrEscrowConsumed is returned when a collateral escrow is settled twice.
	ErrEscrowConsumed = errors.New("collateral escrow already settled")
)

Functions

This section is empty.

Types

type Receipt added in v1.3.8

type Receipt struct {
	BlockHash ids.ID `json:"blockHash"`
	TxIndex   uint32 `json:"txIndex"`
	// RespHash is the SHA-256 of the d-chain's response wire bytes (clob_submit
	// fills, or a clob_place / clob_cancel ack) — the idempotency witness. A
	// retry that finds this receipt is a no-op; it never re-relays.
	RespHash ids.ID `json:"respHash"`
}

Receipt records an in-flight ZAP relay: the d-chain operation it triggered, keyed by the consensus binding (blockHash, txIndex) so the same logical order (submit, place, or cancel) maps to exactly one relay across re-execution / reorg / retry.

type State

type State struct {
	// contains filtered or unexported fields
}

State manages the proxy's persistent state.

func New

func New(db, receiptDB database.Database) *State

New creates a new state manager. db is the per-block state layer (a versiondb committed atomically at accept); receiptDB is the DURABLE base DB the relay write-ahead receipts are written through to, so an idempotency witness survives a crash between Verify and Accept (db.Abort discards db's in-memory layer; receiptDB is untouched). Pass the same base DB the versiondb wraps.

func (*State) Close

func (s *State) Close() error

Close is a no-op flush hook (the proxy writes through to the DB directly).

func (*State) ConsumeEscrow added in v1.3.8

func (s *State) ConsumeEscrow(ref ids.ID) error

ConsumeEscrow deletes a collateral escrow once it has been settled, so the same locked collateral can never be refunded twice. It refuses to consume an absent escrow (ErrEscrowConsumed) — the settle path's single-claim guard, mirroring MarkConsumed on the import leg.

func (*State) GetEscrow added in v1.3.8

func (s *State) GetEscrow(ref ids.ID) (asset ids.ID, amount uint64, found bool, err error)

GetEscrow returns the (asset, amount) locked under a collateral ref. found is false when no escrow exists (e.g. a relay that was not preceded by an import in this proxy — then there is nothing to refund and nothing to settle).

func (*State) GetLastBlock

func (s *State) GetLastBlock() (ids.ID, uint64)

GetLastBlock returns the last accepted block ID and height.

func (*State) GetNonce

func (s *State) GetNonce(addr ids.ShortID) (uint64, error)

GetNonce returns the current nonce for an account (0 if unseen).

func (*State) GetReceipt added in v1.3.8

func (s *State) GetReceipt(blockHash ids.ID, txIndex uint32) (*Receipt, bool, error)

GetReceipt returns the relay receipt bound to (blockHash, txIndex), if any. It reads the DURABLE receiptDB so a write-ahead intent recorded before a crash is seen on the post-restart re-Verify — the heart of the double-submit guard.

func (*State) Initialize

func (s *State) Initialize() error

Initialize loads the last-accepted block pointer from the database.

func (*State) IsConsumed added in v1.3.8

func (s *State) IsConsumed(utxoID ids.ID) (bool, error)

IsConsumed reports whether an exported UTXO was already claimed by an Import.

func (*State) MarkConsumed added in v1.3.8

func (s *State) MarkConsumed(utxoID ids.ID) error

MarkConsumed records that an exported UTXO has been claimed. It refuses a double-spend: a UTXO already in the set returns ErrUTXOAlreadySpent.

func (*State) PutEscrow added in v1.3.8

func (s *State) PutEscrow(ref ids.ID, asset ids.ID, amount uint64) error

PutEscrow records the (asset, amount) an Import locked under a collateral ref. The stored value is asset(32)||amount(8). Recording is the import leg of the conservation equation; the matching ConsumeEscrow at settle pays the refund.

func (*State) PutReceipt added in v1.3.8

func (s *State) PutReceipt(r *Receipt) error

PutReceipt finalizes a relay receipt — phase 2 — after the relay returns, stamping the response hash onto the already-durable intent. The stored value is blockHash||respHash (the txIndex lives in the key); blockHash is redundant-but-cheap provenance. Written to the DURABLE receiptDB so it shares one home with the intent it upgrades.

func (*State) RecordRelayIntent added in v1.3.8

func (s *State) RecordRelayIntent(blockHash ids.ID, txIndex uint32) error

RecordRelayIntent durably records the write-ahead intent to relay (blockHash, txIndex) — phase 1 — BEFORE the relay fires. After this returns, a crash that aborts the versiondb cannot erase the witness, so re-Verify finds it (GetReceipt) and refuses to re-submit. RespHash is left zero until the relay returns and PutReceipt finalizes it. Writing through receiptDB.Put makes the witness durable immediately (not deferred to the block commit).

func (*State) SetLastBlock

func (s *State) SetLastBlock(blockID ids.ID, height uint64) error

SetLastBlock sets the last accepted block.

func (*State) SetNonce added in v1.3.8

func (s *State) SetNonce(addr ids.ShortID, nonce uint64) error

SetNonce persists the nonce for an account.

func (*State) StateHash added in v1.3.8

func (s *State) StateHash() (ids.ID, error)

StateHash returns a deterministic SHA-256 commitment over the proxy's CONSENSUS state: every consumed-UTXO marker, collateral escrow, replay nonce, and the last-block pointer. It is the value the block's StateRoot binds, so two nodes whose consensus state actually diverges (a different consumed set, a settled- vs-unsettled escrow) ALWAYS produce different roots — divergence can never hide behind a matching block hash.

RELAY RECEIPTS ARE DELIBERATELY EXCLUDED (RED finding #9). Under the carried- fills model the d-chain relay is performed exactly ONCE, by the block PROPOSER, at build (VM.obtainFills); the proposer writes a relay receipt as proposer-LOCAL idempotency bookkeeping, but a VALIDATOR that merely parses the block bytes never relays and so never writes that receipt. Folding receipts into the StateRoot would therefore make the proposer's root diverge from every validator's for the IDENTICAL block — reintroducing a fork. The receipt is liveness bookkeeping, not consensus state, and must not be committed here. (It remains durable in receiptDB and is still GetReceipt-able to gate a proposer rebuild.)

The walk reads the versiondb `db` (which merges this block's staged in-memory writes over the durable base and drops deleted keys, e.g. a consumed escrow is absent), skipping the receipt prefix. Keys come out in lexicographic order, so the digest is order-independent of write history and identical across nodes. Every entry is folded length-prefixed (len(key)||key||len(value)||value) so no two distinct keyspaces collide by concatenation. The state is tiny by the proxy-statelessness invariant, so the walk per block is cheap.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL