diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c95207b..0e39affc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Added `StoreReplica` gRPC service with endpoints for streaming blocks and proofs ([#1987](https://github.com/0xMiden/node/pull/1987)). - Replaced the network monitor's JavaScript dashboard with a server-rendered Maud + HTMX frontend ([#2024](https://github.com/0xMiden/node/pull/2024)). - [BREAKING] Removed `CheckNullifiers` endpoint ([#2049](https://github.com/0xMiden/node/pull/2049)). +- Added RPC validation to reject submitted batches that exceed protocol account or note budgets ([#2037](https://github.com/0xMiden/node/issues/2037)). ## v0.14.10 (2026-05-29) diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index c194ac6f4..b27938dae 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -37,7 +37,13 @@ use miden_protocol::transaction::{ TxAccountUpdate, }; use miden_protocol::utils::serde::{Deserializable, Serializable}; -use miden_protocol::{MIN_PROOF_SECURITY_LEVEL, Word}; +use miden_protocol::{ + MAX_ACCOUNTS_PER_BATCH, + MAX_INPUT_NOTES_PER_BATCH, + MAX_OUTPUT_NOTES_PER_BATCH, + MIN_PROOF_SECURITY_LEVEL, + Word, +}; use miden_tx::TransactionVerifier; use miden_tx_batch_prover::LocalBatchProver; use tonic::{IntoRequest, Request, Response, Status}; @@ -565,6 +571,8 @@ impl api_server::Api for RpcService { ))); } + validate_batch_budget(&proposed_batch)?; + // Only allow deployment transactions for new network accounts. for tx in proposed_batch.transactions() { if tx.account_id().is_network() @@ -716,6 +724,31 @@ fn strip_output_note_decorators<'a>( }) } +fn validate_batch_budget(batch: &ProposedBatch) -> Result<(), Status> { + let accounts = batch.transactions().len(); + let input_notes = batch + .transactions() + .iter() + .map(|tx| tx.input_notes().num_notes() as usize) + .sum::(); + let output_notes = batch + .transactions() + .iter() + .map(|tx| tx.output_notes().num_notes()) + .sum::(); + + if accounts > MAX_ACCOUNTS_PER_BATCH + || input_notes > MAX_INPUT_NOTES_PER_BATCH + || output_notes > MAX_OUTPUT_NOTES_PER_BATCH + { + return Err(Status::invalid_argument(format!( + "batch exceeds batch budget: max {MAX_ACCOUNTS_PER_BATCH} account updates, {MAX_INPUT_NOTES_PER_BATCH} input notes, and {MAX_OUTPUT_NOTES_PER_BATCH} output notes" + ))); + } + + Ok(()) +} + // LIMIT HELPERS // ================================================================================================