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:
- NONCES — per-account replay protection for proxy txs.
- 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).
- 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.
- 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 ¶
- Variables
- type Receipt
- type State
- func (s *State) Close() error
- func (s *State) ConsumeEscrow(ref ids.ID) error
- func (s *State) GetEscrow(ref ids.ID) (owner ids.ShortID, asset ids.ID, amount uint64, found bool, err error)
- func (s *State) GetLastBlock() (ids.ID, uint64)
- func (s *State) GetNonce(addr ids.ShortID) (uint64, error)
- func (s *State) GetReceipt(blockHash ids.ID, txIndex uint32) (*Receipt, bool, error)
- func (s *State) Initialize() error
- func (s *State) IsConsumed(utxoID ids.ID) (bool, error)
- func (s *State) MarkConsumed(utxoID ids.ID) error
- func (s *State) PutEscrow(ref ids.ID, owner ids.ShortID, asset ids.ID, amount uint64) error
- func (s *State) PutReceipt(r *Receipt) error
- func (s *State) RecordRelayIntent(blockHash ids.ID, txIndex uint32) error
- func (s *State) SetLastBlock(blockID ids.ID, height uint64) error
- func (s *State) SetNonce(addr ids.ShortID, nonce uint64) error
- func (s *State) StateHash() (ids.ID, error)
Constants ¶
This section is empty.
Variables ¶
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 ¶
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) ConsumeEscrow ¶ added in v1.3.8
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) (owner ids.ShortID, asset ids.ID, amount uint64, found bool, err error)
GetEscrow returns the (owner, 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). owner is the recorded owner the settle leg binds settle-authority and the payout target to.
func (*State) GetLastBlock ¶
GetLastBlock returns the last accepted block ID and height.
func (*State) GetReceipt ¶ added in v1.3.8
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 ¶
Initialize loads the last-accepted block pointer from the database.
func (*State) IsConsumed ¶ added in v1.3.8
IsConsumed reports whether an exported UTXO was already claimed by an Import.
func (*State) MarkConsumed ¶ added in v1.3.8
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
PutEscrow records the (owner, asset, amount) an Import locked under a collateral ref. The stored value is owner(20)||asset(32)||amount(8). owner is the recorded owner of the consumed C->D object (the authenticated cross-chain value's owner, bound in executeImport) — the only account that may later settle this escrow and the sole payout target for its proceeds + refund. 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
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
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 ¶
SetLastBlock sets the last accepted block.
func (*State) StateHash ¶ added in v1.3.8
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.