From 424e6c69126a392b225f4b910ffd11d627d2602b Mon Sep 17 00:00:00 2001 From: Dario Gabriel Lipicar Date: Thu, 20 Aug 2026 22:57:02 -0300 Subject: [PATCH] docs: add README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the quick start, the API shape, and the three things that are not guessable from the method list: the provider must support eth_getProof; eth_call/estimateGas/createAccessList carry a non-standard third positional parameter; and an idle proxy does not advance its light client, which is why the keep-alive exists. Also states why `network` and `logLevel` are whitelisted — they are the two fields that reach a Nim quit() and would take the host process down. Co-Authored-By: Claude Opus 5 --- README.md | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..aa2de64 --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +# logos-verified-proxy-module + +Light-client-verified Ethereum JSON-RPC for Logos, wrapping status-im's +[`nimbus_verified_proxy`](https://github.com/status-im/nimbus-eth1/tree/master/nimbus_verified_proxy) +in its C library form (`libverifproxy`). + +An ordinary RPC client forwards a request to a provider and **trusts the answer**. +This module doesn't: it syncs the beacon-chain light client from a trusted block +root and verifies every `eth_*` response against the attested execution state, +requesting Merkle proofs from the (untrusted) provider. A provider that lies +produces an *error*, not a wrong value. + +No extra process, no local port — the verification runs in-process, and results +come back over the normal Logos RPC surface. + +## Quick start + +```bash +nix build && lm methods ./result/lib/verified_proxy_module_plugin.so +``` + +Get a trusted block root — this is the root of trust, so take it from a source +you trust, not from the same provider you are about to verify: + +```bash +curl -s https://beaconstate.info/eth/v1/beacon/headers/finalized | jq -r '.data.root' +``` + +`config.json`: + +```json +{ + "network": "mainnet", + "trustedBlockRoot": "0x...", + "executionApiUrls": ["wss://eth-mainnet.example/v2/"], + "beaconApiUrls": ["https://beaconstate.info"] +} +``` + +Then: + +```bash +logosctl call verified_proxy_module configure json:@config.json +``` + +```bash +logosctl call verified_proxy_module start && logosctl call verified_proxy_module status +``` + +```bash +logosctl call verified_proxy_module ethGetBalance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 latest +``` + +The execution provider **must support `eth_getProof`** — verification is +impossible without it. (Infura notably does not.) + +## API + +| Method | Notes | +|---|---| +| `configure(config)` | Validate and store config. Synchronous; starts nothing. | +| `getConfig()` | Effective config, credentials redacted. | +| `start()` / `stop()` | Blocking, bounded by `startTimeoutMs` / `drainTimeoutMs`. | +| `ok()` / `status()` | Health probe and full state. `status()` never blocks on the proxy thread. | +| `rpc(method, params)` | Any method the proxy supports. `params` is a JSON-RPC array. | +| `ethBlockNumber()`, `ethGetBalance(...)`, `ethCall(...)`, … | Typed wrappers over the same path. | + +Events: `proxyStarted`, `proxyStopped`, `proxyStateChanged`. + +All RPC methods are **synchronous** — they return the verified result, or an +error, within `callTimeoutMs`. Consumers that want concurrency use the generated +`Async` twin on their side; the module is `concurrency: "multi"`, so +blocked callers do not stall each other. + +### `rpc()` and `optimisticStateFetch` + +`eth_call`, `eth_estimateGas` and `eth_createAccessList` take a **third +positional parameter**, `optimisticStateFetch` (a bool) — an upstream extension +to the standard JSON-RPC signature. The typed wrappers supply it; anything +calling `rpc()` with a hand-built params array must too. + +## Configuration + +Required: `trustedBlockRoot` (`0x` + 64 hex), `executionApiUrls`, +`beaconApiUrls`. `network` is one of `mainnet`, `sepolia`, `hoodi`. OP-Stack L2 +is enabled by setting `opExecutionApiUrls` (there is no `op-*` network name in +the library's JSON config — that is a CLI-only option on the standalone binary). + +Module-side knobs: `callTimeoutMs` (30000), `startTimeoutMs` (120000), +`drainTimeoutMs` (2000), `pumpIntervalMs` (50), `maxInFlight` (64), +`keepAlive` (`off` | `interval` | `continuous`), `keepAliveIntervalMs` (1000), +`autoStart` (false). Upstream tuning lives under `tuning`. + +Config is persisted to the host-provided per-instance directory and reloaded on +load. `VERIFIED_PROXY_MODULE_CONFIG` (inline JSON or a path) supplies a +deploy-time default. + +### Two fields are validated for safety, not tidiness + +`network` and `logLevel` are whitelisted **before** they can reach the library, +because an unrecognised value there reaches a Nim `quit()` that would terminate +the whole host process: + +* an unknown network reaches nimbus-eth2's `getMetadataForNetwork`, whose + fallthrough is `fatal` + `quit 1`; +* a log level Nim's `updateLogLevel` rejects reaches `setupLogging`'s `quit 1`. + +Neither is validated upstream. Everything else — bad JSON, a missing +`trustedBlockRoot`, a malformed URL — is already caught and turned into a +`NULL` return, so validating it here only improves the error message. + +## The keep-alive, and a caveat worth knowing + +`processVerifProxyTasks` only advances chronos while a call is in flight, so an +**idle proxy does not advance its light client**. The heartbeat +(`keepAlive: "interval"`, the default) issues `eth_syncing`, which drives the +sync loop and touches no execution backend. `keepAlive: "continuous"` keeps it +turning permanently; `"off"` accepts cold starts. + +Sync observability is limited by the library: there is no exported getter for +the finalized/optimistic slot, and `eth_syncing` returns a hardcoded `false`. +`status().state == "degraded"` therefore means "up, but heartbeats are failing", +inferred from those calls' error strings. + +## Development + +```bash +nix build .#unit-tests && ./result-tests/bin/verified_proxy_module_tests +``` + +Unit tests link a mocked `libverifproxy` (`tests.mockCLibs`), so they never +build the ~25-minute upstream toolchain. Unlike a synchronous mock, it queues +completions and drains them only from the pump, so the cross-thread design is +actually exercised. + +```bash +nix build .#libverifproxy # the upstream archive alone (slow, cached) +``` + +## Licence + +MIT / Apache-2.0, matching the Logos workspace.