From 502fc8c1a5148a77890d74caa553923f50d6c7a8 Mon Sep 17 00:00:00 2001 From: sergerad Date: Tue, 31 Mar 2026 12:05:13 +1300 Subject: [PATCH 1/5] Add proof to get block endpoint --- crates/rpc/src/server/api.rs | 4 ++-- crates/store/src/blocks.rs | 8 ++++++++ crates/store/src/server/rpc_api.rs | 21 +++++++++++++-------- crates/store/src/state/mod.rs | 11 +++++++++++ proto/proto/internal/store.proto | 4 ++-- proto/proto/rpc.proto | 4 ++-- proto/proto/types/blockchain.proto | 18 ++++++++++++++++++ 7 files changed, 56 insertions(+), 14 deletions(-) diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index f819054a2..f06efd021 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -233,8 +233,8 @@ impl api_server::Api for RpcService { async fn get_block_by_number( &self, - request: Request, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { let request = request.into_inner(); debug!(target: COMPONENT, ?request); diff --git a/crates/store/src/blocks.rs b/crates/store/src/blocks.rs index ad34ff0dd..4678e7e4e 100644 --- a/crates/store/src/blocks.rs +++ b/crates/store/src/blocks.rs @@ -96,6 +96,14 @@ impl BlockStore { fs_err::write(block_path, data) } + pub async fn load_proof(&self, block_num: BlockNumber) -> std::io::Result>> { + match tokio::fs::read(self.proof_path(block_num)).await { + Ok(data) => Ok(Some(data)), + Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(err) => Err(err), + } + } + // PROOF STORAGE // -------------------------------------------------------------------------------------------- diff --git a/crates/store/src/server/rpc_api.rs b/crates/store/src/server/rpc_api.rs index 2ef33295e..35fb18608 100644 --- a/crates/store/src/server/rpc_api.rs +++ b/crates/store/src/server/rpc_api.rs @@ -237,19 +237,24 @@ impl rpc_server::Rpc for StoreApi { async fn get_block_by_number( &self, - request: Request, - ) -> Result, Status> { + request: Request, + ) -> Result, Status> { let request = request.into_inner(); debug!(target: COMPONENT, ?request); - let block = self - .state - .load_block(request.block_num.into()) - .await - .map_err(GetBlockByNumberError::from)?; + // Load block from state. + let block_num = BlockNumber::from(request.block_num); + let block = self.state.load_block(block_num).await.map_err(GetBlockByNumberError::from)?; + + // Load proof from state. + let proof = if request.include_proof.unwrap_or(false) { + self.state.load_proof(block_num).await.map_err(GetBlockByNumberError::from)? + } else { + None + }; - Ok(Response::new(proto::blockchain::MaybeBlock { block })) + Ok(Response::new(proto::blockchain::GetBlockByNumberResponse { block, proof })) } async fn get_account( diff --git a/crates/store/src/state/mod.rs b/crates/store/src/state/mod.rs index 418c890de..91b1706d0 100644 --- a/crates/store/src/state/mod.rs +++ b/crates/store/src/state/mod.rs @@ -842,6 +842,17 @@ impl State { self.block_store.load_block(block_num).await.map_err(Into::into) } + /// Loads a block proof from the block store. Returns `Ok(None)` if the proof is not found. + pub async fn load_proof( + &self, + block_num: BlockNumber, + ) -> Result>, DatabaseError> { + if block_num > self.latest_block_num().await { + return Ok(None); + } + self.block_store.load_proof(block_num).await.map_err(Into::into) + } + /// Returns the latest block number. pub async fn latest_block_num(&self) -> BlockNumber { self.inner.read().await.latest_block_num() diff --git a/proto/proto/internal/store.proto b/proto/proto/internal/store.proto index 311c1482d..b13ac2368 100644 --- a/proto/proto/internal/store.proto +++ b/proto/proto/internal/store.proto @@ -34,8 +34,8 @@ service Rpc { // Returns the latest details the specified account. rpc GetAccount(rpc.AccountRequest) returns (rpc.AccountResponse) {} - // Returns raw block data for the specified block number. - rpc GetBlockByNumber(blockchain.BlockNumber) returns (blockchain.MaybeBlock) {} + // Returns raw block data for the specified block number, optionally including the block proof. + rpc GetBlockByNumber(blockchain.GetBlockByNumberRequest) returns (blockchain.GetBlockByNumberResponse) {} // Retrieves block header by given block number. Optionally, it also returns the MMR path // and current chain length to authenticate the block's inclusion. diff --git a/proto/proto/rpc.proto b/proto/proto/rpc.proto index 2823083d5..762134471 100644 --- a/proto/proto/rpc.proto +++ b/proto/proto/rpc.proto @@ -40,8 +40,8 @@ service Api { // Returns the latest details of the specified account. rpc GetAccount(AccountRequest) returns (AccountResponse) {} - // Returns raw block data for the specified block number. - rpc GetBlockByNumber(blockchain.BlockNumber) returns (blockchain.MaybeBlock) {} + // Returns raw block data for the specified block number, optionally including the block proof. + rpc GetBlockByNumber(blockchain.GetBlockByNumberRequest) returns (blockchain.GetBlockByNumberResponse) {} // Retrieves block header by given block number. Optionally, it also returns the MMR path // and current chain length to authenticate the block's inclusion. diff --git a/proto/proto/types/blockchain.proto b/proto/proto/types/blockchain.proto index e87a3648d..b0ae8c8ba 100644 --- a/proto/proto/types/blockchain.proto +++ b/proto/proto/types/blockchain.proto @@ -28,6 +28,24 @@ message MaybeBlock { optional bytes block = 1; } +// Request for retrieving a block by its number, optionally including the block proof. +message GetBlockByNumberRequest { + // The block number of the target block. + fixed32 block_num = 1; + // Whether to include the block proof in the response. + optional bool include_proof = 2; +} + +// Response containing the block data and optionally its proof. +message GetBlockByNumberResponse { + // The requested block data encoded using [miden_serde_utils::Serializable] implementation for + // [miden_protocol::block::Block]. + optional bytes block = 1; + // The block proof encoded using [miden_serde_utils::Serializable] implementation for + // [miden_protocol::block::BlockProof], if requested and available. + optional bytes proof = 2; +} + // Represents a block number. message BlockNumber { // The block number of the target block. From 5021742e56bd816d1ccea9f8f84f2f2cbe3fb061 Mon Sep 17 00:00:00 2001 From: sergerad Date: Tue, 31 Mar 2026 12:16:43 +1300 Subject: [PATCH 2/5] doc string --- crates/store/src/blocks.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/store/src/blocks.rs b/crates/store/src/blocks.rs index 4678e7e4e..dfb4d7e47 100644 --- a/crates/store/src/blocks.rs +++ b/crates/store/src/blocks.rs @@ -1,3 +1,12 @@ +//! File-based storage for raw block data and block proofs. +//! +//! Block data is stored under `{store_dir}/{epoch:04x}/block_{block_num:08x}.dat`, and proof data +//! for proven blocks is stored under `{store_dir}/{epoch:04x}/proof_{block_num:08x}.dat`. +//! +//! The epoch is derived from the 16 most significant bits of the block number (i.e., +//! `block_num >> 16`), and both the epoch and block number are formatted as zero-padded +//! hexadecimal strings. + use std::io::ErrorKind; use std::ops::Not; use std::path::PathBuf; From 8138222eb0acf6c3a31f0f253c5806f6806ce2b6 Mon Sep 17 00:00:00 2001 From: sergerad Date: Tue, 31 Mar 2026 12:18:54 +1300 Subject: [PATCH 3/5] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9926c5631..b0a981236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Added per-IP gRPC rate limiting across services as well as global concurrent connection limit ([#1746](https://github.com/0xMiden/node/issues/1746)). - Added limit to execution cycles for a transaction network, configurable through CLI args (`--ntx-builder.max-tx-cycles`) ([#1801](https://github.com/0xMiden/node/issues/1801)). - Added monitor version and network name to the network monitor dashboard, network name is configurable via `--network-name` / `MIDEN_MONITOR_NETWORK_NAME` ([#1838](https://github.com/0xMiden/node/pull/1838)). +- [BREAKING] Changed `GetBlockByNumber` to accept a `GetBlockByNumberRequest` (with optional `include_proof` flag) and returns a `GetBlockByNumberResponse` containing the block and an optional block proof ([#1864](https://github.com/0xMiden/node/pull/1864)). ### Changes From fc288a24cf1aaae60ce6780d44d7e5a743db8bef Mon Sep 17 00:00:00 2001 From: sergerad Date: Tue, 31 Mar 2026 12:19:11 +1300 Subject: [PATCH 4/5] Move changelog line --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a981236..631a4c470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.15.0 (TBD) + +- [BREAKING] Changed `GetBlockByNumber` to accept a `GetBlockByNumberRequest` (with optional `include_proof` flag) and returns a `GetBlockByNumberResponse` containing the block and an optional block proof ([#1864](https://github.com/0xMiden/node/pull/1864)). + ## v0.14.0 (TBD) ### Enhancements @@ -21,7 +25,6 @@ - Added per-IP gRPC rate limiting across services as well as global concurrent connection limit ([#1746](https://github.com/0xMiden/node/issues/1746)). - Added limit to execution cycles for a transaction network, configurable through CLI args (`--ntx-builder.max-tx-cycles`) ([#1801](https://github.com/0xMiden/node/issues/1801)). - Added monitor version and network name to the network monitor dashboard, network name is configurable via `--network-name` / `MIDEN_MONITOR_NETWORK_NAME` ([#1838](https://github.com/0xMiden/node/pull/1838)). -- [BREAKING] Changed `GetBlockByNumber` to accept a `GetBlockByNumberRequest` (with optional `include_proof` flag) and returns a `GetBlockByNumberResponse` containing the block and an optional block proof ([#1864](https://github.com/0xMiden/node/pull/1864)). ### Changes From 661d6c464641ddd1cd3740774f8a1324cc590f9c Mon Sep 17 00:00:00 2001 From: Serge Radinovich <47865535+sergerad@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:18:08 +1300 Subject: [PATCH 5/5] unwrap_or_default Co-authored-by: Mirko <48352201+Mirko-von-Leipzig@users.noreply.github.com> --- crates/store/src/server/rpc_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/store/src/server/rpc_api.rs b/crates/store/src/server/rpc_api.rs index 35fb18608..d5a913673 100644 --- a/crates/store/src/server/rpc_api.rs +++ b/crates/store/src/server/rpc_api.rs @@ -248,7 +248,7 @@ impl rpc_server::Rpc for StoreApi { let block = self.state.load_block(block_num).await.map_err(GetBlockByNumberError::from)?; // Load proof from state. - let proof = if request.include_proof.unwrap_or(false) { + let proof = if request.include_proof.unwrap_or_default() { self.state.load_proof(block_num).await.map_err(GetBlockByNumberError::from)? } else { None