Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions crates/nexum-runtime/src/host/local_store_redb.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
//! `nexum:host/local-store` backend.
//!
//! Single redb file under `EngineConfig.engine.state_dir`. Each module is
//! namespaced host-side by a fixed 32-byte prefix `keccak256(module_name)`
//! prepended to every key, so modules sharing a key string see disjoint
//! data and cannot forge a key into another's range. keccak256 matches ENS
//! node derivation (ADR-0003).
//! Single redb file under `EngineConfig.engine.state_dir`. The contract is
//! namespace isolation: each module sees a disjoint key range that no other
//! module can read, write, or forge its way into, even if two modules use
//! the same key string. The host implements this today with a fixed 32-byte
//! `keccak256(module_name)` prefix prepended to every key (see ADR-0003 for
//! why keccak256 - it matches ENS node derivation); that prefix scheme is an
//! implementation detail, free to change, not part of the specified
//! guarantee.

#![allow(clippy::result_large_err)]

Expand Down
6 changes: 3 additions & 3 deletions docs/diagrams/diagrams.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ classDiagram
| **Supervisor** | Orchestrates boot and event dispatch. Creates one `HostState` per module. On each incoming block or chain-log, calls `dispatch_block` / `dispatch_chain_log` to fan the event out to subscribed modules. |
| **ProviderPool** | Holds one alloy `DynProvider` per chain. `wss://` chains get a pubsub provider that supports both subscriptions and requests. `https://` chains get HTTP-only (subscriptions unavailable, by design - ADR-0002). |
| **OrderBookPool** | Holds one `OrderBookApi` client per known CoW chain (Mainnet, Gnosis, Sepolia, ArbitrumOne, Base). Instantiated via `OrderBookPool::default()` at boot (ADR-0005). |
| **LocalStore** | A single redb embedded database at `state_dir`. All modules write into the same file. Keys are prefixed host-side as `[32-byte module namespace][raw_key]` so two modules never collide, and the namespace is unspoofable (ADR-0003). The namespace is `keccak256(module_name)` for locally-loaded modules and `ens_namehash(name)` for ENS-discovered modules. |
| **LocalStore** | A single redb embedded database at `state_dir`. All modules write into the same file, but the contract guarantees namespace isolation - each module sees a disjoint, non-forgeable key range. The host implements this today by prefixing every key host-side (`keccak256(module_name)` for locally-loaded modules, `ens_namehash(name)` for ENS-discovered ones - see ADR-0003), an implementation detail rather than part of the specified guarantee. |
| **HostState** | The per-module runtime context. `wasmtime::component::bindgen!` generates one trait per WIT interface (e.g. `shepherd::cow::cow_api::Host`); `HostState` implements each trait, and the linker registers all trait implementations once at boot. Fields: `wasi: WasiCtx` + `table: ResourceTable` (the constrained WASI surface), `limits: StoreLimits` (memory/table caps), `http_ctx: WasiHttpCtx` + `http_gate: HttpGate` (the wasi:http outgoing gate holding the module's `[capabilities.http].allow` list), `module_namespace` (log tagging), `ext` (extension backends), `chain` (provider pool seam), `store` (namespace-prefixed local-store handle). |
| **EventLoop** | Runs `futures::stream::select_all` over a `Vec<Pin<Box<dyn Stream<Item = Event> + Send>>>`. The loop never exits until SIGINT/SIGTERM. Each fired event is forwarded to `Supervisor` for fan-out. |
| **TwapModule** | The TWAP watcher WASM component. On a `Log` event (ConditionalOrderCreated): persists the registration in `local-store`. On a `Block` event: iterates all watches and, for each, makes an `eth_call` via `chain.request`, decodes the result via `alloy_sol_types` (in-module), builds an `OrderCreation` via `cowprotocol` types (consumed via wasm32 feature), and submits via `cow-api.submit-order`. Orderbook errors flow through `OrderPostError::retry_hint`. All polling logic lives in the module, not the host (ADR-0006). |
Expand All @@ -165,7 +165,7 @@ graph TD
SC["shepherd:cow@0.2.0\n(CoW Protocol extensions)"]

NH --> n1["chain ✅ implemented\nrequest(chain-id, method, params)\nrequest-batch(chain-id, requests)\n - \nsubscribe-blocks · subscribe-logs →\n engine-managed via module.toml subscriptions\nregister-address · unregister-address →\n 🕓 deferred to 0.3 (ADR-0008)"]
NH --> n2["local-store ✅ implemented\nget(key) · set(key, value)\ndelete(key) · list-keys(prefix)\nnamespacing: 32-byte hash prefix (ADR-0003)"]
NH --> n2["local-store ✅ implemented\nget(key) · set(key, value)\ndelete(key) · list-keys(prefix)\ncontract: per-module namespace isolation (ADR-0003)"]
NH --> n3["identity · messaging · remote-store\n✅ stubs (Unsupported) - full impl in 0.3"]
NH --> n4["logging ✅ implemented"]

Expand All @@ -184,7 +184,7 @@ graph TD
|---|---|
| **nexum:host@0.2.0** | The base WIT package. Any module running in the engine - CoW-aware or not - imports from here. Defines shared types (`chain-id`, `chain-log`, `fault`) used by both packages. |
| **chain** | Reads from the blockchain via JSON-RPC. `request` sends a single call; `request-batch` sends several in one round-trip. **Subscriptions are not callable WIT functions** - they are declared in `module.toml` and opened by the engine at boot. Dynamic `register-address` for factory patterns is deferred to 0.3 (ADR-0008). |
| **local-store** | Persistent key-value storage that survives restarts. Operations: `get(key)`, `set(key, value)`, `delete(key)`, `list-keys(prefix)`. The host prefixes every key with a 32-byte deterministic namespace (`keccak256(module_name)` locally, or `ens_namehash(name)` when ENS-loaded) so modules are fully isolated and the namespace cannot be spoofed (ADR-0003). |
| **local-store** | Persistent key-value storage that survives restarts. Operations: `get(key)`, `set(key, value)`, `delete(key)`, `list-keys(prefix)`. The contract is namespace isolation - each module's keys are disjoint from every other module's and cannot be spoofed. The host implements this today via a deterministic key prefix (`keccak256(module_name)` locally, or `ens_namehash(name)` when ENS-loaded - see ADR-0003), an implementation detail rather than the specified guarantee. |
| **identity · messaging · remote-store** | Capabilities stubbed at 0.2 - they return `Unsupported`. `identity` will provide keystore-backed signing. `messaging` will send Waku messages. `remote-store` will read/write Swarm/IPFS. |
| **logging** | Lightweight utility. `logging` emits to the engine's `tracing` subscriber (inherits `RUST_LOG` filters). Time and secure randomness are available ambiently via `wasi:clocks` and `wasi:random`. |
| **(outbound HTTP)** | Not a `nexum:host` interface: a module that declares the `http` capability imports the standard `wasi:http/outgoing-handler`, and the host checks every outgoing request against the manifest's `[capabilities.http].allow` list before any connection is made. |
Expand Down
Loading