Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
117 changes: 40 additions & 77 deletions crates/proto/src/domain/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ impl TryFrom<proto::rpc_store::account_storage_details::AccountStorageMapDetails

let slot_index = slot_index.try_into().map_err(ConversionError::TryFromIntError)?;

// Extract map_entries from the entries field
let map_entries = match entries {
Some(entries) => entries
// Extract map_entries from the MapEntries message
let map_entries = if let Some(entries) = entries {
entries
.entries
.into_iter()
.map(|entry| {
Expand All @@ -220,8 +220,9 @@ impl TryFrom<proto::rpc_store::account_storage_details::AccountStorageMapDetails
.try_into()?;
Ok((key, value))
})
.collect::<Result<Vec<_>, ConversionError>>()?,
None => Vec::new(),
.collect::<Result<Vec<_>, ConversionError>>()?
} else {
Vec::new()
};

Ok(Self {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -611,74 +617,31 @@ impl From<AccountDetails> for proto::rpc_store::account_proof_response::AccountD
}
}

// ACCOUNT PROOF
impl From<AccountStorageMapDetails>
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<proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry>
for StorageMapEntry
{
type Error = ConversionError;

fn try_from(
value: proto::rpc_store::account_storage_details::account_storage_map_details::map_entries::StorageMapEntry,
) -> Result<Self, Self::Error> {
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<StorageMapEntry>
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,
}
}
}
Expand Down
59 changes: 31 additions & 28 deletions crates/proto/src/generated/rpc_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<super::super::primitives::Digest>,
/// 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
Expand Down Expand Up @@ -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<u8>>,
/// 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<super::AccountVaultDetails>,
}
Expand Down Expand Up @@ -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")]
Expand All @@ -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<super::account::AccountId>,
/// 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<u32>,
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<BlockRange>,
/// 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<u32>,
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1004,9 +1007,9 @@ pub mod rpc_server {
tonic::Response<super::SyncNullifiersResponse>,
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.
Expand All @@ -1020,20 +1023,20 @@ pub mod rpc_server {
tonic::Response<super::SyncNotesResponse>,
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,
Expand Down
5 changes: 5 additions & 0 deletions crates/store/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading