From e6631f201fa74a431f7198f66b5f041c97b9140f Mon Sep 17 00:00:00 2001 From: Ollie202 Date: Mon, 25 May 2026 17:40:56 +0100 Subject: [PATCH] fix(rpc): move submit proof checks off async runtime --- CHANGELOG.md | 1 + crates/rpc/src/server/api.rs | 44 +++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c4ac070..3944518a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - [BREAKING] Removed `CheckNullifiers` endpoint ([#2049](https://github.com/0xMiden/node/pull/2049)). - Replaced blocking-in-async operations in the validator, remote prover, and ntx-builder with `spawn_blocking` to avoid starving the Tokio runtime ([#2041](https://github.com/0xMiden/node/pull/2041)). - Replaced local store block proving with `spawn_blocking` to avoid starving the Tokio runtime ([#1976](https://github.com/0xMiden/node/issues/1976)). +- Replaced blocking proof verification in RPC transaction submission endpoints with `spawn_blocking` to avoid starving the Tokio runtime ([#1976](https://github.com/0xMiden/node/issues/1976)). - Implemented persistent RocksDB backend for `AccountStateForest`, improving startup time ([#2020](https://github.com/0xMiden/node/pull/2020)). - [BREAKING] Replaced binding URL env vars and CLI flags with listen socket addresses ([#2054](https://github.com/0xMiden/node/pull/2054)). - [BREAKING] `BlockRange.block_to` is now required for all RPC endpoints ([#2056](https://github.com/0xMiden/node/pull/2056)). diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 2aa5ea9cc..84189395a 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -27,6 +27,7 @@ use miden_node_utils::limiter::{ QueryParamStorageMapKeyTotalLimit, }; use miden_node_utils::lru_cache::LruCache; +use miden_node_utils::spawn::spawn_blocking_in_current_span; use miden_node_utils::tracing::OpenTelemetrySpanExt; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber}; @@ -526,14 +527,20 @@ impl api_server::Api for RpcService { )); } - let tx_verifier = TransactionVerifier::new(MIN_PROOF_SECURITY_LEVEL); - tx_verifier.verify(&tx).map_err(|err| { - Status::invalid_argument(format!( - "Invalid proof for transaction {}: {}", - tx.id(), - err.as_report() - )) - })?; + let tx_id = tx.id(); + spawn_blocking_in_current_span(move || { + TransactionVerifier::new(MIN_PROOF_SECURITY_LEVEL).verify(&tx).map_err(|err| { + Status::invalid_argument(format!( + "Invalid proof for transaction {}: {}", + tx_id, + err.as_report() + )) + }) + }) + .await + .map_err(|err| { + Status::internal(format!("transaction proof verification task failed: {err}")) + })??; // Transaction inputs must be provided in order to allow for transaction re-execution via // the Validator. @@ -613,11 +620,22 @@ impl api_server::Api for RpcService { // // Need to do this because ProvenBatch has no real kernel yet, so we can only // really check that the calculated proof matches the one given in the request. - let expected_proof = LocalBatchProver::new(MIN_PROOF_SECURITY_LEVEL) - .prove(proposed_batch.clone()) - .map_err(|err| { - Status::invalid_argument(err.as_report_context("proposed block proof failed")) - })?; + let expected_proof = spawn_blocking_in_current_span({ + let proposed_batch = proposed_batch.clone(); + move || { + LocalBatchProver::new(MIN_PROOF_SECURITY_LEVEL).prove(proposed_batch).map_err( + |err| { + Status::invalid_argument( + err.as_report_context("proposed block proof failed"), + ) + }, + ) + } + }) + .await + .map_err(|err| { + Status::internal(format!("batch proof verification task failed: {err}")) + })??; if expected_proof != proven_batch { return Err(Status::invalid_argument("batch proof did not match proposed batch"));