Skip to content

feat(host): add backend component traits over the concrete pools#99

Merged
mfw78 merged 1 commit into
developfrom
feat/host-component-traits
Jul 2, 2026
Merged

feat(host): add backend component traits over the concrete pools#99
mfw78 merged 1 commit into
developfrom
feat/host-component-traits

Conversation

@mfw78

@mfw78 mfw78 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Adds the backend component seam: a new host::component module defining traits over the concrete capability backends. ChainProvider, CowApi, and HttpClient are async (declared in impl Future + Send form so generic consumers can hold the futures across await points in tokio tasks; deliberately not dyn-compatible, since the forthcoming runtime-generic layer consumes them through generic bounds only). StateStore with its per-module StateHandle, Clock, and Random stay sync, mirroring the underlying operations. The existing backends implement them in place: ProviderPool, OrderBookPool, LocalStore/ModuleStore, plus injectable defaults SystemClock, OsRandom, and UnsupportedHttp whose behaviour is identical to today's direct calls (including the byte-identical HTTP stub message).

The change is purely additive: no caller, HostState, supervisor, or dependency changes. Method shapes mirror the existing inherent signatures one-to-one, delegation is fully qualified to avoid name-shadowing recursion, and a conformance test module pins trait satisfaction, trait-versus-inherent dispatch equivalence, and the clock/random behaviour.

Why

The concrete pools are wired directly into HostState, so backends cannot be swapped or faked. This seam is the foundation the RuntimeTypes associated-type lattice, the component builders, and the mock runtime preset all build on; landing it additively keeps the risk of the later generic refactor contained.

Closes #85 (part of the epic in #77).

Testing

Full workspace verification through the nix dev shell (fmt, clippy with -D warnings, rustdoc with -D warnings, tests), all green. The new host::component tests pass 4/4, including a trait-dispatch-equals-inherent-dispatch check against ProviderPool and behavioural checks on SystemClock and OsRandom. Every mirrored signature was verified against the real inherent method before writing; no mismatches.

AI Assistance: Claude Code used for implementation, adversarial review, and PR preparation.

fn request(
&self,
chain_id: u64,
method: &str,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleanup should be file at a later time whereby the typing should be stronger on this such that the methods should be GET / PUT/ DELETE etc, which are all enums, so can use some enum with IntoStaticStr perhaps (look at existing crates to reduce implementation scope).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #101.

/// REST passthrough against the chain's orderbook base URL.
fn request(
&self,
chain_id: u64,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chain_id can likely be strictly typed as well and from alloy_chains

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #102.

/// Open a `newHeads` block subscription on `chain_id`.
fn subscribe_blocks(
&self,
chain_id: u64,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chain_id can likely be strictly typed and from alloy_chains

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #102.

Comment on lines +31 to +32
method: String,
params_json: String,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if there is anything in the alloy RPC types that can be used in place of raw method / params such that we can also close out security problems for potential eth_sign injection.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #103 (allowlist enforcement itself remains #50).

/// Outbound HTTP backend for `http::fetch` (post-allowlist).
pub trait HttpClient {
/// Execute `req` and return the response.
fn fetch(&self, req: Request) -> impl Future<Output = Result<Response, HostError>> + Send;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if these Request / Response actually come from some inherent types already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #104.

@@ -0,0 +1,18 @@
//! Randomness seam mirroring the direct `getrandom::fill` call in the

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if there's some natively injected randomness available from wasmtime

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #106.

Comment on lines +19 to +29
/// Per-module key-value handle; mirrors the inherent `ModuleStore` API.
pub trait StateHandle {
/// Fetch a value; `Ok(None)` when absent.
fn get(&self, key: &str) -> Result<Option<Vec<u8>>, StorageError>;
/// Insert or overwrite.
fn set(&self, key: &str, value: &[u8]) -> Result<(), StorageError>;
/// Delete; idempotent.
fn delete(&self, key: &str) -> Result<(), StorageError>;
/// Enumerate module-visible keys starting with `prefix`.
fn list_keys(&self, prefix: &str) -> Result<Vec<String>, StorageError>;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walking across the WASM boundary is expensive, therefore if there are batch operations to be done, such should be available to be done so that a transaction can be represented as a sequence of get/get_many/set_setmany, and potentially look at some combinators / filters so that filter of keys / values can be done within the WASM host side and not in the runtime necessarily before it crosses the boundary. At the same time, file another issue as we need to check if host resource use during some host function call counts for fuel in wasmtime (don't want to allow unlimited time to be burnt on host calls).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in #105 (batching/filters) and #107 (fuel accounting on host calls).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant