diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f636deb4..602fd9bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Implemented storage map `DataStore` function ([#1226](https://github.com/0xMiden/miden-node/pull/1226)). - [BREAKING] Renamed `RemoteProverProxy` to `RemoteProverClient` ([#1236](https://github.com/0xMiden/miden-node/pull/1236)). - Added pagination to `SyncNotes` endpoint ([#1257](https://github.com/0xMiden/miden-node/pull/1257)). +- [BREAKING] Response type nuances of `GetAccountProof` in the public store API (#[1277](https://github.com/0xMiden/miden-node/pull/1277)). ## v0.11.2 (2025-09-10) diff --git a/crates/proto/src/domain/account.rs b/crates/proto/src/domain/account.rs index 825fd759f..494f16671 100644 --- a/crates/proto/src/domain/account.rs +++ b/crates/proto/src/domain/account.rs @@ -200,9 +200,9 @@ impl TryFrom entries + // Extract map_entries from the MapEntries message + let map_entries = if let Some(entries) = entries { + entries .entries .into_iter() .map(|entry| { @@ -220,8 +220,9 @@ impl TryFrom, ConversionError>>()?, - None => Vec::new(), + .collect::, ConversionError>>()? + } else { + Vec::new() }; Ok(Self { @@ -368,12 +369,14 @@ impl AccountVaultDetails { } } } + pub fn empty() -> Self { Self { too_many_assets: false, assets: Vec::new(), } } + fn too_many() -> Self { Self { too_many_assets: true, @@ -424,31 +427,34 @@ impl AccountStorageMapDetails { const MAX_RETURN_ENTRIES: usize = 1000; pub fn new(slot_index: u8, slot_data: SlotData, storage_map: &StorageMap) -> Self { - let exceeds = if let SlotData::MapKeys(keys) = &slot_data { - keys.len() > Self::MAX_RETURN_ENTRIES - } else { - storage_map.entries().nth(Self::MAX_RETURN_ENTRIES).is_some() - }; - if exceeds { + match slot_data { + SlotData::All => Self::from_all_entries(slot_index, storage_map), + SlotData::MapKeys(keys) => Self::from_specific_keys(slot_index, &keys[..], storage_map), + } + } + + fn from_all_entries(slot_index: u8, storage_map: &StorageMap) -> Self { + if storage_map.num_entries() > Self::MAX_RETURN_ENTRIES { Self::too_many_entries(slot_index) } else { - Self::from_slot_data(slot_index, slot_data, storage_map) + let map_entries = Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v))); + Self { + slot_index, + too_many_entries: false, + map_entries, + } } } - pub fn from_slot_data(slot_index: u8, slot_data: SlotData, storage_map: &StorageMap) -> Self { - let map_entries = match slot_data { - SlotData::All => Vec::from_iter(storage_map.entries().map(|(k, v)| (*k, *v))), - SlotData::MapKeys(keys) => { - Vec::from_iter(keys.iter().map(|key| (*key, storage_map.get(key)))) - }, - }; - Self { - slot_index, - too_many_entries: false, - map_entries, + fn from_specific_keys(slot_index: u8, keys: &[Word], storage_map: &StorageMap) -> Self { + if keys.len() > Self::MAX_RETURN_ENTRIES { + Self::too_many_entries(slot_index) + } else { + // TODO For now, we return all entries instead of specific keys with proofs + Self::from_all_entries(slot_index, storage_map) } } + pub fn too_many_entries(slot_index: u8) -> Self { Self { slot_index, @@ -611,74 +617,31 @@ impl From for proto::rpc_store::account_proof_response::AccountD } } -// ACCOUNT PROOF impl From for proto::rpc_store::account_storage_details::AccountStorageMapDetails { fn from(value: AccountStorageMapDetails) -> Self { + use proto::rpc_store::account_storage_details::account_storage_map_details; + let AccountStorageMapDetails { slot_index, too_many_entries, map_entries, } = value; - // Create the entries wrapped in MapEntries message - let entries = proto::rpc_store::account_storage_details::account_storage_map_details::MapEntries { - entries: map_entries - .into_iter() - .map(|(key, value)| { - proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry { - key: Some(key.into()), - value: Some(value.into()), - } - }) - .collect(), - }; + let entries = Some(account_storage_map_details::MapEntries { + entries: Vec::from_iter(map_entries.into_iter().map(|(key, value)| { + account_storage_map_details::map_entries::StorageMapEntry { + key: Some(key.into()), + value: Some(value.into()), + } + })), + }); Self { slot_index: u32::from(slot_index), too_many_entries, - entries: Some(entries), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct StorageMapEntry { - pub key: Word, - pub value: Word, -} - -impl - TryFrom - for StorageMapEntry -{ - type Error = ConversionError; - - fn try_from( - value: proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry, - ) -> Result { - let proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry { - key, - value - } = value; - - Ok(Self { - key: key.ok_or(proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry::missing_field(stringify!(key)))?.try_into()?, - value: value.ok_or(proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry::missing_field(stringify!(value)))?.try_into()?, - }) - } -} - -impl From - for proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry -{ - fn from(value: StorageMapEntry) -> Self { - let StorageMapEntry { key, value } = value; - - Self { - key: Some(proto::primitives::Digest::from(key)), - value: Some(proto::primitives::Digest::from(value)), + entries, } } } diff --git a/crates/proto/src/generated/rpc_store.rs b/crates/proto/src/generated/rpc_store.rs index da0af6342..5f6f05816 100644 --- a/crates/proto/src/generated/rpc_store.rs +++ b/crates/proto/src/generated/rpc_store.rs @@ -30,11 +30,13 @@ pub mod account_proof_request { /// Request the details for a public account. #[derive(Clone, PartialEq, ::prost::Message)] pub struct AccountDetailRequest { - /// Last known code commitment to the client. The response will include account code - /// only if its commitment is different from this value or if the value is not present. + /// Last known code commitment to the requester. The response will include account code + /// only if its commitment is different from this value. + /// + /// If the field is ommiteed, the response will not include the account code. #[prost(message, optional, tag = "1")] pub code_commitment: ::core::option::Option, - /// Last known asset vault commitment to the client. The response will include asset vault data + /// Last known asset vault commitment to the requester. The response will include asset vault data /// only if its commitment is different from this value. If the value is not present in the /// request, the response will not contain one either. /// If the number of to-be-returned asset entries exceed a threshold, they have to be requested @@ -107,7 +109,8 @@ pub mod account_proof_response { /// Account code; empty if code commitments matched or none was requested #[prost(bytes = "vec", optional, tag = "3")] pub code: ::core::option::Option<::prost::alloc::vec::Vec>, - /// Account asset vault data; empty if vault commitments matched + /// Account asset vault data; empty if vault commitments matched or the requester + /// omitted it in the request. #[prost(message, optional, tag = "4")] pub vault_details: ::core::option::Option, } @@ -231,12 +234,12 @@ pub mod sync_nullifiers_response { } /// State synchronization request. /// -/// Specifies state updates the client is interested in. The server will return the first block which +/// Specifies state updates the requester is interested in. The server will return the first block which /// contains a note matching `note_tags` or the chain tip. And the corresponding updates to /// `account_ids` for that block range. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncStateRequest { - /// Last block known by the client. The response will contain data starting from the next block, + /// Last block known by the requester. The response will contain data starting from the next block, /// until the first block which contains a note of matching the requested tag, or the chain tip /// if there are no notes. #[prost(fixed32, tag = "1")] @@ -248,7 +251,7 @@ pub struct SyncStateRequest { /// it won't be included in the response. #[prost(message, repeated, tag = "2")] pub account_ids: ::prost::alloc::vec::Vec, - /// Specifies the tags which the client is interested in. + /// Specifies the tags which the requester is interested in. #[prost(fixed32, repeated, tag = "3")] pub note_tags: ::prost::alloc::vec::Vec, } @@ -277,7 +280,7 @@ pub struct SyncStateResponse { } /// Account vault synchronization request. /// -/// Allows clients to sync asset values for specific public accounts within a block range. +/// Allows requesters to sync asset values for specific public accounts within a block range. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncAccountVaultRequest { /// Block range from which to start synchronizing. @@ -317,14 +320,14 @@ pub struct AccountVaultUpdate { } /// Note synchronization request. /// -/// Specifies note tags that client is interested in. The server will return the first block which +/// Specifies note tags that requester is interested in. The server will return the first block which /// contains a note matching `note_tags` or the chain tip. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncNotesRequest { /// Block range from which to start synchronizing. #[prost(message, optional, tag = "1")] pub block_range: ::core::option::Option, - /// Specifies the tags which the client is interested in. + /// Specifies the tags which the requester is interested in. #[prost(fixed32, repeated, tag = "2")] pub note_tags: ::prost::alloc::vec::Vec, } @@ -349,7 +352,7 @@ pub struct SyncNotesResponse { } /// Storage map synchronization request. /// -/// Allows clients to sync storage map values for specific public accounts within a block range, +/// Allows requesters to sync storage map values for specific public accounts within a block range, /// with support for cursor-based pagination to handle large storage maps. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncStorageMapsRequest { @@ -410,11 +413,11 @@ pub struct BlockRange { } /// Represents pagination information for chunked responses. /// -/// Pagination is done using block numbers as the axis, allowing clients to request +/// Pagination is done using block numbers as the axis, allowing requesters to request /// data in chunks by specifying block ranges and continuing from where the previous /// response left off. /// -/// To request the next chunk, the client should use `block_num + 1` from the previous response +/// To request the next chunk, the requester should use `block_num + 1` from the previous response /// as the `block_from` for the next request. #[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct PaginationInfo { @@ -431,7 +434,7 @@ pub struct PaginationInfo { } /// Transactions synchronization request. /// -/// Allows clients to sync transactions for specific accounts within a block range. +/// Allows requesters to sync transactions for specific accounts within a block range. #[derive(Clone, PartialEq, ::prost::Message)] pub struct SyncTransactionsRequest { /// Block range from which to start synchronizing. @@ -779,9 +782,9 @@ pub mod rpc_client { .insert(GrpcMethod::new("rpc_store.Rpc", "SyncNullifiers")); self.inner.unary(req, path, codec).await } - /// Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in. + /// Returns info which can be used by the requester to sync up to the tip of chain for the notes they are interested in. /// - /// Client specifies the `note_tags` they are interested in, and the block height from which to search for new for + /// requester specifies the `note_tags` they are interested in, and the block height from which to search for new for /// matching notes for. The request will then return the next block containing any note matching the provided tags. /// /// The response includes each note's metadata and inclusion proof. @@ -809,20 +812,20 @@ pub mod rpc_client { req.extensions_mut().insert(GrpcMethod::new("rpc_store.Rpc", "SyncNotes")); self.inner.unary(req, path, codec).await } - /// Returns info which can be used by the client to sync up to the latest state of the chain - /// for the objects (accounts, notes, nullifiers) the client is interested in. + /// Returns info which can be used by the requester to sync up to the latest state of the chain + /// for the objects (accounts, notes, nullifiers) the requester is interested in. /// /// This request returns the next block containing requested data. It also returns `chain_tip` - /// which is the latest block number in the chain. Client is expected to repeat these requests + /// which is the latest block number in the chain. requester is expected to repeat these requests /// in a loop until `response.block_header.block_num == response.chain_tip`, at which point - /// the client is fully synchronized with the chain. + /// the requester is fully synchronized with the chain. /// /// Each request also returns info about new notes, nullifiers etc. created. It also returns /// Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain /// MMR peaks and chain MMR nodes. /// /// For preserving some degree of privacy, note tags and nullifiers filters contain only high - /// part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + /// part of hashes. Thus, returned data contains excessive notes and nullifiers, requester can make /// additional filtering of that data on its side. pub async fn sync_state( &mut self, @@ -1004,9 +1007,9 @@ pub mod rpc_server { tonic::Response, tonic::Status, >; - /// Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in. + /// Returns info which can be used by the requester to sync up to the tip of chain for the notes they are interested in. /// - /// Client specifies the `note_tags` they are interested in, and the block height from which to search for new for + /// requester specifies the `note_tags` they are interested in, and the block height from which to search for new for /// matching notes for. The request will then return the next block containing any note matching the provided tags. /// /// The response includes each note's metadata and inclusion proof. @@ -1020,20 +1023,20 @@ pub mod rpc_server { tonic::Response, tonic::Status, >; - /// Returns info which can be used by the client to sync up to the latest state of the chain - /// for the objects (accounts, notes, nullifiers) the client is interested in. + /// Returns info which can be used by the requester to sync up to the latest state of the chain + /// for the objects (accounts, notes, nullifiers) the requester is interested in. /// /// This request returns the next block containing requested data. It also returns `chain_tip` - /// which is the latest block number in the chain. Client is expected to repeat these requests + /// which is the latest block number in the chain. requester is expected to repeat these requests /// in a loop until `response.block_header.block_num == response.chain_tip`, at which point - /// the client is fully synchronized with the chain. + /// the requester is fully synchronized with the chain. /// /// Each request also returns info about new notes, nullifiers etc. created. It also returns /// Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain /// MMR peaks and chain MMR nodes. /// /// For preserving some degree of privacy, note tags and nullifiers filters contain only high - /// part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + /// part of hashes. Thus, returned data contains excessive notes and nullifiers, requester can make /// additional filtering of that data on its side. async fn sync_state( &self, diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index effccc31d..69546170f 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -890,6 +890,9 @@ impl State { /// Returns the respective account proof with optional details, such as asset and storage /// entries. + /// + /// Note: The `block_num` parameter in the request is currently ignored and will always + /// return the current state. Historical block support will be implemented in a future update. #[allow(clippy::too_many_lines)] pub async fn get_account_proof( &self, @@ -901,6 +904,8 @@ impl State { let inner_state = self.inner.read().await; let account_id = account_request.account_id; + // TODO: Implement historical block support using account_request.block_num + // For now, we always return the current state let account_details = if let Some(AccountDetailRequest { code_commitment, asset_vault_commitment, diff --git a/proto/proto/store/rpc.proto b/proto/proto/store/rpc.proto index 1da00771c..1bbc37f67 100644 --- a/proto/proto/store/rpc.proto +++ b/proto/proto/store/rpc.proto @@ -47,9 +47,9 @@ service Rpc { // Note that only 16-bit prefixes are supported at this time. rpc SyncNullifiers(SyncNullifiersRequest) returns (SyncNullifiersResponse) {} - // Returns info which can be used by the client to sync up to the tip of chain for the notes they are interested in. + // Returns info which can be used by the requester to sync up to the tip of chain for the notes they are interested in. // - // Client specifies the `note_tags` they are interested in, and the block height from which to search for new for + // requester specifies the `note_tags` they are interested in, and the block height from which to search for new for // matching notes for. The request will then return the next block containing any note matching the provided tags. // // The response includes each note's metadata and inclusion proof. @@ -58,20 +58,20 @@ service Rpc { // tip of the chain. rpc SyncNotes(SyncNotesRequest) returns (SyncNotesResponse) {} - // Returns info which can be used by the client to sync up to the latest state of the chain - // for the objects (accounts, notes, nullifiers) the client is interested in. + // Returns info which can be used by the requester to sync up to the latest state of the chain + // for the objects (accounts, notes, nullifiers) the requester is interested in. // // This request returns the next block containing requested data. It also returns `chain_tip` - // which is the latest block number in the chain. Client is expected to repeat these requests + // which is the latest block number in the chain. requester is expected to repeat these requests // in a loop until `response.block_header.block_num == response.chain_tip`, at which point - // the client is fully synchronized with the chain. + // the requester is fully synchronized with the chain. // // Each request also returns info about new notes, nullifiers etc. created. It also returns // Chain MMR delta that can be used to update the state of Chain MMR. This includes both chain // MMR peaks and chain MMR nodes. // // For preserving some degree of privacy, note tags and nullifiers filters contain only high - // part of hashes. Thus, returned data contains excessive notes and nullifiers, client can make + // part of hashes. Thus, returned data contains excessive notes and nullifiers, requester can make // additional filtering of that data on its side. rpc SyncState(SyncStateRequest) returns (SyncStateResponse) {} @@ -127,11 +127,13 @@ message AccountProofRequest { } } - // Last known code commitment to the client. The response will include account code - // only if its commitment is different from this value or if the value is not present. + // Last known code commitment to the requester. The response will include account code + // only if its commitment is different from this value. + // + // If the field is ommiteed, the response will not include the account code. optional primitives.Digest code_commitment = 1; - // Last known asset vault commitment to the client. The response will include asset vault data + // Last known asset vault commitment to the requester. The response will include asset vault data // only if its commitment is different from this value. If the value is not present in the // request, the response will not contain one either. // If the number of to-be-returned asset entries exceed a threshold, they have to be requested @@ -165,7 +167,8 @@ message AccountProofResponse { // Account code; empty if code commitments matched or none was requested optional bytes code = 3; - // Account asset vault data; empty if vault commitments matched + // Account asset vault data; empty if vault commitments matched or the requester + // omitted it in the request. optional AccountVaultDetails vault_details = 4; } @@ -275,11 +278,11 @@ message SyncNullifiersResponse { // State synchronization request. // -// Specifies state updates the client is interested in. The server will return the first block which +// Specifies state updates the requester is interested in. The server will return the first block which // contains a note matching `note_tags` or the chain tip. And the corresponding updates to // `account_ids` for that block range. message SyncStateRequest { - // Last block known by the client. The response will contain data starting from the next block, + // Last block known by the requester. The response will contain data starting from the next block, // until the first block which contains a note of matching the requested tag, or the chain tip // if there are no notes. fixed32 block_num = 1; @@ -291,7 +294,7 @@ message SyncStateRequest { // it won't be included in the response. repeated account.AccountId account_ids = 2; - // Specifies the tags which the client is interested in. + // Specifies the tags which the requester is interested in. repeated fixed32 note_tags = 3; } @@ -322,7 +325,7 @@ message SyncStateResponse { // Account vault synchronization request. // -// Allows clients to sync asset values for specific public accounts within a block range. +// Allows requesters to sync asset values for specific public accounts within a block range. message SyncAccountVaultRequest { // Block range from which to start synchronizing. // @@ -362,13 +365,13 @@ message AccountVaultUpdate { // Note synchronization request. // -// Specifies note tags that client is interested in. The server will return the first block which +// Specifies note tags that requester is interested in. The server will return the first block which // contains a note matching `note_tags` or the chain tip. message SyncNotesRequest { // Block range from which to start synchronizing. BlockRange block_range = 1; - // Specifies the tags which the client is interested in. + // Specifies the tags which the requester is interested in. repeated fixed32 note_tags = 2; } @@ -395,7 +398,7 @@ message SyncNotesResponse { // Storage map synchronization request. // -// Allows clients to sync storage map values for specific public accounts within a block range, +// Allows requesters to sync storage map values for specific public accounts within a block range, // with support for cursor-based pagination to handle large storage maps. message SyncStorageMapsRequest { // Block range from which to start synchronizing. @@ -460,11 +463,11 @@ message BlockRange { // Represents pagination information for chunked responses. // -// Pagination is done using block numbers as the axis, allowing clients to request +// Pagination is done using block numbers as the axis, allowing requesters to request // data in chunks by specifying block ranges and continuing from where the previous // response left off. // -// To request the next chunk, the client should use `block_num + 1` from the previous response +// To request the next chunk, the requester should use `block_num + 1` from the previous response // as the `block_from` for the next request. message PaginationInfo { // Current chain tip @@ -483,7 +486,7 @@ message PaginationInfo { // Transactions synchronization request. // -// Allows clients to sync transactions for specific accounts within a block range. +// Allows requesters to sync transactions for specific accounts within a block range. message SyncTransactionsRequest { // Block range from which to start synchronizing. BlockRange block_range = 1;