From ee5bc5c6c8ff9d011681dd9c9a9b115a64a2d04a Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Tue, 21 Apr 2026 21:11:04 +0200 Subject: [PATCH 01/16] fix rpc & set up genesis --- bin/keygen/src/setup.rs | 34 ++++++++++++++++++++++++++++++---- bin/kora/src/cli.rs | 4 +--- crates/node/rpc/src/server.rs | 29 +++++++++++++++++++++-------- docker/Justfile | 2 +- docker/compose/devnet.yaml | 15 +++------------ 5 files changed, 56 insertions(+), 28 deletions(-) diff --git a/bin/keygen/src/setup.rs b/bin/keygen/src/setup.rs index 5c75762..ca97a9f 100644 --- a/bin/keygen/src/setup.rs +++ b/bin/keygen/src/setup.rs @@ -120,10 +120,36 @@ pub(crate) fn run(args: SetupArgs) -> Result<()> { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs(), - allocations: vec![GenesisAllocation { - address: "0x0000000000000000000000000000000000000001".to_string(), - balance: "1000000000000000000000000".to_string(), - }], + allocations: vec![ + GenesisAllocation { + address: "0x0000000000000000000000000000000000000001".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0xEb1Ba7Fc58b3416361a0EE07d140c91410c0AA8c".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0xa883208a74152107475a3Fa6b0c21121894B647F".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0x105be5081ceba05be11976150abc277ee365fc3f".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0x30b68d56AE9173566055a69ee7cCB0E755B6a201".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0xDdE169289B51C512268D0b11EE2b15160b1e1793".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + GenesisAllocation { + address: "0xde738C4084dDE5083A7959235Fd230e27eAFC63B".to_string(), + balance: "1000000000000000000000000".to_string(), + }, + ], }; let genesis_path = args.output_dir.join("genesis.json"); fs::write(&genesis_path, serde_json::to_string_pretty(&genesis)?)?; diff --git a/bin/kora/src/cli.rs b/bin/kora/src/cli.rs index e754105..f53afd8 100644 --- a/bin/kora/src/cli.rs +++ b/bin/kora/src/cli.rs @@ -150,9 +150,7 @@ impl Cli { .map_err(|e| eyre::eyre!("Failed to load genesis: {}", e))?; tracing::info!(allocations = bootstrap.genesis_alloc.len(), "Loaded genesis configuration"); - // Create RPC state that will be updated by consensus - let rpc_port = 8545 + dkg_output.share_index as u16; - let rpc_addr: std::net::SocketAddr = format!("0.0.0.0:{}", rpc_port).parse()?; + let rpc_addr: std::net::SocketAddr = "0.0.0.0:8545".parse()?; let node_state = NodeState::new(config.chain_id, dkg_output.share_index); let runner = ProductionRunner::new( diff --git a/crates/node/rpc/src/server.rs b/crates/node/rpc/src/server.rs index a6a6f38..c8cc608 100644 --- a/crates/node/rpc/src/server.rs +++ b/crates/node/rpc/src/server.rs @@ -71,7 +71,8 @@ fn build_cors_layer(config: &CorsConfig) -> CorsLayer { /// RPC server for exposing node status via HTTP and Ethereum JSON-RPC. pub struct RpcServer { state: NodeState, - addr: SocketAddr, + http_addr: SocketAddr, + jsonrpc_addr: SocketAddr, chain_id: u64, tx_submit: Option, state_provider: S, @@ -83,19 +84,28 @@ impl std::fmt::Debug for RpcServer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RpcServer") .field("state", &self.state) - .field("addr", &self.addr) + .field("http_addr", &self.http_addr) + .field("jsonrpc_addr", &self.jsonrpc_addr) .field("chain_id", &self.chain_id) .field("tx_submit", &self.tx_submit.is_some()) .finish() } } +/// Compute a default HTTP address by incrementing the port of the given address. +fn default_http_addr(jsonrpc_addr: SocketAddr) -> SocketAddr { + SocketAddr::new(jsonrpc_addr.ip(), jsonrpc_addr.port() + 1) +} + impl RpcServer { /// Create a new RPC server with default (noop) state provider. + /// + /// The JSON-RPC server binds to `addr`; the HTTP status server binds to `addr.port() + 1`. pub fn new(state: NodeState, addr: SocketAddr) -> Self { Self { state, - addr, + http_addr: default_http_addr(addr), + jsonrpc_addr: addr, chain_id: 1, tx_submit: None, state_provider: NoopStateProvider, @@ -108,7 +118,8 @@ impl RpcServer { pub fn with_chain_id(state: NodeState, addr: SocketAddr, chain_id: u64) -> Self { Self { state, - addr, + http_addr: default_http_addr(addr), + jsonrpc_addr: addr, chain_id, tx_submit: None, state_provider: NoopStateProvider, @@ -128,7 +139,8 @@ impl RpcServer { ) -> Self { Self { state, - addr, + http_addr: default_http_addr(addr), + jsonrpc_addr: addr, chain_id, tx_submit: None, state_provider, @@ -162,7 +174,8 @@ impl RpcServer { pub fn from_config(state: NodeState, config: RpcServerConfig, state_provider: S) -> Self { Self { state, - addr: config.http_addr, + http_addr: config.http_addr, + jsonrpc_addr: config.jsonrpc_addr, chain_id: config.chain_id, tx_submit: None, state_provider, @@ -175,8 +188,8 @@ impl RpcServer { /// /// This spawns background tasks for both HTTP and JSON-RPC servers and returns immediately. pub fn start(self) -> RpcServerHandle { - let http_addr = self.addr; - let jsonrpc_addr = self.addr; + let http_addr = self.http_addr; + let jsonrpc_addr = self.jsonrpc_addr; let node_state = Arc::new(self.state); let node_state_for_jsonrpc = Arc::clone(&node_state); let chain_id = self.chain_id; diff --git a/docker/Justfile b/docker/Justfile index 9b0c078..451c888 100644 --- a/docker/Justfile +++ b/docker/Justfile @@ -41,7 +41,7 @@ status: @echo "" @echo "Endpoints:" @echo " P2P: localhost:30400-30403" - @echo " RPC: localhost:8540-8543 (disabled)" + @echo " RPC: localhost:8545-8548" @echo " Prometheus: http://localhost:9090" @echo " Grafana: http://localhost:3000" diff --git a/docker/compose/devnet.yaml b/docker/compose/devnet.yaml index d96ff6e..4cf98b6 100644 --- a/docker/compose/devnet.yaml +++ b/docker/compose/devnet.yaml @@ -177,9 +177,6 @@ services: validator-node0: <<: *validator-common hostname: node0 - depends_on: - init-config: - condition: service_completed_successfully entrypoint: ["/scripts/entrypoint.sh", "validator"] volumes: - shared_config:/shared:ro @@ -199,8 +196,6 @@ services: <<: *validator-common hostname: node1 depends_on: - init-config: - condition: service_completed_successfully validator-node0: condition: service_healthy entrypoint: ["/scripts/entrypoint.sh", "validator"] @@ -216,15 +211,13 @@ services: - HEALTHCHECK_MODE=ready ports: - "30401:30303" - - "8546:8546" + - "8546:8545" - "9001:9002" validator-node2: <<: *validator-common hostname: node2 depends_on: - init-config: - condition: service_completed_successfully validator-node0: condition: service_healthy entrypoint: ["/scripts/entrypoint.sh", "validator"] @@ -240,15 +233,13 @@ services: - HEALTHCHECK_MODE=ready ports: - "30402:30303" - - "8547:8547" + - "8547:8545" - "9002:9002" validator-node3: <<: *validator-common hostname: node3 depends_on: - init-config: - condition: service_completed_successfully validator-node0: condition: service_healthy entrypoint: ["/scripts/entrypoint.sh", "validator"] @@ -264,7 +255,7 @@ services: - HEALTHCHECK_MODE=ready ports: - "30403:30303" - - "8548:8548" + - "8548:8545" - "9003:9002" prometheus: From c6047c3dd5aec2fc03060d2eaea62a578d7f519c Mon Sep 17 00:00:00 2001 From: Jacob Gadikian Date: Mon, 27 Apr 2026 23:16:26 +0200 Subject: [PATCH 02/16] fix dashboard and enhance rpc --- Cargo.lock | 1 + crates/node/ledger/src/lib.rs | 10 ++++++++++ crates/node/runner/Cargo.toml | 1 + crates/node/runner/src/runner.rs | 23 +++++++++++++++-------- docker/scripts/devnet-stats.sh | 6 +++--- 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa389cc..cc17d6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3642,6 +3642,7 @@ dependencies = [ "kora-dkg", "kora-domain", "kora-executor", + "kora-indexer", "kora-ledger", "kora-marshal", "kora-overlay", diff --git a/crates/node/ledger/src/lib.rs b/crates/node/ledger/src/lib.rs index 3a13a89..adc072f 100644 --- a/crates/node/ledger/src/lib.rs +++ b/crates/node/ledger/src/lib.rs @@ -131,6 +131,16 @@ impl LedgerView { self.genesis_block.clone() } + /// Return a cloneable handle to the underlying QMDB state. + /// + /// The returned handle shares the same backing store and can be used + /// for read-only queries (balance, nonce, code, storage) without + /// acquiring the ledger mutex. + pub async fn qmdb_state(&self) -> QmdbState { + let inner = self.inner.lock().await; + inner.qmdb.state() + } + /// Submit a transaction into the mempool. pub async fn submit_tx(&self, tx: Tx) -> bool { let inner = self.inner.lock().await; diff --git a/crates/node/runner/Cargo.toml b/crates/node/runner/Cargo.toml index bc77b10..64e2b56 100644 --- a/crates/node/runner/Cargo.toml +++ b/crates/node/runner/Cargo.toml @@ -13,6 +13,7 @@ kora-consensus.workspace = true kora-domain.workspace = true kora-dkg.workspace = true kora-executor.workspace = true +kora-indexer.workspace = true kora-ledger.workspace = true kora-marshal.workspace = true kora-overlay.workspace = true diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index 8e99335..478cfac 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -154,16 +154,8 @@ impl ProductionRunner { use commonware_runtime::Runner; use kora_transport::NetworkConfigExt; - let rpc_config = self.rpc_config.clone(); - let executor = tokio::Runner::default(); executor.start(|context| async move { - // Start RPC server if configured - if let Some((state, addr)) = rpc_config { - let rpc = kora_rpc::RpcServer::new(state, addr); - drop(rpc.start()); - } - let validator_key = config .validator_key() .map_err(|e| anyhow::anyhow!("failed to load validator key: {}", e))?; @@ -210,6 +202,21 @@ impl NodeRunner for ProductionRunner { .await .context("init qmdb")?; + if let Some((node_state, addr)) = &self.rpc_config { + let qmdb_state = state.qmdb_state().await; + let block_index = Arc::new(kora_indexer::BlockIndex::new()); + let indexed_provider = + kora_rpc::IndexedStateProvider::new(block_index, qmdb_state); + let rpc = kora_rpc::RpcServer::with_state_provider( + node_state.clone(), + *addr, + self.chain_id, + indexed_provider, + ); + drop(rpc.start()); + info!(addr = %addr, "RPC server started with live state provider"); + } + let ledger = LedgerService::new(state.clone()); spawn_ledger_observers(ledger.clone(), context.clone()); diff --git a/docker/scripts/devnet-stats.sh b/docker/scripts/devnet-stats.sh index a422a90..093d2e6 100755 --- a/docker/scripts/devnet-stats.sh +++ b/docker/scripts/devnet-stats.sh @@ -86,7 +86,7 @@ render() { if [[ "$status" != "{}" ]]; then # Parse with single jq call local parsed - parsed=$(echo "$status" | jq -r '[.uptime_secs // 0, .current_view // 0, .finalized_count // 0, .nullified_count // 0, .proposed_count // 0, .is_leader // false] | @tsv' 2>/dev/null) + parsed=$(echo "$status" | jq -r '[.uptimeSecs // .uptime_secs // 0, .currentView // .current_view // 0, .finalizedCount // .finalized_count // 0, .nullifiedCount // .nullified_count // 0, .proposedCount // .proposed_count // 0, .isLeader // .is_leader // false] | @tsv' 2>/dev/null) if [[ -n "$parsed" ]]; then read -r uptime view finalized nullified proposed leader <<< "$parsed" @@ -100,7 +100,7 @@ render() { [[ $uptime -gt $max_uptime ]] && max_uptime=$uptime [[ $view -gt $max_view ]] && max_view=$view total_finalized=$finalized - ((healthy_count++)) + ((++healthy_count)) local uptime_str=$(format_uptime "$uptime") local leader_str="-" @@ -125,7 +125,7 @@ render() { else printf "│ ${CYAN}%-5s${NC} │ ${RED}offline${NC} │ - │ - │ - │ - │ - │ - │ - │\n" "$i" fi - ((i++)) + ((++i)) done <<< "$all_status" echo -e "└───────┴──────────┴────────────┴──────────┴────────────┴────────────┴────────────┴────────────┴────────┘" From 552c2a7e9c93e8a95b5093fe9b4b6510f3b4a3c6 Mon Sep 17 00:00:00 2001 From: JaeLeex Date: Tue, 28 Apr 2026 15:23:14 -0400 Subject: [PATCH 03/16] feat(rpc): implement eth_call + eth_estimateGas; fix eth_getCode for unknown addrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements three RPC fixes that block forge/cannon/most JSON-RPC clients against Kora 0.1.0. Closes #2; addresses the deferred-deploy items from #1. ## Bugs fixed A. `eth_getCode` returned `-32001 account not found` for unknown addresses, violating EIP-1474 which mandates `0x` for accounts with no code. Many tools branch on `getCode === '0x'` to decide "is this a contract?" — the error broke that check. B. `eth_call` returned `-32004 method not implemented`. `IndexedStateProvider` inherited the trait default but never wired the existing `RevmExecutor` to serve calls. C. `eth_estimateGas` had the same root cause as B. ## Approach - Added `RevmExecutor::simulate_call` that mirrors `BlockExecutor::execute` for one transaction but discards state changes — reuses the existing `StateDbAdapter` `DatabaseRef` impl, the same `Context::new(db, spec_id)` + `modify_*_chained` pattern, and the same `evm.replay()` invocation. - Added `RevmExecutor::estimate_gas` that binary-searches over `simulate_call` in the `[21k, block_gas_limit]` range, capped at 25 iterations. - Added `kora_executor::CallParams` — a small parameter struct so the executor doesn't depend on rpc-layer types. `IndexedStateProvider` converts `CallRequest` → `CallParams` inline. - Added a new `ExecutionError::Revert(Bytes)` variant so revert payloads can be passed through to clients (Solidity ABI-decodable). - Patched `IndexedStateProvider::code()` to map `AccountNotFound` and the empty/zero code-hash sentinels to `Ok(Bytes::new())` per EIP-1474. - Wired `Arc` into `IndexedStateProvider` via constructor; added `with_chain_id` convenience for tests and one-off setups. Runner passes the executor at startup (one extra `Arc::new` line). ## What's still broken (out of scope — separate issue) - `eth_blockNumber` returns 0 — the indexer's `head_block_number()` doesn't advance because consensus → indexer wiring is missing (`BlockContextProvider` / `FinalizedReporter` are not feeding `index.insert_block(...)`). - `eth_getBlockByNumber("latest")` returns null — same root cause. - `eth_getTransactionReceipt`/`eth_getTransactionByHash` return null — same root cause; receipts are indexed only after blocks insert. - `net_peerCount = 0` on devnet — believed intentional for single-node, but worth documenting. - `eth_sendRawTransaction` accepts duplicate and far-future nonces without rejection — mempool-level validation gap. ## Pre-existing clippy fixes CI clippy was failing on test code unrelated to this PR. Fixed the smallest set of warnings touching the same crates so this PR turns CI green: - `crates/node/rpc/src/server.rs` — `default_http_addr` made `const fn`. - `crates/node/rpc/src/config.rs` — three `redundant_clone` test warnings fixed by also asserting against the original (preserves test intent). - `crates/node/executor/src/{context,revm}.rs` — five `field_reassign_with_default` warnings rewritten as struct-update-syntax with `..Header::default()`. ## Verification ``` cargo check --workspace # green cargo clippy -p kora-executor -p kora-rpc -p kora-runner --all-targets -- -Dwarn # green cargo test -p kora-executor -p kora-rpc --lib # 86 passing ``` End-to-end (against a local Kora running this branch): ``` curl -X POST -H 'Content-Type: application/json' \ --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0xdead...","latest"],"id":1}' \ http://localhost:8545 # Expect: {"result":"0x"} not -32001 curl -X POST ... eth_call ... # expect a result, not -32004 ``` Then `bunx cannon build packages/exchange/cannonfile.toml --rpc-url http://localhost:8545` should complete, unblocking the deferred ClearingHouse + MarketRegistry stack from issue #1. Closes #2. Refs #1. --- Cargo.lock | 2 + crates/node/executor/src/context.rs | 14 +- crates/node/executor/src/error.rs | 9 +- crates/node/executor/src/lib.rs | 2 +- crates/node/executor/src/revm.rs | 190 +++++++++++++++++++++--- crates/node/rpc/Cargo.toml | 2 + crates/node/rpc/src/config.rs | 4 + crates/node/rpc/src/indexed_provider.rs | 142 +++++++++++++++--- crates/node/rpc/src/server.rs | 2 +- crates/node/runner/src/runner.rs | 3 +- 10 files changed, 325 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc17d6c..99736d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3606,10 +3606,12 @@ dependencies = [ name = "kora-rpc" version = "0.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "async-trait", "axum", "jsonrpsee", + "kora-executor", "kora-indexer", "kora-traits", "parking_lot", diff --git a/crates/node/executor/src/context.rs b/crates/node/executor/src/context.rs index c7ef8aa..b71b697 100644 --- a/crates/node/executor/src/context.rs +++ b/crates/node/executor/src/context.rs @@ -93,12 +93,14 @@ mod tests { #[test] fn parent_block_from_header() { - let mut header = Header::default(); - header.number = 100; - header.timestamp = 1234567890; - header.gas_limit = 30_000_000; - header.gas_used = 15_000_000; - header.base_fee_per_gas = Some(1000); + let header = Header { + number: 100, + timestamp: 1234567890, + gas_limit: 30_000_000, + gas_used: 15_000_000, + base_fee_per_gas: Some(1000), + ..Header::default() + }; let hash = B256::repeat_byte(0xab); let parent = ParentBlock::from_header(&header, hash); diff --git a/crates/node/executor/src/error.rs b/crates/node/executor/src/error.rs index 66b8b01..e17a047 100644 --- a/crates/node/executor/src/error.rs +++ b/crates/node/executor/src/error.rs @@ -1,6 +1,6 @@ //! Execution error types. -use alloy_primitives::B256; +use alloy_primitives::{B256, Bytes}; use revm::database_interface::DBErrorMarker; use thiserror::Error; @@ -19,6 +19,13 @@ pub enum ExecutionError { #[error("transaction execution failed: {0}")] TxExecution(String), + /// Call reverted with output data. + /// + /// Carries the revert payload so callers can decode `Error(string)` + /// or custom-error data per Solidity ABI conventions. + #[error("execution reverted")] + Revert(Bytes), + /// Invalid transaction. #[error("invalid transaction: {0}")] InvalidTx(String), diff --git a/crates/node/executor/src/lib.rs b/crates/node/executor/src/lib.rs index 2ba3db5..0ffc67d 100644 --- a/crates/node/executor/src/lib.rs +++ b/crates/node/executor/src/lib.rs @@ -21,7 +21,7 @@ mod outcome; pub use outcome::{ExecutionOutcome, ExecutionReceipt}; mod revm; -pub use revm::{RevmExecutor, calculate_base_fee}; +pub use revm::{CallParams, RevmExecutor, calculate_base_fee}; mod traits; pub use traits::BlockExecutor; diff --git a/crates/node/executor/src/revm.rs b/crates/node/executor/src/revm.rs index e9ea688..9992744 100644 --- a/crates/node/executor/src/revm.rs +++ b/crates/node/executor/src/revm.rs @@ -171,6 +171,157 @@ impl Default for RevmExecutor { } } +/// Parameters for a read-only EVM call (used by `eth_call`/`eth_estimateGas`). +/// +/// Mirrors the JSON-RPC `CallRequest` shape but with non-optional defaults +/// resolved, so the executor does not depend on rpc-layer types. +#[derive(Clone, Debug, Default)] +pub struct CallParams { + /// Caller address (`from`). Defaults to zero address if unspecified. + pub from: alloy_primitives::Address, + /// Recipient address. `None` is contract creation (CREATE). + pub to: Option, + /// Value to transfer. + pub value: U256, + /// Calldata (or initcode for CREATE). + pub data: Bytes, + /// Gas limit. `None` falls back to the block gas limit. + pub gas_limit: Option, + /// Effective gas price. + pub gas_price: u128, + /// Caller nonce. + pub nonce: u64, +} + +impl RevmExecutor { + /// Run a read-only call simulation against `state`. + /// + /// Mirrors [`BlockExecutor::execute`] for one transaction but discards + /// the resulting state changes — used to serve `eth_call` and as a + /// primitive for [`Self::estimate_gas`]. + /// + /// # Errors + /// + /// - [`ExecutionError::Revert`] with the revert output if the call reverted. + /// - [`ExecutionError::TxExecution`] for halts and revm-internal errors. + /// - [`ExecutionError::InvalidTx`] if the request fails to build a valid + /// `TxEnv` (bad gas/value combination, etc.). + pub fn simulate_call( + &self, + state: &S, + params: CallParams, + context: &BlockContext, + ) -> Result { + let adapter = StateDbAdapter::new(state.clone()); + let db = State::builder().with_database_ref(adapter).build(); + + type Db = State>>; + let ctx: Context, Journal>, ()> = + Context::new(db, self.config.spec_id); + let ctx = ctx + .modify_cfg_chained(|cfg| { + cfg.chain_id = self.config.chain_id; + }) + .modify_block_chained(|blk: &mut BlockEnv| { + blk.number = U256::from(context.header.number); + blk.timestamp = U256::from(context.header.timestamp); + blk.beneficiary = context.header.beneficiary; + blk.gas_limit = context.header.gas_limit; + blk.basefee = context.header.base_fee_per_gas.unwrap_or_default(); + blk.prevrandao = Some(context.prevrandao); + }); + + let mut evm = ctx.build_mainnet(); + + let tx_env = + call_params_to_tx_env(¶ms, self.config.chain_id, context.header.gas_limit)?; + evm.set_tx(tx_env); + + let result_and_state = + evm.replay().map_err(|e| ExecutionError::TxExecution(format!("{:?}", e)))?; + + match result_and_state.result { + ExecutionResult::Success { output, .. } => match output { + Output::Call(bytes) => Ok(bytes), + Output::Create(bytes, _) => Ok(bytes), + }, + ExecutionResult::Revert { output, .. } => Err(ExecutionError::Revert(output)), + ExecutionResult::Halt { reason, .. } => { + Err(ExecutionError::TxExecution(format!("halt: {:?}", reason))) + } + } + } + + /// Estimate gas via binary search over [`Self::simulate_call`]. + /// + /// Confirms the call succeeds at the upper bound (request gas or block + /// gas limit), then binary-searches for the minimum gas at which the + /// call still succeeds. Bounded to 25 iterations (≈log2(30M)). + /// + /// # Errors + /// + /// Same as [`Self::simulate_call`] — propagates a revert/halt at the + /// upper bound; otherwise returns the converged minimum. + pub fn estimate_gas( + &self, + state: &S, + mut params: CallParams, + context: &BlockContext, + ) -> Result { + let upper = + params.gas_limit.unwrap_or(context.header.gas_limit).min(context.header.gas_limit); + + // Confirm the call succeeds at the upper bound. + params.gas_limit = Some(upper); + self.simulate_call(state, params.clone(), context)?; + + let mut lo = 21_000u64; + let mut hi = upper; + let mut best = upper; + let mut iters = 0u32; + while lo + 1 < hi && iters < 25 { + iters += 1; + let mid = lo + (hi - lo) / 2; + params.gas_limit = Some(mid); + match self.simulate_call(state, params.clone(), context) { + Ok(_) => { + best = mid; + hi = mid; + } + Err(_) => { + lo = mid; + } + } + } + Ok(best) + } +} + +/// Build a revm [`revm::context::TxEnv`] from a [`CallParams`]. +/// +/// Differs from [`decode_tx_env`] in that there is no signature to recover — +/// caller comes from `params.from` directly. +fn call_params_to_tx_env( + params: &CallParams, + chain_id: u64, + default_gas_limit: u64, +) -> Result { + let kind = params.to.map_or(TxKind::Create, TxKind::Call); + let gas_limit = params.gas_limit.unwrap_or(default_gas_limit); + + revm::context::TxEnv::builder() + .caller(params.from) + .gas_limit(gas_limit) + .gas_price(params.gas_price) + .value(params.value) + .data(params.data.clone()) + .nonce(params.nonce) + .chain_id(Some(chain_id)) + .kind(kind) + .build() + .map_err(|e| ExecutionError::InvalidTx(format!("build call tx env: {:?}", e))) +} + /// Calculate the expected base fee for the next block (EIP-1559). pub fn calculate_base_fee( parent_base_fee: u64, @@ -580,8 +731,7 @@ mod tests { GasLimitBounds { min: 5000, max: 30_000_000, max_delta_divisor: 1024 }, )); - let mut header = Header::default(); - header.gas_limit = 1000; + let mut header = Header { gas_limit: 1000, ..Header::default() }; assert!( >::validate_header(&executor, &header) .is_err() @@ -613,11 +763,13 @@ mod tests { base_fee_per_gas: None, }; - let mut header = Header::default(); - header.parent_hash = B256::repeat_byte(1); - header.number = 101; - header.timestamp = 1001; - header.gas_limit = 30_000_000; + let mut header = Header { + parent_hash: B256::repeat_byte(1), + number: 101, + timestamp: 1001, + gas_limit: 30_000_000, + ..Header::default() + }; assert!(executor.validate_header_against_parent(&header, &parent).is_ok()); @@ -638,11 +790,13 @@ mod tests { base_fee_per_gas: None, }; - let mut header = Header::default(); - header.parent_hash = B256::repeat_byte(1); - header.number = 101; - header.timestamp = 999; - header.gas_limit = 30_000_000; + let header = Header { + parent_hash: B256::repeat_byte(1), + number: 101, + timestamp: 999, + gas_limit: 30_000_000, + ..Header::default() + }; assert!(executor.validate_header_against_parent(&header, &parent).is_err()); } @@ -660,11 +814,13 @@ mod tests { base_fee_per_gas: None, }; - let mut header = Header::default(); - header.parent_hash = B256::repeat_byte(1); - header.number = 101; - header.timestamp = 1001; - header.gas_limit = 35_000_000; + let header = Header { + parent_hash: B256::repeat_byte(1), + number: 101, + timestamp: 1001, + gas_limit: 35_000_000, + ..Header::default() + }; assert!(executor.validate_header_against_parent(&header, &parent).is_err()); } diff --git a/crates/node/rpc/Cargo.toml b/crates/node/rpc/Cargo.toml index b604782..4cee98e 100644 --- a/crates/node/rpc/Cargo.toml +++ b/crates/node/rpc/Cargo.toml @@ -20,6 +20,7 @@ tower-http = { version = "0.6", features = ["cors"] } jsonrpsee = { version = "0.24", features = ["server", "macros"] } # Alloy types +alloy-consensus.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } # Async @@ -39,6 +40,7 @@ tracing.workspace = true parking_lot = "0.12" # Kora crates +kora-executor = { path = "../executor" } kora-indexer = { path = "../../storage/indexer" } kora-traits = { path = "../../storage/traits" } diff --git a/crates/node/rpc/src/config.rs b/crates/node/rpc/src/config.rs index 51acefa..0679038 100644 --- a/crates/node/rpc/src/config.rs +++ b/crates/node/rpc/src/config.rs @@ -267,6 +267,8 @@ mod tests { assert_eq!(cloned.rate_limit.requests_per_second, 250); assert_eq!(cloned.max_connections, 75); + // Reference `original` after the clone so the lint sees both values used. + assert_eq!(original.rate_limit.requests_per_second, 250); } #[test] @@ -276,6 +278,7 @@ mod tests { assert_eq!(cloned.allowed_origins, vec!["*"]); assert_eq!(cloned.max_age, 86400); + assert_eq!(original.allowed_origins, vec!["*"]); } #[test] @@ -285,6 +288,7 @@ mod tests { assert_eq!(cloned.requests_per_second, 500); assert_eq!(cloned.burst_size, 1000); + assert_eq!(original.requests_per_second, 500); } #[test] diff --git a/crates/node/rpc/src/indexed_provider.rs b/crates/node/rpc/src/indexed_provider.rs index 97e345e..5e14f8a 100644 --- a/crates/node/rpc/src/indexed_provider.rs +++ b/crates/node/rpc/src/indexed_provider.rs @@ -5,8 +5,10 @@ use std::sync::Arc; +use alloy_consensus::Header; use alloy_primitives::{Address, B256, Bytes, U64, U256}; use async_trait::async_trait; +use kora_executor::{BlockContext, CallParams, RevmExecutor}; use kora_indexer::{BlockIndex, IndexedBlock, IndexedReceipt, IndexedTransaction, LogFilter}; use kora_traits::{StateDbError, StateDbRead}; @@ -14,33 +16,46 @@ use crate::{ error::RpcError, state_provider::StateProvider, types::{ - BlockNumberOrTag, BlockTag, BlockTransactions, RpcBlock, RpcLog, RpcLogFilter, + BlockNumberOrTag, BlockTag, BlockTransactions, CallRequest, RpcBlock, RpcLog, RpcLogFilter, RpcTransaction, RpcTransactionReceipt, }, }; /// State provider that combines indexed block data with live state queries. /// -/// Uses [`BlockIndex`] for block, transaction, and receipt lookups, and -/// delegates account state queries (balance, nonce, code, storage) to -/// a generic state database implementation. +/// Uses [`BlockIndex`] for block, transaction, and receipt lookups, delegates +/// account state queries (balance, nonce, code, storage) to a generic state +/// database implementation, and uses a [`RevmExecutor`] to serve `eth_call` +/// and `eth_estimateGas` against the live state. #[derive(Debug)] pub struct IndexedStateProvider { index: Arc, state: S, + executor: Arc, } impl IndexedStateProvider { - /// Creates a new indexed state provider. + /// Creates a new indexed state provider with an explicit executor. #[must_use] - pub const fn new(index: Arc, state: S) -> Self { - Self { index, state } + pub const fn new(index: Arc, state: S, executor: Arc) -> Self { + Self { index, state, executor } + } + + /// Creates a new indexed state provider with a default executor for the + /// given chain id. + #[must_use] + pub fn with_chain_id(index: Arc, state: S, chain_id: u64) -> Self { + Self::new(index, state, Arc::new(RevmExecutor::new(chain_id))) } } impl Clone for IndexedStateProvider { fn clone(&self) -> Self { - Self { index: Arc::clone(&self.index), state: self.state.clone() } + Self { + index: Arc::clone(&self.index), + state: self.state.clone(), + executor: Arc::clone(&self.executor), + } } } @@ -67,8 +82,22 @@ impl StateProvider for IndexedStateProvi address: Address, _block: Option, ) -> Result { - let code_hash = self.state.code_hash(&address).await.map_err(state_error_to_rpc)?; - self.state.code(&code_hash).await.map_err(state_error_to_rpc) + // EIP-1474: `eth_getCode` MUST return `0x` for unknown accounts and + // for EOAs without code, NOT an error. Many tools branch on + // `getCode === '0x'` to decide "is this a contract?". + let code_hash = match self.state.code_hash(&address).await { + Ok(hash) => hash, + Err(StateDbError::AccountNotFound(_)) => return Ok(Bytes::new()), + Err(e) => return Err(state_error_to_rpc(e)), + }; + if code_hash == B256::ZERO || code_hash == alloy_primitives::KECCAK256_EMPTY { + return Ok(Bytes::new()); + } + match self.state.code(&code_hash).await { + Ok(bytes) => Ok(bytes), + Err(StateDbError::CodeNotFound(_)) => Ok(Bytes::new()), + Err(e) => Err(state_error_to_rpc(e)), + } } async fn storage( @@ -105,6 +134,26 @@ impl StateProvider for IndexedStateProvi Ok(self.index.head_block_number()) } + async fn call( + &self, + request: CallRequest, + block: Option, + ) -> Result { + let block_ctx = self.block_context_for(block)?; + let params = call_request_to_params(request); + self.executor.simulate_call(&self.state, params, &block_ctx).map_err(execution_error_to_rpc) + } + + async fn estimate_gas( + &self, + request: CallRequest, + block: Option, + ) -> Result { + let block_ctx = self.block_context_for(block)?; + let params = call_request_to_params(request); + self.executor.estimate_gas(&self.state, params, &block_ctx).map_err(execution_error_to_rpc) + } + async fn get_logs(&self, filter: RpcLogFilter) -> Result, RpcError> { let from_block = filter.from_block.as_ref().map(|b| self.resolve_block_number(b)).transpose()?; @@ -166,6 +215,63 @@ impl IndexedStateProvider { BlockTag::Earliest => Ok(0), } } + + /// Build a `BlockContext` for the requested block tag, falling back to a + /// generous default when no block is indexed yet (so `eth_call` against + /// a fresh chain still works). + fn block_context_for(&self, block: Option) -> Result { + let block_num = match block { + Some(b) => self.resolve_block_number(&b)?, + None => self.index.head_block_number(), + }; + if let Some(indexed) = self.index.get_block_by_number(block_num) { + let header = Header { + number: indexed.number, + timestamp: indexed.timestamp, + gas_limit: indexed.gas_limit, + base_fee_per_gas: indexed.base_fee_per_gas, + ..Header::default() + }; + Ok(BlockContext::new(header, indexed.parent_hash, B256::ZERO)) + } else { + let header = Header { + number: 0, + timestamp: 0, + gas_limit: 30_000_000, + base_fee_per_gas: Some(1_000_000_000), + ..Header::default() + }; + Ok(BlockContext::new(header, B256::ZERO, B256::ZERO)) + } + } +} + +/// Convert a JSON-RPC `CallRequest` into executor `CallParams`. +fn call_request_to_params(req: CallRequest) -> CallParams { + let gas_price: u128 = + req.gas_price.or(req.max_fee_per_gas).unwrap_or_default().try_into().unwrap_or(u128::MAX); + CallParams { + from: req.from.unwrap_or_default(), + to: req.to, + value: req.value.unwrap_or_default(), + data: req.input.or(req.data).unwrap_or_default(), + gas_limit: req.gas.map(|g| g.to::()), + gas_price, + nonce: req.nonce.map(|n| n.to::()).unwrap_or(0), + } +} + +/// Map an executor `ExecutionError` into an `RpcError` for the JSON-RPC layer. +fn execution_error_to_rpc(err: kora_executor::ExecutionError) -> RpcError { + use kora_executor::ExecutionError as E; + match err { + E::Revert(data) => RpcError::ExecutionFailed(format!("execution reverted: {data}")), + E::TxExecution(msg) | E::InvalidTx(msg) | E::TxDecode(msg) | E::BlockValidation(msg) => { + RpcError::ExecutionFailed(msg) + } + E::State(s) => state_error_to_rpc(s), + E::CodeNotFound(h) => RpcError::StateError(format!("code not found: {h}")), + } } fn state_error_to_rpc(err: StateDbError) -> RpcError { @@ -346,7 +452,7 @@ mod tests { #[tokio::test] async fn test_balance() { let index = Arc::new(BlockIndex::new()); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let balance = provider.balance(Address::ZERO, None).await.unwrap(); assert_eq!(balance, U256::from(1000)); @@ -355,7 +461,7 @@ mod tests { #[tokio::test] async fn test_nonce() { let index = Arc::new(BlockIndex::new()); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let nonce = provider.nonce(Address::ZERO, None).await.unwrap(); assert_eq!(nonce, 42); @@ -367,7 +473,7 @@ mod tests { let block_hash = B256::repeat_byte(1); index.insert_block(create_test_block(1, block_hash), vec![], vec![]); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let block = provider.block_by_number(BlockNumberOrTag::Number(U64::from(1))).await.unwrap(); assert!(block.is_some()); @@ -380,7 +486,7 @@ mod tests { let block_hash = B256::repeat_byte(1); index.insert_block(create_test_block(1, block_hash), vec![], vec![]); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let block = provider.block_by_hash(block_hash).await.unwrap(); assert!(block.is_some()); @@ -398,7 +504,7 @@ mod tests { vec![], ); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let tx = provider.transaction_by_hash(tx_hash).await.unwrap(); assert!(tx.is_some()); @@ -416,7 +522,7 @@ mod tests { vec![create_test_receipt(tx_hash, block_hash, 1)], ); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let receipt = provider.receipt_by_hash(tx_hash).await.unwrap(); assert!(receipt.is_some()); @@ -430,7 +536,7 @@ mod tests { let index = Arc::new(BlockIndex::new()); index.insert_block(create_test_block(5, B256::repeat_byte(5)), vec![], vec![]); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let num = provider.block_number().await.unwrap(); assert_eq!(num, 5); @@ -441,7 +547,7 @@ mod tests { let index = Arc::new(BlockIndex::new()); index.insert_block(create_test_block(10, B256::repeat_byte(10)), vec![], vec![]); - let provider = IndexedStateProvider::new(index, MockState); + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let block = provider.block_by_number(BlockNumberOrTag::Tag(BlockTag::Latest)).await.unwrap(); diff --git a/crates/node/rpc/src/server.rs b/crates/node/rpc/src/server.rs index c8cc608..a6007f1 100644 --- a/crates/node/rpc/src/server.rs +++ b/crates/node/rpc/src/server.rs @@ -93,7 +93,7 @@ impl std::fmt::Debug for RpcServer { } /// Compute a default HTTP address by incrementing the port of the given address. -fn default_http_addr(jsonrpc_addr: SocketAddr) -> SocketAddr { +const fn default_http_addr(jsonrpc_addr: SocketAddr) -> SocketAddr { SocketAddr::new(jsonrpc_addr.ip(), jsonrpc_addr.port() + 1) } diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index 478cfac..e3ae295 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -205,8 +205,9 @@ impl NodeRunner for ProductionRunner { if let Some((node_state, addr)) = &self.rpc_config { let qmdb_state = state.qmdb_state().await; let block_index = Arc::new(kora_indexer::BlockIndex::new()); + let rpc_executor = Arc::new(RevmExecutor::new(self.chain_id)); let indexed_provider = - kora_rpc::IndexedStateProvider::new(block_index, qmdb_state); + kora_rpc::IndexedStateProvider::new(block_index, qmdb_state, rpc_executor); let rpc = kora_rpc::RpcServer::with_state_provider( node_state.clone(), *addr, From 974e672e70723ec1b07fb9d4ac4036a8bff61e38 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Tue, 28 Apr 2026 22:17:01 +0200 Subject: [PATCH 04/16] Update dependencies and enhance DKG protocol - Updated versions of various dependencies in Cargo.lock, including `alloy-consensus`, `alloy-eips`, and `commonware` packages. - Refactored DKG protocol to improve clarity and functionality, including renaming methods for better understanding of dealer logs. - Introduced a new configuration for MCP servers in `.cursor/mcp.json`. - Adjusted buffer pool references in network and transport modules to use `CacheRef` for improved memory management. - Enhanced unit tests for DKG to ensure comprehensive coverage of dealer log requirements and finalization conditions. --- .cursor/mcp.json | 7 + Cargo.lock | 2065 ++++++++--------- Cargo.toml | 28 +- bin/keygen/src/dkg_deal.rs | 20 +- crates/e2e/src/harness.rs | 12 +- crates/network/marshal/Cargo.toml | 1 - crates/network/marshal/src/actor.rs | 42 +- crates/network/marshal/src/archive.rs | 11 +- crates/network/marshal/src/broadcast.rs | 12 +- crates/network/marshal/src/peers.rs | 22 +- crates/network/marshal/tests/common/mod.rs | 2 +- crates/network/marshal/tests/integration.rs | 38 +- crates/network/transport/src/builder.rs | 8 +- crates/network/transport/src/ext.rs | 10 +- .../network/transport/src/network_provider.rs | 6 +- crates/node/dkg/src/ceremony.rs | 29 +- crates/node/dkg/src/protocol.rs | 33 +- crates/node/dkg/src/tests.rs | 17 +- crates/node/dkg/src/transport.rs | 8 +- crates/node/domain/src/block.rs | 2 +- crates/node/executor/src/revm.rs | 2 +- crates/node/ledger/src/lib.rs | 25 +- crates/node/reporters/src/lib.rs | 2 +- crates/node/runner/src/app.rs | 21 +- crates/node/runner/src/runner.rs | 35 +- crates/node/runner/src/scheme.rs | 15 +- crates/node/service/src/service.rs | 4 +- crates/node/service/src/stubs.rs | 11 +- crates/node/simplex/src/config.rs | 15 +- crates/node/simplex/src/engine.rs | 13 +- crates/node/simplex/src/pool.rs | 14 +- crates/storage/backend/src/accounts.rs | 47 +- crates/storage/backend/src/backend.rs | 46 +- crates/storage/backend/src/code.rs | 45 +- crates/storage/backend/src/config.rs | 33 +- crates/storage/backend/src/storage.rs | 49 +- crates/storage/backend/src/types.rs | 33 +- crates/storage/handlers/src/adapter.rs | 4 +- docker/Justfile | 6 +- docker/scripts/devnet-run.sh | 63 +- docker/scripts/devnet-stats.sh | 24 +- 41 files changed, 1460 insertions(+), 1420 deletions(-) create mode 100644 .cursor/mcp.json diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..969b0be --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,7 @@ +{ + "mcpServers": { + "commonware-library": { + "url": "https://mcp.commonware.xyz" + } + } +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cc17d6c..26ad1fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -41,9 +42,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.30" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f374d3c6d729268bbe2d0e0ff992bb97898b2df756691a62ee1d5f0506bc39" +checksum = "84e0378e959aa6a885897522080a990e80eb317f1e9a222a604492ea50e13096" dependencies = [ "alloy-primitives", "num_enum", @@ -52,16 +53,42 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.5.2" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f16daaf7e1f95f62c6c3bf8a3fc3d78b08ae9777810c0bb5e94966c7cd57ef0" +dependencies = [ + "alloy-eips 1.8.3", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 1.8.3", + "alloy-trie", + "alloy-tx-macros 1.8.3", + "auto_impl", + "borsh", + "c-kzg", + "derive_more", + "either", + "k256", + "once_cell", + "secp256k1 0.30.0", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-consensus" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1958f0294ecc05ebe7b3c9a8662a3e221c2523b7f2bcd94c7a651efbd510bf" +checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" dependencies = [ - "alloy-eips", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.0.1", "alloy-trie", - "alloy-tx-macros", + "alloy-tx-macros 2.0.1", "auto_impl", "borsh", "c-kzg", @@ -69,7 +96,7 @@ dependencies = [ "either", "k256", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "secp256k1 0.30.0", "serde", "serde_json", @@ -79,15 +106,15 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.5.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f752e99497ddc39e22d547d7dfe516af10c979405a034ed90e69b914b7dddeae" +checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 2.0.1", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.0.1", "serde", ] @@ -132,21 +159,22 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" +checksum = "ec6ae911a2fc304a7cb80a79fb7bed6d1474aed4e7c203df1f8ff538f64fc78d" dependencies = [ "alloy-primitives", "alloy-rlp", "borsh", + "once_cell", "serde", ] [[package]] name = "alloy-eips" -version = "1.5.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "813a67f87e56b38554d18b182616ee5006e8e2bf9df96a0df8bf29dff1d52e3f" +checksum = "e6ef28c9fdad22d4eec52d894f5f2673a0895f1e5ef196734568e68c0f6caca8" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -154,28 +182,48 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 1.8.3", + "auto_impl", + "borsh", + "c-kzg", + "derive_more", + "either", + "serde", + "serde_with", + "sha2 0.10.9", +] + +[[package]] +name = "alloy-eips" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" +dependencies = [ + "alloy-eip2124", + "alloy-eip2930", + "alloy-eip7702", + "alloy-eip7928", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 2.0.1", "auto_impl", "borsh", "c-kzg", "derive_more", "either", - "ethereum_ssz", - "ethereum_ssz_derive", "serde", "serde_with", "sha2 0.10.9", - "thiserror 2.0.18", ] [[package]] name = "alloy-evm" -version = "0.27.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1582933a9fc27c0953220eb4f18f6492ff577822e9a8d848890ff59f6b4f5beb" +checksum = "c1ceeea6dcbbcd4e546b27700763a6f6c3b3fee30054209884f521078b6fda4f" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 2.0.1", + "alloy-eips 2.0.1", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -183,10 +231,9 @@ dependencies = [ "alloy-sol-types", "auto_impl", "derive_more", - "op-alloy", - "op-revm", "revm", "thiserror 2.0.18", + "tracing", ] [[package]] @@ -204,9 +251,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8708475665cc00e081c085886e68eada2f64cfa08fc668213a9231655093d4de" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -214,80 +261,39 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-json-rpc" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dd146b3de349a6ffaa4e4e319ab3a90371fb159fb0bddeb1c7bbe8b1792eff" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "http", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c12278ffbb8872dfba3b2f17d8ea5e8503c2df5155d9bc5ee342794bde505c3" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "auto_impl", - "derive_more", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror 2.0.18", -] - [[package]] name = "alloy-network-primitives" -version = "1.5.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833037c04917bc2031541a60e8249e4ab5500e24c637c1c62e95e963a655d66f" +checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 2.0.1", + "alloy-eips 2.0.1", "alloy-primitives", - "alloy-serde", + "alloy-serde 2.0.1", "serde", ] [[package]] name = "alloy-primitives" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b88cf92ed20685979ed1d8472422f0c6c2d010cec77caf63aaa7669cc1a7bc2" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", - "foldhash", + "foldhash 0.2.0", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.14.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "rand 0.9.2", + "rand 0.9.4", "rapidhash", "ruint", "rustc-hash", @@ -295,47 +301,11 @@ dependencies = [ "sha3", ] -[[package]] -name = "alloy-provider" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eafa840b0afe01c889a3012bb2fde770a544f74eab2e2870303eb0a5fb869c48" -dependencies = [ - "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-signer", - "alloy-sol-types", - "alloy-transport", - "async-stream", - "async-trait", - "auto_impl", - "dashmap", - "either", - "futures", - "futures-utils-wasm", - "lru", - "parking_lot", - "pin-project", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "wasmtimer", -] - [[package]] name = "alloy-rlp" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -344,78 +314,44 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.12" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", -] - -[[package]] -name = "alloy-rpc-client" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12768ae6303ec764905a8a7cd472aea9072f9f9c980d18151e26913da8ae0123" -dependencies = [ - "alloy-json-rpc", - "alloy-primitives", - "alloy-transport", - "futures", - "pin-project", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower 0.5.3", - "tracing", - "wasmtimer", -] - -[[package]] -name = "alloy-rpc-types-any" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1cf5a093e437dfd62df48e480f24e1a3807632358aad6816d7a52875f1c04aa" -dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", + "syn 2.0.117", ] [[package]] name = "alloy-rpc-types-engine" -version = "1.5.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336ef381c7409f23c69f6e79bddc1917b6e832cff23e7a5cf84b9381d53582e6" +checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 2.0.1", + "alloy-eips 2.0.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.0.1", "derive_more", - "ethereum_ssz", - "ethereum_ssz_derive", - "rand 0.8.5", "serde", "strum", ] [[package]] name = "alloy-rpc-types-eth" -version = "1.5.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e97603095020543a019ab133e0e3dc38cd0819f19f19bdd70c642404a54751" +checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" dependencies = [ - "alloy-consensus", + "alloy-consensus 2.0.1", "alloy-consensus-any", - "alloy-eips", + "alloy-eips 2.0.1", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 2.0.1", "alloy-sol-types", "itertools 0.14.0", "serde", @@ -426,9 +362,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.5.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946a0d413dbb5cd9adba0de5f8a1a34d5b77deda9b69c1d7feed8fc875a1aa26" +checksum = "11ece63b89294b8614ab3f483560c08d016930f842bf36da56bf0b764a15c11e" dependencies = [ "alloy-primitives", "serde", @@ -436,57 +372,53 @@ dependencies = [ ] [[package]] -name = "alloy-signer" -version = "1.5.2" +name = "alloy-serde" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f7481dc8316768f042495eaf305d450c32defbc9bce09d8bf28afcd956895bb" +checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" dependencies = [ "alloy-primitives", - "async-trait", - "auto_impl", - "either", - "elliptic-curve", - "k256", - "thiserror 2.0.18", + "serde", + "serde_json", ] [[package]] name = "alloy-sol-macro" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fa1ca7e617c634d2bd9fa71f9ec8e47c07106e248b9fcbd3eaddc13cabd625" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c00c0c3a75150a9dc7c8c679ca21853a137888b4e1c5569f92d7e2b15b5102" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro-error2", "proc-macro2", "quote", "sha3", - "syn 2.0.114", + "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297db260eb4d67c105f68d6ba11b8874eec681caec5505eab8fbebee97f790bc" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" dependencies = [ "const-hex", "dunce", @@ -494,25 +426,25 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b91b13181d3bcd23680fd29d7bc861d1f33fbe90fdd0af67162434aeba902d" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", - "winnow", + "winnow 0.7.15", ] [[package]] name = "alloy-sol-types" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc442cc2a75207b708d481314098a0f8b6f7b58e3148dd8d8cc7407b0d6f9385" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -521,54 +453,43 @@ dependencies = [ ] [[package]] -name = "alloy-transport" -version = "1.5.2" +name = "alloy-trie" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f169b85eb9334871db986e7eaf59c58a03d86a30cc68b846573d47ed0656bb" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" dependencies = [ - "alloy-json-rpc", - "auto_impl", - "base64", + "alloy-primitives", + "alloy-rlp", "derive_more", - "futures", - "futures-utils-wasm", - "parking_lot", + "nybbles", "serde", - "serde_json", + "smallvec", "thiserror 2.0.18", - "tokio", - "tower 0.5.3", "tracing", - "url", - "wasmtimer", ] [[package]] -name = "alloy-trie" -version = "0.9.3" +name = "alloy-tx-macros" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428aa0f0e0658ff091f8f667c406e034b431cb10abd39de4f507520968acc499" +checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" dependencies = [ - "alloy-primitives", - "alloy-rlp", - "arrayvec", - "derive_more", - "nybbles", - "serde", - "smallvec", - "tracing", + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] name = "alloy-tx-macros" -version = "1.5.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ceac797eb8a56bdf5ab1fab353072c17d472eab87645ca847afe720db3246d" +checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" dependencies = [ - "darling 0.21.3", + "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -588,9 +509,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -603,15 +524,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -638,9 +559,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "ark-bls12-381" @@ -772,7 +693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -810,7 +731,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -899,7 +820,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -909,7 +830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -919,7 +840,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -929,7 +850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -944,43 +865,9 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" dependencies = [ - "serde", "zeroize", ] -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - [[package]] name = "async-trait" version = "0.1.89" @@ -989,7 +876,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1016,7 +903,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1027,9 +914,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.4" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -1038,9 +925,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.37.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -1050,9 +937,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", "bytes", @@ -1151,9 +1038,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitvec" @@ -1169,16 +1056,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.3" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.3.0", "zeroize", ] @@ -1214,32 +1101,33 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byte-slice-cast" @@ -1255,18 +1143,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "2.1.5" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" dependencies = [ "blst", "cc", @@ -1285,9 +1173,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.55" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -1315,7 +1203,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -1333,9 +1221,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "num-traits", @@ -1383,9 +1271,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.56" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1393,9 +1281,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.56" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -1405,27 +1293,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -1438,24 +1326,22 @@ checksum = "5417da527aa9bf6a1e10a781231effd1edd3ee82f27d5f8529ac9b279babce96" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "commonware-broadcast" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf3ce8bf336b0ed47c5596de852dca00af534663376108e30b5d1554a226762" +checksum = "afe7362c8942f20f0eab11756932b7d1c41f4cc99e142cb563e17a04b40095d5" dependencies = [ - "bytes", "commonware-codec", "commonware-cryptography", "commonware-macros", "commonware-p2p", "commonware-runtime", "commonware-utils", - "futures", "prometheus-client", "thiserror 2.0.18", "tracing", @@ -1463,29 +1349,55 @@ dependencies = [ [[package]] name = "commonware-codec" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "932a888c88c67ed0a2e35861cb3106f0238826ef99af3fd79e1da5cfa682fe6b" +checksum = "f06e32817f35fb517ceb6102d984f9a85fde85666c96f053638e323b8597f2f7" dependencies = [ "bytes", + "cfg-if", + "commonware-macros", "paste", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "thiserror 2.0.18", ] +[[package]] +name = "commonware-coding" +version = "2026.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e60b2b324de47773c3d4af4d83bfc76d2c287ba7f2d6eb8c2aa5068f877b4bb" +dependencies = [ + "bytes", + "commonware-codec", + "commonware-cryptography", + "commonware-macros", + "commonware-math", + "commonware-parallel", + "commonware-storage", + "commonware-utils", + "num-rational", + "rand 0.8.6", + "rand_core 0.6.4", + "rayon", + "reed-solomon-simd", + "thiserror 2.0.18", +] + [[package]] name = "commonware-consensus" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e59253a02e2157aa1a487b1506d2453627b7b6d0434aede81e9ff5ee1232b700" +checksum = "4a67374d82c69e870105f010b895f1768952df5d0fa0d0550dedf162de16f44e" dependencies = [ "bytes", "cfg-if", "commonware-broadcast", "commonware-codec", + "commonware-coding", "commonware-cryptography", "commonware-macros", + "commonware-math", "commonware-p2p", "commonware-parallel", "commonware-resolver", @@ -1495,18 +1407,19 @@ dependencies = [ "futures", "pin-project", "prometheus-client", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rand_distr", + "rayon", "thiserror 2.0.18", "tracing", ] [[package]] name = "commonware-cryptography" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf84215a142eb4b5c633ef048fc3d39210e74dd4c00943667421ad65fecbc86" +checksum = "f0f09b55dd5510c3b7613a573606a41961c2788709ffde053d4e644bec0bff2c" dependencies = [ "anyhow", "aws-lc-rs", @@ -1516,6 +1429,7 @@ dependencies = [ "cfg-if", "chacha20poly1305", "commonware-codec", + "commonware-macros", "commonware-math", "commonware-parallel", "commonware-utils", @@ -1524,8 +1438,10 @@ dependencies = [ "ecdsa", "ed25519-consensus", "getrandom 0.2.17", + "num-rational", + "num-traits", "p256", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "rand_core 0.6.4", "sha2 0.10.9", @@ -1536,28 +1452,36 @@ dependencies = [ [[package]] name = "commonware-macros" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fba0d95a5099b688944998efe0c2c65d7929355e9c9203ff3db5e5fffba4c1a" +checksum = "4cd313d9299e13bf995999c7a0ed8cc570eef6cd0972fcffc6e2c682cfba6663" +dependencies = [ + "commonware-macros-impl", + "tokio", +] + +[[package]] +name = "commonware-macros-impl" +version = "2026.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc385e646d91b5397c93816985421878d627839834f7cf85a8da2ac9f8b98b7" dependencies = [ - "futures", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", - "toml 0.9.11+spec-1.1.0", - "tracing", - "tracing-subscriber 0.3.22", + "syn 2.0.117", + "toml 0.9.12+spec-1.1.0", ] [[package]] name = "commonware-math" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634c6488502ca976d1013b230e34a46f3998883d583f1ea4483b14a81231eb3e" +checksum = "e5d834ed8bf601e113b9cd2ba284dd0e95adf558933dc727f52f8879434cb286" dependencies = [ "bytes", "commonware-codec", + "commonware-macros", "commonware-parallel", "commonware-utils", "rand_core 0.6.4", @@ -1565,14 +1489,14 @@ dependencies = [ [[package]] name = "commonware-p2p" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27005677f181e0324c339800da0729f987a20904dcb3d4dd7c188a0334f7e387" +checksum = "2c93f730bf4aaeadffb589eb50e431f7a5f8495c158dda1127c61f6e74c597ab" dependencies = [ - "bytes", "commonware-codec", "commonware-cryptography", "commonware-macros", + "commonware-parallel", "commonware-runtime", "commonware-stream", "commonware-utils", @@ -1583,7 +1507,7 @@ dependencies = [ "num-rational", "num-traits", "prometheus-client", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rand_distr", "thiserror 2.0.18", @@ -1592,19 +1516,20 @@ dependencies = [ [[package]] name = "commonware-parallel" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ce87c7e89435d3730a4fe18c83f0502d8953fae90e62bd1384164c9239786e" +checksum = "db29306a40279ad54d06b42c623a05fbb5333546b5003c921796bc856b423106" dependencies = [ "cfg-if", + "commonware-macros", "rayon", ] [[package]] name = "commonware-resolver" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c8f548df3773f33ec9d45f55327d239eb49688fcbd481a73f50a59c7002699f" +checksum = "00dfe9932b33cc31a04b7c68bf543eef7e6d04b70cf6a53880d03407d60a01e6" dependencies = [ "bytes", "commonware-codec", @@ -1616,18 +1541,17 @@ dependencies = [ "commonware-utils", "futures", "prometheus-client", - "rand 0.8.5", + "rand 0.8.6", "thiserror 2.0.18", "tracing", ] [[package]] name = "commonware-runtime" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "699b7fd1f4a028442313f1c98cca47765bc4294e56884ca21efe0de5e38f89a5" +checksum = "8d4ae4c804d0d9c1df615b1c7846e4e5e64fdb4228685487cb67803e67388411" dependencies = [ - "async-lock", "axum", "bytes", "cfg-if", @@ -1637,6 +1561,7 @@ dependencies = [ "commonware-parallel", "commonware-utils", "criterion", + "crossbeam-queue", "futures", "getrandom 0.2.17", "governor", @@ -1645,7 +1570,7 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry_sdk", "prometheus-client", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rayon", "sha2 0.10.9", @@ -1654,15 +1579,16 @@ dependencies = [ "tokio", "tracing", "tracing-opentelemetry", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] name = "commonware-storage" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e97b508b9e1527fabe81f91776ff7bfac13cd98759d2a8877c369143d9c4f6" +checksum = "ca1c42cf37aa27c3f83c31591cad4f1d96317eff81c1eb442e17191adcf9b413" dependencies = [ + "ahash", "anyhow", "bytes", "cfg-if", @@ -1683,11 +1609,10 @@ dependencies = [ [[package]] name = "commonware-stream" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd88ee96b41da08924fe093f7be3299988698f58a768340f045e8a6120a4f234" +checksum = "b6c15b328d5f05fff750368a71e2307c380cce52df9712a5b30199b8af4e700c" dependencies = [ - "bytes", "chacha20poly1305", "commonware-codec", "commonware-cryptography", @@ -1695,7 +1620,7 @@ dependencies = [ "commonware-runtime", "commonware-utils", "futures", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "thiserror 2.0.18", "x25519-dalek", @@ -1704,13 +1629,14 @@ dependencies = [ [[package]] name = "commonware-utils" -version = "0.0.65" +version = "2026.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dff0b48bf9e9ca3efdfd14aba67dbbc8da1f6132329c477e562592747dd052a" +checksum = "faf66d7b5c89489d71b0669bda2e014e7c9ffcdf65629ae31886efe5361b1179" dependencies = [ "bytes", "cfg-if", "commonware-codec", + "commonware-macros", "futures", "getrandom 0.2.17", "hashbrown 0.16.1", @@ -1718,29 +1644,22 @@ dependencies = [ "num-integer", "num-rational", "num-traits", + "parking_lot", "pin-project", - "rand 0.8.5", + "rand 0.8.6", "thiserror 2.0.18", + "tokio", "zeroize", ] -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "const-hex" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "proptest", "serde_core", ] @@ -1753,11 +1672,12 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" dependencies = [ "const_format_proc_macros", + "konst", ] [[package]] @@ -1796,6 +1716,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1811,6 +1741,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -1822,9 +1761,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc-fast" @@ -1888,6 +1827,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1939,7 +1887,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "fiat-crypto", "rustc_version 0.4.1", @@ -1955,7 +1903,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1973,73 +1921,37 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[package]] -name = "darling_core" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.114", + "darling_core", + "darling_macro", ] [[package]] name = "darling_core" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", "serde", "strsim", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core 0.21.3", + "darling_core", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2068,9 +1980,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -2089,13 +2001,13 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2117,7 +2029,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.117", "unicode-xid", ] @@ -2150,7 +2062,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2210,7 +2122,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2268,7 +2180,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2287,67 +2199,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "ethereum_serde_utils" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc1355dbb41fbbd34ec28d4fb2a57d9a70c67ac3c19f6a5ca4d4a176b9e997a" -dependencies = [ - "alloy-primitives", - "hex", - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "ethereum_ssz" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c" -dependencies = [ - "alloy-primitives", - "ethereum_serde_utils", - "itertools 0.13.0", - "serde", - "serde_derive", - "smallvec", - "typenum", -] - -[[package]] -name = "ethereum_ssz_derive" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "eyre" version = "0.6.12" @@ -2360,9 +2211,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fastrlp" @@ -2415,17 +2266,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand 0.8.6", "rustc-hex", "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -2470,9 +2333,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -2485,9 +2348,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -2495,15 +2358,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -2512,32 +2375,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -2547,9 +2410,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -2559,16 +2422,9 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - [[package]] name = "generic-array" version = "0.14.7" @@ -2602,11 +2458,24 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + [[package]] name = "glob" version = "0.3.3" @@ -2630,7 +2499,7 @@ dependencies = [ "parking_lot", "portable-atomic", "quanta", - "rand 0.9.2", + "rand 0.9.4", "smallvec", "spinning_top", "web-time", @@ -2659,7 +2528,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.13.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -2696,6 +2565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", + "foldhash 0.1.5", ] [[package]] @@ -2706,11 +2576,17 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.2.0", "serde", "serde_core", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "heck" version = "0.5.0" @@ -2794,9 +2670,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -2809,7 +2685,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -2817,15 +2692,14 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", "hyper-util", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", @@ -2849,14 +2723,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -2899,12 +2772,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -2912,9 +2786,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -2925,9 +2799,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -2939,15 +2813,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -2959,15 +2833,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -2978,6 +2852,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -2997,9 +2877,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3022,7 +2902,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3044,12 +2924,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -3065,15 +2945,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -3114,9 +2994,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jobserver" @@ -3130,10 +3010,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -3166,7 +3048,7 @@ dependencies = [ "http-body-util", "jsonrpsee-types", "parking_lot", - "rand 0.8.5", + "rand 0.8.6", "rustc-hash", "serde", "serde_json", @@ -3185,7 +3067,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3244,18 +3126,18 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] name = "keccak-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" +checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -3275,14 +3157,29 @@ dependencies = [ "kora-config", "kora-dkg", "kora-domain", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "toml 0.8.23", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "kora" version = "0.1.0" @@ -3302,7 +3199,7 @@ dependencies = [ "serde_json", "tokio", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -3348,7 +3245,7 @@ dependencies = [ "commonware-codec", "commonware-cryptography", "ed25519-consensus", - "rand 0.8.5", + "rand 0.8.6", "rstest", "serde", "serde_json", @@ -3361,7 +3258,7 @@ dependencies = [ name = "kora-consensus" version = "0.1.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.8.3", "alloy-primitives", "commonware-cryptography", "futures", @@ -3385,7 +3282,7 @@ dependencies = [ "commonware-cryptography", "commonware-utils", "kora-domain", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -3400,7 +3297,7 @@ dependencies = [ "commonware-runtime", "commonware-utils", "hex", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "serde", "serde_json", @@ -3413,8 +3310,8 @@ dependencies = [ name = "kora-domain" version = "0.1.0" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-evm", "alloy-primitives", "bytes", @@ -3433,7 +3330,7 @@ dependencies = [ name = "kora-e2e" version = "0.1.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.8.3", "alloy-primitives", "anyhow", "commonware-consensus", @@ -3455,7 +3352,7 @@ dependencies = [ "kora-reporters", "kora-simplex", "kora-transport-sim", - "rand 0.8.5", + "rand 0.8.6", "rstest", "thiserror 2.0.18", "tokio", @@ -3466,8 +3363,8 @@ dependencies = [ name = "kora-executor" version = "0.1.0" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-primitives", "alloy-rlp", "futures", @@ -3507,7 +3404,7 @@ dependencies = [ name = "kora-ledger" version = "0.1.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.8.3", "alloy-evm", "alloy-primitives", "commonware-cryptography", @@ -3540,11 +3437,10 @@ dependencies = [ "commonware-runtime", "commonware-storage", "commonware-utils", - "futures", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -3626,7 +3522,7 @@ dependencies = [ name = "kora-runner" version = "0.1.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.8.3", "alloy-primitives", "anyhow", "commonware-codec", @@ -3652,7 +3548,7 @@ dependencies = [ "kora-service", "kora-simplex", "kora-transport", - "rand 0.8.5", + "rand 0.8.6", "tracing", ] @@ -3731,7 +3627,7 @@ dependencies = [ "kora-service", "kora-transport", "prometheus-client", - "rand 0.8.5", + "rand 0.8.6", "thiserror 2.0.18", ] @@ -3739,15 +3635,15 @@ dependencies = [ name = "kora-txpool" version = "0.1.0" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-primitives", "alloy-rlp", "k256", "kora-domain", "kora-traits", "parking_lot", - "rand 0.8.5", + "rand 0.8.6", "rstest", "sha3", "thiserror 2.0.18", @@ -3761,11 +3657,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.180" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libm" @@ -3775,36 +3677,36 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "loadgen" version = "0.1.0" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-primitives", "clap", "eyre", "futures", "hex", "k256", - "rand 0.8.5", + "rand 0.8.6", "reqwest", "serde", "serde_json", "sha3", "tokio", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -3822,15 +3724,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "macro-string" version = "0.1.4" @@ -3839,7 +3732,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3859,9 +3752,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -3871,9 +3764,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -3882,9 +3775,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -3905,9 +3798,9 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -3956,9 +3849,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -4013,9 +3906,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -4023,21 +3916,21 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "nybbles" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5676b5c379cf5b03da1df2b3061c4a4e2aa691086a56ac923e08c143f53f59" +checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" dependencies = [ "alloy-rlp", "cfg-if", @@ -4060,143 +3953,29 @@ dependencies = [ name = "objc2-io-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" -dependencies = [ - "libc", - "objc2-core-foundation", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "op-alloy" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b8fee21003dd4f076563de9b9d26f8c97840157ef78593cd7f262c5ca99848" -dependencies = [ - "op-alloy-consensus", - "op-alloy-network", - "op-alloy-provider", - "op-alloy-rpc-types", - "op-alloy-rpc-types-engine", -] - -[[package]] -name = "op-alloy-consensus" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network", - "alloy-primitives", - "alloy-rlp", - "alloy-rpc-types-eth", - "alloy-serde", - "derive_more", - "serde", - "thiserror 2.0.18", -] - -[[package]] -name = "op-alloy-network" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4034183dca6bff6632e7c24c92e75ff5f0eabb58144edb4d8241814851334d47" -dependencies = [ - "alloy-consensus", - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-eth", - "alloy-signer", - "op-alloy-consensus", - "op-alloy-rpc-types", -] - -[[package]] -name = "op-alloy-provider" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6753d90efbaa8ea8bcb89c1737408ca85fa60d7adb875049d3f382c063666f86" -dependencies = [ - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-engine", - "alloy-transport", - "async-trait", - "op-alloy-rpc-types-engine", -] - -[[package]] -name = "op-alloy-rpc-types" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd87c6b9e5b6eee8d6b76f41b04368dca0e9f38d83338e5b00e730c282098a4" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "derive_more", - "op-alloy-consensus", - "serde", - "serde_json", - "thiserror 2.0.18", +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", ] [[package]] -name = "op-alloy-rpc-types-engine" -version = "0.23.1" +name = "once_cell" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727699310a18cdeed32da3928c709e2704043b6584ed416397d5da65694efc" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-rpc-types-engine", - "alloy-serde", - "derive_more", - "ethereum_ssz", - "ethereum_ssz_derive", - "op-alloy-consensus", - "serde", - "sha2 0.10.9", - "snap", - "thiserror 2.0.18", -] +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] -name = "op-revm" -version = "15.0.0" +name = "oorandom" +version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40" -dependencies = [ - "auto_impl", - "revm", - "serde", -] +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "opaque-debug" @@ -4206,9 +3985,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" dependencies = [ "bitflags", "cfg-if", @@ -4227,20 +4006,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", @@ -4277,9 +4056,9 @@ dependencies = [ [[package]] name = "opentelemetry-otlp" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" +checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" dependencies = [ "http", "opentelemetry", @@ -4316,7 +4095,7 @@ dependencies = [ "futures-util", "opentelemetry", "percent-encoding", - "rand 0.9.2", + "rand 0.9.4", "thiserror 2.0.18", "tokio", "tokio-stream", @@ -4359,15 +4138,9 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" version = "0.12.5" @@ -4405,9 +4178,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -4444,7 +4217,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4458,35 +4231,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkcs8" @@ -4500,9 +4267,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plotters" @@ -4538,7 +4305,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -4551,9 +4318,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -4573,6 +4340,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -4595,11 +4372,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -4621,7 +4398,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4635,9 +4412,9 @@ dependencies = [ [[package]] name = "prometheus-client" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4500adecd7af8e0e9f4dbce15cfee07ce913fbf6ad605cc468b83f2d531ee94" +checksum = "cca3d75b4566b9a29fe1ed623587fb058e826eb329a0be4b7c4da1ebb2d7a6ca" dependencies = [ "dtoa", "itoa", @@ -4653,20 +4430,20 @@ checksum = "9adf1691c04c0a5ff46ff8f262b58beb07b0dbb61f96f9f54f6cbd82106ed87f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "proptest" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", "bitflags", "num-traits", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -4695,7 +4472,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4721,9 +4498,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -4734,6 +4511,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -4742,9 +4525,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -4754,9 +4537,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -4809,7 +4592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -4823,9 +4606,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.2.1" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ "rustversion", ] @@ -4841,9 +4624,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -4859,6 +4642,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "readme-rustdocifier" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ad765b21a08b1a8e5cdce052719188a23772bcbefb3c439f0baaf62c56ceac" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -4868,6 +4657,18 @@ dependencies = [ "bitflags", ] +[[package]] +name = "reed-solomon-simd" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffef0520d30fbd4151fb20e262947ae47fb0ab276a744a19b6398438105a072" +dependencies = [ + "cpufeatures 0.2.17", + "fixedbitset", + "once_cell", + "readme-rustdocifier", +] + [[package]] name = "ref-cast" version = "1.0.25" @@ -4885,14 +4686,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -4902,9 +4703,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -4913,9 +4714,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -4967,9 +4768,9 @@ dependencies = [ [[package]] name = "revm" -version = "34.0.0" +version = "38.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" +checksum = "91202d39dbe8e8d10e9e8f2b76c30da68ecd1d25be69ba6d853ad0d03a3a398a" dependencies = [ "revm-bytecode", "revm-context", @@ -4986,9 +4787,9 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "8.0.0" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457" +checksum = "bdbb3a3d735efa94c91f2ef6bf20a35f99a77bc78f3e25bd758336901bdf9661" dependencies = [ "bitvec", "phf", @@ -4998,9 +4799,9 @@ dependencies = [ [[package]] name = "revm-context" -version = "13.0.0" +version = "16.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5" +checksum = "c5f68d928d8b228e0faeb1c6ed75c4fde7d124f1ddf9119b67e7a0ad4041237d" dependencies = [ "bitvec", "cfg-if", @@ -5015,9 +4816,9 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "14.0.0" +version = "17.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" +checksum = "1f3758e6167c4ba7a59a689c519a047edaefcd4c37d74f279b93ed87bc8aece4" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -5031,11 +4832,11 @@ dependencies = [ [[package]] name = "revm-database" -version = "10.0.0" +version = "13.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" +checksum = "c281a1f11d3bcb8c0bba1199ed6bcb001d1aeb3d4fb366819e14f88723989a4e" dependencies = [ - "alloy-eips", + "alloy-eips 1.8.3", "revm-bytecode", "revm-database-interface", "revm-primitives", @@ -5045,9 +4846,9 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "9.0.0" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d" +checksum = "d89efb9832a4e3742bb4ded5f7fe5bf905e8860e69427d4dfec153484fc6d304" dependencies = [ "auto_impl", "either", @@ -5060,9 +4861,9 @@ dependencies = [ [[package]] name = "revm-handler" -version = "15.0.0" +version = "18.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" +checksum = "783e903d6922b7f5f9a940d1bb229530502d2924b1aed9d5ca5a94ebf065d460" dependencies = [ "auto_impl", "derive-where", @@ -5079,9 +4880,9 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "15.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" +checksum = "8216ad58422090d0daa9eb430e0a081f7ad07e7fd30681dee71f8420c99624e0" dependencies = [ "auto_impl", "either", @@ -5097,9 +4898,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "32.0.0" +version = "35.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" +checksum = "1ece9f41b69658c15d748288a4dbdfc06a63f3ce93d983af440de3f1631dce6a" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -5110,9 +4911,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "32.0.0" +version = "34.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c1285c848d240678bf69cb0f6179ff5a4aee6fc8e921d89708087197a0aff3" +checksum = "a346a8cc6c8c39bd65306641c692191299c0a7b63d38810e39e8fe9b92378660" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -5125,6 +4926,7 @@ dependencies = [ "cfg-if", "k256", "p256", + "revm-context-interface", "revm-primitives", "ripemd", "secp256k1 0.31.1", @@ -5133,9 +4935,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "22.0.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba580c56a8ec824a64f8a1683577876c2e1dbe5247044199e9b881421ad5dcf9" +checksum = "0c99bda77d9661521ba0b4bc04558c6692074f01e65dd420fa3b893033d9b8a2" dependencies = [ "alloy-primitives", "num_enum", @@ -5145,9 +4947,9 @@ dependencies = [ [[package]] name = "revm-state" -version = "9.0.0" +version = "11.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" +checksum = "c32490ed687dba31c3c882beb8c20408bdd30ef96690d8f145b0ee9a87040bfe" dependencies = [ "alloy-eip7928", "bitflags", @@ -5231,15 +5033,15 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.117", "unicode-ident", ] [[package]] name = "ruint" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" dependencies = [ "alloy-rlp", "ark-ff 0.3.0", @@ -5254,8 +5056,8 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.5", - "rand 0.9.2", + "rand 0.8.6", + "rand 0.9.4", "rlp", "ruint-macro", "serde_core", @@ -5271,9 +5073,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc-hex" @@ -5296,14 +5098,14 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -5314,9 +5116,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "once_cell", "rustls-pki-types", @@ -5327,18 +5129,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.9" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", "rustls-pki-types", @@ -5365,9 +5167,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -5380,9 +5182,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -5401,9 +5203,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -5439,7 +5241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys 0.10.1", "serde", ] @@ -5451,7 +5253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.2", + "rand 0.9.4", "secp256k1-sys 0.11.0", ] @@ -5475,12 +5277,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -5488,9 +5290,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -5507,9 +5309,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "semver-parser" @@ -5547,7 +5349,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5556,7 +5358,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "itoa", "memchr", "serde", @@ -5586,9 +5388,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -5607,17 +5409,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -5626,14 +5428,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.21.3", + "darling", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5653,7 +5455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -5665,7 +5467,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.9.0", "opaque-debug", ] @@ -5677,15 +5479,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -5693,9 +5495,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" +checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" dependencies = [ "cc", "cfg-if", @@ -5757,20 +5559,14 @@ dependencies = [ "serde", ] -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5785,7 +5581,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "sha1", ] @@ -5850,7 +5646,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5878,9 +5674,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -5889,14 +5685,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.4" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2379beea9476b89d0237078be761cf8e012d92d5ae4ae0c9a329f974838870fc" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5916,7 +5712,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5935,12 +5731,12 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5962,12 +5758,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -5999,7 +5795,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6010,7 +5806,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6033,9 +5829,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -6054,9 +5850,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -6064,9 +5860,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -6084,9 +5880,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.49.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -6101,13 +5897,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6170,17 +5966,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", - "serde_spanned 1.0.4", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -6201,39 +5997,48 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.2", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.2", ] [[package]] @@ -6244,15 +6049,15 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a286e33f82f8a1ee2df63f4fa35c0becf4a85a0cb03091a15fd7bf0b402dc94a" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" dependencies = [ "async-trait", "base64", @@ -6271,9 +6076,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c55a2d6a14174563de34409c9f92ff981d006f56da9c6ecd40d9d4a31500b0" +checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" dependencies = [ "bytes", "prost", @@ -6362,7 +6167,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6398,7 +6203,7 @@ dependencies = [ "tracing", "tracing-core", "tracing-log", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "web-time", ] @@ -6423,9 +6228,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -6450,9 +6255,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -6480,15 +6285,15 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-xid" @@ -6596,18 +6401,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -6618,23 +6432,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6642,45 +6452,65 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] [[package]] -name = "wasmtimer" -version = "0.4.3" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" dependencies = [ - "futures", - "js-sys", - "parking_lot", - "pin-utils", - "slab", - "wasm-bindgen", + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver 1.0.28", ] [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -6794,7 +6624,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6805,7 +6635,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6883,16 +6713,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -6910,31 +6731,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -6952,84 +6756,42 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -7037,16 +6799,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" +name = "winnow" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -7056,12 +6821,100 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver 1.0.28", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wyz" @@ -7086,9 +6939,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -7097,54 +6950,54 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.37" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.37" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -7165,14 +7018,14 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -7181,9 +7034,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -7192,20 +7045,20 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1966f8ac2c1f76987d69a74d0e0f929241c10e78136434e3be70ff7f58f64214" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index 2b3b9aa..3361f0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,18 +74,18 @@ kora-rpc = { path = "crates/node/rpc" } kora-txpool = { path = "crates/node/txpool" } kora-e2e = { path = "crates/e2e" } -commonware-p2p = "0.0.65" -commonware-utils = "0.0.65" -commonware-codec = "0.0.65" -commonware-stream = "0.0.65" -commonware-macros = "0.0.65" -commonware-storage = "0.0.65" -commonware-runtime = "0.0.65" -commonware-resolver = "0.0.65" -commonware-parallel = "0.0.65" -commonware-broadcast = "0.0.65" -commonware-consensus = "0.0.65" -commonware-cryptography = "0.0.65" +commonware-p2p = "2026.4.0" +commonware-utils = "2026.4.0" +commonware-codec = "2026.4.0" +commonware-stream = "2026.4.0" +commonware-macros = "2026.4.0" +commonware-storage = "2026.4.0" +commonware-runtime = "2026.4.0" +commonware-resolver = "2026.4.0" +commonware-parallel = "2026.4.0" +commonware-broadcast = "2026.4.0" +commonware-consensus = "2026.4.0" +commonware-cryptography = "2026.4.0" # Alloy alloy-primitives = "1.0" @@ -94,8 +94,8 @@ alloy-eips = "1.0" alloy-rlp = "0.3" # Execution -revm = { version = "34.0.0", default-features = false } -alloy-evm = { version = "0.27.0", default-features = false } +revm = { version = "38.0.0", default-features = false } +alloy-evm = { version = "0.34.0", default-features = false } # Async tokio = { version = "1", features = ["full"] } diff --git a/bin/keygen/src/dkg_deal.rs b/bin/keygen/src/dkg_deal.rs index 9009734..e98df5a 100644 --- a/bin/keygen/src/dkg_deal.rs +++ b/bin/keygen/src/dkg_deal.rs @@ -76,6 +76,15 @@ pub(crate) fn run(args: DkgDealArgs) -> Result<()> { .try_collect() .map_err(|_| eyre::eyre!("Duplicate participants"))?; + let participant_keys: Vec = participants_set + .iter() + .map(|pk| { + let mut bytes = Vec::new(); + pk.write(&mut bytes); + hex::encode(bytes) + }) + .collect(); + let mut rng = rand::rngs::OsRng; tracing::info!("Generating BLS threshold key shares"); @@ -98,15 +107,6 @@ pub(crate) fn run(args: DkgDealArgs) -> Result<()> { "Generated group public key and polynomial" ); - let participant_keys: Vec = participants - .iter() - .map(|pk| { - let mut bytes = Vec::new(); - pk.write(&mut bytes); - hex::encode(bytes) - }) - .collect(); - for (i, pk) in participants.iter().enumerate() { let share = shares.get_value(pk).ok_or_else(|| eyre::eyre!("Missing share for node{}", i))?; @@ -126,7 +126,7 @@ pub(crate) fn run(args: DkgDealArgs) -> Result<()> { let output_path = node_dir.join("output.json"); fs::write(&output_path, serde_json::to_string_pretty(&output_json)?)?; - let share_json = ShareJson { index: i as u32, secret: hex::encode(&share_bytes) }; + let share_json = ShareJson { index: share.index.get(), secret: hex::encode(&share_bytes) }; let share_path = node_dir.join("share.key"); fs::write(&share_path, serde_json::to_string_pretty(&share_json)?)?; diff --git a/crates/e2e/src/harness.rs b/crates/e2e/src/harness.rs index 4f039f2..0784be1 100644 --- a/crates/e2e/src/harness.rs +++ b/crates/e2e/src/harness.rs @@ -296,7 +296,7 @@ async fn start_single_node( partition_prefix: &str, ) -> anyhow::Result { let quota = DefaultQuota::init(); - let buffer_pool = DefaultPool::init(); + let page_cache = DefaultPool::init(context); let block_cfg = block_codec_cfg(); let mut control = { @@ -312,7 +312,6 @@ async fn start_single_node( // Initialize ledger let state = LedgerView::init( context.with_label(&format!("state_{index}")), - buffer_pool.clone(), format!("{partition_prefix}-qmdb-{index}"), bootstrap.genesis_alloc.clone(), ) @@ -340,7 +339,7 @@ async fn start_single_node( control.clone(), manager, scheme.clone(), - buffer_pool.clone(), + page_cache.clone(), block_cfg, channels.marshal.blocks, channels.marshal.backfill, @@ -384,13 +383,14 @@ async fn start_single_node( replay_buffer: NZUsize!(1024 * 1024), write_buffer: NZUsize!(1024 * 1024), leader_timeout: Duration::from_secs(1), - notarization_timeout: Duration::from_secs(2), - nullify_retry: Duration::from_secs(5), + certification_timeout: Duration::from_secs(2), + timeout_retry: Duration::from_secs(5), fetch_timeout: Duration::from_secs(1), activity_timeout: ViewDelta::new(20), skip_timeout: ViewDelta::new(10), fetch_concurrent: 8, - buffer_pool, + page_cache, + forwarding: simplex::ForwardingPolicy::Disabled, }, ); engine.start(channels.simplex.votes, channels.simplex.certs, channels.simplex.resolver); diff --git a/crates/network/marshal/Cargo.toml b/crates/network/marshal/Cargo.toml index 5605a7b..557dae1 100644 --- a/crates/network/marshal/Cargo.toml +++ b/crates/network/marshal/Cargo.toml @@ -21,7 +21,6 @@ commonware-resolver.workspace = true commonware-runtime.workspace = true commonware-storage.workspace = true commonware-utils.workspace = true -futures.workspace = true rand.workspace = true rand_core.workspace = true diff --git a/crates/network/marshal/src/actor.rs b/crates/network/marshal/src/actor.rs index 83dd5d7..675df92 100644 --- a/crates/network/marshal/src/actor.rs +++ b/crates/network/marshal/src/actor.rs @@ -8,9 +8,9 @@ use std::num::{NonZeroU64, NonZeroUsize}; use commonware_consensus::{ Block, marshal::{ - Mailbox, - actor::Actor, - config::Config, + Config, + core::{Actor, Mailbox}, + standard::Standard, store::{Blocks, Certificates}, }, simplex::scheme::Scheme, @@ -18,7 +18,7 @@ use commonware_consensus::{ }; use commonware_cryptography::certificate::Provider; use commonware_parallel::Sequential; -use commonware_runtime::{Clock, Metrics, Spawner, Storage, buffer::PoolRef}; +use commonware_runtime::{BufferPooler, Clock, Metrics, Spawner, Storage, buffer::paged::CacheRef}; use commonware_utils::{Acknowledgement, NZU64, NZUsize}; use rand_core::CryptoRngCore; @@ -99,14 +99,18 @@ impl ActorInitializer { finalizations_by_height: FC, finalized_blocks: FB, provider: P, - buffer_pool: PoolRef, + page_cache: CacheRef, block_codec_config: B::Cfg, - ) -> (Actor, Mailbox, Height) + ) -> ( + Actor, P, FC, FB, FixedEpocher, Sequential, A>, + Mailbox>, + Height, + ) where - E: CryptoRngCore + Spawner + Metrics + Clock + Storage, + E: BufferPooler + CryptoRngCore + Spawner + Metrics + Clock + Storage, B: Block, - P: Provider>, - FC: Certificates, + P: Provider>, + FC: Certificates, FB: Blocks, A: Acknowledgement, { @@ -117,12 +121,13 @@ impl ActorInitializer { mailbox_size: Self::DEFAULT_MAILBOX_SIZE, view_retention_timeout: Self::DEFAULT_VIEW_RETENTION_TIMEOUT, prunable_items_per_section: Self::DEFAULT_PRUNABLE_ITEMS_PER_SECTION, - buffer_pool, + page_cache, replay_buffer: Self::DEFAULT_REPLAY_BUFFER, key_write_buffer: Self::DEFAULT_KEY_WRITE_BUFFER, value_write_buffer: Self::DEFAULT_VALUE_WRITE_BUFFER, block_codec_config, max_repair: Self::DEFAULT_MAX_REPAIR, + max_pending_acks: NZUsize!(1024), strategy: Sequential, }; @@ -139,15 +144,19 @@ impl ActorInitializer { finalizations_by_height: FC, finalized_blocks: FB, provider: P, - buffer_pool: PoolRef, + page_cache: CacheRef, block_codec_config: B::Cfg, partition_prefix: impl Into, - ) -> (Actor, Mailbox, Height) + ) -> ( + Actor, P, FC, FB, FixedEpocher, Sequential, A>, + Mailbox>, + Height, + ) where - E: CryptoRngCore + Spawner + Metrics + Clock + Storage, + E: BufferPooler + CryptoRngCore + Spawner + Metrics + Clock + Storage, B: Block, - P: Provider>, - FC: Certificates, + P: Provider>, + FC: Certificates, FB: Blocks, A: Acknowledgement, { @@ -158,12 +167,13 @@ impl ActorInitializer { mailbox_size: Self::DEFAULT_MAILBOX_SIZE, view_retention_timeout: Self::DEFAULT_VIEW_RETENTION_TIMEOUT, prunable_items_per_section: Self::DEFAULT_PRUNABLE_ITEMS_PER_SECTION, - buffer_pool, + page_cache, replay_buffer: Self::DEFAULT_REPLAY_BUFFER, key_write_buffer: Self::DEFAULT_KEY_WRITE_BUFFER, value_write_buffer: Self::DEFAULT_VALUE_WRITE_BUFFER, block_codec_config, max_repair: Self::DEFAULT_MAX_REPAIR, + max_pending_acks: NZUsize!(1024), strategy: Sequential, }; diff --git a/crates/network/marshal/src/archive.rs b/crates/network/marshal/src/archive.rs index 5a49051..4250713 100644 --- a/crates/network/marshal/src/archive.rs +++ b/crates/network/marshal/src/archive.rs @@ -3,7 +3,7 @@ use std::num::{NonZeroU16, NonZeroU64, NonZeroUsize}; use commonware_codec::Codec; -use commonware_runtime::{Clock, Metrics, Spawner, Storage, buffer::PoolRef}; +use commonware_runtime::{BufferPooler, Clock, Metrics, Spawner, Storage, buffer::paged::CacheRef}; use commonware_storage::archive::immutable::{Archive, Config}; use commonware_utils::{NZU16, NZU64, NZUsize, sequence::Array}; @@ -65,7 +65,7 @@ impl ArchiveInitializer { codec_config: V::Cfg, ) -> Result, commonware_storage::archive::Error> where - E: Spawner + Storage + Metrics + Clock + Clone, + E: BufferPooler + Spawner + Storage + Metrics + Clock + Clone, K: Array, V: Codec + Send + Sync, { @@ -77,7 +77,8 @@ impl ArchiveInitializer { freezer_table_resize_frequency: Self::DEFAULT_FREEZER_TABLE_RESIZE_FREQUENCY, freezer_table_resize_chunk_size: Self::DEFAULT_FREEZER_TABLE_RESIZE_CHUNK_SIZE, freezer_key_partition: format!("{prefix}-freezer-key"), - freezer_key_buffer_pool: PoolRef::new( + freezer_key_page_cache: CacheRef::from_pooler( + &ctx, Self::DEFAULT_PAGE_SIZE, Self::DEFAULT_PAGE_CACHE_SIZE, ), @@ -103,7 +104,7 @@ impl ArchiveInitializer { codec_config: V::Cfg, ) -> Result, commonware_storage::archive::Error> where - E: Spawner + Storage + Metrics + Clock + Clone, + E: BufferPooler + Spawner + Storage + Metrics + Clock + Clone, K: Array, V: Codec + Send + Sync, { @@ -118,7 +119,7 @@ impl ArchiveInitializer { codec_config: V::Cfg, ) -> Result, commonware_storage::archive::Error> where - E: Spawner + Storage + Metrics + Clock + Clone, + E: BufferPooler + Spawner + Storage + Metrics + Clock + Clone, K: Array, V: Codec + Send + Sync, { diff --git a/crates/network/marshal/src/broadcast.rs b/crates/network/marshal/src/broadcast.rs index 17a885d..2b07737 100644 --- a/crates/network/marshal/src/broadcast.rs +++ b/crates/network/marshal/src/broadcast.rs @@ -3,7 +3,8 @@ use commonware_broadcast::buffered::{Config, Engine, Mailbox}; use commonware_codec::Codec; use commonware_cryptography::{Committable, Digestible, PublicKey}; -use commonware_runtime::{Clock, Metrics, Spawner}; +use commonware_p2p::Provider; +use commonware_runtime::{BufferPooler, Clock, Metrics, Spawner}; /// Initializes the buffered broadcast engine with sensible defaults. #[derive(Debug, Clone, Copy)] @@ -24,15 +25,17 @@ impl BroadcastInitializer { /// Initializes the buffered broadcast engine. /// /// Returns the engine and a mailbox for sending messages. - pub fn init( + pub fn init( ctx: E, public_key: P, + peer_provider: D, codec_config: M::Cfg, - ) -> (Engine, Mailbox) + ) -> (Engine, Mailbox) where - E: Clock + Spawner + Metrics, + E: BufferPooler + Clock + Spawner + Metrics, P: PublicKey, M: Committable + Digestible + Codec, + D: Provider, { let config = Config { public_key, @@ -40,6 +43,7 @@ impl BroadcastInitializer { deque_size: Self::DEFAULT_DEQUE_SIZE, priority: Self::DEFAULT_PRIORITY, codec_config, + peer_provider, }; Engine::new(ctx, config) } diff --git a/crates/network/marshal/src/peers.rs b/crates/network/marshal/src/peers.rs index d292391..b34903a 100644 --- a/crates/network/marshal/src/peers.rs +++ b/crates/network/marshal/src/peers.rs @@ -4,16 +4,16 @@ use std::time::Duration; use commonware_consensus::{ Block, - marshal::{ - ingress::handler::{Message, Request}, - resolver::p2p::Config, + marshal::resolver::{ + handler::{Message, Request}, + p2p::Config, }, }; use commonware_cryptography::PublicKey; -use commonware_p2p::{Blocker, Manager, Receiver, Sender}; +use commonware_p2p::{Blocker, Provider, Receiver, Sender}; use commonware_resolver::p2p; -use commonware_runtime::{Clock, Metrics, Spawner}; -use futures::channel::mpsc; +use commonware_runtime::{BufferPooler, Clock, Metrics, Spawner}; +use commonware_utils::channel::mpsc; use rand::Rng; /// Initializes the p2p resolver with the given parameters. @@ -45,14 +45,14 @@ impl PeerInitializer { pub fn init( ctx: &E, public_key: P, - manager: C, + peer_provider: C, blocker: Bl, backfill: (S, R), - ) -> (mpsc::Receiver>, p2p::Mailbox, P>) + ) -> (mpsc::Receiver>, p2p::Mailbox, P>) where - E: Rng + Spawner + Clock + Metrics, + E: BufferPooler + Rng + Spawner + Clock + Metrics, P: PublicKey, - C: Manager, + C: Provider, Bl: Blocker, B: Block, S: Sender, @@ -60,7 +60,7 @@ impl PeerInitializer { { let resolver_cfg = Config { public_key, - manager, + peer_provider, blocker, mailbox_size: Self::DEFAULT_MAILBOX_SIZE, initial: Self::DEFAULT_INITIAL_DELAY, diff --git a/crates/network/marshal/tests/common/mod.rs b/crates/network/marshal/tests/common/mod.rs index 939cb32..d26c279 100644 --- a/crates/network/marshal/tests/common/mod.rs +++ b/crates/network/marshal/tests/common/mod.rs @@ -51,7 +51,7 @@ impl Committable for Block { } impl BlockTrait for Block { - fn parent(&self) -> Self::Commitment { + fn parent(&self) -> Self::Digest { self.parent } } diff --git a/crates/network/marshal/tests/integration.rs b/crates/network/marshal/tests/integration.rs index cdc2fb2..0514daa 100644 --- a/crates/network/marshal/tests/integration.rs +++ b/crates/network/marshal/tests/integration.rs @@ -19,15 +19,15 @@ use std::{ use commonware_consensus::{ Heightable, Reporter, - marshal::Update, + marshal::{Update, core::Mailbox, standard::Standard}, simplex::{ - scheme::bls12381_threshold, + scheme::bls12381_threshold::standard as bls12381_threshold, types::{Activity, Finalization, Finalize, Notarization, Notarize, Proposal}, }, types::{Epoch, Height, Round, View}, }; use commonware_cryptography::{ - Committable, Digestible, Hasher as _, + Digestible, Hasher as _, bls12381::primitives::variant::MinPk, certificate::{ConstantProvider, Scheme as CertificateScheme, mocks::Fixture}, ed25519::PublicKey, @@ -39,8 +39,8 @@ use commonware_p2p::{ simulated::{self, Link, Network, Oracle}, }; use commonware_parallel::Sequential; -use commonware_runtime::{Clock, Metrics, Quota, Runner, buffer::PoolRef, deterministic}; -use commonware_utils::{Acknowledgement, NZU16, NZUsize}; +use commonware_runtime::{Clock, Metrics, Quota, Runner, deterministic}; +use commonware_utils::{Acknowledgement, NZU16, NZUsize, ordered::Set}; use kora_marshal::{ActorInitializer, ArchiveInitializer, BroadcastInitializer, PeerInitializer}; use crate::common::Block; @@ -67,7 +67,7 @@ const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX); #[derive(Clone, Default)] struct MockApplication { blocks: Arc>>, - tip: Arc::Commitment)>>>, + tip: Arc::Digest)>>>, } impl MockApplication { @@ -86,7 +86,7 @@ impl Reporter for MockApplication { self.blocks.lock().unwrap().insert(height, block); ack.acknowledge(); } - Update::Tip(height, commitment) => { + Update::Tip(_, height, commitment) => { *self.tip.lock().unwrap() = Some((height, commitment)); } } @@ -120,9 +120,7 @@ async fn setup_validator( oracle: &mut Oracle, validator: K, provider: ConstantProvider, -) -> (MockApplication, commonware_consensus::marshal::Mailbox, Height) { - let buffer_pool = PoolRef::new(NZU16!(1024), NZUsize!(10)); - +) -> (MockApplication, Mailbox>, Height) { // 1. Use PeerInitializer::init() for the resolver let control = oracle.control(validator.clone()); let backfill = control.register(1, TEST_QUOTA).await.unwrap(); @@ -136,8 +134,12 @@ async fn setup_validator( ); // 2. Use BroadcastInitializer::init() for the broadcast engine - let (broadcast_engine, buffer) = - BroadcastInitializer::init::<_, _, B>(context.clone(), validator.clone(), ()); + let (broadcast_engine, buffer) = BroadcastInitializer::init::<_, _, B, _>( + context.clone(), + validator.clone(), + oracle.manager(), + (), + ); let network = control.register(2, TEST_QUOTA).await.unwrap(); broadcast_engine.start(network); @@ -161,7 +163,11 @@ async fn setup_validator( finalizations_by_height, finalized_blocks, provider, - buffer_pool, + commonware_runtime::buffer::paged::CacheRef::from_pooler( + &context, + NZU16!(1024), + NZUsize!(10), + ), (), ) .await; @@ -200,7 +206,7 @@ fn test_start_marshal_and_finalize_block() { simulated::Config { max_size: 1024 * 1024, disconnect_on_block: true, - tracked_peer_sets: None, + tracked_peer_sets: NZUsize!(1), }, ); network.start(); @@ -279,7 +285,7 @@ fn test_start_marshal_multiple_validators() { simulated::Config { max_size: 1024 * 1024, disconnect_on_block: true, - tracked_peer_sets: Some(3), + tracked_peer_sets: NZUsize!(3), }, ); network.start(); @@ -290,7 +296,7 @@ fn test_start_marshal_multiple_validators() { // Register peer set let mut manager = oracle.manager(); - manager.update(0, participants.clone().try_into().unwrap()).await; + manager.track(0, Set::from_iter_dedup(participants.clone())).await; // Setup multiple validators let mut applications = Vec::new(); diff --git a/crates/network/transport/src/builder.rs b/crates/network/transport/src/builder.rs index 1b18f7c..6296579 100644 --- a/crates/network/transport/src/builder.rs +++ b/crates/network/transport/src/builder.rs @@ -4,7 +4,9 @@ use std::num::NonZeroU32; use commonware_cryptography::Signer; use commonware_p2p::authenticated::discovery; -use commonware_runtime::{Clock, Metrics, Network as RNetwork, Quota, Resolver, Spawner}; +use commonware_runtime::{ + BufferPooler, Clock, Metrics, Network as RNetwork, Quota, Resolver, Spawner, +}; use rand_core::CryptoRngCore; use crate::{ @@ -56,7 +58,7 @@ impl TransportConfig { /// ``` pub fn build(self, context: E) -> NetworkTransport where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, { self.build_with_quota(context, default_quota()) } @@ -67,7 +69,7 @@ impl TransportConfig { /// rate limit for all channels. pub fn build_with_quota(self, context: E, quota: Quota) -> NetworkTransport where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, { let backlog = self.backlog; diff --git a/crates/network/transport/src/ext.rs b/crates/network/transport/src/ext.rs index 8a5780f..e49ee08 100644 --- a/crates/network/transport/src/ext.rs +++ b/crates/network/transport/src/ext.rs @@ -4,7 +4,7 @@ use std::net::SocketAddr; use commonware_cryptography::ed25519; use commonware_p2p::Ingress; -use commonware_runtime::{Clock, Metrics, Network as RNetwork, Resolver, Spawner}; +use commonware_runtime::{BufferPooler, Clock, Metrics, Network as RNetwork, Resolver, Spawner}; use kora_config::NetworkConfig; use rand_core::CryptoRngCore; @@ -24,7 +24,7 @@ pub trait NetworkConfigExt { context: E, ) -> Result, TransportError> where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics; + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics; /// Build a production transport. /// @@ -35,7 +35,7 @@ pub trait NetworkConfigExt { context: E, ) -> Result, TransportError> where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics; + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics; } impl NetworkConfigExt for NetworkConfig { @@ -45,7 +45,7 @@ impl NetworkConfigExt for NetworkConfig { context: E, ) -> Result, TransportError> where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, { let (listen_addr, dialable, bootstrappers) = parse_network_config(self)?; @@ -68,7 +68,7 @@ impl NetworkConfigExt for NetworkConfig { context: E, ) -> Result, TransportError> where - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, { let (listen_addr, dialable, bootstrappers) = parse_network_config(self)?; diff --git a/crates/network/transport/src/network_provider.rs b/crates/network/transport/src/network_provider.rs index 3c65653..5e29c4c 100644 --- a/crates/network/transport/src/network_provider.rs +++ b/crates/network/transport/src/network_provider.rs @@ -4,7 +4,9 @@ use std::fmt; use commonware_cryptography::Signer; use commonware_p2p::authenticated::discovery; -use commonware_runtime::{Clock, Metrics, Network as RNetwork, Quota, Resolver, Spawner}; +use commonware_runtime::{ + BufferPooler, Clock, Metrics, Network as RNetwork, Quota, Resolver, Spawner, +}; use rand_core::CryptoRngCore; use crate::{ @@ -57,7 +59,7 @@ impl fmt::Debug for NetworkControl

{ impl TransportProvider for NetworkTransportProvider where C: Signer, - E: Spawner + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + RNetwork + Resolver + Metrics, { type Control = NetworkControl; type Error = TransportError; diff --git a/crates/node/dkg/src/ceremony.rs b/crates/node/dkg/src/ceremony.rs index 5f04a37..4bbf9b6 100644 --- a/crates/node/dkg/src/ceremony.rs +++ b/crates/node/dkg/src/ceremony.rs @@ -172,14 +172,14 @@ impl DkgCeremony { Ok(output) } - /// Phase 2: Collect dealer messages from quorum and send acks. + /// Phase 2: Collect dealer messages from all participants and send acks. async fn run_phase2( &self, network: &DkgNetwork, participant: &mut DkgParticipant, ) -> Result<(), DkgError> { info!( - required = participant.required_quorum(), + required = participant.required_dealer_logs(), total = participant.total_participants(), "Phase 2: Collecting dealer messages and sending acks" ); @@ -194,7 +194,7 @@ impl DkgCeremony { let received = participant.received_dealer_count(); let acks_sent = participant.acks_sent_count(); - let required = participant.required_quorum(); + let required = participant.required_dealer_logs(); let total = participant.total_participants(); // Log progress periodically @@ -213,13 +213,13 @@ impl DkgCeremony { last_progress_log = Instant::now(); } - // Phase 2 completes when we have quorum dealer messages AND have sent acks + // Phase 2 completes when every participant's dealer messages are acknowledged. if received >= required && acks_sent >= required { info!( received, acks_sent, required, - "Phase 2 complete: received quorum dealer messages and sent acks" + "Phase 2 complete: received all dealer messages and sent acks" ); return Ok(()); } @@ -301,7 +301,7 @@ impl DkgCeremony { participant: &mut DkgParticipant, ) -> Result<(), DkgError> { info!( - required = participant.required_quorum(), + required = participant.required_dealer_logs(), "Phase 4: Collecting dealer logs for finalization" ); @@ -315,7 +315,7 @@ impl DkgCeremony { self.send_outgoing(network, participant)?; let logs = participant.dealer_log_count(); - let required = participant.required_quorum(); + let required = participant.required_dealer_logs(); // Log progress periodically if last_progress_log.elapsed() >= Duration::from_secs(PROGRESS_LOG_INTERVAL_SECS) { @@ -353,7 +353,7 @@ impl DkgCeremony { Err(DkgError::CeremonyFailed(format!( "Phase 4 timeout: only collected {}/{} dealer logs", participant.dealer_log_count(), - participant.required_quorum() + participant.required_dealer_logs() ))) } @@ -368,16 +368,9 @@ impl DkgCeremony { let start = Instant::now(); - // Give leader time to start first - if !self.is_leader() { - tokio::time::sleep(Duration::from_secs(2)).await; - } - - // For non-bootstrap nodes, wait for network initialization - if !self.config.bootstrap_peers.is_empty() { - // Simple delay to allow network setup rather than legacy ping - tokio::time::sleep(Duration::from_secs(3)).await; - } + // Give all DKG containers time to join the p2p overlay before the phase-1 + // broadcasts, which are not replayed if sent before peers are reachable. + tokio::time::sleep(Duration::from_secs(10)).await; info!(elapsed = ?start.elapsed(), "Peer initialization complete"); Ok(()) diff --git a/crates/node/dkg/src/protocol.rs b/crates/node/dkg/src/protocol.rs index 28be385..2fa8904 100644 --- a/crates/node/dkg/src/protocol.rs +++ b/crates/node/dkg/src/protocol.rs @@ -10,7 +10,7 @@ use commonware_cryptography::{ Hasher as _, Sha256, bls12381::{ dkg::{ - Dealer, DealerLog, DealerPrivMsg, DealerPubMsg, Info, Player, PlayerAck, + Dealer, DealerLog, DealerPrivMsg, DealerPubMsg, Info, Logs, Player, PlayerAck, SignedDealerLog, }, primitives::{sharing::Mode, variant::MinSig}, @@ -419,6 +419,14 @@ impl DkgParticipant { self.session.ceremony_id } + fn logs_for_verification(&self) -> Logs { + let mut logs = Logs::new(self.info.clone()); + for (dealer, log) in &self.dealer_logs { + logs.record(dealer.clone(), log.clone()); + } + logs + } + /// Start the dealer phase - generate and return messages to send. pub fn start_dealer(&mut self) -> Result<(), DkgError> { let mut rng = rand::rngs::OsRng; @@ -754,10 +762,9 @@ impl DkgParticipant { self.ready_players.len() } - /// Check if we have enough dealer logs to finalize. + /// Check if we have all dealer logs needed to finalize. pub fn can_finalize(&self) -> bool { - let required = N3f1::quorum(self.config.participants.len()) as usize; - self.dealer_logs.len() >= required + self.dealer_logs.len() >= self.required_dealer_logs() } /// Finalize the DKG and produce output. @@ -770,7 +777,7 @@ impl DkgParticipant { return Err(DkgError::CeremonyFailed(format!( "Not enough dealer logs: {} < {}", self.dealer_logs.len(), - N3f1::quorum(self.config.participants.len()) + self.required_dealer_logs() ))); } @@ -806,11 +813,13 @@ impl DkgParticipant { "Comparing dealer log keys to config participants" ); + let mut rng = rand::rngs::OsRng; + // Debug: try to observe the logs first to understand what's failing use commonware_cryptography::bls12381::dkg::observe; - match observe::( - self.info.clone(), - self.dealer_logs.clone(), + match observe::( + &mut rng, + self.logs_for_verification(), &Sequential, ) { Ok(observed) => { @@ -829,8 +838,9 @@ impl DkgParticipant { } } + let logs = self.logs_for_verification(); let (output, share) = player - .finalize::(self.dealer_logs.clone(), &Sequential) + .finalize::(&mut rng, logs, &Sequential) .map_err(|e| DkgError::Crypto(format!("Failed to finalize: {:?}", e)))?; self.finalized = true; @@ -887,6 +897,11 @@ impl DkgParticipant { N3f1::quorum(self.config.participants.len()) as usize } + /// Get the number of dealer logs required for this initial DKG ceremony. + pub const fn required_dealer_logs(&self) -> usize { + self.config.n() + } + /// Check if a public key is a participant in this DKG. pub fn is_participant(&self, pk: &ed25519::PublicKey) -> bool { self.config.participants.contains(pk) diff --git a/crates/node/dkg/src/tests.rs b/crates/node/dkg/src/tests.rs index 7c46ead..1ac83b0 100644 --- a/crates/node/dkg/src/tests.rs +++ b/crates/node/dkg/src/tests.rs @@ -48,6 +48,7 @@ fn test_participant_creation() { assert_eq!(participant.dealer_log_count(), 0); assert_eq!(participant.required_quorum(), 3); // n=4, f=1, quorum=3 + assert_eq!(participant.required_dealer_logs(), 4); assert!(!participant.can_finalize()); // Verify session was created @@ -230,6 +231,20 @@ fn test_local_dkg_simulation() { } } + // A quorum-sized subset is not sufficient for the initial ceremony: every validator + // must finalize over the same dealer set or they can derive different group keys. + for (from_idx, _, msg) in &dealer_logs { + if *from_idx != 0 { + let from_pk = keys[*from_idx].public_key(); + let _ = participants[0].handle_message(&from_pk, msg.clone()); + if participants[0].dealer_log_count() >= participants[0].required_quorum() { + break; + } + } + } + assert_eq!(participants[0].dealer_log_count(), participants[0].required_quorum()); + assert!(!participants[0].can_finalize()); + // Deliver dealer logs to all for (from_idx, _, msg) in dealer_logs { let from_pk = keys[from_idx].public_key(); @@ -245,7 +260,7 @@ fn test_local_dkg_simulation() { "participant {} should be able to finalize (has {} logs, needs {})", i, p.dealer_log_count(), - p.required_quorum() + p.required_dealer_logs() ); } diff --git a/crates/node/dkg/src/transport.rs b/crates/node/dkg/src/transport.rs index 69c0027..a9ef26d 100644 --- a/crates/node/dkg/src/transport.rs +++ b/crates/node/dkg/src/transport.rs @@ -11,7 +11,7 @@ use commonware_p2p::{ Ingress, Manager, Receiver as ReceiverTrait, Recipients, Sender as SenderTrait, authenticated::discovery, }; -use commonware_runtime::{Clock, Handle, Metrics, Network, Quota, Resolver, Spawner}; +use commonware_runtime::{BufferPooler, Clock, Handle, Metrics, Network, Quota, Resolver, Spawner}; use commonware_utils::ordered::Set; use rand_core::CryptoRngCore; @@ -196,7 +196,7 @@ impl DkgTransportConfig { /// and starts the network. pub fn build(self, context: E) -> DkgTransport where - E: Spawner + Clock + CryptoRngCore + Network + Resolver + Metrics, + E: Spawner + BufferPooler + Clock + CryptoRngCore + Network + Resolver + Metrics, { let (mut network, oracle) = discovery::Network::new(context.with_label("dkg-network"), self.inner); @@ -216,7 +216,7 @@ impl DkgTransport { /// /// This should be called with the DKG ceremony participants before starting. pub async fn set_participants(&mut self, participants: Set) { - self.oracle.update(0, participants).await; + self.oracle.track(0, participants).await; } /// Send a message to a specific peer. @@ -247,7 +247,7 @@ impl DkgTransport { /// /// Returns the sender's public key and the message bytes. pub async fn recv(&mut self) -> Option<(ed25519::PublicKey, Bytes)> { - self.receiver.recv().await.ok() + self.receiver.recv().await.ok().map(|(sender, message)| (sender, Bytes::from(message))) } } diff --git a/crates/node/domain/src/block.rs b/crates/node/domain/src/block.rs index ddac18d..acd923e 100644 --- a/crates/node/domain/src/block.rs +++ b/crates/node/domain/src/block.rs @@ -67,7 +67,7 @@ impl commonware_consensus::Heightable for Block { } impl commonware_consensus::Block for Block { - fn parent(&self) -> Self::Commitment { + fn parent(&self) -> Self::Digest { digest_for_block_id(&self.parent) } } diff --git a/crates/node/executor/src/revm.rs b/crates/node/executor/src/revm.rs index e9ea688..eedb0b5 100644 --- a/crates/node/executor/src/revm.rs +++ b/crates/node/executor/src/revm.rs @@ -243,7 +243,7 @@ impl BlockExecutor for RevmExecutor { let result_and_state = evm.replay().map_err(|e| ExecutionError::TxExecution(format!("{:?}", e)))?; - let gas_used = result_and_state.result.gas_used(); + let gas_used = result_and_state.result.tx_gas_used(); cumulative_gas = cumulative_gas.saturating_add(gas_used); let receipt = diff --git a/crates/node/ledger/src/lib.rs b/crates/node/ledger/src/lib.rs index adc072f..13950a2 100644 --- a/crates/node/ledger/src/lib.rs +++ b/crates/node/ledger/src/lib.rs @@ -9,7 +9,7 @@ use std::{collections::BTreeSet, fmt, sync::Arc}; use alloy_primitives::{Address, B256, U256}; use commonware_cryptography::Committable as _; -use commonware_runtime::{Metrics as _, buffer::PoolRef, tokio}; +use commonware_runtime::{Metrics as _, tokio}; use futures::{channel::mpsc::UnboundedReceiver, lock::Mutex}; use kora_consensus::{ ConsensusError, Mempool as _, SeedTracker as _, Snapshot, SnapshotStore as _, @@ -78,11 +78,10 @@ impl LedgerView { /// Initialize a ledger view with a QMDB backend built from the provided settings. pub async fn init( context: tokio::Context, - buffer_pool: PoolRef, partition_prefix: String, genesis_alloc: Vec<(Address, U256)>, ) -> LedgerResult { - let config = QmdbConfig::new(partition_prefix, buffer_pool); + let config = QmdbConfig::new(partition_prefix); Self::init_with_config(context, config, genesis_alloc).await } @@ -423,8 +422,7 @@ mod tests { use alloy_consensus::Header; use alloy_primitives::{Address, B256, Bytes, U256}; use commonware_cryptography::Committable as _; - use commonware_runtime::{Runner, buffer::PoolRef, tokio}; - use commonware_utils::{NZU16, NZUsize}; + use commonware_runtime::{Runner, tokio}; use k256::ecdsa::SigningKey; use kora_domain::{Block, ConsensusDigest, Tx, evm::Evm}; use kora_executor::{BlockContext, BlockExecutor, RevmExecutor}; @@ -435,8 +433,6 @@ mod tests { static PARTITION_COUNTER: AtomicUsize = AtomicUsize::new(0); - const BUFFER_BLOCK_BYTES: u16 = 16_384; - const BUFFER_BLOCK_COUNT: usize = 10_000; const GENESIS_BALANCE: u64 = 1_000_000; const DUPLICATE_BALANCE: u64 = 500_000; const TRANSFER_ONE: u64 = 10; @@ -475,10 +471,6 @@ mod tests { format!("{prefix}-{id}") } - fn test_buffer_pool() -> PoolRef { - PoolRef::new(NZU16!(BUFFER_BLOCK_BYTES), NZUsize!(BUFFER_BLOCK_COUNT)) - } - fn transfer_tx(from_key: &SigningKey, to: Address, value: u64, nonce: u64) -> Tx { Evm::sign_eip1559_transfer( from_key, @@ -507,14 +499,9 @@ mod tests { partition_prefix: &str, allocations: Vec<(Address, U256)>, ) -> LedgerSetup { - let ledger = LedgerView::init( - context, - test_buffer_pool(), - next_partition(partition_prefix), - allocations, - ) - .await - .expect("init ledger"); + let ledger = LedgerView::init(context, next_partition(partition_prefix), allocations) + .await + .expect("init ledger"); let service = LedgerService::new(ledger.clone()); let genesis = service.genesis_block(); let genesis_digest = genesis.commitment(); diff --git a/crates/node/reporters/src/lib.rs b/crates/node/reporters/src/lib.rs index 40b7284..aaae479 100644 --- a/crates/node/reporters/src/lib.rs +++ b/crates/node/reporters/src/lib.rs @@ -11,7 +11,7 @@ use commonware_consensus::{ Block as _, Reporter, marshal::Update, simplex::{ - scheme::bls12381_threshold::{Scheme, Seedable as _}, + scheme::bls12381_threshold::vrf::{Scheme, Seedable as _}, types::Activity, }, }; diff --git a/crates/node/runner/src/app.rs b/crates/node/runner/src/app.rs index e1e1aff..d9a2112 100644 --- a/crates/node/runner/src/app.rs +++ b/crates/node/runner/src/app.rs @@ -5,7 +5,8 @@ use std::{collections::BTreeSet, time::Instant}; use alloy_consensus::Header; use alloy_primitives::{Address, B256, Bytes}; use commonware_consensus::{ - Application, Block as _, VerifyingApplication, marshal::ingress::mailbox::AncestorStream, + Application, Block as _, VerifyingApplication, + marshal::ancestry::{AncestorStream, BlockProvider}, simplex::types::Context, }; use commonware_cryptography::{Committable as _, certificate::Scheme as CertScheme}; @@ -259,11 +260,14 @@ where async move { self.ledger.genesis_block() } } - fn propose( + fn propose( &mut self, _context: (Env, Self::Context), - mut ancestry: AncestorStream, - ) -> impl std::future::Future> + Send { + mut ancestry: AncestorStream, + ) -> impl std::future::Future> + Send + where + A: BlockProvider, + { let node_state = self.node_state.clone(); async move { let start = Instant::now(); @@ -298,11 +302,14 @@ where S: CertScheme + Send + Sync + 'static, E: BlockExecutor, Tx = Bytes> + Clone + Send + Sync + 'static, { - fn verify( + fn verify( &mut self, _context: (Env, Self::Context), - mut ancestry: AncestorStream, - ) -> impl std::future::Future + Send { + mut ancestry: AncestorStream, + ) -> impl std::future::Future + Send + where + A: BlockProvider, + { async move { let start = Instant::now(); diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index 478cfac..d58d508 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -5,14 +5,17 @@ use alloy_primitives::{Address, B256}; use anyhow::Context as _; use commonware_consensus::{ Reporters, - application::marshaled::Marshaled, + marshal::{ + core::Mailbox, + standard::{Inline, Standard}, + }, simplex::{self, elector::Random, types::Finalization}, types::{Epoch, FixedEpocher, ViewDelta}, }; use commonware_cryptography::{bls12381::primitives::variant::MinSig, ed25519}; use commonware_p2p::Manager; use commonware_parallel::Sequential; -use commonware_runtime::{Metrics as _, Spawner, buffer::PoolRef, tokio}; +use commonware_runtime::{Metrics as _, Spawner, buffer::paged::CacheRef, tokio}; use commonware_utils::{NZU64, NZUsize, acknowledgement::Exact}; use futures::StreamExt; use kora_domain::{Block, BlockCfg, BootstrapConfig, ConsensusDigest, LedgerEvent, TxCfg}; @@ -34,11 +37,11 @@ const PARTITION_PREFIX: &str = "kora"; type Peer = ed25519::PublicKey; type CertArchive = Finalization; -type MarshalMailbox = commonware_consensus::marshal::Mailbox; +type MarshalMailbox = Mailbox>; type NodeStateRptr = NodeStateReporter; -fn default_buffer_pool() -> PoolRef { - DefaultPool::init() +fn default_page_cache(context: &tokio::Context) -> CacheRef { + DefaultPool::init(context) } const fn block_codec_cfg() -> BlockCfg { @@ -187,15 +190,14 @@ impl NodeRunner for ProductionRunner { info!(chain_id = self.chain_id, "Starting production validator"); let validators = self.scheme.participants().clone(); - transport.oracle.update(0, validators).await; + transport.oracle.track(0, validators).await; info!(count = self.scheme.participants().len(), "Registered validators with oracle"); - let buffer_pool = default_buffer_pool(); + let page_cache = default_page_cache(&context); let block_cfg = block_codec_cfg(); let state = LedgerView::init( context.with_label("state"), - buffer_pool.clone(), format!("{}-qmdb", self.partition_prefix), self.bootstrap.genesis_alloc.clone(), ) @@ -205,8 +207,7 @@ impl NodeRunner for ProductionRunner { if let Some((node_state, addr)) = &self.rpc_config { let qmdb_state = state.qmdb_state().await; let block_index = Arc::new(kora_indexer::BlockIndex::new()); - let indexed_provider = - kora_rpc::IndexedStateProvider::new(block_index, qmdb_state); + let indexed_provider = kora_rpc::IndexedStateProvider::new(block_index, qmdb_state); let rpc = kora_rpc::RpcServer::with_state_provider( node_state.clone(), *addr, @@ -240,9 +241,10 @@ impl NodeRunner for ProductionRunner { transport.marshal.backfill, ); - let (broadcast_engine, buffer) = BroadcastInitializer::init::<_, Peer, Block>( + let (broadcast_engine, buffer) = BroadcastInitializer::init::<_, Peer, Block, _>( context.with_label("broadcast"), my_pk.clone(), + transport.oracle.clone(), block_cfg, ); broadcast_engine.start(transport.marshal.blocks); @@ -271,7 +273,7 @@ impl NodeRunner for ProductionRunner { finalizations_by_height, finalized_blocks, scheme_provider, - buffer_pool.clone(), + page_cache.clone(), block_cfg, ) .await; @@ -289,7 +291,7 @@ impl NodeRunner for ProductionRunner { app = app.with_node_state(state.clone()); } let marshaled = - Marshaled::new(context.with_label("marshaled"), app, marshal_mailbox.clone(), epocher); + Inline::new(context.with_label("marshaled"), app, marshal_mailbox.clone(), epocher); let seed_reporter = SeedReporter::::new(ledger.clone()); let node_state_reporter = self @@ -320,13 +322,14 @@ impl NodeRunner for ProductionRunner { replay_buffer: NZUsize!(1024 * 1024), write_buffer: NZUsize!(1024 * 1024), leader_timeout: Duration::from_millis(500), - notarization_timeout: Duration::from_secs(1), - nullify_retry: Duration::from_secs(2), + certification_timeout: Duration::from_secs(1), + timeout_retry: Duration::from_secs(2), fetch_timeout: Duration::from_millis(500), activity_timeout: ViewDelta::new(20), skip_timeout: ViewDelta::new(10), fetch_concurrent: 8, - buffer_pool, + page_cache, + forwarding: simplex::ForwardingPolicy::Disabled, }, ); engine.start(transport.simplex.votes, transport.simplex.certs, transport.simplex.resolver); diff --git a/crates/node/runner/src/scheme.rs b/crates/node/runner/src/scheme.rs index 65dc686..10d88f0 100644 --- a/crates/node/runner/src/scheme.rs +++ b/crates/node/runner/src/scheme.rs @@ -1,9 +1,13 @@ use std::{num::NonZeroU32, path::Path}; use commonware_codec::{Read, ReadExt}; -use commonware_consensus::simplex::scheme::bls12381_threshold; +use commonware_consensus::simplex::scheme::bls12381_threshold::vrf as bls12381_threshold; use commonware_cryptography::{ - bls12381::primitives::{group::Share, sharing::Sharing, variant::MinSig}, + bls12381::primitives::{ + group::Share, + sharing::{ModeVersion, Sharing}, + variant::MinSig, + }, ed25519, }; use commonware_utils::ordered::Set; @@ -33,8 +37,11 @@ pub fn load_threshold_scheme(data_dir: &Path) -> anyhow::Result let participants_set = Set::from_iter_dedup(participants); - let group_poly = Sharing::::read_cfg(&mut output.public_polynomial.as_slice(), &n_cfg) - .map_err(|e| anyhow::anyhow!("failed to decode public polynomial: {:?}", e))?; + let group_poly = Sharing::::read_cfg( + &mut output.public_polynomial.as_slice(), + &(n_cfg, ModeVersion::v0()), + ) + .map_err(|e| anyhow::anyhow!("failed to decode public polynomial: {:?}", e))?; let share = Share::read_cfg(&mut output.share_secret.as_slice(), &()) .map_err(|e| anyhow::anyhow!("failed to decode share: {:?}", e))?; diff --git a/crates/node/service/src/service.rs b/crates/node/service/src/service.rs index 80d8d92..13174c1 100644 --- a/crates/node/service/src/service.rs +++ b/crates/node/service/src/service.rs @@ -113,10 +113,10 @@ impl LegacyNodeService { let validators = self.config.consensus.build_validator_set()?; if !validators.is_empty() { - let validator_set = validators + let validator_set: commonware_utils::ordered::Set<_> = validators .try_into() .map_err(|_| eyre::eyre!("failed to convert validator set"))?; - transport.oracle.update(0, validator_set).await; + transport.oracle.track(0, validator_set).await; tracing::info!("registered validators with oracle"); } diff --git a/crates/node/service/src/stubs.rs b/crates/node/service/src/stubs.rs index 6dd5c26..7379d23 100644 --- a/crates/node/service/src/stubs.rs +++ b/crates/node/service/src/stubs.rs @@ -8,8 +8,7 @@ use std::future::Future; use commonware_consensus::{CertifiableAutomaton, Relay, Reporter, types::Epoch}; use commonware_cryptography::sha256; -use commonware_utils::channels::fallible::OneshotExt as _; -use futures::channel::oneshot; +use commonware_utils::channel::{fallible::OneshotExt as _, oneshot}; /// Stub digest type (SHA-256). pub type StubDigest = sha256::Digest; @@ -72,8 +71,14 @@ pub struct StubRelay; #[allow(clippy::manual_async_fn)] impl Relay for StubRelay { type Digest = StubDigest; + type PublicKey = StubPublicKey; + type Plan = (); - fn broadcast(&mut self, _payload: Self::Digest) -> impl Future + Send { + fn broadcast( + &mut self, + _payload: Self::Digest, + _plan: Self::Plan, + ) -> impl Future + Send { async {} } } diff --git a/crates/node/simplex/src/config.rs b/crates/node/simplex/src/config.rs index 8f1057f..92bf4a1 100644 --- a/crates/node/simplex/src/config.rs +++ b/crates/node/simplex/src/config.rs @@ -4,16 +4,15 @@ use std::time::Duration; use commonware_consensus::{ CertifiableAutomaton, Relay, Reporter, - simplex::{self, elector::Random, types::Activity}, + simplex::{self, ForwardingPolicy, Plan, elector::Random, types::Activity}, types::{Epoch, ViewDelta}, }; use commonware_cryptography::{Digest, certificate::Scheme}; use commonware_p2p::Blocker; use commonware_parallel::Sequential; +use commonware_runtime::buffer::paged::CacheRef; use commonware_utils::NZUsize; -use crate::DefaultPool; - /// Default mailbox size for internal consensus channels. pub const DEFAULT_MAILBOX_SIZE: usize = 1024; @@ -68,6 +67,7 @@ impl DefaultConfig { #[allow(clippy::type_complexity)] pub fn init( partition: impl Into, + page_cache: CacheRef, scheme: S, blocker: B, automaton: A, @@ -80,7 +80,7 @@ impl DefaultConfig { B: Blocker, D: Digest, A: CertifiableAutomaton, Digest = D>, - R: Relay, + R: Relay>, F: Reporter>, { simplex::Config { @@ -96,14 +96,15 @@ impl DefaultConfig { epoch: Epoch::zero(), replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER), - buffer_pool: DefaultPool::init(), leader_timeout: DEFAULT_LEADER_TIMEOUT, - notarization_timeout: DEFAULT_NOTARIZATION_TIMEOUT, - nullify_retry: DEFAULT_NULLIFY_RETRY, + certification_timeout: DEFAULT_NOTARIZATION_TIMEOUT, + timeout_retry: DEFAULT_NULLIFY_RETRY, fetch_timeout: DEFAULT_FETCH_TIMEOUT, activity_timeout: DEFAULT_ACTIVITY_TIMEOUT, skip_timeout: DEFAULT_SKIP_TIMEOUT, fetch_concurrent: DEFAULT_FETCH_CONCURRENT, + page_cache, + forwarding: ForwardingPolicy::Disabled, } } } diff --git a/crates/node/simplex/src/engine.rs b/crates/node/simplex/src/engine.rs index 5d4302b..5fb2656 100644 --- a/crates/node/simplex/src/engine.rs +++ b/crates/node/simplex/src/engine.rs @@ -2,15 +2,15 @@ use commonware_consensus::{ CertifiableAutomaton, Relay, Reporter, - simplex::{self, elector::Random, types::Activity}, + simplex::{self, Plan, elector::Random, types::Activity}, }; use commonware_cryptography::Digest; use commonware_p2p::{Blocker, Receiver, Sender}; use commonware_parallel::Sequential; -use commonware_runtime::{Clock, Handle, Metrics, Spawner, Storage}; +use commonware_runtime::{BufferPooler, Clock, Handle, Metrics, Spawner, Storage}; use rand_core::CryptoRngCore; -use crate::DefaultConfig; +use crate::{DefaultConfig, DefaultPool}; /// The default simplex engine constructor. /// @@ -47,13 +47,13 @@ impl DefaultEngine { resolver_network: (RS, RR), ) -> Handle<()> where - E: Clock + CryptoRngCore + Spawner + Storage + Metrics, + E: BufferPooler + Clock + CryptoRngCore + Spawner + Storage + Metrics, S: simplex::scheme::Scheme, Random: simplex::elector::Config, B: Blocker, D: Digest, A: CertifiableAutomaton, Digest = D>, - R: Relay, + R: Relay>, F: Reporter>, VS: Sender, VR: Receiver, @@ -62,8 +62,9 @@ impl DefaultEngine { RS: Sender, RR: Receiver, { + let page_cache = DefaultPool::init(&context); let config: simplex::Config = - DefaultConfig::init(partition, scheme, blocker, automaton, relay, reporter); + DefaultConfig::init(partition, page_cache, scheme, blocker, automaton, relay, reporter); let engine = simplex::Engine::new(context, config); engine.start(vote_network, certificate_network, resolver_network) } diff --git a/crates/node/simplex/src/pool.rs b/crates/node/simplex/src/pool.rs index f6d649c..ac430c2 100644 --- a/crates/node/simplex/src/pool.rs +++ b/crates/node/simplex/src/pool.rs @@ -1,6 +1,6 @@ //! Provides a default buffer pool implementation. -use commonware_runtime::buffer::PoolRef; +use commonware_runtime::{BufferPooler, buffer::paged::CacheRef}; use commonware_utils::{NZU16, NZUsize}; /// Default page size in bytes (64 KiB). @@ -17,11 +17,11 @@ pub const DEFAULT_POOL_CAPACITY: usize = 10_000; pub struct DefaultPool; impl DefaultPool { - /// Initializes a default [`PoolRef`]. + /// Initializes a default [`CacheRef`]. /// /// Uses a page size of 16 KiB and a capacity of 10,000 pages. - pub fn init() -> PoolRef { - PoolRef::new(NZU16!(DEFAULT_PAGE_SIZE), NZUsize!(DEFAULT_POOL_CAPACITY)) + pub fn init(pooler: &impl BufferPooler) -> CacheRef { + CacheRef::from_pooler(pooler, NZU16!(DEFAULT_PAGE_SIZE), NZUsize!(DEFAULT_POOL_CAPACITY)) } } @@ -55,7 +55,9 @@ mod tests { } #[test] - fn pool_ref_can_be_initialized() { - let _pool = DefaultPool::init(); + fn default_pool_is_constructible() { + let pool = DefaultPool; + let debug_str = format!("{:?}", pool); + assert!(debug_str.contains("DefaultPool")); } } diff --git a/crates/storage/backend/src/accounts.rs b/crates/storage/backend/src/accounts.rs index 938109c..8d64261 100644 --- a/crates/storage/backend/src/accounts.rs +++ b/crates/storage/backend/src/accounts.rs @@ -2,12 +2,12 @@ use alloy_primitives::Address; use commonware_cryptography::sha256::Digest as QmdbDigest; -use commonware_storage::{kv::Batchable as _, qmdb::any::VariableConfig, translator::EightCap}; +use commonware_storage::{qmdb::any::VariableConfig, translator::EightCap}; use kora_qmdb::{AccountEncoding, QmdbBatchable, QmdbGettable}; use crate::{ BackendError, - types::{AccountDb, AccountDbDirty, AccountKey, AccountValue, Context, StoreSlot}, + types::{AccountDb, AccountKey, AccountValue, Context, StoreSlot}, }; /// Account partition backed by commonware-storage. @@ -23,14 +23,14 @@ pub struct AccountStore { } pub(crate) struct AccountStoreDirty { - inner: AccountDbDirty, + inner: AccountDb, } impl AccountStore { /// Initialize the account store. pub async fn init( context: Context, - config: VariableConfig, + config: VariableConfig, ) -> Result { let inner = AccountDb::init(context, config) .await @@ -44,14 +44,13 @@ impl AccountStore { } pub(crate) fn into_dirty(self) -> Result { - let inner = self.inner.into_inner()?; - Ok(AccountStoreDirty { inner: inner.into_mutable() }) + Ok(AccountStoreDirty { inner: self.inner.into_inner()? }) } } impl AccountStoreDirty { pub(crate) fn root(self) -> QmdbDigest { - self.inner.into_merkleized().root() + self.inner.root() } } @@ -91,13 +90,17 @@ impl QmdbBatchable for AccountStore { I::IntoIter: Send, { let inner = self.inner.take()?; - let mut dirty = inner.into_mutable(); - let mapped = - ops.into_iter().map(|(address, value)| (account_key(address), value.map(AccountValue))); - dirty.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string()))?; - let merkleized = dirty.into_merkleized(); - let (inner, _) = - merkleized.commit(None).await.map_err(|e| BackendError::Storage(e.to_string()))?; + let mut batch = inner.new_batch(); + for (address, value) in ops { + batch = batch.write(account_key(address), value.map(AccountValue)); + } + let merkleized = batch + .merkleize(&inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + let mut inner = inner; + inner.apply_batch(merkleized).await.map_err(|e| BackendError::Storage(e.to_string()))?; + inner.commit().await.map_err(|e| BackendError::Storage(e.to_string()))?; self.inner.restore(inner); Ok(()) } @@ -124,8 +127,18 @@ impl QmdbBatchable for AccountStoreDirty { I: IntoIterator)> + Send, I::IntoIter: Send, { - let mapped = - ops.into_iter().map(|(address, value)| (account_key(address), value.map(AccountValue))); - self.inner.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string())) + let mut batch = self.inner.new_batch(); + for (address, value) in ops { + batch = batch.write(account_key(address), value.map(AccountValue)); + } + let merkleized = batch + .merkleize(&self.inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + self.inner + .apply_batch(merkleized) + .await + .map(|_| ()) + .map_err(|e| BackendError::Storage(e.to_string())) } } diff --git a/crates/storage/backend/src/backend.rs b/crates/storage/backend/src/backend.rs index 16ccc00..2031eaf 100644 --- a/crates/storage/backend/src/backend.rs +++ b/crates/storage/backend/src/backend.rs @@ -4,8 +4,11 @@ use alloy_primitives::B256; use async_trait::async_trait; use commonware_codec::RangeCfg; use commonware_cryptography::sha256::Digest as QmdbDigest; -use commonware_runtime::{Metrics as _, buffer::PoolRef}; -use commonware_storage::{qmdb::any::VariableConfig, translator::EightCap}; +use commonware_runtime::{Metrics as _, buffer::paged::CacheRef}; +use commonware_storage::{ + journal::contiguous::variable::Config as JournalConfig, + merkle::journaled::Config as MerkleConfig, qmdb::any::VariableConfig, translator::EightCap, +}; use commonware_utils::{NZU64, NZUsize}; use kora_handlers::{HandleError, RootProvider}; use kora_qmdb::{ChangeSet, QmdbStore, StateRoot}; @@ -170,36 +173,43 @@ struct DirtyStores { fn store_config( prefix: &str, name: &str, - buffer_pool: PoolRef, + page_cache: CacheRef, log_codec_config: C, -) -> VariableConfig { +) -> VariableConfig { VariableConfig { - mmr_journal_partition: format!("{prefix}-{name}-mmr"), - mmr_metadata_partition: format!("{prefix}-{name}-mmr-meta"), - mmr_items_per_blob: NZU64!(128), - mmr_write_buffer: NZUsize!(1024 * 1024), - log_partition: format!("{prefix}-{name}-log"), - log_write_buffer: NZUsize!(1024 * 1024), - log_compression: None, - log_codec_config, - log_items_per_blob: NZU64!(128), + merkle_config: MerkleConfig { + journal_partition: format!("{prefix}-{name}-mmr"), + metadata_partition: format!("{prefix}-{name}-mmr-meta"), + items_per_blob: NZU64!(128), + write_buffer: NZUsize!(1024 * 1024), + thread_pool: None, + page_cache: page_cache.clone(), + }, + journal_config: JournalConfig { + partition: format!("{prefix}-{name}-log"), + items_per_section: NZU64!(128), + compression: None, + codec_config: ((), log_codec_config), + page_cache, + write_buffer: NZUsize!(1024 * 1024), + }, translator: EightCap, - thread_pool: None, - buffer_pool, } } async fn open_stores(context: Context, config: &QmdbBackendConfig) -> Result { + let page_cache = CacheRef::from_pooler(&context, config.page_size, config.page_cache_size); + let accounts = AccountStore::init( context.with_label("accounts"), - store_config(&config.partition_prefix, "accounts", config.buffer_pool.clone(), ()), + store_config(&config.partition_prefix, "accounts", page_cache.clone(), ()), ) .await .map_err(|e| BackendError::Storage(e.to_string()))?; let storage = StorageStore::init( context.with_label("storage"), - store_config(&config.partition_prefix, "storage", config.buffer_pool.clone(), ()), + store_config(&config.partition_prefix, "storage", page_cache.clone(), ()), ) .await .map_err(|e| BackendError::Storage(e.to_string()))?; @@ -209,7 +219,7 @@ async fn open_stores(context: Context, config: &QmdbBackendConfig) -> Result, ())>, + config: VariableConfig, ()))>, ) -> Result { let inner = CodeDb::init(context, config) .await @@ -43,14 +43,13 @@ impl CodeStore { } pub(crate) fn into_dirty(self) -> Result { - let inner = self.inner.into_inner()?; - Ok(CodeStoreDirty { inner: inner.into_mutable() }) + Ok(CodeStoreDirty { inner: self.inner.into_inner()? }) } } impl CodeStoreDirty { pub(crate) fn root(self) -> QmdbDigest { - self.inner.into_merkleized().root() + self.inner.root() } } @@ -88,12 +87,17 @@ impl QmdbBatchable for CodeStore { I::IntoIter: Send, { let inner = self.inner.take()?; - let mut dirty = inner.into_mutable(); - let mapped = ops.into_iter().map(|(hash, value)| (code_key(hash), value)); - dirty.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string()))?; - let merkleized = dirty.into_merkleized(); - let (inner, _) = - merkleized.commit(None).await.map_err(|e| BackendError::Storage(e.to_string()))?; + let mut batch = inner.new_batch(); + for (hash, value) in ops { + batch = batch.write(code_key(hash), value); + } + let merkleized = batch + .merkleize(&inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + let mut inner = inner; + inner.apply_batch(merkleized).await.map_err(|e| BackendError::Storage(e.to_string()))?; + inner.commit().await.map_err(|e| BackendError::Storage(e.to_string()))?; self.inner.restore(inner); Ok(()) } @@ -115,7 +119,18 @@ impl QmdbBatchable for CodeStoreDirty { I: IntoIterator)> + Send, I::IntoIter: Send, { - let mapped = ops.into_iter().map(|(hash, value)| (code_key(hash), value)); - self.inner.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string())) + let mut batch = self.inner.new_batch(); + for (hash, value) in ops { + batch = batch.write(code_key(hash), value); + } + let merkleized = batch + .merkleize(&self.inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + self.inner + .apply_batch(merkleized) + .await + .map(|_| ()) + .map_err(|e| BackendError::Storage(e.to_string())) } } diff --git a/crates/storage/backend/src/config.rs b/crates/storage/backend/src/config.rs index 1f5bc97..b1beedd 100644 --- a/crates/storage/backend/src/config.rs +++ b/crates/storage/backend/src/config.rs @@ -1,20 +1,43 @@ //! Configuration types for the backend. -use commonware_runtime::buffer::PoolRef; +use std::num::{NonZeroU16, NonZeroUsize}; + +use commonware_utils::{NZU16, NZUsize}; + +const DEFAULT_PAGE_SIZE: NonZeroU16 = NZU16!(16 * 1024); +const DEFAULT_PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(1_024); /// Configuration for the full QMDB backend. #[derive(Clone)] pub struct QmdbBackendConfig { /// Prefix used to derive partition names. pub partition_prefix: String, - /// Buffer pool shared by underlying QMDB partitions. - pub buffer_pool: PoolRef, + /// Logical page size used by the underlying QMDB page cache. + pub page_size: NonZeroU16, + /// Number of logical pages retained by the underlying QMDB page cache. + pub page_cache_size: NonZeroUsize, } impl QmdbBackendConfig { /// Create a new backend config for the given partition prefix. - pub fn new(partition_prefix: impl Into, buffer_pool: PoolRef) -> Self { - Self { partition_prefix: partition_prefix.into(), buffer_pool } + pub fn new(partition_prefix: impl Into) -> Self { + Self { + partition_prefix: partition_prefix.into(), + page_size: DEFAULT_PAGE_SIZE, + page_cache_size: DEFAULT_PAGE_CACHE_SIZE, + } + } + + /// Override the QMDB page cache settings. + #[must_use] + pub const fn with_page_cache( + mut self, + page_size: NonZeroU16, + page_cache_size: NonZeroUsize, + ) -> Self { + self.page_size = page_size; + self.page_cache_size = page_cache_size; + self } } diff --git a/crates/storage/backend/src/storage.rs b/crates/storage/backend/src/storage.rs index 2cff4bf..703ba2d 100644 --- a/crates/storage/backend/src/storage.rs +++ b/crates/storage/backend/src/storage.rs @@ -2,14 +2,12 @@ use alloy_primitives::U256; use commonware_cryptography::sha256::Digest as QmdbDigest; -use commonware_storage::{kv::Batchable as _, qmdb::any::VariableConfig, translator::EightCap}; +use commonware_storage::{qmdb::any::VariableConfig, translator::EightCap}; use kora_qmdb::{QmdbBatchable, QmdbGettable, StorageKey}; use crate::{ BackendError, - types::{ - Context, StorageDb, StorageDbDirty, StorageKey as StorageKeyBytes, StorageValue, StoreSlot, - }, + types::{Context, StorageDb, StorageKey as StorageKeyBytes, StorageValue, StoreSlot}, }; /// Storage partition backed by commonware-storage. @@ -25,14 +23,14 @@ pub struct StorageStore { } pub(crate) struct StorageStoreDirty { - inner: StorageDbDirty, + inner: StorageDb, } impl StorageStore { /// Initialize the storage store. pub async fn init( context: Context, - config: VariableConfig, + config: VariableConfig, ) -> Result { let inner = StorageDb::init(context, config) .await @@ -46,14 +44,13 @@ impl StorageStore { } pub(crate) fn into_dirty(self) -> Result { - let inner = self.inner.into_inner()?; - Ok(StorageStoreDirty { inner: inner.into_mutable() }) + Ok(StorageStoreDirty { inner: self.inner.into_inner()? }) } } impl StorageStoreDirty { pub(crate) fn root(self) -> QmdbDigest { - self.inner.into_merkleized().root() + self.inner.root() } } @@ -93,13 +90,17 @@ impl QmdbBatchable for StorageStore { I::IntoIter: Send, { let inner = self.inner.take()?; - let mut dirty = inner.into_mutable(); - let mapped = - ops.into_iter().map(|(key, value)| (storage_key(key), value.map(StorageValue))); - dirty.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string()))?; - let merkleized = dirty.into_merkleized(); - let (inner, _) = - merkleized.commit(None).await.map_err(|e| BackendError::Storage(e.to_string()))?; + let mut batch = inner.new_batch(); + for (key, value) in ops { + batch = batch.write(storage_key(key), value.map(StorageValue)); + } + let merkleized = batch + .merkleize(&inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + let mut inner = inner; + inner.apply_batch(merkleized).await.map_err(|e| BackendError::Storage(e.to_string()))?; + inner.commit().await.map_err(|e| BackendError::Storage(e.to_string()))?; self.inner.restore(inner); Ok(()) } @@ -126,8 +127,18 @@ impl QmdbBatchable for StorageStoreDirty { I: IntoIterator)> + Send, I::IntoIter: Send, { - let mapped = - ops.into_iter().map(|(key, value)| (storage_key(key), value.map(StorageValue))); - self.inner.write_batch(mapped).await.map_err(|e| BackendError::Storage(e.to_string())) + let mut batch = self.inner.new_batch(); + for (key, value) in ops { + batch = batch.write(storage_key(key), value.map(StorageValue)); + } + let merkleized = batch + .merkleize(&self.inner, None) + .await + .map_err(|e| BackendError::Storage(e.to_string()))?; + self.inner + .apply_batch(merkleized) + .await + .map(|_| ()) + .map_err(|e| BackendError::Storage(e.to_string())) } } diff --git a/crates/storage/backend/src/types.rs b/crates/storage/backend/src/types.rs index 7126478..c370a16 100644 --- a/crates/storage/backend/src/types.rs +++ b/crates/storage/backend/src/types.rs @@ -5,10 +5,7 @@ use bytes::{Buf, BufMut}; use commonware_codec::{EncodeSize, Error as CodecError, Read, Write}; use commonware_cryptography::sha256::Sha256 as QmdbHasher; use commonware_runtime::tokio; -use commonware_storage::{ - qmdb::{NonDurable, Unmerkleized, any}, - translator::EightCap, -}; +use commonware_storage::{merkle::mmr, qmdb::any, translator::EightCap}; use commonware_utils::sequence::FixedBytes; use kora_qmdb::AccountEncoding; @@ -75,40 +72,24 @@ impl Read for StorageValue { } } -pub(crate) type AccountDb = - any::unordered::variable::Db; -pub(crate) type StorageDb = - any::unordered::variable::Db; -pub(crate) type CodeDb = - any::unordered::variable::Db, QmdbHasher, EightCap>; - -pub(crate) type AccountDbDirty = any::unordered::variable::Db< +pub(crate) type AccountDb = any::unordered::variable::Db< + mmr::Family, Context, AccountKey, AccountValue, QmdbHasher, EightCap, - Unmerkleized, - NonDurable, >; -pub(crate) type StorageDbDirty = any::unordered::variable::Db< +pub(crate) type StorageDb = any::unordered::variable::Db< + mmr::Family, Context, StorageKey, StorageValue, QmdbHasher, EightCap, - Unmerkleized, - NonDurable, ->; -pub(crate) type CodeDbDirty = any::unordered::variable::Db< - Context, - CodeKey, - Vec, - QmdbHasher, - EightCap, - Unmerkleized, - NonDurable, >; +pub(crate) type CodeDb = + any::unordered::variable::Db, QmdbHasher, EightCap>; pub(crate) struct StoreSlot(Option); diff --git a/crates/storage/handlers/src/adapter.rs b/crates/storage/handlers/src/adapter.rs index 94c6f2d..7efb17b 100644 --- a/crates/storage/handlers/src/adapter.rs +++ b/crates/storage/handlers/src/adapter.rs @@ -14,7 +14,7 @@ use revm::{ DatabaseCommit, DatabaseRef, async_db::{DatabaseAsyncRef, WrapDatabaseAsync}, }, - primitives::HashMap, + primitives::AddressMap, state::Account, }; @@ -208,7 +208,7 @@ where S: QmdbGettable + QmdbBatchable, C: QmdbGettable> + QmdbBatchable>, { - fn commit(&mut self, changes: HashMap) { + fn commit(&mut self, changes: AddressMap) { use std::collections::BTreeMap; use kora_qmdb::AccountUpdate; diff --git a/docker/Justfile b/docker/Justfile index 451c888..997ac1e 100644 --- a/docker/Justfile +++ b/docker/Justfile @@ -64,7 +64,7 @@ redo-dkg: echo "Removing old DKG shares..." for i in 0 1 2 3; do - docker run --rm -v kora-devnet_data_node${i}:/data alpine rm -f /data/share.key /data/output.json + docker run --rm -v kora-devnet_data_node${i}:/data alpine rm -f /data/share.key /data/output.json /data/dkg_state.json done echo "Starting interactive DKG ceremony..." @@ -73,9 +73,9 @@ redo-dkg: echo "Waiting for DKG ceremony to complete..." timeout=300 while true; do - EXITED=$(docker compose -f compose/devnet.yaml ps --format json 2>/dev/null | \ + EXITED=$(docker compose -f compose/devnet.yaml ps -a --format json 2>/dev/null | \ jq -r 'select(.Service | startswith("dkg-")) | select(.State == "exited") | select(.ExitCode == 0) | .Service' 2>/dev/null | wc -l | tr -d ' ') - FAILED=$(docker compose -f compose/devnet.yaml ps --format json 2>/dev/null | \ + FAILED=$(docker compose -f compose/devnet.yaml ps -a --format json 2>/dev/null | \ jq -r 'select(.Service | startswith("dkg-")) | select(.State == "exited") | select(.ExitCode != 0) | .Service' 2>/dev/null | wc -l | tr -d ' ') [[ "$FAILED" -gt 0 ]] && echo "DKG ceremony failed!" && exit 1 diff --git a/docker/scripts/devnet-run.sh b/docker/scripts/devnet-run.sh index 2599dbb..82b7539 100755 --- a/docker/scripts/devnet-run.sh +++ b/docker/scripts/devnet-run.sh @@ -116,6 +116,48 @@ print_endpoints() { echo "" } +check_dkg_outputs() { + local expected_checksum="" + + for i in 0 1 2 3; do + local volume="kora-devnet_data_node${i}" + + if ! docker volume inspect "$volume" >/dev/null 2>&1; then + return 1 + fi + + if ! docker run --rm -v "${volume}:/data" alpine \ + test -f /data/share.key -a -f /data/output.json >/dev/null 2>&1; then + return 1 + fi + + local checksum + checksum=$(docker run --rm -v "${volume}:/data" alpine \ + sha256sum /data/output.json 2>/dev/null | awk '{print $1}') + + if [[ -z "$checksum" ]]; then + return 1 + fi + + if [[ -z "$expected_checksum" ]]; then + expected_checksum="$checksum" + elif [[ "$checksum" != "$expected_checksum" ]]; then + return 1 + fi + done + + return 0 +} + +clear_dkg_outputs() { + for i in 0 1 2 3; do + local volume="kora-devnet_data_node${i}" + docker volume inspect "$volume" >/dev/null 2>&1 || continue + docker run --rm -v "${volume}:/data" alpine \ + rm -f /data/share.key /data/output.json /data/dkg_state.json >/dev/null 2>&1 || true + done +} + cd "$(dirname "$0")/.." print_header @@ -137,9 +179,11 @@ docker volume inspect kora-devnet_shared_config >/dev/null 2>&1 && \ docker run --rm -v kora-devnet_shared_config:/shared alpine test -f /shared/peers.json 2>/dev/null && \ CONFIG_EXISTS=true -docker volume inspect kora-devnet_data_node0 >/dev/null 2>&1 && \ - docker run --rm -v kora-devnet_data_node0:/data alpine test -f /data/share.key 2>/dev/null && \ +if check_dkg_outputs; then SHARES_EXIST=true +else + clear_dkg_outputs +fi echo "" @@ -171,6 +215,11 @@ if [[ "$INTERACTIVE_DKG" == "true" ]]; then if [[ "$SHARES_EXIST" != "true" ]]; then echo "" print_phase "1.5/3" "Interactive DKG Ceremony" + + # DKG jobs use the same node0..node3 hostnames as validators. Stop validators first so + # Docker DNS cannot route ceremony traffic to stale validator containers. + docker compose -f compose/devnet.yaml stop \ + validator-node0 validator-node1 validator-node2 validator-node3 >/dev/null 2>&1 || true # Start DKG nodes run_with_spinner "Starting DKG nodes..." docker compose -f compose/devnet.yaml --profile interactive-dkg up -d \ @@ -222,7 +271,15 @@ if [[ "$INTERACTIVE_DKG" == "true" ]]; then fi else if [[ "$SHARES_EXIST" != "true" ]]; then - print_success "Threshold shares generated (trusted dealer)" + echo "" + print_phase "1.5/3" "Trusted Dealer DKG" + + if run_with_spinner "Generating threshold shares..." docker compose -f compose/devnet.yaml run --rm init-config; then + print_success "Threshold shares generated" + else + print_error "Trusted dealer DKG failed" + exit 1 + fi else print_skip "Threshold shares exist" fi diff --git a/docker/scripts/devnet-stats.sh b/docker/scripts/devnet-stats.sh index 093d2e6..404033e 100755 --- a/docker/scripts/devnet-stats.sh +++ b/docker/scripts/devnet-stats.sh @@ -65,10 +65,12 @@ render() { echo -e "${BOLD}${CYAN}Node Status${NC}" echo -e "┌───────┬──────────┬────────────┬──────────┬────────────┬────────────┬────────────┬────────────┬────────┐" - echo -e "│ ${BOLD}Node${NC} │ ${BOLD}RPC${NC} │ ${BOLD}Uptime${NC} │ ${BOLD}View${NC} │ ${BOLD}Finalized${NC} │ ${BOLD}Nullified${NC} │ ${BOLD}Proposed${NC} │ ${BOLD}Throughput${NC} │ ${BOLD}Leader${NC} │" + echo -e "│ ${BOLD}Node${NC} │ ${BOLD}Status${NC} │ ${BOLD}Uptime${NC} │ ${BOLD}View${NC} │ ${BOLD}Finalized${NC} │ ${BOLD}Nullified${NC} │ ${BOLD}Proposed${NC} │ ${BOLD}Throughput${NC} │ ${BOLD}Leader${NC} │" echo -e "├───────┼──────────┼────────────┼──────────┼────────────┼────────────┼────────────┼────────────┼────────┤" + local rpc_count=0 local healthy_count=0 + local stalled_count=0 local max_uptime=0 local total_finalized=0 local max_view=0 @@ -86,11 +88,12 @@ render() { if [[ "$status" != "{}" ]]; then # Parse with single jq call local parsed - parsed=$(echo "$status" | jq -r '[.uptimeSecs // .uptime_secs // 0, .currentView // .current_view // 0, .finalizedCount // .finalized_count // 0, .nullifiedCount // .nullified_count // 0, .proposedCount // .proposed_count // 0, .isLeader // .is_leader // false] | @tsv' 2>/dev/null) + parsed=$(echo "$status" | jq -r '[.validatorIndex // .validator_index // empty, .uptimeSecs // .uptime_secs // 0, .currentView // .current_view // 0, .finalizedCount // .finalized_count // 0, .nullifiedCount // .nullified_count // 0, .proposedCount // .proposed_count // 0, .isLeader // .is_leader // false] | @tsv' 2>/dev/null) if [[ -n "$parsed" ]]; then - read -r uptime view finalized nullified proposed leader <<< "$parsed" + read -r validator_index uptime view finalized nullified proposed leader <<< "$parsed" + validator_index="${validator_index:-$i}" uptime="${uptime:-0}" view="${view:-0}" finalized="${finalized:-0}" @@ -100,11 +103,18 @@ render() { [[ $uptime -gt $max_uptime ]] && max_uptime=$uptime [[ $view -gt $max_view ]] && max_view=$view total_finalized=$finalized - ((++healthy_count)) + ((++rpc_count)) local uptime_str=$(format_uptime "$uptime") local leader_str="-" [[ "$leader" == "true" ]] && leader_str="${MAGENTA}★${NC}" + local rpc_status="${GREEN}online${NC} " + if [[ $view -eq 0 && $finalized -eq 0 && $proposed -eq 0 && $uptime -gt 10 ]]; then + rpc_status="${YELLOW}stalled${NC}" + ((++stalled_count)) + else + ((++healthy_count)) + fi # Calculate throughput (blocks/sec) local throughput_str="-" @@ -117,8 +127,8 @@ render() { [[ $tps_int -gt $max_throughput ]] && max_throughput=$tps_int fi - printf "│ ${CYAN}%-5s${NC} │ ${GREEN}online${NC} │ %-10s │ %-8s │ %-10s │ %-10s │ %-10s │ %-10s │ %b │\n" \ - "$i" "$uptime_str" "$view" "$finalized" "$nullified" "$proposed" "$throughput_str" "$leader_str" + printf "│ ${CYAN}%-5s${NC} │ %b │ %-10s │ %-8s │ %-10s │ %-10s │ %-10s │ %-10s │ %b │\n" \ + "$validator_index" "$rpc_status" "$uptime_str" "$view" "$finalized" "$nullified" "$proposed" "$throughput_str" "$leader_str" else printf "│ ${CYAN}%-5s${NC} │ ${RED}offline${NC} │ - │ - │ - │ - │ - │ - │ - │\n" "$i" fi @@ -150,7 +160,7 @@ render() { throughput_str=$(awk "BEGIN {printf \"%.2f b/s\", $max_throughput / 100}") fi - echo -e " ${DIM}Healthy:${NC} ${health_color}${healthy_count}/4${NC} │ ${DIM}Threshold:${NC} $threshold │ ${DIM}View:${NC} ${CYAN}$max_view${NC} │ ${DIM}Finalized:${NC} ${GREEN}$total_finalized${NC} │ ${DIM}Throughput:${NC} ${CYAN}$throughput_str${NC} │ ${DIM}Uptime:${NC} $uptime_str" + echo -e " ${DIM}Consensus:${NC} ${health_color}${healthy_count}/4${NC} │ ${DIM}RPC:${NC} ${GREEN}${rpc_count}/4${NC} │ ${DIM}Stalled:${NC} ${YELLOW}${stalled_count}${NC} │ ${DIM}Threshold:${NC} $threshold │ ${DIM}View:${NC} ${CYAN}$max_view${NC} │ ${DIM}Finalized:${NC} ${GREEN}$total_finalized${NC} │ ${DIM}Throughput:${NC} ${CYAN}$throughput_str${NC} │ ${DIM}Uptime:${NC} $uptime_str" # Endpoints echo "" From 8b86b844df299ef0b8d3bfd9657ff5affced8fc1 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Tue, 28 Apr 2026 22:34:57 +0200 Subject: [PATCH 05/16] Implement secondary peer functionality and update documentation - Added support for secondary peers in the Kora devnet, allowing them to follow validator traffic without participating in consensus. - Updated the `Cargo.toml` and `Cargo.lock` files to include new dependencies for commonware modules. - Enhanced the README and Docker documentation to explain the role and setup of secondary peers. - Modified the CLI to accept secondary peer arguments and adjusted the setup process to create secondary peer identity keys. - Updated Docker configurations to include a secondary peer service in the devnet setup. - Refactored network transport logic to track both primary and secondary peers effectively. --- Cargo.lock | 5 + README.md | 7 +- bin/keygen/src/setup.rs | 39 +++++++ bin/kora/Cargo.toml | 5 + bin/kora/src/cli.rs | 113 ++++++++++++++++--- crates/e2e/src/harness.rs | 30 ++--- crates/network/transport-sim/src/context.rs | 29 ++++- crates/network/transport-sim/src/provider.rs | 6 +- crates/network/transport/README.md | 2 +- crates/network/transport/src/builder.rs | 2 +- crates/node/runner/src/runner.rs | 24 +++- crates/utilities/crypto/src/test_utils.rs | 2 +- docker/Justfile | 7 +- docker/README.md | 60 +++++++++- docker/compose/devnet.yaml | 28 ++++- docker/scripts/devnet-run.sh | 47 +++++++- docker/scripts/entrypoint.sh | 28 +++++ 17 files changed, 380 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ad1fd..74ef022 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3187,7 +3187,11 @@ dependencies = [ "clap", "commonware-codec", "commonware-cryptography", + "commonware-p2p", + "commonware-runtime", + "commonware-utils", "eyre", + "futures", "hex", "kora-cli", "kora-config", @@ -3196,6 +3200,7 @@ dependencies = [ "kora-rpc", "kora-runner", "kora-service", + "kora-transport", "serde_json", "tokio", "tracing", diff --git a/README.md b/README.md index 484abc6..8e7bdb5 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,11 @@ Start the devnet with interactive DKG (Distributed Key Generation): just devnet ``` +The Docker devnet starts four validator nodes plus one secondary peer. Secondary +peers are authenticated Commonware P2P nodes that follow validator traffic without +participating in consensus. See the [Docker devnet README](./docker/README.md#secondary-peers) +for how a secondary peer joins the network. + > [!TIP] > See the [Justfile](./Justfile) for other useful commands. @@ -53,7 +58,7 @@ https://github.com/user-attachments/assets/8ea05477-039d-4dd2-a7f3-660f179299f7 ## Architecture -The devnet runs in three phases. Phase 0 generates ed25519 identity keys for each validator node. Phase 1 is the DKG ceremony, an interactive threshold key generation process using Ed25519 simplex consensus where validators collaborate to generate a shared BLS12-381 threshold key. Phase 2 launches full validator nodes running BLS12-381 threshold consensus, the REVM execution engine, and QMDB state storage. +The devnet runs in three phases. Phase 0 generates ed25519 identity keys for each validator node and the built-in secondary peer. Phase 1 is the DKG ceremony, an interactive threshold key generation process using Ed25519 simplex consensus where validators collaborate to generate a shared BLS12-381 threshold key. Phase 2 launches full validator nodes running BLS12-381 threshold consensus, the REVM execution engine, and QMDB state storage, then starts the secondary peer as a non-voting P2P follower. Observability is provided through Prometheus metrics with Grafana dashboards for monitoring node health, consensus performance, and execution metrics. diff --git a/bin/keygen/src/setup.rs b/bin/keygen/src/setup.rs index ca97a9f..3e805e9 100644 --- a/bin/keygen/src/setup.rs +++ b/bin/keygen/src/setup.rs @@ -14,6 +14,9 @@ pub(crate) struct SetupArgs { #[arg(long, default_value = "4")] pub validators: usize, + #[arg(long, default_value = "0")] + pub secondary_peers: usize, + #[arg(long, default_value = "3")] pub threshold: u32, @@ -32,6 +35,7 @@ struct PeersConfig { validators: usize, threshold: u32, participants: Vec, + secondary_participants: Vec, bootstrappers: BTreeMap, } @@ -66,6 +70,7 @@ pub(crate) fn run(args: SetupArgs) -> Result<()> { fs::create_dir_all(&args.output_dir).wrap_err("Failed to create output directory")?; let mut participants = Vec::with_capacity(args.validators); + let mut secondary_participants = Vec::with_capacity(args.secondary_peers); let mut bootstrappers = BTreeMap::new(); for i in 0..args.validators { @@ -104,10 +109,43 @@ pub(crate) fn run(args: SetupArgs) -> Result<()> { tracing::info!(node = i, path = ?key_path, "Wrote identity key"); } + for i in 0..args.secondary_peers { + let node_dir = args.output_dir.join(format!("secondary{}", i)); + fs::create_dir_all(&node_dir) + .wrap_err_with(|| format!("Failed to create secondary{} dir", i))?; + + let key_path = node_dir.join("validator.key"); + let key = if key_path.exists() { + tracing::info!(node = i, "Loading existing secondary identity key"); + let bytes = fs::read(&key_path)?; + let mut seed = [0u8; 32]; + seed.copy_from_slice(&bytes); + ed25519::PrivateKey::from(ed25519_consensus::SigningKey::from(seed)) + } else { + tracing::info!(node = i, "Generating new secondary identity key"); + let mut seed = [0u8; 32]; + rand::rngs::OsRng.fill_bytes(&mut seed); + fs::write(&key_path, seed)?; + ed25519::PrivateKey::from(ed25519_consensus::SigningKey::from(seed)) + }; + + let public_key = key.public_key(); + let pk_hex = hex::encode(Encode::encode(&public_key)); + secondary_participants.push(pk_hex.clone()); + + let node_config = + NodeSetupConfig { validator_index: i, public_key: pk_hex, port: args.base_port }; + let config_path = node_dir.join("setup.json"); + fs::write(&config_path, serde_json::to_string_pretty(&node_config)?)?; + + tracing::info!(node = i, path = ?key_path, "Wrote secondary identity key"); + } + let peers = PeersConfig { validators: args.validators, threshold: args.threshold, participants, + secondary_participants, bootstrappers, }; let peers_path = args.output_dir.join("peers.json"); @@ -157,6 +195,7 @@ pub(crate) fn run(args: SetupArgs) -> Result<()> { tracing::info!("Setup complete"); tracing::info!(" Validators: {}", args.validators); + tracing::info!(" Secondary peers: {}", args.secondary_peers); tracing::info!(" Threshold: {}", args.threshold); tracing::info!(" Chain ID: {}", args.chain_id); diff --git a/bin/kora/Cargo.toml b/bin/kora/Cargo.toml index 16e3287..229df56 100644 --- a/bin/kora/Cargo.toml +++ b/bin/kora/Cargo.toml @@ -14,11 +14,16 @@ kora-dkg.workspace = true kora-runner.workspace = true kora-domain.workspace = true kora-rpc.workspace = true +kora-transport.workspace = true commonware-cryptography.workspace = true commonware-codec.workspace = true +commonware-p2p.workspace = true +commonware-runtime.workspace = true +commonware-utils.workspace = true clap.workspace = true +futures.workspace = true tokio.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/bin/kora/src/cli.rs b/bin/kora/src/cli.rs index f53afd8..94122d9 100644 --- a/bin/kora/src/cli.rs +++ b/bin/kora/src/cli.rs @@ -31,6 +31,7 @@ pub(crate) struct Cli { pub(crate) enum Commands { Dkg(DkgArgs), Validator(ValidatorArgs), + Secondary(SecondaryArgs), } #[derive(clap::Args, Debug)] @@ -50,6 +51,13 @@ pub(crate) struct ValidatorArgs { pub peers: Option, } +#[derive(clap::Args, Debug)] +pub(crate) struct SecondaryArgs { + /// Path to peers.json file containing primary and secondary peer information. + #[arg(long)] + pub peers: PathBuf, +} + impl Cli { pub(crate) fn load_config(&self) -> eyre::Result { let mut config = NodeConfig::load(self.config.as_deref())?; @@ -68,6 +76,7 @@ impl Cli { match &self.command { Some(Commands::Dkg(args)) => self.run_dkg(args), Some(Commands::Validator(args)) => self.run_validator(args), + Some(Commands::Secondary(args)) => self.run_secondary(args), None => self.run_legacy(), } } @@ -132,17 +141,16 @@ impl Cli { .map_err(|e| eyre::eyre!("Failed to load threshold scheme: {}", e))?; tracing::info!("Loaded threshold signing scheme"); + let mut secondary_participants = Vec::new(); if let Some(ref peers_path) = args.peers { let peers = load_peers(peers_path)?; - config.network.bootstrap_peers = peers - .bootstrappers - .iter() - .map(|(pk, addr)| format!("{}@{}", hex::encode(pk.as_ref()), addr)) - .collect(); + config.network.bootstrap_peers = format_bootstrappers(&peers.bootstrappers); tracing::info!( bootstrap_peers = config.network.bootstrap_peers.len(), "Loaded bootstrap peers from peers.json" ); + + secondary_participants = peers.secondary_participants; } let genesis_path = config.data_dir.join("genesis.json"); @@ -159,11 +167,62 @@ impl Cli { kora_config::DEFAULT_GAS_LIMIT, bootstrap, ) - .with_rpc(node_state, rpc_addr); + .with_rpc(node_state, rpc_addr) + .with_secondary_peers(secondary_participants); runner.run_standalone(config).map_err(|e| eyre::eyre!("Runner failed: {}", e.0)) } + fn run_secondary(&self, args: &SecondaryArgs) -> eyre::Result<()> { + use commonware_p2p::{Manager, TrackedPeers}; + use commonware_runtime::Runner; + use commonware_utils::ordered::Set; + use kora_transport::NetworkConfigExt; + + let mut config = self.load_config()?; + let peers = load_peers(&args.peers)?; + config.network.bootstrap_peers = format_bootstrappers(&peers.bootstrappers); + + let identity_key = config.validator_key()?; + let my_pk = commonware_cryptography::Signer::public_key(&identity_key); + if !peers.secondary_participants.contains(&my_pk) { + return Err(eyre::eyre!( + "secondary identity is not listed in peers.json secondary_participants" + )); + } + + tracing::info!( + chain_id = config.chain_id, + bootstrap_peers = config.network.bootstrap_peers.len(), + secondary_peers = peers.secondary_participants.len(), + "Starting secondary peer" + ); + + let executor = commonware_runtime::tokio::Runner::default(); + executor.start(|context| async move { + let mut transport = config + .network + .build_local_transport(identity_key, context.clone()) + .map_err(|e| eyre::eyre!("failed to build transport: {}", e))?; + + transport + .oracle + .track( + 0, + TrackedPeers::new( + Set::from_iter_dedup(peers.participants), + Set::from_iter_dedup(peers.secondary_participants), + ), + ) + .await; + + tracing::info!("secondary peer joined network"); + futures::future::pending::<()>().await; + #[allow(unreachable_code)] + Ok::<(), eyre::Error>(()) + }) + } + fn run_legacy(&self) -> eyre::Result<()> { let config = self.load_config()?; @@ -177,10 +236,20 @@ impl Cli { #[derive(Debug)] struct PeersInfo { participants: Vec, + secondary_participants: Vec, threshold: u32, bootstrappers: Vec<(commonware_cryptography::ed25519::PublicKey, String)>, } +fn format_bootstrappers( + bootstrappers: &[(commonware_cryptography::ed25519::PublicKey, String)], +) -> Vec { + bootstrappers + .iter() + .map(|(pk, addr)| format!("{}@{}", hex::encode(pk.as_ref()), addr)) + .collect() +} + fn load_peers(path: &PathBuf) -> eyre::Result { use commonware_codec::ReadExt; @@ -197,12 +266,15 @@ fn load_peers(path: &PathBuf) -> eyre::Result { .filter_map(|v| v.as_str().map(String::from)) .collect(); - let mut participants = Vec::with_capacity(participants_hex.len()); - for pk_hex in &participants_hex { - let bytes = hex::decode(pk_hex)?; - let pk = commonware_cryptography::ed25519::PublicKey::read(&mut bytes.as_slice())?; - participants.push(pk); - } + let participants = parse_public_keys(&participants_hex)?; + + let secondary_participants_hex: Vec = json["secondary_participants"] + .as_array() + .map(|participants| { + participants.iter().filter_map(|v| v.as_str().map(String::from)).collect() + }) + .unwrap_or_default(); + let secondary_participants = parse_public_keys(&secondary_participants_hex)?; let bootstrappers_obj = json["bootstrappers"].as_object().ok_or_else(|| eyre::eyre!("missing bootstrappers"))?; @@ -215,5 +287,20 @@ fn load_peers(path: &PathBuf) -> eyre::Result { bootstrappers.push((pk, addr_str.to_string())); } - Ok(PeersInfo { participants, threshold, bootstrappers }) + Ok(PeersInfo { participants, secondary_participants, threshold, bootstrappers }) +} + +fn parse_public_keys( + keys: &[String], +) -> eyre::Result> { + use commonware_codec::ReadExt; + + let mut public_keys = Vec::with_capacity(keys.len()); + for pk_hex in keys { + let bytes = hex::decode(pk_hex)?; + let pk = commonware_cryptography::ed25519::PublicKey::read(&mut bytes.as_slice())?; + public_keys.push(pk); + } + + Ok(public_keys) } diff --git a/crates/e2e/src/harness.rs b/crates/e2e/src/harness.rs index 0784be1..1acbeba 100644 --- a/crates/e2e/src/harness.rs +++ b/crates/e2e/src/harness.rs @@ -10,14 +10,14 @@ use alloy_primitives::{Address, B256, U256}; use anyhow::Context as _; use commonware_consensus::{ Reporter, Reporters, - application::marshaled::Marshaled, + marshal::standard::{Inline, Standard}, simplex::{self, elector::Random, types::Finalization}, types::{Epoch, FixedEpocher, ViewDelta}, }; use commonware_cryptography::{bls12381::primitives::variant::MinSig, ed25519}; use commonware_p2p::{Manager as _, simulated}; use commonware_parallel::Sequential; -use commonware_runtime::{Clock, Metrics, Runner as _, Spawner, buffer::PoolRef, tokio}; +use commonware_runtime::{Clock, Metrics, Runner as _, Spawner, buffer::paged::CacheRef, tokio}; use commonware_utils::{NZU64, NZUsize, TryCollect as _, ordered::Set}; use futures::{StreamExt as _, channel::mpsc}; use kora_crypto::{ThresholdScheme, threshold_schemes}; @@ -209,13 +209,13 @@ async fn start_network( simulated::Config { max_size: MAX_MSG_SIZE as u32, disconnect_on_block: true, - tracked_peer_sets: None, + tracked_peer_sets: NZUsize!(4), }, ); network.start(); let control = SimControl::new(oracle); - control.manager().update(0, participants).await; + control.manager().track(0, participants).await; control } @@ -350,7 +350,7 @@ async fn start_single_node( // Create marshaled application let epocher = FixedEpocher::new(NZU64!(EPOCH_LENGTH)); - let marshaled = Marshaled::new( + let marshaled = Inline::new( context.with_label(&format!("marshaled_{index}")), app, marshal_mailbox.clone(), @@ -432,13 +432,13 @@ async fn start_marshal( control: simulated::Control, manager: M, scheme: ThresholdScheme, - buffer_pool: PoolRef, + buffer_pool: CacheRef, block_codec_config: BlockCfg, blocks: (simulated::Sender, simulated::Receiver), backfill: (simulated::Sender, simulated::Receiver), application: R, partition_prefix: &str, -) -> anyhow::Result> +) -> anyhow::Result>> where M: commonware_p2p::Manager, R: Reporter> + Send + 'static, @@ -472,14 +472,15 @@ where let resolver = PeerInitializer::init::<_, _, _, Block, _, _, _>( &ctx, public_key.clone(), - manager, + manager.clone(), control, backfill, ); - let (broadcast_engine, buffer) = BroadcastInitializer::init::<_, PublicKey, Block>( + let (broadcast_engine, buffer) = BroadcastInitializer::init::<_, PublicKey, Block, M>( ctx.with_label("broadcast"), public_key, + manager, block_codec_config, ); broadcast_engine.start(blocks); @@ -647,7 +648,8 @@ use std::collections::BTreeSet; use alloy_primitives::Bytes; use commonware_consensus::{ - Application, Block as _, VerifyingApplication, marshal::ingress::mailbox::AncestorStream, + Application, Block as _, VerifyingApplication, + marshal::ancestry::{AncestorStream, BlockProvider}, simplex::types::Context, }; use commonware_cryptography::{Committable as _, certificate::Scheme as CertScheme}; @@ -829,10 +831,10 @@ where async move { self.ledger.genesis_block() } } - fn propose( + fn propose>( &mut self, _context: (Env, Self::Context), - mut ancestry: AncestorStream, + mut ancestry: AncestorStream, ) -> impl std::future::Future> + Send { async move { let parent = ancestry.next().await?; @@ -846,10 +848,10 @@ where Env: Rng + Spawner + Metrics + Clock, S: CertScheme + Send + Sync + 'static, { - fn verify( + fn verify>( &mut self, _context: (Env, Self::Context), - mut ancestry: AncestorStream, + mut ancestry: AncestorStream, ) -> impl std::future::Future + Send { async move { let mut blocks_to_verify = Vec::new(); diff --git a/crates/network/transport-sim/src/context.rs b/crates/network/transport-sim/src/context.rs index 5b38a8e..165379c 100644 --- a/crates/network/transport-sim/src/context.rs +++ b/crates/network/transport-sim/src/context.rs @@ -101,6 +101,30 @@ impl commonware_runtime::Metrics for SimContext { } } + fn with_attribute(&self, key: &str, value: impl fmt::Display) -> Self { + Self { + inner: self.inner.with_attribute(key, value), + force_base_addr: false, + port_offset: self.port_offset, + } + } + + fn with_scope(&self) -> Self { + Self { + inner: self.inner.with_scope(), + force_base_addr: false, + port_offset: self.port_offset, + } + } + + fn with_span(&self) -> Self { + Self { + inner: self.inner.with_span(), + force_base_addr: false, + port_offset: self.port_offset, + } + } + fn register, H: Into>(&self, name: N, help: H, metric: impl Metric) { self.inner.register(name, help, metric); } @@ -121,11 +145,6 @@ impl commonware_runtime::Spawner for SimContext { self } - fn instrumented(mut self) -> Self { - self.inner = self.inner.instrumented(); - self - } - fn spawn(self, f: F) -> commonware_runtime::Handle where F: FnOnce(Self) -> Fut + Send + 'static, diff --git a/crates/network/transport-sim/src/provider.rs b/crates/network/transport-sim/src/provider.rs index 87819a3..f02c936 100644 --- a/crates/network/transport-sim/src/provider.rs +++ b/crates/network/transport-sim/src/provider.rs @@ -9,6 +9,7 @@ use std::{ use commonware_cryptography::PublicKey; use commonware_p2p::{Manager as _, simulated}; use commonware_runtime::{Quota, tokio}; +use commonware_utils::NZUsize; use kora_config::NodeConfig; use kora_service::TransportProvider; use kora_transport::{ @@ -77,7 +78,7 @@ impl SimControl

{ epoch: u64, validators: commonware_utils::ordered::Set

, ) { - self.manager().update(epoch, validators).await; + self.manager().track(epoch, validators).await; } /// Returns a peer control handle for channel registration. @@ -160,7 +161,8 @@ pub fn create_sim_network( max_size: u32, disconnect_on_block: bool, ) -> SimControl

{ - let config = simulated::Config { max_size, disconnect_on_block, tracked_peer_sets: None }; + let config = + simulated::Config { max_size, disconnect_on_block, tracked_peer_sets: NZUsize!(4) }; let (network, oracle) = simulated::Network::new(context, config); network.start(); diff --git a/crates/network/transport/README.md b/crates/network/transport/README.md index ccc246d..27e1b97 100644 --- a/crates/network/transport/README.md +++ b/crates/network/transport/README.md @@ -38,7 +38,7 @@ let (cert_sender, cert_receiver) = transport.simplex.certs; let (block_sender, block_receiver) = transport.marshal.blocks; // Register validator set -transport.oracle.update(0, validators).await; +transport.oracle.track(0, validators).await; ``` ## License diff --git a/crates/network/transport/src/builder.rs b/crates/network/transport/src/builder.rs index 6296579..ac597ee 100644 --- a/crates/network/transport/src/builder.rs +++ b/crates/network/transport/src/builder.rs @@ -47,7 +47,7 @@ impl TransportConfig { /// let transport = config.build(context)?; /// /// // Register validators with oracle - /// transport.oracle.update(0, validators).await; + /// transport.oracle.track(0, validators).await; /// /// // Pass channels to consumers /// engine.start( diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index d58d508..ce17bb6 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -13,10 +13,10 @@ use commonware_consensus::{ types::{Epoch, FixedEpocher, ViewDelta}, }; use commonware_cryptography::{bls12381::primitives::variant::MinSig, ed25519}; -use commonware_p2p::Manager; +use commonware_p2p::{Manager, TrackedPeers}; use commonware_parallel::Sequential; use commonware_runtime::{Metrics as _, Spawner, buffer::paged::CacheRef, tokio}; -use commonware_utils::{NZU64, NZUsize, acknowledgement::Exact}; +use commonware_utils::{NZU64, NZUsize, acknowledgement::Exact, ordered::Set}; use futures::StreamExt; use kora_domain::{Block, BlockCfg, BootstrapConfig, ConsensusDigest, LedgerEvent, TxCfg}; use kora_executor::{BlockContext, RevmExecutor}; @@ -123,6 +123,8 @@ pub struct ProductionRunner { pub partition_prefix: String, /// Optional RPC configuration (state, bind address). pub rpc_config: Option<(kora_rpc::NodeState, std::net::SocketAddr)>, + /// Secondary peers authorized to follow validator traffic without participating in consensus. + pub secondary_peers: Vec, } impl ProductionRunner { @@ -140,6 +142,7 @@ impl ProductionRunner { bootstrap, partition_prefix: PARTITION_PREFIX.to_string(), rpc_config: None, + secondary_peers: Vec::new(), } } @@ -149,6 +152,13 @@ impl ProductionRunner { self.rpc_config = Some((state, addr)); self } + + /// Configure secondary peers that should be tracked by the P2P oracle. + #[must_use] + pub fn with_secondary_peers(mut self, peers: Vec) -> Self { + self.secondary_peers = peers; + self + } } impl ProductionRunner { @@ -190,8 +200,14 @@ impl NodeRunner for ProductionRunner { info!(chain_id = self.chain_id, "Starting production validator"); let validators = self.scheme.participants().clone(); - transport.oracle.track(0, validators).await; - info!(count = self.scheme.participants().len(), "Registered validators with oracle"); + let secondary = Set::from_iter_dedup(self.secondary_peers.iter().cloned()); + let secondary_count = secondary.len(); + transport.oracle.track(0, TrackedPeers::new(validators, secondary)).await; + info!( + validators = self.scheme.participants().len(), + secondary_peers = secondary_count, + "Registered primary and secondary peers with oracle" + ); let page_cache = default_page_cache(&context); let block_cfg = block_codec_cfg(); diff --git a/crates/utilities/crypto/src/test_utils.rs b/crates/utilities/crypto/src/test_utils.rs index 0f1c1f2..72a860a 100644 --- a/crates/utilities/crypto/src/test_utils.rs +++ b/crates/utilities/crypto/src/test_utils.rs @@ -1,5 +1,5 @@ use anyhow::Context as _; -use commonware_consensus::simplex::scheme::bls12381_threshold; +use commonware_consensus::simplex::scheme::bls12381_threshold::vrf as bls12381_threshold; use commonware_cryptography::{ Signer as _, bls12381::{ diff --git a/docker/Justfile b/docker/Justfile index 997ac1e..dadda88 100644 --- a/docker/Justfile +++ b/docker/Justfile @@ -33,7 +33,7 @@ reset: restart: down devnet restart-validators: - docker compose -f compose/devnet.yaml restart validator-node0 validator-node1 validator-node2 validator-node3 + docker compose -f compose/devnet.yaml restart validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 status: @echo "" @@ -41,12 +41,13 @@ status: @echo "" @echo "Endpoints:" @echo " P2P: localhost:30400-30403" + @echo " Secondary: localhost:30500" @echo " RPC: localhost:8545-8548" @echo " Prometheus: http://localhost:9090" @echo " Grafana: http://localhost:3000" logs: - docker compose -f compose/devnet.yaml logs -f validator-node0 validator-node1 validator-node2 validator-node3 + docker compose -f compose/devnet.yaml logs -f validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 logs-dkg: docker compose -f compose/devnet.yaml --profile interactive-dkg logs -f dkg-node0 dkg-node1 dkg-node2 dkg-node3 @@ -87,7 +88,7 @@ redo-dkg: done docker compose -f compose/devnet.yaml --profile interactive-dkg stop dkg-node0 dkg-node1 dkg-node2 dkg-node3 2>/dev/null || true - docker compose -f compose/devnet.yaml up -d validator-node0 validator-node1 validator-node2 validator-node3 + docker compose -f compose/devnet.yaml up -d validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 echo "DKG complete. Validators restarted." lint: diff --git a/docker/README.md b/docker/README.md index c9a02a2..bd008f1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,6 +4,10 @@ This directory contains Docker configurations for running a local Kora devnet wi - **Interactive DKG** (production-like, no single party learns the master secret) - **Trusted Dealer DKG** (fast, insecure, for local development) +The devnet also starts one **secondary peer**. Secondary peers are authorized P2P nodes +that follow validator traffic but are not consensus participants and do not receive DKG +shares. + ## Quick Start From the repository root: @@ -21,13 +25,15 @@ just trusted-devnet 2. Generate validator identity keys (init-setup) 3. Run interactive DKG ceremony across 4 nodes 4. Start 4 validator nodes with threshold BLS consensus -5. Start Prometheus and Grafana for observability (optional) +5. Start 1 secondary peer that joins through the validators +6. Start Prometheus and Grafana for observability (optional) ### Trusted Dealer (fast) 1. Build the Docker image 2. Generate validator identity keys and run trusted dealer DKG (init-config) 3. Start 4 validator nodes with threshold BLS consensus -4. Start Prometheus and Grafana for observability (optional) +4. Start 1 secondary peer that joins through the validators +5. Start Prometheus and Grafana for observability (optional) ## Commands @@ -92,6 +98,10 @@ Run from repository root (`just `) or from `docker/` directory (`just │ - REVM execution │ │ - QMDB state storage │ ├─────────────────────────────────────────────────────────────────┤ +│ Secondary peer (secondary-node0) │ +│ - Authenticated P2P participant │ +│ - Tracks validator traffic without joining consensus │ +├─────────────────────────────────────────────────────────────────┤ │ Observability (optional): prometheus + grafana │ │ - Prometheus: http://localhost:9090 │ │ - Grafana: http://localhost:3000 (admin/admin) │ @@ -114,6 +124,10 @@ Run from repository root (`just `) or from `docker/` directory (`just │ - REVM execution │ │ - QMDB state storage │ ├─────────────────────────────────────────────────────────────────┤ +│ Secondary peer (secondary-node0) │ +│ - Authenticated P2P participant │ +│ - Tracks validator traffic without joining consensus │ +├─────────────────────────────────────────────────────────────────┤ │ Observability (optional): prometheus + grafana │ │ - Prometheus: http://localhost:9090 │ │ - Grafana: http://localhost:3000 (admin/admin) │ @@ -130,6 +144,7 @@ Run from repository root (`just `) or from `docker/` directory (`just | validator-node1 P2P | 30401 | P2P networking | | validator-node2 P2P | 30402 | P2P networking | | validator-node3 P2P | 30403 | P2P networking | +| secondary-node0 P2P | 30500 | Secondary P2P networking | | validator-node0 RPC | 8545 | JSON-RPC endpoint | | validator-node1 RPC | 8546 | JSON-RPC endpoint | | validator-node2 RPC | 8547 | JSON-RPC endpoint | @@ -155,6 +170,45 @@ Environment variables (set in `.env` or export): | `BOOTSTRAP_PEERS` | - | Bootstrap peer addresses | | `HEALTHCHECK_MODE` | - | Health check mode (dkg, ready) | +## Secondary Peers + +Kora follows Commonware's primary/secondary peer model. Validators are tracked as +the primary peer set and secondary peers are tracked as followers. Primary peers +drive consensus progress; secondary peers can establish authenticated transport +connections and follow replicated data without signing consensus messages. + +The Docker devnet generates `secondary0` during setup, stores its identity in the +`data_secondary0` volume, and lists its public key in +`/shared/peers.json` under `secondary_participants`. Validators load that file on +startup and call Commonware's `Manager::track` with validators as `primary` and +`secondary0` as `secondary`. + +To run the built-in secondary peer: + +```bash +just devnet + +# The secondary peer is exposed on localhost:30500 +docker compose -f docker/compose/devnet.yaml ps secondary-node0 +``` + +To join from another process, use an identity that is already listed in +`peers.json` under `secondary_participants`, copy the matching `validator.key` +into the peer's data directory, and start: + +```bash +kora secondary \ + --data-dir /path/to/secondary0 \ + --peers /path/to/peers.json \ + --chain-id 1337 +``` + +For a local Docker devnet, `node0:30303` is the bootstrap address inside the +Compose network. From outside Docker, rewrite the bootstrapper address for +`node0` in `peers.json` to the published endpoint `localhost:30400`, and keep +the secondary peer's public key in `peers.json` before starting or restarting +validators. + ### Grafana Environment Variables | Variable | Default | Description | @@ -245,7 +299,7 @@ just logs-node validator-node0 - Check specific node: `just logs-node validator-node0` **Port conflicts:** -- Check if ports 30400-30403, 8545-8548, 9000-9003, 9090, or 3000 are in use +- Check if ports 30400-30403, 30500, 8545-8548, 9000-9003, 9090, or 3000 are in use - Stop conflicting services or modify port mappings in `compose/devnet.yaml` **Container won't start:** diff --git a/docker/compose/devnet.yaml b/docker/compose/devnet.yaml index 4cf98b6..b18a326 100644 --- a/docker/compose/devnet.yaml +++ b/docker/compose/devnet.yaml @@ -9,6 +9,7 @@ volumes: data_node1: data_node2: data_node3: + data_secondary0: shared_config: prometheus_data: grafana_data: @@ -46,11 +47,12 @@ services: echo "[init] Running keygen setup..." && \ /usr/local/bin/keygen setup \ --validators=4 \ + --secondary-peers=1 \ --threshold=3 \ --chain-id=${CHAIN_ID:-1337} \ --output-dir=/shared && \ echo "[init] Setting permissions..." && \ - chown -R 1000:1000 /shared/node0 /shared/node1 /shared/node2 /shared/node3 && \ + chown -R 1000:1000 /shared/node0 /shared/node1 /shared/node2 /shared/node3 /shared/secondary0 && \ echo "[init] Setup complete (run DKG ceremony next)" volumes: - shared_config:/shared @@ -58,6 +60,7 @@ services: - data_node1:/shared/node1 - data_node2:/shared/node2 - data_node3:/shared/node3 + - data_secondary0:/shared/secondary0 # Setup + trusted dealer DKG (for fast local dev) init-config: @@ -69,6 +72,7 @@ services: echo "[init] Running keygen setup..." && \ /usr/local/bin/keygen setup \ --validators=4 \ + --secondary-peers=1 \ --threshold=3 \ --chain-id=${CHAIN_ID:-1337} \ --output-dir=/shared && \ @@ -78,7 +82,7 @@ services: --threshold=3 \ --output-dir=/shared && \ echo "[init] Setting permissions..." && \ - chown -R 1000:1000 /shared/node0 /shared/node1 /shared/node2 /shared/node3 && \ + chown -R 1000:1000 /shared/node0 /shared/node1 /shared/node2 /shared/node3 /shared/secondary0 && \ echo "[init] Init complete" volumes: - shared_config:/shared @@ -86,6 +90,7 @@ services: - data_node1:/shared/node1 - data_node2:/shared/node2 - data_node3:/shared/node3 + - data_secondary0:/shared/secondary0 # Interactive DKG nodes - run ceremony then exit dkg-node0: @@ -258,6 +263,25 @@ services: - "8548:8545" - "9003:9002" + secondary-node0: + <<: *validator-common + hostname: secondary0 + depends_on: + validator-node0: + condition: service_healthy + entrypoint: ["/scripts/entrypoint.sh", "secondary"] + volumes: + - shared_config:/shared:ro + - data_secondary0:/data + environment: + - RUST_LOG=${RUST_LOG:-info} + - CHAIN_ID=${CHAIN_ID:-1337} + - IS_BOOTSTRAP=false + - BOOTSTRAP_PEERS=node0:30303 + - HEALTHCHECK_MODE=ready + ports: + - "30500:30303" + prometheus: image: prom/prometheus:latest profiles: ["observability"] diff --git a/docker/scripts/devnet-run.sh b/docker/scripts/devnet-run.sh index 82b7539..b0890eb 100755 --- a/docker/scripts/devnet-run.sh +++ b/docker/scripts/devnet-run.sh @@ -109,6 +109,7 @@ print_endpoints() { echo "" echo -e "${BOLD}Endpoints${NC}" echo -e " ${DIM}P2P:${NC} localhost:30400-30403" + echo -e " ${DIM}Secondary:${NC} localhost:30500" echo -e " ${DIM}Prometheus:${NC} http://localhost:9090" echo -e " ${DIM}Grafana:${NC} http://localhost:3000" echo "" @@ -177,6 +178,8 @@ SHARES_EXIST=false docker volume inspect kora-devnet_shared_config >/dev/null 2>&1 && \ docker run --rm -v kora-devnet_shared_config:/shared alpine test -f /shared/peers.json 2>/dev/null && \ + docker volume inspect kora-devnet_data_secondary0 >/dev/null 2>&1 && \ + docker run --rm -v kora-devnet_data_secondary0:/data alpine test -f /data/validator.key 2>/dev/null && \ CONFIG_EXISTS=true if check_dkg_outputs; then @@ -287,11 +290,11 @@ fi echo "" -# Phase 2: Validators -print_phase "2/3" "Starting validators" +# Phase 2: Validators and secondary peers +print_phase "2/3" "Starting validators and secondary peers" -run_with_spinner "Launching validator containers..." docker compose -f compose/devnet.yaml ${COMPOSE_PROFILES:+--profile observability} up -d \ - validator-node0 validator-node1 validator-node2 validator-node3 \ +run_with_spinner "Launching validator and secondary containers..." docker compose -f compose/devnet.yaml ${COMPOSE_PROFILES:+--profile observability} up -d \ + validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 \ ${COMPOSE_PROFILES:+prometheus grafana} # Wait for validators with spinner @@ -320,6 +323,31 @@ while true; do sleep 0.15 done +start_time=$(date +%s) +timeout=120 + +while true; do + SECONDARY_HEALTH=$(docker compose -f compose/devnet.yaml ps --format json 2>/dev/null | \ + jq -r 'select(.Service == "secondary-node0") | .Health' 2>/dev/null || echo "unknown") + + elapsed=$(($(date +%s) - start_time)) + + if [[ "$SECONDARY_HEALTH" == "healthy" ]]; then + clear_line + print_success "Secondary peer healthy" + break + fi + + if [[ "$elapsed" -ge "$timeout" ]]; then + clear_line + print_error "Timeout waiting for secondary peer" + exit 1 + fi + + spinner "Waiting for secondary peer... (${SECONDARY_HEALTH:-unknown}, ${elapsed}s)" + sleep 0.15 +done + echo "" # Phase 3: Ready @@ -343,6 +371,17 @@ for i in 0 1 2 3; do printf " ${GREEN}│${NC} node%-6s ${GREEN}│${NC} %b ${GREEN}│${NC} 3040%-3s ${GREEN}│${NC}\n" "$i" "$status_str" "$i" done +secondary_status=$(docker compose -f compose/devnet.yaml ps --format json 2>/dev/null | \ + jq -r 'select(.Service == "secondary-node0") | .Health' 2>/dev/null || echo "unknown") + +if [[ "$secondary_status" == "healthy" ]]; then + secondary_status_str="${GREEN}healthy${NC} " +else + secondary_status_str="${YELLOW}${secondary_status}${NC}" +fi + +printf " ${GREEN}│${NC} secondary0 ${GREEN}│${NC} %b ${GREEN}│${NC} 30500 ${GREEN}│${NC}\n" "$secondary_status_str" + echo -e " ${GREEN}└────────────┴────────────┴─────────┘${NC}" print_endpoints diff --git a/docker/scripts/entrypoint.sh b/docker/scripts/entrypoint.sh index 16605ad..fc86d45 100644 --- a/docker/scripts/entrypoint.sh +++ b/docker/scripts/entrypoint.sh @@ -82,6 +82,34 @@ case "$MODE" in --chain-id "$CHAIN_ID" \ "$@" ;; + + secondary) + log "Running secondary peer mode..." + + [[ -f "${SHARED_DIR}/peers.json" ]] || error "peers.json not found" + [[ -f "${DATA_DIR}/validator.key" ]] || error "validator.key not found" + + touch "${DATA_DIR}/.ready" + + if [[ "$IS_BOOTSTRAP" != "true" && -n "$BOOTSTRAP_PEERS" ]]; then + BOOTSTRAP_HOST=$(echo "$BOOTSTRAP_PEERS" | cut -d: -f1) + BOOTSTRAP_PORT=$(echo "$BOOTSTRAP_PEERS" | cut -d: -f2) + + log "Waiting for bootstrap peer ${BOOTSTRAP_HOST}:${BOOTSTRAP_PORT}..." + timeout=120 + while ! nc -z "$BOOTSTRAP_HOST" "$BOOTSTRAP_PORT" 2>/dev/null; do + timeout=$((timeout - 1)) + [[ $timeout -le 0 ]] && error "Timeout waiting for bootstrap peer" + sleep 1 + done + fi + + exec /usr/local/bin/kora secondary \ + --data-dir "$DATA_DIR" \ + --peers "${SHARED_DIR}/peers.json" \ + --chain-id "$CHAIN_ID" \ + "$@" + ;; *) exec "$MODE" "$@" From ef27bf60917ab7fcabe6da980b4361c0ce1ec9e0 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Tue, 28 Apr 2026 22:42:31 +0200 Subject: [PATCH 06/16] Enhance devnet-stats script to include follower node status - Added functionality to fetch and display the status of the secondary peer in the devnet-stats script. - Introduced new variables for follower service and P2P port. - Updated the render function to include follower health, state, uptime, and network information in the output. - Adjusted the endpoints section to reflect the follower P2P port. --- docker/scripts/devnet-stats.sh | 63 ++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/docker/scripts/devnet-stats.sh b/docker/scripts/devnet-stats.sh index 404033e..f08b4a5 100755 --- a/docker/scripts/devnet-stats.sh +++ b/docker/scripts/devnet-stats.sh @@ -15,6 +15,8 @@ NC='\033[0m' REFRESH_INTERVAL=${1:-0.3} CHAIN_ID="${CHAIN_ID:-1337}" RPC_PORTS=(8545 8546 8547 8548) +FOLLOWER_SERVICE="secondary-node0" +FOLLOWER_P2P_PORT=30500 cleanup() { tput cnorm @@ -53,6 +55,17 @@ fetch_all_statuses() { rm -rf "$tmpdir" } +fetch_follower_info() { + docker compose -f compose/devnet.yaml ps --format json 2>/dev/null | \ + jq -r "select(.Service == \"$FOLLOWER_SERVICE\") | [ + .Health // .State // \"unknown\", + .State // \"unknown\", + (.RunningFor // \"-\"), + ([.Publishers[]? | select(.TargetPort == 30303 and .PublishedPort != 0) | .PublishedPort] | unique | join(\",\")), + .Name // \"$FOLLOWER_SERVICE\" + ] | @tsv" 2>/dev/null || true +} + render() { tput cup 0 0 local now=$(date "+%H:%M:%S") @@ -75,6 +88,12 @@ render() { local total_finalized=0 local max_view=0 local max_throughput=0 + local follower_status="offline" + local follower_color=$RED + local follower_state="-" + local follower_uptime="-" + local follower_p2p="$FOLLOWER_P2P_PORT" + local follower_container="$FOLLOWER_SERVICE" # Fetch all statuses in parallel local all_status @@ -137,6 +156,39 @@ render() { fi ((++i)) done <<< "$all_status" + + local follower_info + follower_info=$(fetch_follower_info) + if [[ -n "$follower_info" ]]; then + local follower_health_value + IFS=$'\t' read -r follower_health_value follower_state follower_uptime follower_p2p follower_container <<< "$follower_info" + follower_uptime="${follower_uptime% ago}" + follower_p2p="${follower_p2p:-$FOLLOWER_P2P_PORT}" + + case "$follower_health_value" in + healthy) + follower_status="healthy" + follower_color=$GREEN + ;; + running) + follower_status="running" + follower_color=$GREEN + ;; + starting) + follower_status="starting" + follower_color=$YELLOW + ;; + *) + follower_status="${follower_health_value:-${follower_state:-unknown}}" + follower_color=$YELLOW + ;; + esac + fi + + local follower_table_uptime="${follower_uptime:0:10}" + local follower_network="P2P ${follower_p2p:-none}" + printf "│ ${CYAN}%-5s${NC} │ ${follower_color}%-8s${NC} │ %-10s │ %-8s │ %-10s │ %-10s │ %-10s │ %-10s │ - │\n" \ + "f0" "$follower_status" "$follower_table_uptime" "follower" "-" "-" "-" "$follower_network" echo -e "└───────┴──────────┴────────────┴──────────┴────────────┴────────────┴────────────┴────────────┴────────┘" @@ -160,15 +212,20 @@ render() { throughput_str=$(awk "BEGIN {printf \"%.2f b/s\", $max_throughput / 100}") fi - echo -e " ${DIM}Consensus:${NC} ${health_color}${healthy_count}/4${NC} │ ${DIM}RPC:${NC} ${GREEN}${rpc_count}/4${NC} │ ${DIM}Stalled:${NC} ${YELLOW}${stalled_count}${NC} │ ${DIM}Threshold:${NC} $threshold │ ${DIM}View:${NC} ${CYAN}$max_view${NC} │ ${DIM}Finalized:${NC} ${GREEN}$total_finalized${NC} │ ${DIM}Throughput:${NC} ${CYAN}$throughput_str${NC} │ ${DIM}Uptime:${NC} $uptime_str" + echo -e " ${DIM}Consensus:${NC} ${health_color}${healthy_count}/4${NC} │ ${DIM}RPC:${NC} ${GREEN}${rpc_count}/4${NC} │ ${DIM}Follower:${NC} ${follower_color}${follower_status}${NC} │ ${DIM}Stalled:${NC} ${YELLOW}${stalled_count}${NC} │ ${DIM}Threshold:${NC} $threshold │ ${DIM}View:${NC} ${CYAN}$max_view${NC} │ ${DIM}Finalized:${NC} ${GREEN}$total_finalized${NC} │ ${DIM}Throughput:${NC} ${CYAN}$throughput_str${NC} │ ${DIM}Uptime:${NC} $uptime_str" + + echo "" + echo -e "${BOLD}${CYAN}Follower Node${NC}" + echo -e " ${DIM}Node:${NC} ${CYAN}f0${NC} │ ${DIM}Role:${NC} secondary │ ${DIM}Service:${NC} $FOLLOWER_SERVICE │ ${DIM}Container:${NC} $follower_container" + echo -e " ${DIM}Health:${NC} ${follower_color}${follower_status}${NC} │ ${DIM}State:${NC} $follower_state │ ${DIM}Uptime:${NC} $follower_uptime │ ${DIM}P2P:${NC} ${follower_p2p:-none} │ ${DIM}RPC:${NC} none" # Endpoints echo "" echo -e "${BOLD}${CYAN}Endpoints${NC}" - echo -e " ${DIM}P2P:${NC} 30400-30403 ${DIM}RPC:${NC} 8545-8548 ${DIM}Metrics:${NC} 9000-9003" + echo -e " ${DIM}P2P:${NC} 30400-30403 ${DIM}Follower P2P:${NC} $FOLLOWER_P2P_PORT ${DIM}RPC:${NC} 8545-8548 ${DIM}Metrics:${NC} 9000-9003" # Clear extra lines - for _ in {1..3}; do + for _ in {1..5}; do printf "%-90s\n" "" done } From 7095c6637e0de860fe54151aaec3284392434504 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Wed, 29 Apr 2026 18:04:11 +0200 Subject: [PATCH 07/16] Update dependencies and integrate kora-indexer - Added `kora-indexer` as a dependency in `Cargo.toml` and updated its path. - Specified versions for `alloy-consensus` and `alloy-eips` in `Cargo.lock`. - Enhanced the `handle_finalized_update` function to utilize `kora-indexer` for block indexing. - Updated `NodeRunner` to initialize `block_index` when RPC configuration is present, allowing for improved state management during RPC calls. --- Cargo.lock | 5 +- crates/node/reporters/Cargo.toml | 3 + crates/node/reporters/src/lib.rs | 273 +++++++++++++++++++++++++------ crates/node/runner/src/runner.rs | 15 +- 4 files changed, 240 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index afda05f..f8e5f2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3487,6 +3487,8 @@ dependencies = [ name = "kora-reporters" version = "0.1.0" dependencies = [ + "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-primitives", "commonware-codec", "commonware-consensus", @@ -3496,6 +3498,7 @@ dependencies = [ "kora-consensus", "kora-domain", "kora-executor", + "kora-indexer", "kora-ledger", "kora-overlay", "kora-qmdb-ledger", @@ -3507,7 +3510,7 @@ dependencies = [ name = "kora-rpc" version = "0.1.0" dependencies = [ - "alloy-consensus", + "alloy-consensus 1.8.3", "alloy-primitives", "async-trait", "axum", diff --git a/crates/node/reporters/Cargo.toml b/crates/node/reporters/Cargo.toml index 593d14c..82288ae 100644 --- a/crates/node/reporters/Cargo.toml +++ b/crates/node/reporters/Cargo.toml @@ -15,6 +15,7 @@ workspace = true kora-consensus = { path = "../consensus" } kora-domain = { path = "../domain" } kora-executor = { path = "../executor" } +kora-indexer = { path = "../../storage/indexer" } kora-ledger = { path = "../ledger" } kora-overlay = { path = "../../storage/overlay" } kora-qmdb-ledger = { path = "../../storage/qmdb-ledger" } @@ -28,6 +29,8 @@ commonware-runtime.workspace = true commonware-utils.workspace = true # Alloy +alloy-consensus.workspace = true +alloy-eips.workspace = true alloy-primitives.workspace = true # Tracing diff --git a/crates/node/reporters/src/lib.rs b/crates/node/reporters/src/lib.rs index aaae479..3a07297 100644 --- a/crates/node/reporters/src/lib.rs +++ b/crates/node/reporters/src/lib.rs @@ -4,8 +4,10 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(not(test), warn(unused_crate_dependencies))] -use std::{fmt, marker::PhantomData}; +use std::{fmt, marker::PhantomData, sync::Arc}; +use alloy_consensus::{Transaction as _, TxEnvelope, transaction::SignerRecoverable as _}; +use alloy_eips::eip2718::Decodable2718 as _; use alloy_primitives::{B256, Bytes, keccak256}; use commonware_consensus::{ Block as _, Reporter, @@ -20,7 +22,8 @@ use commonware_runtime::{Spawner as _, tokio}; use commonware_utils::acknowledgement::Acknowledgement as _; use kora_consensus::BlockExecution; use kora_domain::{Block, ConsensusDigest, PublicKey}; -use kora_executor::{BlockContext, BlockExecutor}; +use kora_executor::{BlockContext, BlockExecutor, ExecutionOutcome}; +use kora_indexer::{BlockIndex, IndexedBlock, IndexedLog, IndexedReceipt, IndexedTransaction}; use kora_ledger::LedgerService; use kora_overlay::OverlayState; use kora_qmdb_ledger::QmdbState; @@ -104,6 +107,7 @@ async fn handle_finalized_update( context: tokio::Context, executor: E, provider: P, + block_index: Option>, update: Update, ) where E: BlockExecutor, Tx = Bytes>, @@ -113,64 +117,87 @@ async fn handle_finalized_update( Update::Tip(..) => {} Update::Block(block, ack) => { let digest = block.commitment(); - if state.query_state_root(digest).await.is_none() { - trace!(?digest, "missing snapshot for finalized block; re-executing"); + let snapshot_exists = state.query_state_root(digest).await.is_some(); + let mut execution_outcome = None; + let mut execution_context = None; + + if !snapshot_exists || block_index.is_some() { + if snapshot_exists { + trace!(?digest, "re-executing finalized block for RPC indexing"); + } else { + trace!(?digest, "missing snapshot for finalized block; re-executing"); + } let parent_digest = block.parent(); - let Some(parent_snapshot) = state.parent_snapshot(parent_digest).await else { - error!(?digest, ?parent_digest, "missing parent snapshot for finalized block"); - ack.acknowledge(); - return; - }; - let block_context = provider.context(&block); - let execution = match BlockExecution::execute( - &parent_snapshot, - &executor, - &block_context, - &block.txs, - ) - .await - { - Ok(result) => result, - Err(err) => { - error!(?digest, error = ?err, "failed to execute finalized block"); - ack.acknowledge(); - return; - } - }; - let merged_changes = - parent_snapshot.state.merge_changes(execution.outcome.changes.clone()); - let state_root = match state - .compute_root_from_store(parent_digest, execution.outcome.changes.clone()) + if let Some(parent_snapshot) = state.parent_snapshot(parent_digest).await { + let block_context = provider.context(&block); + let execution = match BlockExecution::execute( + &parent_snapshot, + &executor, + &block_context, + &block.txs, + ) .await - { - Ok(root) => root, - Err(err) => { - error!(?digest, error = ?err, "failed to compute qmdb root"); + { + Ok(result) => result, + Err(err) => { + error!(?digest, error = ?err, "failed to execute finalized block"); + ack.acknowledge(); + return; + } + }; + + let state_root = match state + .compute_root_from_store(parent_digest, execution.outcome.changes.clone()) + .await + { + Ok(root) => root, + Err(err) => { + error!(?digest, error = ?err, "failed to compute qmdb root"); + ack.acknowledge(); + return; + } + }; + if state_root != block.state_root { + warn!( + ?digest, + expected = ?block.state_root, + computed = ?state_root, + "state root mismatch for finalized block" + ); ack.acknowledge(); return; } - }; - if state_root != block.state_root { + + if !snapshot_exists { + let merged_changes = + parent_snapshot.state.merge_changes(execution.outcome.changes.clone()); + let next_state = + OverlayState::new(parent_snapshot.state.base(), merged_changes); + state + .insert_snapshot( + digest, + parent_digest, + next_state, + state_root, + execution.outcome.changes.clone(), + &block.txs, + ) + .await; + } + + execution_outcome = Some(execution.outcome); + execution_context = Some(block_context); + } else if snapshot_exists { warn!( ?digest, - expected = ?block.state_root, - computed = ?state_root, - "state root mismatch for finalized block" + ?parent_digest, + "missing parent snapshot for cached finalized block; skipping RPC indexing replay" ); + } else { + error!(?digest, ?parent_digest, "missing parent snapshot for finalized block"); ack.acknowledge(); return; } - let next_state = OverlayState::new(parent_snapshot.state.base(), merged_changes); - state - .insert_snapshot( - digest, - parent_digest, - next_state, - state_root, - execution.outcome.changes, - &block.txs, - ) - .await; } else { trace!(?digest, "using cached snapshot for finalized block"); } @@ -191,6 +218,11 @@ async fn handle_finalized_update( ack.acknowledge(); return; } + if let (Some(index), Some(outcome), Some(block_context)) = + (block_index.as_ref(), execution_outcome.as_ref(), execution_context.as_ref()) + { + index_finalized_block(index, &block, block_context, outcome); + } state.prune_mempool(&block.txs).await; // Marshal waits for the application to acknowledge processing before advancing the // delivery floor. Without this, the node can stall on finalized block delivery. @@ -199,6 +231,135 @@ async fn handle_finalized_update( } } +#[derive(Clone, Debug)] +struct TxMetadata { + from: alloy_primitives::Address, + to: Option, + value: alloy_primitives::U256, + gas_limit: u64, + gas_price: u128, + input: Bytes, + nonce: u64, +} + +fn index_finalized_block( + index: &BlockIndex, + block: &Block, + block_context: &BlockContext, + outcome: &ExecutionOutcome, +) { + let block_hash = block.id().0; + let transaction_hashes = block.txs.iter().map(|tx| keccak256(&tx.bytes)).collect::>(); + let tx_metadata = block.txs.iter().map(|tx| decode_tx_metadata(&tx.bytes)).collect::>(); + + let indexed_block = IndexedBlock { + hash: block_hash, + number: block.height, + parent_hash: block.parent.0, + state_root: block.state_root.0, + timestamp: block_context.header.timestamp, + gas_limit: block_context.header.gas_limit, + gas_used: outcome.gas_used, + base_fee_per_gas: block_context.header.base_fee_per_gas, + transaction_hashes, + }; + + let indexed_txs = tx_metadata + .iter() + .enumerate() + .filter_map(|(idx, metadata)| { + let metadata = metadata.as_ref()?; + let hash = keccak256(&block.txs[idx].bytes); + Some(IndexedTransaction { + hash, + block_hash, + block_number: block.height, + index: idx as u64, + from: metadata.from, + to: metadata.to, + value: metadata.value, + gas_limit: metadata.gas_limit, + gas_price: metadata.gas_price, + input: metadata.input.clone(), + nonce: metadata.nonce, + }) + }) + .collect(); + + let mut next_log_index = 0u64; + let indexed_receipts = outcome + .receipts + .iter() + .enumerate() + .filter_map(|(idx, receipt)| { + let metadata = tx_metadata.get(idx)?.as_ref()?; + let logs = receipt + .logs() + .iter() + .map(|log| { + let (topics, data) = log.data.clone().split(); + let log_index = next_log_index; + next_log_index += 1; + IndexedLog { address: log.address, topics, data, log_index } + }) + .collect(); + + Some(IndexedReceipt { + transaction_hash: receipt.tx_hash, + block_hash, + block_number: block.height, + transaction_index: idx as u64, + from: metadata.from, + to: metadata.to, + cumulative_gas_used: receipt.cumulative_gas_used(), + gas_used: receipt.gas_used, + contract_address: receipt.contract_address, + logs, + status: receipt.success(), + }) + }) + .collect(); + + index.insert_block(indexed_block, indexed_txs, indexed_receipts); +} + +fn decode_tx_metadata(tx_bytes: &Bytes) -> Option { + let envelope = match TxEnvelope::decode_2718(&mut tx_bytes.as_ref()) { + Ok(envelope) => envelope, + Err(err) => { + warn!(error = %err, "failed to decode finalized transaction for indexing"); + return None; + } + }; + let from = match envelope.recover_signer() { + Ok(from) => from, + Err(err) => { + warn!(error = %err, "failed to recover finalized transaction sender for indexing"); + return None; + } + }; + + Some(TxMetadata { + from, + to: envelope.to(), + value: envelope.value(), + gas_limit: envelope.gas_limit(), + gas_price: effective_gas_price(&envelope), + input: envelope.input().clone(), + nonce: envelope.nonce(), + }) +} + +fn effective_gas_price(envelope: &TxEnvelope) -> u128 { + match envelope { + TxEnvelope::Legacy(tx) => tx.tx().gas_price, + TxEnvelope::Eip2930(tx) => tx.tx().gas_price, + TxEnvelope::Eip1559(tx) => tx.tx().max_fee_per_gas, + TxEnvelope::Eip4844(tx) => tx.tx().tx().max_fee_per_gas, + TxEnvelope::Eip7702(tx) => tx.tx().max_fee_per_gas, + } +} + #[derive(Clone)] /// Persists finalized blocks. pub struct FinalizedReporter { @@ -210,6 +371,8 @@ pub struct FinalizedReporter { executor: E, /// Provider that builds block execution context. provider: P, + /// Optional RPC block index updated after finalized blocks are persisted. + block_index: Option>, } impl fmt::Debug for FinalizedReporter { @@ -230,7 +393,14 @@ where executor: E, provider: P, ) -> Self { - Self { state, context, executor, provider } + Self { state, context, executor, provider, block_index: None } + } + + /// Attach the RPC-visible block index to update when blocks finalize. + #[must_use] + pub fn with_block_index(mut self, block_index: Arc) -> Self { + self.block_index = Some(block_index); + self } } @@ -246,8 +416,9 @@ where let context = self.context.clone(); let executor = self.executor.clone(); let provider = self.provider.clone(); + let block_index = self.block_index.clone(); async move { - handle_finalized_update(state, context, executor, provider, update).await; + handle_finalized_update(state, context, executor, provider, block_index, update).await; } } } diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index ca4ee5c..a288d3b 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -220,12 +220,16 @@ impl NodeRunner for ProductionRunner { .await .context("init qmdb")?; + let block_index = + self.rpc_config.as_ref().map(|_| Arc::new(kora_indexer::BlockIndex::new())); if let Some((node_state, addr)) = &self.rpc_config { let qmdb_state = state.qmdb_state().await; - let block_index = Arc::new(kora_indexer::BlockIndex::new()); let rpc_executor = Arc::new(RevmExecutor::new(self.chain_id)); - let indexed_provider = - kora_rpc::IndexedStateProvider::new(block_index, qmdb_state, rpc_executor); + let indexed_provider = kora_rpc::IndexedStateProvider::new( + block_index.clone().expect("block index is initialized with RPC"), + qmdb_state, + rpc_executor, + ); let rpc = kora_rpc::RpcServer::with_state_provider( node_state.clone(), *addr, @@ -246,8 +250,11 @@ impl NodeRunner for ProductionRunner { let executor = RevmExecutor::new(self.chain_id); let context_provider = RevmContextProvider { gas_limit: self.gas_limit }; - let finalized_reporter = + let mut finalized_reporter = FinalizedReporter::new(ledger.clone(), context.clone(), executor, context_provider); + if let Some(block_index) = block_index { + finalized_reporter = finalized_reporter.with_block_index(block_index); + } let scheme_provider = ConstantSchemeProvider::from(self.scheme.clone()); From beb637a814cfd7f0745c1b166c72e81b281c0cb9 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Wed, 29 Apr 2026 18:16:42 +0200 Subject: [PATCH 08/16] Enhance Ethereum JSON-RPC API and update dependencies - Added `alloy-eips` and `k256` as dependencies in `Cargo.toml` and updated `Cargo.lock`. - Introduced `sha3` dependency for cryptographic functions. - Modified `EthApiImpl` to support pending transactions with a new `pending_txs` field. - Updated transaction submission callback to return a future, allowing for asynchronous handling. - Enhanced block retrieval methods to include an option for full transaction details. - Implemented a new function to convert raw transactions to RPC format. - Improved tests for block retrieval and transaction handling, ensuring robust functionality. --- Cargo.lock | 4 + crates/node/rpc/Cargo.toml | 3 + crates/node/rpc/src/eth.rs | 173 +++++++++++++++++++++--- crates/node/rpc/src/indexed_provider.rs | 148 +++++++++++++++----- crates/node/rpc/src/lib.rs | 4 +- crates/node/rpc/src/server.rs | 33 ++++- crates/node/rpc/src/state_provider.rs | 19 ++- crates/node/runner/Cargo.toml | 1 + crates/node/runner/src/runner.rs | 36 ++++- crates/node/txpool/src/pool.rs | 8 +- crates/node/txpool/src/validator.rs | 19 +++ crates/storage/indexer/src/store.rs | 13 ++ 12 files changed, 395 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8e5f2e..ab7ec2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3511,16 +3511,19 @@ name = "kora-rpc" version = "0.1.0" dependencies = [ "alloy-consensus 1.8.3", + "alloy-eips 1.8.3", "alloy-primitives", "async-trait", "axum", "jsonrpsee", + "k256", "kora-executor", "kora-indexer", "kora-traits", "parking_lot", "serde", "serde_json", + "sha3", "thiserror 2.0.18", "tokio", "tower 0.5.3", @@ -3558,6 +3561,7 @@ dependencies = [ "kora-service", "kora-simplex", "kora-transport", + "kora-txpool", "rand 0.8.6", "tracing", ] diff --git a/crates/node/rpc/Cargo.toml b/crates/node/rpc/Cargo.toml index 4cee98e..94eecea 100644 --- a/crates/node/rpc/Cargo.toml +++ b/crates/node/rpc/Cargo.toml @@ -21,6 +21,7 @@ jsonrpsee = { version = "0.24", features = ["server", "macros"] } # Alloy types alloy-consensus.workspace = true +alloy-eips.workspace = true alloy-primitives = { workspace = true, features = ["serde"] } # Async @@ -47,3 +48,5 @@ kora-traits = { path = "../../storage/traits" } [dev-dependencies] tokio = { workspace = true, features = ["rt", "macros"] } serde_json.workspace = true +k256.workspace = true +sha3.workspace = true diff --git a/crates/node/rpc/src/eth.rs b/crates/node/rpc/src/eth.rs index 1ccf9af..af8726f 100644 --- a/crates/node/rpc/src/eth.rs +++ b/crates/node/rpc/src/eth.rs @@ -1,7 +1,9 @@ //! Ethereum JSON-RPC API implementation. -use std::sync::Arc; +use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; +use alloy_consensus::{Transaction as _, TxEnvelope, transaction::SignerRecoverable as _}; +use alloy_eips::eip2718::Decodable2718 as _; use alloy_primitives::{Address, B256, Bytes, U64, U256}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use tokio::sync::RwLock; @@ -180,8 +182,11 @@ pub struct FeeHistory { /// Transaction submission callback type. /// /// Called when a raw transaction is submitted via `eth_sendRawTransaction`. -/// Returns true if the transaction was accepted, false otherwise. -pub type TxSubmitCallback = Arc bool + Send + Sync>; +/// Resolves successfully only if the transaction was accepted. +pub type TxSubmitFuture = Pin> + Send>>; + +/// Async transaction submission callback type. +pub type TxSubmitCallback = Arc TxSubmitFuture + Send + Sync>; /// Ethereum API implementation with state provider. pub struct EthApiImpl { @@ -189,6 +194,7 @@ pub struct EthApiImpl { block_height: Arc, tx_submit: Option, state_provider: Arc>, + pending_txs: Arc>>, } impl std::fmt::Debug for EthApiImpl { @@ -209,6 +215,7 @@ impl EthApiImpl { block_height: Arc::new(std::sync::atomic::AtomicU64::new(0)), tx_submit: None, state_provider: Arc::new(RwLock::new(state_provider)), + pending_txs: Arc::new(RwLock::new(HashMap::new())), } } @@ -219,6 +226,7 @@ impl EthApiImpl { block_height: Arc::new(std::sync::atomic::AtomicU64::new(0)), tx_submit: Some(tx_submit), state_provider: Arc::new(RwLock::new(state_provider)), + pending_txs: Arc::new(RwLock::new(HashMap::new())), } } @@ -290,13 +298,13 @@ impl EthApiServer for EthApiImpl { async fn send_raw_transaction(&self, data: Bytes) -> RpcResult { let tx_hash = alloy_primitives::keccak256(&data); + let pending_tx = raw_tx_to_pending_rpc(&data)?; - if let Some(ref submit) = self.tx_submit - && !submit(data) - { - return Err(RpcError::InvalidTransaction("transaction rejected".into()).into()); + if let Some(ref submit) = self.tx_submit { + submit(data).await?; } + self.pending_txs.write().await.insert(tx_hash, pending_tx); Ok(tx_hash) } @@ -322,24 +330,29 @@ impl EthApiServer for EthApiImpl { async fn get_block_by_number( &self, block: BlockNumberOrTag, - _full_transactions: bool, + full_transactions: bool, ) -> RpcResult> { let provider = self.state_provider.read().await; - provider.block_by_number(block).await.map_err(Into::into) + provider.block_by_number(block, full_transactions).await.map_err(Into::into) } async fn get_block_by_hash( &self, hash: B256, - _full_transactions: bool, + full_transactions: bool, ) -> RpcResult> { let provider = self.state_provider.read().await; - provider.block_by_hash(hash).await.map_err(Into::into) + provider.block_by_hash(hash, full_transactions).await.map_err(Into::into) } async fn get_transaction_by_hash(&self, hash: B256) -> RpcResult> { let provider = self.state_provider.read().await; - provider.transaction_by_hash(hash).await.map_err(Into::into) + let indexed = provider.transaction_by_hash(hash).await?; + if indexed.is_some() { + self.pending_txs.write().await.remove(&hash); + return Ok(indexed); + } + Ok(self.pending_txs.read().await.get(&hash).cloned()) } async fn get_transaction_receipt( @@ -361,16 +374,27 @@ impl EthApiServer for EthApiImpl { async fn fee_history( &self, block_count: U64, - _newest_block: BlockNumberOrTag, + newest_block: BlockNumberOrTag, reward_percentiles: Option>, ) -> RpcResult { - let count = block_count.to::().min(1024); + let provider = self.state_provider.read().await; + let head = provider + .block_number() + .await + .unwrap_or_else(|_| self.block_height.load(std::sync::atomic::Ordering::Relaxed)); + let newest = match newest_block { + BlockNumberOrTag::Number(n) => n.to::().min(head), + BlockNumberOrTag::Tag(_) | BlockNumberOrTag::Latest => head, + }; + let requested = block_count.to::().min(1024); + let count = requested.min(newest.saturating_add(1)) as usize; + let oldest = newest.saturating_add(1).saturating_sub(count as u64); let base_fee = U256::from(1_000_000_000u64); Ok(FeeHistory { base_fee_per_gas: vec![base_fee; count + 1], - gas_used_ratio: vec![0.5; count], - oldest_block: U64::ZERO, + gas_used_ratio: vec![0.0; count], + oldest_block: U64::from(oldest), reward: reward_percentiles.map(|percentiles| { vec![vec![U256::from(1_000_000_000u64); percentiles.len()]; count] }), @@ -463,11 +487,110 @@ impl Web3ApiServer for Web3ApiImpl { } } +fn raw_tx_to_pending_rpc(data: &Bytes) -> Result { + let envelope = TxEnvelope::decode_2718(&mut data.as_ref()) + .map_err(|err| RpcError::InvalidTransaction(format!("failed to decode: {err}")))?; + let from = envelope + .recover_signer() + .map_err(|err| RpcError::InvalidTransaction(format!("failed to recover signer: {err}")))?; + let signature = envelope.signature(); + let hash = alloy_primitives::keccak256(data); + + Ok(RpcTransaction { + hash, + nonce: U64::from(envelope.nonce()), + block_hash: None, + block_number: None, + transaction_index: None, + from, + to: envelope.to(), + value: envelope.value(), + gas: U64::from(envelope.gas_limit()), + gas_price: U256::from(effective_gas_price(&envelope)), + input: envelope.input().clone(), + tx_type: U64::from(transaction_type(&envelope)), + chain_id: envelope.chain_id().map(U64::from), + max_fee_per_gas: max_fee_per_gas(&envelope).map(U256::from), + max_priority_fee_per_gas: max_priority_fee_per_gas(&envelope).map(U256::from), + v: U64::from(u64::from(signature.v())), + r: signature.r(), + s: signature.s(), + }) +} + +fn transaction_type(envelope: &TxEnvelope) -> u64 { + match envelope { + TxEnvelope::Legacy(_) => 0, + TxEnvelope::Eip2930(_) => 1, + TxEnvelope::Eip1559(_) => 2, + TxEnvelope::Eip4844(_) => 3, + TxEnvelope::Eip7702(_) => 4, + } +} + +fn effective_gas_price(envelope: &TxEnvelope) -> u128 { + match envelope { + TxEnvelope::Legacy(tx) => tx.tx().gas_price, + TxEnvelope::Eip2930(tx) => tx.tx().gas_price, + TxEnvelope::Eip1559(tx) => tx.tx().max_fee_per_gas, + TxEnvelope::Eip4844(tx) => tx.tx().tx().max_fee_per_gas, + TxEnvelope::Eip7702(tx) => tx.tx().max_fee_per_gas, + } +} + +fn max_fee_per_gas(envelope: &TxEnvelope) -> Option { + match envelope { + TxEnvelope::Legacy(_) | TxEnvelope::Eip2930(_) => None, + TxEnvelope::Eip1559(tx) => Some(tx.tx().max_fee_per_gas), + TxEnvelope::Eip4844(tx) => Some(tx.tx().tx().max_fee_per_gas), + TxEnvelope::Eip7702(tx) => Some(tx.tx().max_fee_per_gas), + } +} + +fn max_priority_fee_per_gas(envelope: &TxEnvelope) -> Option { + match envelope { + TxEnvelope::Legacy(_) | TxEnvelope::Eip2930(_) => None, + TxEnvelope::Eip1559(tx) => Some(tx.tx().max_priority_fee_per_gas), + TxEnvelope::Eip4844(tx) => Some(tx.tx().tx().max_priority_fee_per_gas), + TxEnvelope::Eip7702(tx) => Some(tx.tx().max_priority_fee_per_gas), + } +} + #[cfg(test)] mod tests { + use alloy_consensus::{SignableTransaction as _, TxEip1559}; + use alloy_eips::eip2718::Encodable2718 as _; + use alloy_primitives::{Signature, TxKind}; + use k256::ecdsa::SigningKey; + use sha3::{Digest as _, Keccak256}; + use super::*; use crate::state_provider::NoopStateProvider; + fn signed_test_tx(chain_id: u64, nonce: u64) -> Bytes { + let mut secret = [0u8; 32]; + secret[31] = 1; + let key = SigningKey::from_bytes((&secret).into()).expect("valid key"); + let tx = TxEip1559 { + chain_id, + nonce, + gas_limit: 21_000, + max_fee_per_gas: 1, + max_priority_fee_per_gas: 1, + to: TxKind::Call(Address::repeat_byte(0xbb)), + value: U256::from(1), + access_list: Default::default(), + input: Bytes::new(), + }; + let digest = Keccak256::new_with_prefix(tx.encoded_for_signing()); + let (sig, recid) = key.sign_digest_recoverable(digest).expect("sign tx"); + let signature = Signature::from((sig, recid)); + let envelope = TxEnvelope::from(tx.into_signed(signature)); + let mut raw = Vec::new(); + envelope.encode_2718(&mut raw); + Bytes::from(raw) + } + #[test] fn web3_client_version() { let api = Web3ApiImpl::new(); @@ -510,15 +633,29 @@ mod tests { let submitted_clone = submitted.clone(); let callback: TxSubmitCallback = Arc::new(move |_| { submitted_clone.store(true, std::sync::atomic::Ordering::Relaxed); - true + Box::pin(async { Ok(()) }) }); let api = EthApiImpl::with_tx_submit(1, NoopStateProvider, callback); - let tx_data = Bytes::from_static(b"fake_tx_data"); + let tx_data = signed_test_tx(1, 0); let result = EthApiServer::send_raw_transaction(&api, tx_data.clone()).await; assert!(result.is_ok()); assert!(submitted.load(std::sync::atomic::Ordering::Relaxed)); assert_eq!(result.unwrap(), alloy_primitives::keccak256(&tx_data)); } + + #[tokio::test] + async fn eth_get_transaction_by_hash_returns_pending_submission() { + let callback: TxSubmitCallback = Arc::new(move |_| Box::pin(async { Ok(()) })); + let api = EthApiImpl::with_tx_submit(1, NoopStateProvider, callback); + let tx_data = signed_test_tx(1, 7); + let hash = EthApiServer::send_raw_transaction(&api, tx_data).await.unwrap(); + + let tx = EthApiServer::get_transaction_by_hash(&api, hash).await.unwrap(); + let tx = tx.expect("pending transaction should be visible"); + assert_eq!(tx.hash, hash); + assert_eq!(tx.nonce, U64::from(7)); + assert!(tx.block_hash.is_none()); + } } diff --git a/crates/node/rpc/src/indexed_provider.rs b/crates/node/rpc/src/indexed_provider.rs index 5e14f8a..398ce61 100644 --- a/crates/node/rpc/src/indexed_provider.rs +++ b/crates/node/rpc/src/indexed_provider.rs @@ -109,15 +109,23 @@ impl StateProvider for IndexedStateProvi self.state.storage(&address, &slot).await.map_err(state_error_to_rpc) } - async fn block_by_number(&self, block: BlockNumberOrTag) -> Result, RpcError> { + async fn block_by_number( + &self, + block: BlockNumberOrTag, + full_transactions: bool, + ) -> Result, RpcError> { let block_num = self.resolve_block_number(&block)?; let indexed = self.index.get_block_by_number(block_num); - Ok(indexed.map(indexed_block_to_rpc)) + Ok(indexed.map(|block| self.indexed_block_to_rpc(block, full_transactions))) } - async fn block_by_hash(&self, hash: B256) -> Result, RpcError> { + async fn block_by_hash( + &self, + hash: B256, + full_transactions: bool, + ) -> Result, RpcError> { let indexed = self.index.get_block_by_hash(&hash); - Ok(indexed.map(indexed_block_to_rpc)) + Ok(indexed.map(|block| self.indexed_block_to_rpc(block, full_transactions))) } async fn transaction_by_hash(&self, hash: B256) -> Result, RpcError> { @@ -199,6 +207,43 @@ impl StateProvider for IndexedStateProvi } impl IndexedStateProvider { + fn indexed_block_to_rpc(&self, block: IndexedBlock, full_transactions: bool) -> RpcBlock { + let transactions = if full_transactions { + let txs = self + .index + .get_transactions_for_block(&block.hash) + .into_iter() + .map(indexed_tx_to_rpc) + .collect(); + BlockTransactions::Full(txs) + } else { + BlockTransactions::Hashes(block.transaction_hashes.clone()) + }; + + RpcBlock { + hash: block.hash, + parent_hash: block.parent_hash, + number: U64::from(block.number), + state_root: block.state_root, + transactions_root: B256::ZERO, + receipts_root: B256::ZERO, + logs_bloom: Bytes::new(), + timestamp: U64::from(block.timestamp), + gas_limit: U64::from(block.gas_limit), + gas_used: U64::from(block.gas_used), + extra_data: Bytes::new(), + mix_hash: B256::ZERO, + nonce: Default::default(), + base_fee_per_gas: block.base_fee_per_gas.map(U256::from), + miner: Address::ZERO, + difficulty: U256::ZERO, + total_difficulty: U256::ZERO, + uncles: vec![], + size: U64::ZERO, + transactions, + } + } + fn resolve_block_number(&self, block: &BlockNumberOrTag) -> Result { match block { BlockNumberOrTag::Number(n) => Ok(n.to::()), @@ -284,31 +329,6 @@ fn state_error_to_rpc(err: StateDbError) -> RpcError { } } -fn indexed_block_to_rpc(block: IndexedBlock) -> RpcBlock { - RpcBlock { - hash: block.hash, - parent_hash: block.parent_hash, - number: U64::from(block.number), - state_root: block.state_root, - transactions_root: B256::ZERO, - receipts_root: B256::ZERO, - logs_bloom: Bytes::new(), - timestamp: U64::from(block.timestamp), - gas_limit: U64::from(block.gas_limit), - gas_used: U64::from(block.gas_used), - extra_data: Bytes::new(), - mix_hash: B256::ZERO, - nonce: Default::default(), - base_fee_per_gas: block.base_fee_per_gas.map(U256::from), - miner: Address::ZERO, - difficulty: U256::ZERO, - total_difficulty: U256::ZERO, - uncles: vec![], - size: U64::ZERO, - transactions: BlockTransactions::Hashes(block.transaction_hashes), - } -} - fn indexed_tx_to_rpc(tx: IndexedTransaction) -> RpcTransaction { RpcTransaction { hash: tx.hash, @@ -398,6 +418,31 @@ mod tests { } } + #[derive(Clone)] + struct MissingAccountState; + + impl StateDbRead for MissingAccountState { + async fn nonce(&self, _address: &Address) -> Result { + Err(StateDbError::AccountNotFound(Address::ZERO)) + } + + async fn balance(&self, _address: &Address) -> Result { + Err(StateDbError::AccountNotFound(Address::ZERO)) + } + + async fn code_hash(&self, address: &Address) -> Result { + Err(StateDbError::AccountNotFound(*address)) + } + + async fn code(&self, _code_hash: &B256) -> Result { + Err(StateDbError::CodeNotFound(B256::ZERO)) + } + + async fn storage(&self, _address: &Address, _slot: &U256) -> Result { + Err(StateDbError::AccountNotFound(Address::ZERO)) + } + } + fn create_test_block(number: u64, hash: B256) -> IndexedBlock { IndexedBlock { hash, @@ -475,7 +520,8 @@ mod tests { let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); - let block = provider.block_by_number(BlockNumberOrTag::Number(U64::from(1))).await.unwrap(); + let block = + provider.block_by_number(BlockNumberOrTag::Number(U64::from(1)), false).await.unwrap(); assert!(block.is_some()); assert_eq!(block.unwrap().hash, block_hash); } @@ -488,11 +534,43 @@ mod tests { let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); - let block = provider.block_by_hash(block_hash).await.unwrap(); + let block = provider.block_by_hash(block_hash, false).await.unwrap(); assert!(block.is_some()); assert_eq!(block.unwrap().number, U64::from(1)); } + #[tokio::test] + async fn test_block_by_number_full_transactions() { + let index = Arc::new(BlockIndex::new()); + let block_hash = B256::repeat_byte(1); + let tx_hash = B256::repeat_byte(2); + let mut block = create_test_block(1, block_hash); + block.transaction_hashes = vec![tx_hash]; + index.insert_block(block, vec![create_test_tx(tx_hash, block_hash, 1)], vec![]); + + let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); + + let block = + provider.block_by_number(BlockNumberOrTag::Number(U64::from(1)), true).await.unwrap(); + let block = block.expect("block should exist"); + match block.transactions { + BlockTransactions::Full(txs) => { + assert_eq!(txs.len(), 1); + assert_eq!(txs[0].hash, tx_hash); + } + BlockTransactions::Hashes(_) => panic!("expected full transactions"), + } + } + + #[tokio::test] + async fn test_code_missing_account_returns_empty() { + let index = Arc::new(BlockIndex::new()); + let provider = IndexedStateProvider::with_chain_id(index, MissingAccountState, 1337); + + let code = provider.code(Address::repeat_byte(0xaa), None).await.unwrap(); + assert!(code.is_empty()); + } + #[tokio::test] async fn test_transaction_by_hash() { let index = Arc::new(BlockIndex::new()); @@ -550,12 +628,14 @@ mod tests { let provider = IndexedStateProvider::with_chain_id(index, MockState, 1337); let block = - provider.block_by_number(BlockNumberOrTag::Tag(BlockTag::Latest)).await.unwrap(); + provider.block_by_number(BlockNumberOrTag::Tag(BlockTag::Latest), false).await.unwrap(); assert!(block.is_some()); assert_eq!(block.unwrap().number, U64::from(10)); - let block = - provider.block_by_number(BlockNumberOrTag::Tag(BlockTag::Earliest)).await.unwrap(); + let block = provider + .block_by_number(BlockNumberOrTag::Tag(BlockTag::Earliest), false) + .await + .unwrap(); assert!(block.is_none()); } } diff --git a/crates/node/rpc/src/lib.rs b/crates/node/rpc/src/lib.rs index 788bc43..4d5a629 100644 --- a/crates/node/rpc/src/lib.rs +++ b/crates/node/rpc/src/lib.rs @@ -13,8 +13,8 @@ pub use error::{RpcError, codes as error_codes}; mod eth; pub use eth::{ - EthApiImpl, EthApiServer, FeeHistory, NetApiImpl, NetApiServer, TxSubmitCallback, Web3ApiImpl, - Web3ApiServer, + EthApiImpl, EthApiServer, FeeHistory, NetApiImpl, NetApiServer, TxSubmitCallback, + TxSubmitFuture, Web3ApiImpl, Web3ApiServer, }; mod kora; diff --git a/crates/node/rpc/src/server.rs b/crates/node/rpc/src/server.rs index a6007f1..6aee83f 100644 --- a/crates/node/rpc/src/server.rs +++ b/crates/node/rpc/src/server.rs @@ -78,6 +78,7 @@ pub struct RpcServer { state_provider: S, cors_config: CorsConfig, max_connections: u32, + peer_count: u64, } impl std::fmt::Debug for RpcServer { @@ -111,6 +112,7 @@ impl RpcServer { state_provider: NoopStateProvider, cors_config: CorsConfig::default(), max_connections: 100, + peer_count: 0, } } @@ -125,6 +127,7 @@ impl RpcServer { state_provider: NoopStateProvider, cors_config: CorsConfig::default(), max_connections: 100, + peer_count: 0, } } } @@ -146,6 +149,7 @@ impl RpcServer { state_provider, cors_config: CorsConfig::default(), max_connections: 100, + peer_count: 0, } } @@ -170,6 +174,13 @@ impl RpcServer { self } + /// Set the initially reported peer count for `net_peerCount`. + #[must_use] + pub const fn with_peer_count(mut self, peer_count: u64) -> Self { + self.peer_count = peer_count; + self + } + /// Create from configuration. pub fn from_config(state: NodeState, config: RpcServerConfig, state_provider: S) -> Self { Self { @@ -181,6 +192,7 @@ impl RpcServer { state_provider, cors_config: config.cors, max_connections: config.max_connections, + peer_count: 0, } } @@ -197,6 +209,7 @@ impl RpcServer { let cors_layer = build_cors_layer(&self.cors_config); let max_connections = self.max_connections; let state_provider = self.state_provider; + let peer_count = self.peer_count; let http_handle = tokio::spawn(async move { let app = Router::new() @@ -239,6 +252,7 @@ impl RpcServer { |submit| EthApiImpl::with_tx_submit(chain_id, state_provider.clone(), submit), ); let net_api = NetApiImpl::new(chain_id); + net_api.set_peer_count(peer_count); let web3_api = Web3ApiImpl::new(); let kora_api = KoraApiImpl::new(node_state_for_jsonrpc); @@ -312,6 +326,7 @@ pub struct JsonRpcServer { tx_submit: Option, state_provider: S, max_connections: u32, + peer_count: u64, } impl std::fmt::Debug for JsonRpcServer { @@ -333,6 +348,7 @@ impl JsonRpcServer { tx_submit: None, state_provider: NoopStateProvider, max_connections: 100, + peer_count: 0, } } } @@ -340,7 +356,14 @@ impl JsonRpcServer { impl JsonRpcServer { /// Create a new JSON-RPC server with a custom state provider. pub fn with_state_provider(addr: SocketAddr, chain_id: u64, state_provider: S) -> Self { - Self { addr, chain_id, tx_submit: None, state_provider, max_connections: 100 } + Self { + addr, + chain_id, + tx_submit: None, + state_provider, + max_connections: 100, + peer_count: 0, + } } /// Set the transaction submission callback. @@ -357,6 +380,13 @@ impl JsonRpcServer { self } + /// Set the initially reported peer count for `net_peerCount`. + #[must_use] + pub const fn with_peer_count(mut self, peer_count: u64) -> Self { + self.peer_count = peer_count; + self + } + /// Start the JSON-RPC server. pub async fn start(self) -> Result { let server = Server::builder() @@ -370,6 +400,7 @@ impl JsonRpcServer { |submit| EthApiImpl::with_tx_submit(self.chain_id, self.state_provider.clone(), submit), ); let net_api = NetApiImpl::new(self.chain_id); + net_api.set_peer_count(self.peer_count); let web3_api = Web3ApiImpl::new(); let mut module = jsonrpsee::RpcModule::new(()); diff --git a/crates/node/rpc/src/state_provider.rs b/crates/node/rpc/src/state_provider.rs index 1dc9c67..0a8c3e5 100644 --- a/crates/node/rpc/src/state_provider.rs +++ b/crates/node/rpc/src/state_provider.rs @@ -47,10 +47,18 @@ pub trait StateProvider: Send + Sync { ) -> Result; /// Get a block by number. - async fn block_by_number(&self, block: BlockNumberOrTag) -> Result, RpcError>; + async fn block_by_number( + &self, + block: BlockNumberOrTag, + full_transactions: bool, + ) -> Result, RpcError>; /// Get a block by hash. - async fn block_by_hash(&self, hash: B256) -> Result, RpcError>; + async fn block_by_hash( + &self, + hash: B256, + full_transactions: bool, + ) -> Result, RpcError>; /// Get a transaction by hash. async fn transaction_by_hash(&self, hash: B256) -> Result, RpcError>; @@ -129,11 +137,16 @@ impl StateProvider for NoopStateProvider { async fn block_by_number( &self, _block: BlockNumberOrTag, + _full_transactions: bool, ) -> Result, RpcError> { Ok(None) } - async fn block_by_hash(&self, _hash: B256) -> Result, RpcError> { + async fn block_by_hash( + &self, + _hash: B256, + _full_transactions: bool, + ) -> Result, RpcError> { Ok(None) } diff --git a/crates/node/runner/Cargo.toml b/crates/node/runner/Cargo.toml index 64e2b56..85eba5d 100644 --- a/crates/node/runner/Cargo.toml +++ b/crates/node/runner/Cargo.toml @@ -23,6 +23,7 @@ kora-rpc.workspace = true kora-service.workspace = true kora-simplex.workspace = true kora-transport.workspace = true +kora-txpool.workspace = true commonware-codec.workspace = true commonware-consensus.workspace = true diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index a288d3b..403615f 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -18,7 +18,7 @@ use commonware_parallel::Sequential; use commonware_runtime::{Metrics as _, Spawner, buffer::paged::CacheRef, tokio}; use commonware_utils::{NZU64, NZUsize, acknowledgement::Exact, ordered::Set}; use futures::StreamExt; -use kora_domain::{Block, BlockCfg, BootstrapConfig, ConsensusDigest, LedgerEvent, TxCfg}; +use kora_domain::{Block, BlockCfg, BootstrapConfig, ConsensusDigest, LedgerEvent, Tx, TxCfg}; use kora_executor::{BlockContext, RevmExecutor}; use kora_ledger::{LedgerService, LedgerView}; use kora_marshal::{ArchiveInitializer, BroadcastInitializer, PeerInitializer}; @@ -26,6 +26,7 @@ use kora_reporters::{BlockContextProvider, FinalizedReporter, NodeStateReporter, use kora_service::{NodeRunContext, NodeRunner}; use kora_simplex::{DEFAULT_MAILBOX_SIZE as MAILBOX_SIZE, DefaultPool}; use kora_transport::NetworkTransport; +use kora_txpool::{PoolConfig, TransactionValidator}; use tracing::{debug, info, trace}; use crate::{RevmApplication, RunnerError, scheme::ThresholdScheme}; @@ -222,6 +223,9 @@ impl NodeRunner for ProductionRunner { let block_index = self.rpc_config.as_ref().map(|_| Arc::new(kora_indexer::BlockIndex::new())); + let ledger = LedgerService::new(state.clone()); + spawn_ledger_observers(ledger.clone(), context.clone()); + if let Some((node_state, addr)) = &self.rpc_config { let qmdb_state = state.qmdb_state().await; let rpc_executor = Arc::new(RevmExecutor::new(self.chain_id)); @@ -230,19 +234,41 @@ impl NodeRunner for ProductionRunner { qmdb_state, rpc_executor, ); + let tx_ledger = ledger.clone(); + let tx_state = state.qmdb_state().await; + let chain_id = self.chain_id; + let tx_submit: kora_rpc::TxSubmitCallback = Arc::new(move |data| { + let ledger = tx_ledger.clone(); + let state = tx_state.clone(); + Box::pin(async move { + let tx = Tx::new(data); + let validator = + TransactionValidator::new(chain_id, state, PoolConfig::default()); + validator + .validate(tx.clone()) + .await + .map_err(|err| kora_rpc::RpcError::InvalidTransaction(err.to_string()))?; + if ledger.submit_tx(tx).await { + Ok(()) + } else { + Err(kora_rpc::RpcError::InvalidTransaction( + "transaction rejected by mempool".to_string(), + )) + } + }) + }); let rpc = kora_rpc::RpcServer::with_state_provider( node_state.clone(), *addr, self.chain_id, indexed_provider, - ); + ) + .with_tx_submit(tx_submit) + .with_peer_count(self.scheme.participants().len().saturating_sub(1) as u64); drop(rpc.start()); info!(addr = %addr, "RPC server started with live state provider"); } - let ledger = LedgerService::new(state.clone()); - spawn_ledger_observers(ledger.clone(), context.clone()); - let validator_key = config .validator_key() .map_err(|e| anyhow::anyhow!("failed to load validator key: {}", e))?; diff --git a/crates/node/txpool/src/pool.rs b/crates/node/txpool/src/pool.rs index dd811c2..e77a695 100644 --- a/crates/node/txpool/src/pool.rs +++ b/crates/node/txpool/src/pool.rs @@ -6,7 +6,7 @@ use std::{ }; use alloy_consensus::{Transaction, TxEnvelope}; -use alloy_eips::eip2718::Decodable2718; +use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{Address, B256, Bytes}; use kora_domain::{Tx, TxId}; use parking_lot::RwLock; @@ -240,7 +240,7 @@ fn current_timestamp() -> u64 { fn tx_to_ordered(tx: &Tx) -> Option { let envelope = TxEnvelope::decode_2718(&mut tx.bytes.as_ref()).ok()?; let sender = recover_sender_from_envelope(&envelope).ok()?; - let hash = alloy_primitives::keccak256(alloy_rlp::encode(&envelope)); + let hash = alloy_primitives::keccak256(&tx.bytes); let nonce = envelope.nonce(); let effective_gas_price = match &envelope { TxEnvelope::Legacy(tx) => tx.tx().gas_price, @@ -305,7 +305,9 @@ impl Mempool for TransactionPool { if tx.nonce == expected_nonce { included_senders.insert(tx.sender, tx.nonce + 1); - result.push(Tx::new(Bytes::from(alloy_rlp::encode(&tx.envelope)))); + let mut raw = Vec::new(); + tx.envelope.encode_2718(&mut raw); + result.push(Tx::new(Bytes::from(raw))); } } diff --git a/crates/node/txpool/src/validator.rs b/crates/node/txpool/src/validator.rs index e4062e6..c12c3ce 100644 --- a/crates/node/txpool/src/validator.rs +++ b/crates/node/txpool/src/validator.rs @@ -94,6 +94,10 @@ impl TransactionValidator { if nonce < state_nonce { return Err(TxPoolError::NonceTooLow { got: nonce, expected: state_nonce }); } + let max_accepted_nonce = state_nonce.saturating_add(self.config.max_txs_per_sender as u64); + if nonce > max_accepted_nonce { + return Err(TxPoolError::NonceGap { got: nonce, expected: state_nonce }); + } let max_cost = max_tx_cost(&envelope); let balance = self @@ -511,6 +515,21 @@ mod tests { assert!(matches!(result, Err(TxPoolError::NonceTooLow { got: 0, expected: 5 }))); } + #[tokio::test] + async fn reject_far_future_nonce() { + let chain_id = 1u64; + let (sender, _, raw_tx) = + sign_eip1559_tx(chain_id, 100, 21000, 1_000_000_000, U256::ZERO, Some(Address::ZERO)); + + let state = + MockState::new().with_account(sender, 0, U256::from(1_000_000_000_000_000_000u64)); + let config = PoolConfig::default().with_max_txs_per_sender(16); + let validator = TransactionValidator::new(chain_id, state, config); + + let result = validator.validate(raw_tx).await; + assert!(matches!(result, Err(TxPoolError::NonceGap { got: 100, expected: 0 }))); + } + #[tokio::test] async fn reject_insufficient_balance() { let chain_id = 1u64; diff --git a/crates/storage/indexer/src/store.rs b/crates/storage/indexer/src/store.rs index f0d3a99..4f51d08 100644 --- a/crates/storage/indexer/src/store.rs +++ b/crates/storage/indexer/src/store.rs @@ -122,6 +122,19 @@ impl BlockIndex { self.transactions.read().get(hash).cloned() } + /// Gets all indexed transactions for a block in transaction-index order. + pub fn get_transactions_for_block(&self, block_hash: &B256) -> Vec { + let mut txs = self + .transactions + .read() + .values() + .filter(|tx| tx.block_hash == *block_hash) + .cloned() + .collect::>(); + txs.sort_by_key(|tx| tx.index); + txs + } + /// Gets a receipt by its transaction hash. pub fn get_receipt(&self, hash: &B256) -> Option { self.receipts.read().get(hash).cloned() From 6a541ec7711a46bbbd3dd88036aefef94681db7d Mon Sep 17 00:00:00 2001 From: JaeLeex Date: Wed, 29 Apr 2026 12:44:46 -0400 Subject: [PATCH 09/16] fix(txpool): raise default max_txs_per_sender 16 -> 256 Bursty contract-deploy workloads (Foundry Forge, Cannon, raw-RPC scripts that submit the full Mirage agents+exchange parity stack of ~39 sequential txs from a single deployer) hit the per-sender nonce-gap cap before any txs mine. The validator computes: max_accepted_nonce = state_nonce + max_txs_per_sender and rejects anything beyond that as `TxPoolError::NonceGap`. With the old default of 16 and `state_nonce = 0` for a fresh deployer, every tx with nonce 17+ is dropped at admission, so devnet deploys silently lose ~half of a 39-tx run even when the chain itself is healthy. 256 covers all current contract-deploy patterns (Mirage = 39, future stacks trend higher with more wiring/seed calls), is still small enough to bound memory per malicious sender, and lives behind the existing `with_max_txs_per_sender(...)` builder so production deployments can tune down if needed. Adds `accept_burst_of_39_sequential_nonces_from_single_sender` which signs 39 contiguous-nonce legacy txs with one key and asserts every one passes validation under `PoolConfig::default()`. Repros the failure with the old cap (`NonceGap { got: 17, expected: 0 }`) and locks in the fix. --- crates/node/txpool/src/config.rs | 6 +-- crates/node/txpool/src/validator.rs | 72 +++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/crates/node/txpool/src/config.rs b/crates/node/txpool/src/config.rs index ca49513..8f28fde 100644 --- a/crates/node/txpool/src/config.rs +++ b/crates/node/txpool/src/config.rs @@ -22,7 +22,7 @@ impl Default for PoolConfig { Self { max_pending_txs: 4096, max_queued_txs: 1024, - max_txs_per_sender: 16, + max_txs_per_sender: 256, max_tx_size: 128 * 1024, // 128 KB min_gas_price: 0, replacement_bump_percent: 10, @@ -36,7 +36,7 @@ impl PoolConfig { Self { max_pending_txs: 4096, max_queued_txs: 1024, - max_txs_per_sender: 16, + max_txs_per_sender: 256, max_tx_size: 128 * 1024, min_gas_price: 0, replacement_bump_percent: 10, @@ -95,7 +95,7 @@ mod tests { let config = PoolConfig::default(); assert_eq!(config.max_pending_txs, 4096); assert_eq!(config.max_queued_txs, 1024); - assert_eq!(config.max_txs_per_sender, 16); + assert_eq!(config.max_txs_per_sender, 256); assert_eq!(config.max_tx_size, 128 * 1024); assert_eq!(config.min_gas_price, 0); assert_eq!(config.replacement_bump_percent, 10); diff --git a/crates/node/txpool/src/validator.rs b/crates/node/txpool/src/validator.rs index c12c3ce..09bcd94 100644 --- a/crates/node/txpool/src/validator.rs +++ b/crates/node/txpool/src/validator.rs @@ -530,6 +530,78 @@ mod tests { assert!(matches!(result, Err(TxPoolError::NonceGap { got: 100, expected: 0 }))); } + /// Sign `count` legacy txs with the same key, returning (sender, raw_txs[]). + /// Each tx has a sequential nonce starting at `start_nonce`. + fn sign_legacy_burst( + chain_id: u64, + start_nonce: u64, + count: u64, + ) -> (Address, Vec) { + let signing_key = SigningKey::random(&mut OsRng); + let verifying_key = signing_key.verifying_key(); + let pubkey = verifying_key.to_encoded_point(false); + let pubkey_bytes = pubkey.as_bytes(); + let pubkey_hash = sha3::Keccak256::digest(&pubkey_bytes[1..]); + let sender = Address::from_slice(&pubkey_hash[12..]); + + let mut raws = Vec::with_capacity(count as usize); + for i in 0..count { + let tx = TxLegacy { + chain_id: Some(chain_id), + nonce: start_nonce + i, + gas_limit: 1_000_000, + gas_price: 1_000_000_000, + to: TxKind::Create, + value: U256::ZERO, + input: Bytes::new(), + }; + let sig_hash = tx.signature_hash(); + let (sig, recovery_id) = + signing_key.sign_prehash_recoverable(sig_hash.as_slice()).unwrap(); + let r = U256::from_be_slice(&sig.r().to_bytes()); + let s = U256::from_be_slice(&sig.s().to_bytes()); + let v = recovery_id.is_y_odd(); + let signature = Signature::new(r, s, v); + let signed = tx.into_signed(signature); + let envelope = TxEnvelope::from(signed); + let mut raw_bytes = Vec::new(); + envelope.encode_2718(&mut raw_bytes); + raws.push(Tx::new(raw_bytes.into())); + } + (sender, raws) + } + + /// Regression: a fresh deployer submitting 39 sequential txs (the typical + /// contract-deploy burst — e.g. the Mirage agents+exchange stack) must not + /// be rejected by the per-sender cap before any of them mine. + /// + /// Pre-fix: `max_txs_per_sender = 16` rejected tx 17+ as `NonceGap` even + /// though the txs are perfectly contiguous from `state_nonce = 0`. Bursty + /// devnet deploys (Foundry Forge + Cannon + raw-RPC scripts) hit this. + #[tokio::test] + async fn accept_burst_of_39_sequential_nonces_from_single_sender() { + let chain_id = 1u64; + let (sender, raws) = sign_legacy_burst(chain_id, 0, 39); + + let state = MockState::new() + .with_account(sender, 0, U256::from(10_000_000_000_000_000_000u128)); + let config = PoolConfig::default(); + let validator = TransactionValidator::new(chain_id, state, config); + + for (i, raw) in raws.into_iter().enumerate() { + let res = validator.validate(raw).await; + assert!( + res.is_ok(), + "tx {} (nonce={}) was rejected: {:?}", + i, + i, + res.err() + ); + let v = res.unwrap(); + assert_eq!(v.nonce, i as u64); + } + } + #[tokio::test] async fn reject_insufficient_balance() { let chain_id = 1u64; From 1647cc8fe647caa92d3d0412be142f2f498bddd7 Mon Sep 17 00:00:00 2001 From: JaeLeex Date: Wed, 29 Apr 2026 13:28:20 -0400 Subject: [PATCH 10/16] diagnostics: warn-log silent tx drops + regression tests for unwired tx_submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devnet 1337 (`http://65.109.61.210:8545`) was running a kora binary built before commit `beb637a` ("Enhance Ethereum JSON-RPC API and update dependencies"), so the runner had no `with_tx_submit(...)` wiring on the `RpcServer`. Symptom in production: - `eth_sendRawTransaction` returned a valid hash for every tx - `eth_getTransactionByHash` returned the full tx (RPC-side `pending_txs` cache populated) - `eth_getTransactionReceipt` was `null` forever - Block production advanced ~2 blocks/s but every block had `transactionsRoot = 0x000…000`, `gasUsed = 0`, `txs = []` - `eth_getTransactionCount(addr, "latest")` and `(addr, "pending")` both stuck at the pre-startup nonce Empty blocks forever, no error anywhere. Took half a day to diagnose because every individual layer looked healthy. This commit adds two small things to make the next occurrence visible within seconds and impossible to merge: ## 1. Producer-side warn log when build_block produces an empty block while the mempool has unincluded txs (`crates/node/runner/src/app.rs`) Captures the smoking gun directly: "mempool_len=N, excluded_len=M, produced empty block." If `N > M` and we still drain zero, something is wrong and operators see it in logs. ## 2. RPC-side tracing for tx flow (`crates/node/runner/src/runner.rs`) `debug!` on successful insert, `warn!` on validator rejection, `warn!` on `ledger.submit_tx` returning false (duplicate or pool error). Fills the gap where a tx silently dies between RPC and mempool. ## 3. Two regression tests in `crates/node/rpc/src/eth.rs` - `send_raw_transaction_with_no_callback_silently_accepts_but_drops` pins the failure mode that hit devnet: when no `tx_submit` is wired, `send_raw_transaction` returns Ok with the hash and populates `pending_txs` but does *not* signal the caller that the tx will not be processed. Test exists so any refactor that "fixes" this by erroring out (or any regression that re-introduces the unwired state) is visible in CI. - `send_raw_transaction_passes_full_tx_bytes_to_callback` strengthens the existing `eth_send_raw_transaction` test (which only checked an `AtomicBool`). Asserts the bytes the callback receives are exactly the bytes the caller submitted — catches re-encoding, truncation, or wrong-buffer regressions. `cargo test -p kora-rpc --lib` → 91/91 passing. `cargo check -p kora-runner` → clean. --- crates/node/rpc/src/eth.rs | 59 ++++++++++++++++++++++++++++++++ crates/node/runner/src/app.rs | 23 +++++++++++++ crates/node/runner/src/runner.rs | 13 ++++--- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/crates/node/rpc/src/eth.rs b/crates/node/rpc/src/eth.rs index af8726f..549fc65 100644 --- a/crates/node/rpc/src/eth.rs +++ b/crates/node/rpc/src/eth.rs @@ -658,4 +658,63 @@ mod tests { assert_eq!(tx.nonce, U64::from(7)); assert!(tx.block_hash.is_none()); } + + /// Regression: when no `tx_submit` callback is wired, `send_raw_transaction` + /// silently accepts the tx and returns the hash, but the tx goes nowhere — + /// no mempool, no producer, no block. This is exactly the failure mode + /// observed on devnet 1337 (`http://65.109.61.210:8545`) where the deployed + /// kora binary predates the runner's `with_tx_submit(...)` wiring (commit + /// `beb637a`): every tx submitted via JSON-RPC was accepted, hash returned, + /// but never included in any block. + /// + /// The fix lives in the runner: always wire `tx_submit` to a real mempool. + /// The downstream observability fix (warn-log when build_block produces an + /// empty block while the mempool is non-empty) lives in `app.rs`. + #[tokio::test] + async fn send_raw_transaction_with_no_callback_silently_accepts_but_drops() { + let api = EthApiImpl::new(1, NoopStateProvider); // no tx_submit + let tx_data = signed_test_tx(1, 0); + let result = EthApiServer::send_raw_transaction(&api, tx_data.clone()).await; + assert!(result.is_ok()); + assert_eq!(result.unwrap(), alloy_primitives::keccak256(&tx_data)); + // The tx is in pending_txs (so getTransactionByHash returns something) — + // that's exactly what makes the bug invisible to operators. + let cached = + EthApiServer::get_transaction_by_hash(&api, alloy_primitives::keccak256(&tx_data)) + .await + .unwrap(); + assert!( + cached.is_some(), + "RPC caches the tx for visibility even though it has nowhere to send it" + ); + } + + /// Regression: the existing `eth_send_raw_transaction` test only verifies + /// that the callback is invoked (a boolean flag). It does not verify that + /// the bytes passed to the callback are the same bytes the caller sent. + /// A regression that mangled the body (e.g. dropped the chainId, re-encoded + /// the envelope, sent a partial slice) would still pass that test. This + /// one captures the actual bytes and compares them. + #[tokio::test] + async fn send_raw_transaction_passes_full_tx_bytes_to_callback() { + let captured: Arc>> = Arc::new(RwLock::new(Vec::new())); + let captured_clone = captured.clone(); + let callback: TxSubmitCallback = Arc::new(move |data| { + let captured_clone = captured_clone.clone(); + Box::pin(async move { + captured_clone.write().await.push(data); + Ok(()) + }) + }); + let api = EthApiImpl::with_tx_submit(1, NoopStateProvider, callback); + let tx_data = signed_test_tx(1, 42); + let _ = EthApiServer::send_raw_transaction(&api, tx_data.clone()).await.unwrap(); + let inner = captured.read().await; + assert_eq!(inner.len(), 1, "callback invoked exactly once"); + assert_eq!( + &inner[0][..], + &tx_data[..], + "callback receives the caller's tx bytes verbatim — no re-encoding, no truncation" + ); + } } diff --git a/crates/node/runner/src/app.rs b/crates/node/runner/src/app.rs index d9a2112..15c9ebb 100644 --- a/crates/node/runner/src/app.rs +++ b/crates/node/runner/src/app.rs @@ -91,8 +91,31 @@ where let (_, mempool, snapshots) = self.ledger.proposal_components().await; let excluded = self.collect_pending_tx_ids(&snapshots, parent_digest); + let mempool_len = mempool.len(); + let excluded_len = excluded.len(); let txs = mempool.build(self.max_txs, &excluded); + // Diagnostic: when the producer builds an empty block while there are + // unincluded txs in the mempool, something is wrong (e.g. RPC tx_submit + // not wired, the excluded set over-collecting, or max_txs misconfigured). + // Log enough state to tell which. + if txs.is_empty() && mempool_len > excluded_len { + warn!( + mempool_len, + excluded_len, + max_txs = self.max_txs, + "build_block: mempool has unincluded txs but produced empty block" + ); + } else { + trace!( + mempool_len, + excluded_len, + drained = txs.len(), + max_txs = self.max_txs, + "build_block: mempool drain" + ); + } + let prevrandao = self.get_prevrandao(parent_digest).await; let height = parent.height + 1; let context = self.block_context(height, prevrandao); diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index 403615f..0e6caa7 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -27,7 +27,7 @@ use kora_service::{NodeRunContext, NodeRunner}; use kora_simplex::{DEFAULT_MAILBOX_SIZE as MAILBOX_SIZE, DefaultPool}; use kora_transport::NetworkTransport; use kora_txpool::{PoolConfig, TransactionValidator}; -use tracing::{debug, info, trace}; +use tracing::{debug, info, trace, warn}; use crate::{RevmApplication, RunnerError, scheme::ThresholdScheme}; @@ -242,15 +242,18 @@ impl NodeRunner for ProductionRunner { let state = tx_state.clone(); Box::pin(async move { let tx = Tx::new(data); + let tx_id = tx.id(); let validator = TransactionValidator::new(chain_id, state, PoolConfig::default()); - validator - .validate(tx.clone()) - .await - .map_err(|err| kora_rpc::RpcError::InvalidTransaction(err.to_string()))?; + validator.validate(tx.clone()).await.map_err(|err| { + warn!(?tx_id, error = %err, "rpc submit: validator rejected tx"); + kora_rpc::RpcError::InvalidTransaction(err.to_string()) + })?; if ledger.submit_tx(tx).await { + debug!(?tx_id, "rpc submit: tx inserted into mempool"); Ok(()) } else { + warn!(?tx_id, "rpc submit: ledger.submit_tx returned false (duplicate or pool error)"); Err(kora_rpc::RpcError::InvalidTransaction( "transaction rejected by mempool".to_string(), )) From 5cb9a50db7127c1a68c01993e0a78ac32cbd4114 Mon Sep 17 00:00:00 2001 From: JaeLeex Date: Wed, 29 Apr 2026 14:10:28 -0400 Subject: [PATCH 11/16] =?UTF-8?q?fix(runner):=20raise=20BLOCK=5FCODEC=5FMA?= =?UTF-8?q?X=5FTX=5FBYTES=201=20KiB=20=E2=86=92=20128=20KiB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The block codec was capped at 1024 bytes per tx, but the txpool validator admits txs up to `PoolConfig::default().max_tx_size = 128 KiB` and the domain-level `BlockCfg::default().tx.max_tx_bytes` is also 128 KiB. So contracts up to 128 KiB pass admission, sit in the mempool, and are then silently dropped by the producer when the codec rejects them at block-build time. Empirical signature on devnet 1337 (`http://65.109.61.210:8545`): - 21000-gas value transfers mine instantly - ~22-byte init-code CREATEs mine instantly - 1 KiB+ Solidity contracts (e.g. RoleRegistry, 1615 bytes input, 10M gas) sit in the mempool indefinitely; producer keeps shipping empty blocks while the tx is right there in `eth_getTransactionByHash` Bumping to 128 KiB matches the validator + the domain default. Full parity restores: validator admits up to N → producer encodes up to N → no silent drops between layers. Updates the same constant in `crates/e2e/src/harness.rs` so test infrastructure stays in sync. The constant could plausibly be hoisted to a single source of truth (or read directly from `BlockCfg::default()`) as a follow-up. --- crates/e2e/src/harness.rs | 2 +- crates/node/runner/src/runner.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/e2e/src/harness.rs b/crates/e2e/src/harness.rs index 1acbeba..9b71a1a 100644 --- a/crates/e2e/src/harness.rs +++ b/crates/e2e/src/harness.rs @@ -37,7 +37,7 @@ use crate::{TestConfig, TestNode, TestSetup}; const MAX_MSG_SIZE: usize = 1024 * 1024; const BLOCK_CODEC_MAX_TXS: usize = 64; -const BLOCK_CODEC_MAX_TX_BYTES: usize = 1024; +const BLOCK_CODEC_MAX_TX_BYTES: usize = 128 * 1024; const EPOCH_LENGTH: u64 = u64::MAX; type Peer = PublicKey; diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index 0e6caa7..fdad5fd 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -32,7 +32,14 @@ use tracing::{debug, info, trace, warn}; use crate::{RevmApplication, RunnerError, scheme::ThresholdScheme}; const BLOCK_CODEC_MAX_TXS: usize = 64; -const BLOCK_CODEC_MAX_TX_BYTES: usize = 1024; +// Match `PoolConfig::default().max_tx_size` (= 128 KiB) and the domain-level +// `BlockCfg::default().tx.max_tx_bytes` (also 128 KiB). The previous 1024-byte +// cap rejected every real contract deploy: the validator admitted contracts +// up to 128 KiB into the mempool, but the block codec then refused to encode +// anything > 1 KiB, so the producer silently skipped them. Trivial value +// transfers and ~22-byte init contracts mined; any actual Solidity contract +// (1+ KiB of bytecode) was dropped. See PR fixing this for the full diagnostic. +const BLOCK_CODEC_MAX_TX_BYTES: usize = 128 * 1024; const EPOCH_LENGTH: u64 = u64::MAX; const PARTITION_PREFIX: &str = "kora"; From cf11265b5b4165feadd4b373d5523648484f7a2d Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Wed, 29 Apr 2026 21:48:06 +0200 Subject: [PATCH 12/16] refactor(executor): update Tokio usage and enhance QmdbHandle for async storage access - Adjusted `Cargo.toml` to refine Tokio features for development dependencies. - Modified `adapter.rs` to utilize `block_in_place` for async operations within a Tokio runtime. - Enhanced `QmdbHandle` in `qmdb.rs` to include a `Mutex` for managing concurrent access to storage, ensuring safe operations during backend partition modifications. - Updated state management in `state.rs` and `ledger.rs` to incorporate the new storage access mechanism, improving concurrency handling during state commits and root computations. --- crates/node/executor/Cargo.toml | 3 ++- crates/node/executor/src/adapter.rs | 12 ++++++++++-- crates/storage/handlers/src/qmdb.rs | 21 ++++++++++++++++++--- crates/storage/handlers/src/state.rs | 5 ++++- crates/storage/qmdb-ledger/src/ledger.rs | 17 +++++++++++++++-- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/crates/node/executor/Cargo.toml b/crates/node/executor/Cargo.toml index 4ee1644..c477320 100644 --- a/crates/node/executor/Cargo.toml +++ b/crates/node/executor/Cargo.toml @@ -17,10 +17,11 @@ kora-qmdb = { path = "../../storage/qmdb" } kora-traits = { path = "../../storage/traits" } revm.workspace = true thiserror.workspace = true +tokio = { workspace = true, features = ["rt"] } [dev-dependencies] rstest.workspace = true -tokio = { workspace = true, features = ["rt", "macros"] } +tokio = { workspace = true, features = ["macros"] } [lints] workspace = true diff --git a/crates/node/executor/src/adapter.rs b/crates/node/executor/src/adapter.rs index d9d1741..9d5cc8e 100644 --- a/crates/node/executor/src/adapter.rs +++ b/crates/node/executor/src/adapter.rs @@ -1,16 +1,24 @@ //! State database adapter for REVM. //! -//! Note: REVM's `DatabaseRef` trait is synchronous, so we use `futures::executor::block_on` -//! to bridge the async StateDb traits into the sync REVM interface. +//! Note: REVM's `DatabaseRef` trait is synchronous, so we bridge async StateDb traits into +//! the sync REVM interface. When executing inside a Tokio runtime, we use `block_in_place` +//! so async storage can continue making progress on runtime workers. use alloy_primitives::{Address, B256, KECCAK256_EMPTY, U256}; use kora_traits::{StateDbError, StateDbRead}; use revm::{bytecode::Bytecode, database_interface::DatabaseRef, state::AccountInfo}; +use tokio::runtime::RuntimeFlavor; use crate::ExecutionError; /// Wrapper for blocking async operations in sync contexts. fn block_on(f: F) -> F::Output { + if let Ok(handle) = tokio::runtime::Handle::try_current() + && handle.runtime_flavor() == RuntimeFlavor::MultiThread + { + return tokio::task::block_in_place(|| handle.block_on(f)); + } + futures::executor::block_on(f) } diff --git a/crates/storage/handlers/src/qmdb.rs b/crates/storage/handlers/src/qmdb.rs index 74c83a7..63ee069 100644 --- a/crates/storage/handlers/src/qmdb.rs +++ b/crates/storage/handlers/src/qmdb.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use kora_qmdb::{ AccountEncoding, AccountUpdate, ChangeSet, QmdbBatchable, QmdbGettable, QmdbStore, StorageKey, }; -use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use tokio::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::HandleError; @@ -34,11 +34,16 @@ pub trait RootProvider: Send + Sync { pub struct QmdbHandle { inner: Arc>>, root_provider: Option>>, + storage_access: Arc>, } impl Clone for QmdbHandle { fn clone(&self) -> Self { - Self { inner: Arc::clone(&self.inner), root_provider: self.root_provider.clone() } + Self { + inner: Arc::clone(&self.inner), + root_provider: self.root_provider.clone(), + storage_access: Arc::clone(&self.storage_access), + } } } @@ -49,13 +54,18 @@ impl QmdbHandle { Self { inner: Arc::new(RwLock::new(QmdbStore::new(accounts, storage, code))), root_provider: None, + storage_access: Arc::new(Mutex::new(())), } } /// Create from an existing `QmdbStore`. #[must_use] pub fn from_store(store: QmdbStore) -> Self { - Self { inner: Arc::new(RwLock::new(store)), root_provider: None } + Self { + inner: Arc::new(RwLock::new(store)), + root_provider: None, + storage_access: Arc::new(Mutex::new(())), + } } /// Set the root provider for state root computation. @@ -70,6 +80,11 @@ impl QmdbHandle { self.root_provider.as_ref() } + /// Acquire the shared gate for operations that open or mutate backend partitions. + pub async fn storage_access(&self) -> MutexGuard<'_, ()> { + self.storage_access.lock().await + } + /// Acquire read lock on the underlying store. pub async fn read(&self) -> RwLockReadGuard<'_, QmdbStore> { self.inner.read().await diff --git a/crates/storage/handlers/src/state.rs b/crates/storage/handlers/src/state.rs index 947c3ab..24333a5 100644 --- a/crates/storage/handlers/src/state.rs +++ b/crates/storage/handlers/src/state.rs @@ -92,10 +92,11 @@ where + 'static, { async fn commit(&self, changes: ChangeSet) -> Result { + let _storage_access = self.storage_access().await; let mut store = self.write().await; store.commit_changes(changes).await.map_err(|e| StateDbError::Storage(e.to_string()))?; + drop(store); - // If we have a root provider, commit and get the state root if let Some(provider) = self.root_provider() { let mut provider = provider.write().await; provider @@ -111,6 +112,7 @@ where async fn compute_root(&self, changes: &ChangeSet) -> Result { // If we have a root provider, use it to compute the root if let Some(provider) = self.root_provider() { + let _storage_access = self.storage_access().await; let mut provider = provider.write().await; provider .compute_root(changes) @@ -149,6 +151,7 @@ where async fn state_root(&self) -> Result { // If we have a root provider, use it to get the state root if let Some(provider) = self.root_provider() { + let _storage_access = self.storage_access().await; let provider = provider.read().await; provider.state_root().await.map_err(|e| StateDbError::RootComputation(e.to_string())) } else { diff --git a/crates/storage/qmdb-ledger/src/ledger.rs b/crates/storage/qmdb-ledger/src/ledger.rs index f0770fe..8be0000 100644 --- a/crates/storage/qmdb-ledger/src/ledger.rs +++ b/crates/storage/qmdb-ledger/src/ledger.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, B256, U256}; use commonware_runtime::tokio::Context; use kora_backend::{ AccountStore, CodeStore, CommonwareBackend, CommonwareRootProvider, QmdbBackendConfig, @@ -8,6 +8,7 @@ use kora_backend::{ }; use kora_domain::StateRoot; use kora_handlers::{HandleError, QmdbHandle, QmdbRefDb as HandlerQmdbRefDb}; +use kora_qmdb::StateRoot as QmdbStateRoot; use kora_traits::{StateDb, StateDbWrite}; use thiserror::Error; use tokio::sync::RwLock; @@ -80,7 +81,19 @@ impl QmdbLedger { /// Commits the provided changes to QMDB and returns the resulting root. pub async fn commit_changes(&self, changes: QmdbChangeSet) -> Result { - let root = StateDbWrite::commit(&self.handle, changes).await?; + let _storage_access = self.handle.storage_access().await; + let mut store = self.handle.write().await; + store + .commit_changes(changes) + .await + .map_err(|e| kora_traits::StateDbError::Storage(e.to_string()))?; + let stores = + store.stores().map_err(|e| kora_traits::StateDbError::Storage(e.to_string()))?; + let root = QmdbStateRoot::compute( + B256::from_slice(stores.accounts.root()?.as_ref()), + B256::from_slice(stores.storage.root()?.as_ref()), + B256::from_slice(stores.code.root()?.as_ref()), + ); Ok(StateRoot(root)) } From ca922ebe9520b6b878838378d12e61b29e63d627 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Wed, 29 Apr 2026 21:56:36 +0200 Subject: [PATCH 13/16] refactor(marshal): enhance peer initialization with resolver types - Updated `lib.rs` to export additional resolver types: `ResolverChannels`, `ResolverMailbox`, and `ResolverReceiver`. - Modified `peers.rs` to utilize these new types in the `PeerInitializer` struct, improving clarity and type safety in peer initialization. - Adjusted function signatures to return the new resolver types, streamlining the interface for peer-related operations. --- crates/network/marshal/src/lib.rs | 2 +- crates/network/marshal/src/peers.rs | 13 +++++++++++-- crates/node/reporters/src/lib.rs | 2 +- crates/node/rpc/src/eth.rs | 8 ++++---- crates/node/runner/src/runner.rs | 5 ++++- crates/node/txpool/src/ordering.rs | 10 +++++----- crates/node/txpool/src/pool.rs | 8 ++++---- crates/node/txpool/src/validator.rs | 20 +++++--------------- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/crates/network/marshal/src/lib.rs b/crates/network/marshal/src/lib.rs index e86c0a8..6ed6346 100644 --- a/crates/network/marshal/src/lib.rs +++ b/crates/network/marshal/src/lib.rs @@ -15,4 +15,4 @@ mod broadcast; pub use broadcast::BroadcastInitializer; mod peers; -pub use peers::PeerInitializer; +pub use peers::{PeerInitializer, ResolverChannels, ResolverMailbox, ResolverReceiver}; diff --git a/crates/network/marshal/src/peers.rs b/crates/network/marshal/src/peers.rs index b34903a..d38150a 100644 --- a/crates/network/marshal/src/peers.rs +++ b/crates/network/marshal/src/peers.rs @@ -9,13 +9,22 @@ use commonware_consensus::{ p2p::Config, }, }; -use commonware_cryptography::PublicKey; +use commonware_cryptography::{Digestible, PublicKey}; use commonware_p2p::{Blocker, Provider, Receiver, Sender}; use commonware_resolver::p2p; use commonware_runtime::{BufferPooler, Clock, Metrics, Spawner}; use commonware_utils::channel::mpsc; use rand::Rng; +/// Receiver for inbound resolver messages. +pub type ResolverReceiver = mpsc::Receiver::Digest>>; + +/// Mailbox used to submit resolver requests. +pub type ResolverMailbox = p2p::Mailbox::Digest>, P>; + +/// Resolver channels returned by peer initialization. +pub type ResolverChannels = (ResolverReceiver, ResolverMailbox); + /// Initializes the p2p resolver with the given parameters. #[derive(Debug, Clone)] pub struct PeerInitializer; @@ -48,7 +57,7 @@ impl PeerInitializer { peer_provider: C, blocker: Bl, backfill: (S, R), - ) -> (mpsc::Receiver>, p2p::Mailbox, P>) + ) -> ResolverChannels where E: BufferPooler + Rng + Spawner + Clock + Metrics, P: PublicKey, diff --git a/crates/node/reporters/src/lib.rs b/crates/node/reporters/src/lib.rs index 3a07297..d998599 100644 --- a/crates/node/reporters/src/lib.rs +++ b/crates/node/reporters/src/lib.rs @@ -350,7 +350,7 @@ fn decode_tx_metadata(tx_bytes: &Bytes) -> Option { }) } -fn effective_gas_price(envelope: &TxEnvelope) -> u128 { +const fn effective_gas_price(envelope: &TxEnvelope) -> u128 { match envelope { TxEnvelope::Legacy(tx) => tx.tx().gas_price, TxEnvelope::Eip2930(tx) => tx.tx().gas_price, diff --git a/crates/node/rpc/src/eth.rs b/crates/node/rpc/src/eth.rs index 549fc65..8f85e26 100644 --- a/crates/node/rpc/src/eth.rs +++ b/crates/node/rpc/src/eth.rs @@ -518,7 +518,7 @@ fn raw_tx_to_pending_rpc(data: &Bytes) -> Result { }) } -fn transaction_type(envelope: &TxEnvelope) -> u64 { +const fn transaction_type(envelope: &TxEnvelope) -> u64 { match envelope { TxEnvelope::Legacy(_) => 0, TxEnvelope::Eip2930(_) => 1, @@ -528,7 +528,7 @@ fn transaction_type(envelope: &TxEnvelope) -> u64 { } } -fn effective_gas_price(envelope: &TxEnvelope) -> u128 { +const fn effective_gas_price(envelope: &TxEnvelope) -> u128 { match envelope { TxEnvelope::Legacy(tx) => tx.tx().gas_price, TxEnvelope::Eip2930(tx) => tx.tx().gas_price, @@ -538,7 +538,7 @@ fn effective_gas_price(envelope: &TxEnvelope) -> u128 { } } -fn max_fee_per_gas(envelope: &TxEnvelope) -> Option { +const fn max_fee_per_gas(envelope: &TxEnvelope) -> Option { match envelope { TxEnvelope::Legacy(_) | TxEnvelope::Eip2930(_) => None, TxEnvelope::Eip1559(tx) => Some(tx.tx().max_fee_per_gas), @@ -547,7 +547,7 @@ fn max_fee_per_gas(envelope: &TxEnvelope) -> Option { } } -fn max_priority_fee_per_gas(envelope: &TxEnvelope) -> Option { +const fn max_priority_fee_per_gas(envelope: &TxEnvelope) -> Option { match envelope { TxEnvelope::Legacy(_) | TxEnvelope::Eip2930(_) => None, TxEnvelope::Eip1559(tx) => Some(tx.tx().max_priority_fee_per_gas), diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index fdad5fd..edf1e9f 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -260,7 +260,10 @@ impl NodeRunner for ProductionRunner { debug!(?tx_id, "rpc submit: tx inserted into mempool"); Ok(()) } else { - warn!(?tx_id, "rpc submit: ledger.submit_tx returned false (duplicate or pool error)"); + warn!( + ?tx_id, + "rpc submit: ledger.submit_tx returned false (duplicate or pool error)" + ); Err(kora_rpc::RpcError::InvalidTransaction( "transaction rejected by mempool".to_string(), )) diff --git a/crates/node/txpool/src/ordering.rs b/crates/node/txpool/src/ordering.rs index c046f5a..5c35b62 100644 --- a/crates/node/txpool/src/ordering.rs +++ b/crates/node/txpool/src/ordering.rs @@ -82,7 +82,7 @@ pub struct SenderQueue { impl SenderQueue { /// Creates a new sender queue. #[must_use] - pub fn new(sender: Address, initial_nonce: u64) -> Self { + pub const fn new(sender: Address, initial_nonce: u64) -> Self { Self { sender, next_nonce: initial_nonce, pending: Vec::new(), queued: Vec::new() } } @@ -136,22 +136,22 @@ impl SenderQueue { } /// Returns the count of pending transactions. - pub fn pending_count(&self) -> usize { + pub const fn pending_count(&self) -> usize { self.pending.len() } /// Returns the count of queued transactions. - pub fn queued_count(&self) -> usize { + pub const fn queued_count(&self) -> usize { self.queued.len() } /// Returns the total count of transactions. - pub fn total_count(&self) -> usize { + pub const fn total_count(&self) -> usize { self.pending.len() + self.queued.len() } /// Returns true if the queue has no transactions. - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.pending.is_empty() && self.queued.is_empty() } } diff --git a/crates/node/txpool/src/pool.rs b/crates/node/txpool/src/pool.rs index e77a695..7ddf98a 100644 --- a/crates/node/txpool/src/pool.rs +++ b/crates/node/txpool/src/pool.rs @@ -330,10 +330,10 @@ impl Mempool for TransactionPool { } for sender in senders_to_check { - if let Some(queue) = inner.by_sender.get(&sender) { - if queue.is_empty() { - inner.by_sender.remove(&sender); - } + if let Some(queue) = inner.by_sender.get(&sender) + && queue.is_empty() + { + inner.by_sender.remove(&sender); } } diff --git a/crates/node/txpool/src/validator.rs b/crates/node/txpool/src/validator.rs index 09bcd94..82046df 100644 --- a/crates/node/txpool/src/validator.rs +++ b/crates/node/txpool/src/validator.rs @@ -171,7 +171,7 @@ fn recover_sender_and_hash(envelope: &TxEnvelope) -> Result<(Address, B256), TxP Ok((sender, hash)) } -fn effective_gas_price(envelope: &TxEnvelope) -> u128 { +const fn effective_gas_price(envelope: &TxEnvelope) -> u128 { match envelope { TxEnvelope::Legacy(tx) => tx.tx().gas_price, TxEnvelope::Eip2930(tx) => tx.tx().gas_price, @@ -532,11 +532,7 @@ mod tests { /// Sign `count` legacy txs with the same key, returning (sender, raw_txs[]). /// Each tx has a sequential nonce starting at `start_nonce`. - fn sign_legacy_burst( - chain_id: u64, - start_nonce: u64, - count: u64, - ) -> (Address, Vec) { + fn sign_legacy_burst(chain_id: u64, start_nonce: u64, count: u64) -> (Address, Vec) { let signing_key = SigningKey::random(&mut OsRng); let verifying_key = signing_key.verifying_key(); let pubkey = verifying_key.to_encoded_point(false); @@ -583,20 +579,14 @@ mod tests { let chain_id = 1u64; let (sender, raws) = sign_legacy_burst(chain_id, 0, 39); - let state = MockState::new() - .with_account(sender, 0, U256::from(10_000_000_000_000_000_000u128)); + let state = + MockState::new().with_account(sender, 0, U256::from(10_000_000_000_000_000_000u128)); let config = PoolConfig::default(); let validator = TransactionValidator::new(chain_id, state, config); for (i, raw) in raws.into_iter().enumerate() { let res = validator.validate(raw).await; - assert!( - res.is_ok(), - "tx {} (nonce={}) was rejected: {:?}", - i, - i, - res.err() - ); + assert!(res.is_ok(), "tx {} (nonce={}) was rejected: {:?}", i, i, res.err()); let v = res.unwrap(); assert_eq!(v.nonce, i as u64); } From a3e34dc835616a4b5c5dcc73784afdab02aee2dc Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Wed, 29 Apr 2026 22:54:05 +0200 Subject: [PATCH 14/16] feat(ledger): inherit parent state root for empty child snapshots - Added logic to return the parent snapshot's state root when the changeset is empty, preventing unnecessary state recomputation. - Introduced a new test to verify that an empty child inherits the state root from its parent after persistence, ensuring correct behavior in ledger operations. --- crates/node/ledger/src/lib.rs | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/crates/node/ledger/src/lib.rs b/crates/node/ledger/src/lib.rs index 13950a2..d2b3544 100644 --- a/crates/node/ledger/src/lib.rs +++ b/crates/node/ledger/src/lib.rs @@ -234,6 +234,13 @@ impl LedgerView { parent: ConsensusDigest, changes: QmdbChangeSet, ) -> LedgerResult { + if changes.is_empty() { + let inner = self.inner.lock().await; + let snapshot = + inner.snapshots.get(&parent).ok_or(ConsensusError::SnapshotNotFound(parent))?; + return Ok(snapshot.state_root); + } + let (changes, state) = { let inner = self.inner.lock().await; let changes = inner.snapshots.merged_changes(parent, changes)?; @@ -591,6 +598,51 @@ mod tests { }); } + #[test] + fn empty_child_inherits_parent_state_root_after_persist() { + // Tokio runtime required for WrapDatabaseAsync in the QMDB adapter. + let executor = tokio::Runner::default(); + executor.start(|context| async move { + // Arrange: create and persist a non-empty parent, matching the timing that can differ + // across validators during consensus. + let from_key = key_from_byte(FROM_BYTE_A); + let to_key = key_from_byte(TO_BYTE_A); + let from = Evm::address_from_key(&from_key); + let to = Evm::address_from_key(&to_key); + let setup = setup_ledger( + context, + "revm-ledger-empty-child", + vec![(from, U256::from(GENESIS_BALANCE)), (to, U256::ZERO)], + ) + .await; + let parent_snapshot = setup + .service + .parent_snapshot(setup.genesis_digest) + .await + .expect("genesis snapshot"); + let parent = build_block_snapshot( + &setup.service, + &setup.genesis, + parent_snapshot, + HEIGHT_ONE, + vec![transfer_tx(&from_key, to, TRANSFER_ONE, 0)], + ) + .await; + assert!(setup.ledger.persist_snapshot(parent.digest).await.expect("persist")); + + // Act: an empty child has no state transition and must not recompute a new QMDB root + // from local persistence metadata. + let empty_root = setup + .service + .compute_root(parent.digest, Default::default()) + .await + .expect("compute empty child root"); + + // Assert + assert_eq!(empty_root, parent.block.state_root); + }); + } + #[test] fn persist_snapshot_duplicate_is_noop() { // Tokio runtime required for WrapDatabaseAsync in the QMDB adapter. From 6491420b05ce20278f79a7da534c15a362b1bd5b Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Thu, 30 Apr 2026 16:41:54 +0200 Subject: [PATCH 15/16] feat(qmdb): add state transition root computation and runtime state cleanup - Introduced a new method in `StateRoot` for computing a deterministic consensus root based on a parent root and state transition changes. - Enhanced the `compute_root_from_store` function in `LedgerView` to utilize the new state transition logic. - Added a script to clear runtime state for Docker volumes, improving development workflow by ensuring clean state between runs. - Updated CLI and runner configurations to initialize the Tokio runtime with a specified storage directory. --- Cargo.lock | 1 + bin/kora/src/cli.rs | 5 ++- crates/node/consensus/src/execution.rs | 15 +++----- crates/node/ledger/Cargo.toml | 1 + crates/node/ledger/src/lib.rs | 20 ++++------- crates/node/runner/src/runner.rs | 4 ++- crates/node/service/src/service.rs | 8 +++-- crates/storage/qmdb/src/root.rs | 47 ++++++++++++++++++++++++++ docker/scripts/devnet-run.sh | 17 ++++++++++ 9 files changed, 89 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab7ec2a..8dc7718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3421,6 +3421,7 @@ dependencies = [ "kora-domain", "kora-executor", "kora-overlay", + "kora-qmdb", "kora-qmdb-ledger", "kora-traits", "thiserror 2.0.18", diff --git a/bin/kora/src/cli.rs b/bin/kora/src/cli.rs index 94122d9..e168afb 100644 --- a/bin/kora/src/cli.rs +++ b/bin/kora/src/cli.rs @@ -198,7 +198,10 @@ impl Cli { "Starting secondary peer" ); - let executor = commonware_runtime::tokio::Runner::default(); + let executor = commonware_runtime::tokio::Runner::new( + commonware_runtime::tokio::Config::default() + .with_storage_directory(config.data_dir.join("runtime")), + ); executor.start(|context| async move { let mut transport = config .network diff --git a/crates/node/consensus/src/execution.rs b/crates/node/consensus/src/execution.rs index 6363246..0ec7f1d 100644 --- a/crates/node/consensus/src/execution.rs +++ b/crates/node/consensus/src/execution.rs @@ -1,7 +1,7 @@ //! Shared block execution helpers. use alloy_primitives::Bytes; -use kora_domain::{StateRoot, Tx}; +use kora_domain::Tx; use kora_executor::{BlockContext, BlockExecutor, ExecutionOutcome}; use kora_traits::StateDb; @@ -12,15 +12,13 @@ use crate::{ConsensusError, Snapshot}; pub struct BlockExecution { /// Execution outcome, including changes and receipts. pub outcome: ExecutionOutcome, - /// Computed state root after applying the execution changes. - pub state_root: StateRoot, } impl BlockExecution { /// Execute a block's transactions against a parent snapshot. /// - /// This helper runs the executor, computes the new state root, and returns the - /// execution outcome for callers to persist or cache. + /// This helper runs the executor and returns the execution outcome for callers to + /// compute deterministic consensus roots, persist state, or cache snapshots. pub async fn execute( parent_snapshot: &Snapshot, executor: &E, @@ -35,11 +33,6 @@ impl BlockExecution { let outcome = executor .execute(&parent_snapshot.state, context, &txs_bytes) .map_err(|e| ConsensusError::Execution(e.to_string()))?; - let state_root = parent_snapshot - .state - .compute_root(&outcome.changes) - .await - .map_err(ConsensusError::StateDb)?; - Ok(Self { outcome, state_root: StateRoot(state_root) }) + Ok(Self { outcome }) } } diff --git a/crates/node/ledger/Cargo.toml b/crates/node/ledger/Cargo.toml index 5407a3e..ac149cd 100644 --- a/crates/node/ledger/Cargo.toml +++ b/crates/node/ledger/Cargo.toml @@ -15,6 +15,7 @@ workspace = true kora-consensus = { path = "../consensus" } kora-domain = { path = "../domain" } kora-overlay = { path = "../../storage/overlay" } +kora-qmdb = { path = "../../storage/qmdb" } kora-qmdb-ledger = { path = "../../storage/qmdb-ledger" } kora-traits = { path = "../../storage/traits" } diff --git a/crates/node/ledger/src/lib.rs b/crates/node/ledger/src/lib.rs index d2b3544..d19d44d 100644 --- a/crates/node/ledger/src/lib.rs +++ b/crates/node/ledger/src/lib.rs @@ -19,8 +19,9 @@ use kora_domain::{ Block, BlockId, ConsensusDigest, LedgerEvent, LedgerEvents, StateRoot, Tx, TxId, }; use kora_overlay::OverlayState; +use kora_qmdb::StateRoot as QmdbStateRoot; use kora_qmdb_ledger::{Error as QmdbError, QmdbChangeSet, QmdbConfig, QmdbLedger, QmdbState}; -use kora_traits::{StateDbError, StateDbRead, StateDbWrite}; +use kora_traits::{StateDbError, StateDbRead}; use thiserror::Error; /// Snapshot type used by the ledger. @@ -228,26 +229,17 @@ impl LedgerView { self.compute_root_from_store(parent, changes).await } - /// Compute a root using the persisted QMDB store plus any pending changes. + /// Compute the deterministic consensus root for a state transition. pub async fn compute_root_from_store( &self, parent: ConsensusDigest, changes: QmdbChangeSet, ) -> LedgerResult { - if changes.is_empty() { + let parent_root = { let inner = self.inner.lock().await; - let snapshot = - inner.snapshots.get(&parent).ok_or(ConsensusError::SnapshotNotFound(parent))?; - return Ok(snapshot.state_root); - } - - let (changes, state) = { - let inner = self.inner.lock().await; - let changes = inner.snapshots.merged_changes(parent, changes)?; - (changes, inner.qmdb.state()) + inner.snapshots.get(&parent).ok_or(ConsensusError::SnapshotNotFound(parent))?.state_root }; - let root = state.compute_root(&changes).await?; - Ok(StateRoot(root)) + Ok(StateRoot(QmdbStateRoot::transition(parent_root.0, &changes))) } /// Persist `digest` and any missing ancestors to QMDB. diff --git a/crates/node/runner/src/runner.rs b/crates/node/runner/src/runner.rs index edf1e9f..f6cc161 100644 --- a/crates/node/runner/src/runner.rs +++ b/crates/node/runner/src/runner.rs @@ -175,7 +175,9 @@ impl ProductionRunner { use commonware_runtime::Runner; use kora_transport::NetworkConfigExt; - let executor = tokio::Runner::default(); + let executor = tokio::Runner::new( + tokio::Config::default().with_storage_directory(config.data_dir.join("runtime")), + ); executor.start(|context| async move { let validator_key = config .validator_key() diff --git a/crates/node/service/src/service.rs b/crates/node/service/src/service.rs index 13174c1..64b3aa4 100644 --- a/crates/node/service/src/service.rs +++ b/crates/node/service/src/service.rs @@ -55,7 +55,9 @@ where R::Error: Into, T::Error: Into, { - let executor = tokio::Runner::default(); + let executor = tokio::Runner::new( + tokio::Config::default().with_storage_directory(self.config.data_dir.join("runtime")), + ); executor.start(|context| async move { self.run_with_context(context).await }) } @@ -94,7 +96,9 @@ impl LegacyNodeService { /// Run the legacy node service. pub fn run(self) -> eyre::Result<()> { - let executor = tokio::Runner::default(); + let executor = tokio::Runner::new( + tokio::Config::default().with_storage_directory(self.config.data_dir.join("runtime")), + ); executor.start(|context| async move { self.run_with_context(context).await }) } diff --git a/crates/storage/qmdb/src/root.rs b/crates/storage/qmdb/src/root.rs index 63d13b7..b6008c5 100644 --- a/crates/storage/qmdb/src/root.rs +++ b/crates/storage/qmdb/src/root.rs @@ -2,7 +2,10 @@ use alloy_primitives::{B256, keccak256}; +use crate::ChangeSet; + const KORA_ROOT_NAMESPACE: &[u8] = b"_KORA_QMDB_ROOT"; +const KORA_TRANSITION_ROOT_NAMESPACE: &[u8] = b"_KORA_STATE_TRANSITION_ROOT"; /// State root computation utility. #[derive(Debug, Clone, Copy)] @@ -18,6 +21,44 @@ impl StateRoot { buf.extend_from_slice(code_root.as_slice()); keccak256(buf) } + + /// Compute a deterministic consensus root from a parent root and state transition. + pub fn transition(parent_root: B256, changes: &ChangeSet) -> B256 { + if changes.is_empty() { + return parent_root; + } + + let mut buf = Vec::new(); + buf.extend_from_slice(KORA_TRANSITION_ROOT_NAMESPACE); + buf.extend_from_slice(parent_root.as_slice()); + buf.extend_from_slice(&(changes.accounts.len() as u64).to_be_bytes()); + + for (address, update) in &changes.accounts { + buf.extend_from_slice(address.as_slice()); + buf.push(u8::from(update.created)); + buf.push(u8::from(update.selfdestructed)); + buf.extend_from_slice(&update.nonce.to_be_bytes()); + buf.extend_from_slice(&update.balance.to_be_bytes::<32>()); + buf.extend_from_slice(update.code_hash.as_slice()); + + match &update.code { + Some(code) => { + buf.push(1); + buf.extend_from_slice(&(code.len() as u64).to_be_bytes()); + buf.extend_from_slice(code); + } + None => buf.push(0), + } + + buf.extend_from_slice(&(update.storage.len() as u64).to_be_bytes()); + for (slot, value) in &update.storage { + buf.extend_from_slice(&slot.to_be_bytes::<32>()); + buf.extend_from_slice(&value.to_be_bytes::<32>()); + } + } + + keccak256(buf) + } } #[cfg(test)] @@ -41,4 +82,10 @@ mod tests { let root2 = StateRoot::compute(B256::repeat_byte(1), B256::ZERO, B256::ZERO); assert_ne!(root1, root2); } + + #[test] + fn empty_transition_keeps_parent_root() { + let parent = B256::repeat_byte(0x42); + assert_eq!(StateRoot::transition(parent, &ChangeSet::new()), parent); + } } diff --git a/docker/scripts/devnet-run.sh b/docker/scripts/devnet-run.sh index b0890eb..de95495 100755 --- a/docker/scripts/devnet-run.sh +++ b/docker/scripts/devnet-run.sh @@ -159,6 +159,19 @@ clear_dkg_outputs() { done } +clear_runtime_state() { + for volume in \ + kora-devnet_data_node0 \ + kora-devnet_data_node1 \ + kora-devnet_data_node2 \ + kora-devnet_data_node3 \ + kora-devnet_data_secondary0; do + docker volume inspect "$volume" >/dev/null 2>&1 || continue + docker run --rm -v "${volume}:/data" alpine \ + rm -rf /data/runtime >/dev/null 2>&1 || true + done +} + cd "$(dirname "$0")/.." print_header @@ -293,6 +306,10 @@ echo "" # Phase 2: Validators and secondary peers print_phase "2/3" "Starting validators and secondary peers" +docker compose -f compose/devnet.yaml stop \ + validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 >/dev/null 2>&1 || true +clear_runtime_state + run_with_spinner "Launching validator and secondary containers..." docker compose -f compose/devnet.yaml ${COMPOSE_PROFILES:+--profile observability} up -d \ validator-node0 validator-node1 validator-node2 validator-node3 secondary-node0 \ ${COMPOSE_PROFILES:+prometheus grafana} From e5b8a533414076366944edd1a18580d6a3ff52f9 Mon Sep 17 00:00:00 2001 From: "jacobgadikian@gmail.com" Date: Thu, 30 Apr 2026 17:07:00 +0200 Subject: [PATCH 16/16] refactor(setup): simplify genesis allocations in setup.rs - Reduced the number of initial GenesisAllocations from seven to one, streamlining the setup process. - Updated the allocation for the address "0x0000000000000000000000000000000000000001" to maintain a balance of "1000000000000000000000000". --- bin/keygen/src/setup.rs | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/bin/keygen/src/setup.rs b/bin/keygen/src/setup.rs index 3e805e9..0f3f286 100644 --- a/bin/keygen/src/setup.rs +++ b/bin/keygen/src/setup.rs @@ -158,36 +158,10 @@ pub(crate) fn run(args: SetupArgs) -> Result<()> { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs(), - allocations: vec![ - GenesisAllocation { - address: "0x0000000000000000000000000000000000000001".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0xEb1Ba7Fc58b3416361a0EE07d140c91410c0AA8c".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0xa883208a74152107475a3Fa6b0c21121894B647F".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0x105be5081ceba05be11976150abc277ee365fc3f".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0x30b68d56AE9173566055a69ee7cCB0E755B6a201".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0xDdE169289B51C512268D0b11EE2b15160b1e1793".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - GenesisAllocation { - address: "0xde738C4084dDE5083A7959235Fd230e27eAFC63B".to_string(), - balance: "1000000000000000000000000".to_string(), - }, - ], + allocations: vec![GenesisAllocation { + address: "0x0000000000000000000000000000000000000001".to_string(), + balance: "1000000000000000000000000".to_string(), + }], }; let genesis_path = args.output_dir.join("genesis.json"); fs::write(&genesis_path, serde_json::to_string_pretty(&genesis)?)?;