From acee4bcf99055dddd5f580edb4fe9b8b18639cae Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Thu, 2 Jan 2025 15:40:42 -0300 Subject: [PATCH 1/2] feat: Enable storage map querying --- CHANGELOG.md | 1 + crates/proto/src/domain/accounts.rs | 50 ++++++++++++++ crates/proto/src/generated/requests.rs | 33 ++++++++- crates/proto/src/generated/responses.rs | 17 ++++- crates/rpc-proto/proto/requests.proto | 28 +++++++- crates/rpc-proto/proto/responses.proto | 18 ++++- crates/rpc/src/server/api.rs | 10 ++- crates/store/src/server/api.rs | 33 +++++---- crates/store/src/state.rs | 92 ++++++++++++++++++------- proto/requests.proto | 28 +++++++- proto/responses.proto | 18 ++++- 11 files changed, 270 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63d12a651..239111ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [BREAKING] Added support for new two `Felt` account ID (#591). - [BREAKING] Inverted `TransactionInputs.missing_unauthenticated_notes` to `found_missing_notes` (#509). +- [BREAKING] Added way of querying account storage map state on `GetAccountProofs` endpoint (#598). ## v0.6.0 (2024-11-05) diff --git a/crates/proto/src/domain/accounts.rs b/crates/proto/src/domain/accounts.rs index c19dbf56f..5f46c1782 100644 --- a/crates/proto/src/domain/accounts.rs +++ b/crates/proto/src/domain/accounts.rs @@ -8,10 +8,12 @@ use miden_objects::{ Digest, }; +use super::try_convert; use crate::{ errors::{ConversionError, MissingFieldHelper}, generated::{ account as proto, + requests::get_account_proofs_request, responses::{AccountBlockInputRecord, AccountTransactionInputRecord}, }, }; @@ -96,6 +98,54 @@ impl From<&AccountInfo> for proto::AccountInfo { } } +// ACCOUNT STORAGE REQUEST +// ================================================================================================ + +/// Represents a request for an account proof alongside specific storage data. +pub struct AccountProofRequest { + pub account_id: AccountId, + pub storage_requests: Vec, +} + +impl TryInto for get_account_proofs_request::AccountRequest { + type Error = ConversionError; + + fn try_into(self) -> Result { + let get_account_proofs_request::AccountRequest { account_id, storage_requests } = self; + + Ok(AccountProofRequest { + account_id: account_id + .clone() + .ok_or(get_account_proofs_request::AccountRequest::missing_field(stringify!( + account_id + )))? + .try_into()?, + storage_requests: try_convert(storage_requests)?, + }) + } +} + +/// Represents a request for an account's storage map values and its proof of existence. +pub struct StorageMapKeysProof { + /// Index of the storage map + pub storage_index: u8, + /// List of requested keys in the map + pub storage_keys: Vec, +} + +impl TryInto for get_account_proofs_request::StorageRequest { + type Error = ConversionError; + + fn try_into(self) -> Result { + let get_account_proofs_request::StorageRequest { storage_slot_index, map_keys } = self; + + Ok(StorageMapKeysProof { + storage_index: storage_slot_index.try_into()?, + storage_keys: try_convert(map_keys)?, + }) + } +} + // ACCOUNT INPUT RECORD // ================================================================================================ diff --git a/crates/proto/src/generated/requests.rs b/crates/proto/src/generated/requests.rs index b58105d61..16b7ba794 100644 --- a/crates/proto/src/generated/requests.rs +++ b/crates/proto/src/generated/requests.rs @@ -148,12 +148,15 @@ pub struct GetAccountStateDeltaRequest { #[prost(fixed32, tag = "3")] pub to_block_num: u32, } +/// Request message to get account proofs. #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetAccountProofsRequest { - /// List of account IDs to get states. + /// A list of account requests, including map keys + values. #[prost(message, repeated, tag = "1")] - pub account_ids: ::prost::alloc::vec::Vec, - /// Optional flag to include header and account code in the response. `false` by default. + pub account_requests: ::prost::alloc::vec::Vec< + get_account_proofs_request::AccountRequest, + >, + /// Optional flag to include header and account code in the response. False by default. #[prost(bool, optional, tag = "2")] pub include_headers: ::core::option::Option, /// Account code commitments corresponding to the last-known `AccountCode` for requested @@ -163,3 +166,27 @@ pub struct GetAccountProofsRequest { #[prost(message, repeated, tag = "3")] pub code_commitments: ::prost::alloc::vec::Vec, } +/// Nested message and enum types in `GetAccountProofsRequest`. +pub mod get_account_proofs_request { + /// Represents per-account requests where each account ID has its own list of + /// (storage_slot_index, map_keys) pairs. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct AccountRequest { + /// The account ID for this request. + #[prost(message, optional, tag = "1")] + pub account_id: ::core::option::Option, + /// List of storage requests for this account. + #[prost(message, repeated, tag = "2")] + pub storage_requests: ::prost::alloc::vec::Vec, + } + /// Represents a storage slot index and the associated map keys. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct StorageRequest { + /// Storage slot index (\[0..255\]) + #[prost(uint32, tag = "1")] + pub storage_slot_index: u32, + /// A list of map keys (Digests) associated with this storage slot. + #[prost(message, repeated, tag = "2")] + pub map_keys: ::prost::alloc::vec::Vec, + } +} diff --git a/crates/proto/src/generated/responses.rs b/crates/proto/src/generated/responses.rs index b635c10ed..a996cdda9 100644 --- a/crates/proto/src/generated/responses.rs +++ b/crates/proto/src/generated/responses.rs @@ -227,8 +227,21 @@ pub struct AccountStateHeader { /// Values of all account storage slots (max 255). #[prost(bytes = "vec", tag = "2")] pub storage_header: ::prost::alloc::vec::Vec, - /// Account code, returned only when none of the request's code commitments match with the - /// current one. + /// Account code, returned only when none of the request's code commitments match + /// the current one. #[prost(bytes = "vec", optional, tag = "3")] pub account_code: ::core::option::Option<::prost::alloc::vec::Vec>, + /// Storage slots information for this account + #[prost(message, repeated, tag = "4")] + pub storage_slots: ::prost::alloc::vec::Vec, +} +/// Represents a single storage slot with the reuqested keys and their respective values. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StorageSlotMapProof { + /// The storage slot index (\[0..255\]). + #[prost(uint32, tag = "1")] + pub storage_slot: u32, + /// Merkle proof of the map value + #[prost(bytes = "vec", tag = "2")] + pub smt_proof: ::prost::alloc::vec::Vec, } diff --git a/crates/rpc-proto/proto/requests.proto b/crates/rpc-proto/proto/requests.proto index 31a0cb68e..f427a8995 100644 --- a/crates/rpc-proto/proto/requests.proto +++ b/crates/rpc-proto/proto/requests.proto @@ -132,11 +132,33 @@ message GetAccountStateDeltaRequest { fixed32 to_block_num = 3; } +// Request message to get account proofs. message GetAccountProofsRequest { - // List of account IDs to get states. - repeated account.AccountId account_ids = 1; - // Optional flag to include header and account code in the response. `false` by default. + // Represents per-account requests where each account ID has its own list of + // (storage_slot_index, map_keys) pairs. + message AccountRequest { + // The account ID for this request. + account.AccountId account_id = 1; + + // List of storage requests for this account. + repeated StorageRequest storage_requests = 2; + } + + // Represents a storage slot index and the associated map keys. + message StorageRequest { + // Storage slot index ([0..255]) + uint32 storage_slot_index = 1; + + // A list of map keys (Digests) associated with this storage slot. + repeated digest.Digest map_keys = 2; + } + + // A list of account requests, including map keys + values. + repeated AccountRequest account_requests = 1; + + // Optional flag to include header and account code in the response. False by default. optional bool include_headers = 2; + // Account code commitments corresponding to the last-known `AccountCode` for requested // accounts. Responses will include only the ones that are not known to the caller. // These are not associated with a specific account but rather, they will be matched against diff --git a/crates/rpc-proto/proto/responses.proto b/crates/rpc-proto/proto/responses.proto index 4f1c017b8..ccce92c84 100644 --- a/crates/rpc-proto/proto/responses.proto +++ b/crates/rpc-proto/proto/responses.proto @@ -195,9 +195,23 @@ message AccountProofsResponse { message AccountStateHeader { // Account header. account.AccountHeader header = 1; + // Values of all account storage slots (max 255). bytes storage_header = 2; - // Account code, returned only when none of the request's code commitments match with the - // current one. + + // Account code, returned only when none of the request's code commitments match + // the current one. optional bytes account_code = 3; + + // Storage slots information for this account + repeated StorageSlotMapProof storage_slots = 4; +} + +// Represents a single storage slot with the reuqested keys and their respective values. +message StorageSlotMapProof { + // The storage slot index ([0..255]). + uint32 storage_slot = 1; + + // Merkle proof of the map value + bytes smt_proof = 2; } diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 241fe9a8a..96bca8096 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -265,13 +265,19 @@ impl api_server::Api for RpcApi { debug!(target: COMPONENT, ?request); - if request.account_ids.len() > MAX_NUM_FOREIGN_ACCOUNTS as usize { + if request.account_requests.len() > MAX_NUM_FOREIGN_ACCOUNTS as usize { return Err(Status::invalid_argument(format!( "Too many accounts requested: {}, limit: {MAX_NUM_FOREIGN_ACCOUNTS}", - request.account_ids.len() + request.account_requests.len() ))); } + if request.account_requests.len() < request.code_commitments.len() { + return Err(Status::invalid_argument( + "The number of code commitments should not exceed the number of requested accounts.", + )); + } + self.store.clone().get_account_proofs(request).await } } diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index fe1808003..3de3dd529 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -2,7 +2,10 @@ use std::{collections::BTreeSet, sync::Arc}; use miden_node_proto::{ convert, - domain::{accounts::AccountInfo, notes::NoteAuthenticationInfo}, + domain::{ + accounts::{AccountInfo, AccountProofRequest}, + notes::NoteAuthenticationInfo, + }, errors::ConversionError, generated::{ self, @@ -477,25 +480,25 @@ impl api_server::Api for StoreApi { &self, request: Request, ) -> Result, Status> { - let request = request.into_inner(); - if request.account_ids.len() < request.code_commitments.len() { - return Err(Status::invalid_argument( - "The number of code commitments should not exceed the number of requested accounts.", - )); - } - debug!(target: COMPONENT, ?request); + let GetAccountProofsRequest { + account_requests, + include_headers, + code_commitments, + } = request.into_inner(); - let include_headers = request.include_headers.unwrap_or_default(); - let account_ids: Vec = read_account_ids(&request.account_ids)?; - let request_code_commitments: BTreeSet = try_convert(request.code_commitments) - .map_err(|err| { - Status::invalid_argument(format!("Invalid code commitment: {}", err)) - })?; + let include_headers = include_headers.unwrap_or_default(); + let request_code_commitments: BTreeSet = try_convert(code_commitments) + .map_err(|err| Status::invalid_argument(format!("Invalid code commitment: {}", err)))?; + + let account_requests: Vec = + try_convert(account_requests).map_err(|err| { + Status::invalid_argument(format!("Invalid account proofs request: {}", err)) + })?; let (block_num, infos) = self .state - .get_account_proofs(account_ids, request_code_commitments, include_headers) + .get_account_proofs(account_requests, request_code_commitments, include_headers) .await?; Ok(Response::new(GetAccountProofsResponse { diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 87c624a11..47afe0a3e 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -5,19 +5,24 @@ use std::{ collections::{BTreeMap, BTreeSet}, - ops::Not, sync::Arc, }; use miden_node_proto::{ convert, - domain::{accounts::AccountInfo, blocks::BlockInclusionProof, notes::NoteAuthenticationInfo}, - generated::responses::{AccountProofsResponse, AccountStateHeader, GetBlockInputsResponse}, + domain::{ + accounts::{AccountInfo, AccountProofRequest, StorageMapKeysProof}, + blocks::BlockInclusionProof, + notes::NoteAuthenticationInfo, + }, + generated::responses::{ + AccountProofsResponse, AccountStateHeader, GetBlockInputsResponse, StorageSlotMapProof, + }, AccountInputRecord, NullifierWitness, }; use miden_node_utils::formatting::format_array; use miden_objects::{ - accounts::{AccountDelta, AccountHeader, AccountId}, + accounts::{AccountDelta, AccountHeader, AccountId, StorageSlot}, block::Block, crypto::{ hash::rpo::RpoDigest, @@ -689,7 +694,7 @@ impl State { /// Returns account proofs with optional account and storage headers. pub async fn get_account_proofs( &self, - account_ids: Vec, + account_requests: Vec, request_code_commitments: BTreeSet, include_headers: bool, ) -> Result<(BlockNumber, Vec), DatabaseError> { @@ -698,11 +703,13 @@ impl State { // because changing one of them would lead to inconsistent state. let inner_state = self.inner.read().await; + let account_ids: Vec = + account_requests.iter().map(|req| req.account_id).collect(); + let state_headers = if !include_headers { BTreeMap::::default() } else { let infos = self.db.select_accounts_by_ids(account_ids.clone()).await?; - if account_ids.len() > infos.len() { let found_ids = infos.iter().map(|info| info.summary.account_id).collect(); return Err(DatabaseError::AccountsNotFoundInDb( @@ -710,26 +717,58 @@ impl State { )); } - infos - .into_iter() - .filter_map(|info| { - info.details.map(|details| { - ( - info.summary.account_id, - AccountStateHeader { - header: Some(AccountHeader::from(&details).into()), - storage_header: details.storage().get_header().to_bytes(), - // Only include account code if the request did not contain it - // (known by the caller) - account_code: request_code_commitments - .contains(&details.code().commitment()) - .not() - .then_some(details.code().to_bytes()), - }, - ) - }) - }) - .collect() + let mut headers_map = BTreeMap::new(); + + for account_info in infos { + if let Some(details) = account_info.details { + let request = + match account_requests.iter().find(|req| req.account_id == details.id()) { + Some(r) => r, + None => panic!(), + }; + + let mut storage_slot_map_keys = Vec::new(); + + for StorageMapKeysProof { storage_index, storage_keys } in + &request.storage_requests + { + if let Some(StorageSlot::Map(storage_map)) = + details.storage().slots().get(*storage_index as usize) + { + for map_key in storage_keys { + let proof = storage_map.open(map_key); + + let slot_map_key = StorageSlotMapProof { + storage_slot: *storage_index as u32, + smt_proof: proof.to_bytes(), + }; + storage_slot_map_keys.push(slot_map_key); + } + } + // TODO: else error? + } + + // Only return code for commitments that the user did not know according ot + // the request + let maybe_account_code = + if !request_code_commitments.contains(&details.code().commitment()) { + Some(details.code().to_bytes()) + } else { + None + }; + + let state_header = AccountStateHeader { + header: Some(AccountHeader::from(&details).into()), + storage_header: details.storage().get_header().to_bytes(), + account_code: maybe_account_code, + storage_slots: storage_slot_map_keys, + }; + + headers_map.insert(account_info.summary.account_id, state_header); + } + } + + headers_map }; let responses = account_ids @@ -737,6 +776,7 @@ impl State { .map(|account_id| { let acc_leaf_idx = LeafIndex::new_max_depth(account_id.prefix().into()); let opening = inner_state.account_tree.open(&acc_leaf_idx); + let state_header = state_headers.get(&account_id).cloned(); AccountProofsResponse { diff --git a/proto/requests.proto b/proto/requests.proto index 31a0cb68e..f427a8995 100644 --- a/proto/requests.proto +++ b/proto/requests.proto @@ -132,11 +132,33 @@ message GetAccountStateDeltaRequest { fixed32 to_block_num = 3; } +// Request message to get account proofs. message GetAccountProofsRequest { - // List of account IDs to get states. - repeated account.AccountId account_ids = 1; - // Optional flag to include header and account code in the response. `false` by default. + // Represents per-account requests where each account ID has its own list of + // (storage_slot_index, map_keys) pairs. + message AccountRequest { + // The account ID for this request. + account.AccountId account_id = 1; + + // List of storage requests for this account. + repeated StorageRequest storage_requests = 2; + } + + // Represents a storage slot index and the associated map keys. + message StorageRequest { + // Storage slot index ([0..255]) + uint32 storage_slot_index = 1; + + // A list of map keys (Digests) associated with this storage slot. + repeated digest.Digest map_keys = 2; + } + + // A list of account requests, including map keys + values. + repeated AccountRequest account_requests = 1; + + // Optional flag to include header and account code in the response. False by default. optional bool include_headers = 2; + // Account code commitments corresponding to the last-known `AccountCode` for requested // accounts. Responses will include only the ones that are not known to the caller. // These are not associated with a specific account but rather, they will be matched against diff --git a/proto/responses.proto b/proto/responses.proto index 4f1c017b8..ccce92c84 100644 --- a/proto/responses.proto +++ b/proto/responses.proto @@ -195,9 +195,23 @@ message AccountProofsResponse { message AccountStateHeader { // Account header. account.AccountHeader header = 1; + // Values of all account storage slots (max 255). bytes storage_header = 2; - // Account code, returned only when none of the request's code commitments match with the - // current one. + + // Account code, returned only when none of the request's code commitments match + // the current one. optional bytes account_code = 3; + + // Storage slots information for this account + repeated StorageSlotMapProof storage_slots = 4; +} + +// Represents a single storage slot with the reuqested keys and their respective values. +message StorageSlotMapProof { + // The storage slot index ([0..255]). + uint32 storage_slot = 1; + + // Merkle proof of the map value + bytes smt_proof = 2; } From 1b7e1810ccecd5c529f8b9b6e71c4dc48aef79b0 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Mon, 13 Jan 2025 16:37:53 -0300 Subject: [PATCH 2/2] reviews: Clarify doc comments; error messages; iterate over requests instead of retreived accounts; README suggestions --- CHANGELOG.md | 4 +- crates/proto/src/domain/accounts.rs | 78 ++++---- crates/proto/src/domain/digest.rs | 14 +- crates/proto/src/domain/merkle.rs | 62 +++---- crates/proto/src/domain/notes.rs | 33 ++-- crates/proto/src/domain/nullifiers.rs | 26 +-- crates/proto/src/domain/transactions.rs | 23 ++- crates/proto/src/generated/requests.rs | 9 +- crates/proto/src/generated/responses.rs | 20 +-- crates/proto/src/generated/store.rs | 230 ------------------------ crates/rpc-proto/proto/requests.proto | 9 +- crates/rpc-proto/proto/responses.proto | 17 +- crates/rpc-proto/proto/store.proto | 3 - crates/store/README.md | 42 +---- crates/store/src/db/mod.rs | 22 +-- crates/store/src/db/sql/mod.rs | 2 + crates/store/src/server/api.rs | 66 +------ crates/store/src/state.rs | 56 ++---- proto/requests.proto | 9 +- proto/responses.proto | 17 +- proto/store.proto | 3 - 21 files changed, 167 insertions(+), 578 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 239111ecd..5ed082383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,11 @@ ### Changes +- Standardized protobuf type aliases (#609). - [BREAKING] Added support for new two `Felt` account ID (#591). - [BREAKING] Inverted `TransactionInputs.missing_unauthenticated_notes` to `found_missing_notes` (#509). -- [BREAKING] Added way of querying account storage map state on `GetAccountProofs` endpoint (#598). +- [BREAKING] Remove store's `ListXXX` endpoints which were intended for test purposes (#608). +- [BREAKING] Added support for storage maps on `GetAccountProofs` endpoint (#598). ## v0.6.0 (2024-11-05) diff --git a/crates/proto/src/domain/accounts.rs b/crates/proto/src/domain/accounts.rs index 5f46c1782..cf358ab04 100644 --- a/crates/proto/src/domain/accounts.rs +++ b/crates/proto/src/domain/accounts.rs @@ -11,17 +11,13 @@ use miden_objects::{ use super::try_convert; use crate::{ errors::{ConversionError, MissingFieldHelper}, - generated::{ - account as proto, - requests::get_account_proofs_request, - responses::{AccountBlockInputRecord, AccountTransactionInputRecord}, - }, + generated as proto, }; // ACCOUNT ID // ================================================================================================ -impl Display for proto::AccountId { +impl Display for proto::account::AccountId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "0x")?; for byte in &self.id { @@ -31,7 +27,7 @@ impl Display for proto::AccountId { } } -impl Debug for proto::AccountId { +impl Debug for proto::account::AccountId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(self, f) } @@ -40,13 +36,13 @@ impl Debug for proto::AccountId { // INTO PROTO ACCOUNT ID // ------------------------------------------------------------------------------------------------ -impl From<&AccountId> for proto::AccountId { +impl From<&AccountId> for proto::account::AccountId { fn from(account_id: &AccountId) -> Self { (*account_id).into() } } -impl From for proto::AccountId { +impl From for proto::account::AccountId { fn from(account_id: AccountId) -> Self { Self { id: account_id.to_bytes() } } @@ -55,10 +51,10 @@ impl From for proto::AccountId { // FROM PROTO ACCOUNT ID // ------------------------------------------------------------------------------------------------ -impl TryFrom for AccountId { +impl TryFrom for AccountId { type Error = ConversionError; - fn try_from(account_id: proto::AccountId) -> Result { + fn try_from(account_id: proto::account::AccountId) -> Result { AccountId::read_from_bytes(&account_id.id).map_err(|_| ConversionError::NotAValidFelt) } } @@ -73,7 +69,7 @@ pub struct AccountSummary { pub block_num: u32, } -impl From<&AccountSummary> for proto::AccountSummary { +impl From<&AccountSummary> for proto::account::AccountSummary { fn from(update: &AccountSummary) -> Self { Self { account_id: Some(update.account_id.into()), @@ -89,7 +85,7 @@ pub struct AccountInfo { pub details: Option, } -impl From<&AccountInfo> for proto::AccountInfo { +impl From<&AccountInfo> for proto::account::AccountInfo { fn from(AccountInfo { summary, details }: &AccountInfo) -> Self { Self { summary: Some(summary.into()), @@ -107,18 +103,21 @@ pub struct AccountProofRequest { pub storage_requests: Vec, } -impl TryInto for get_account_proofs_request::AccountRequest { +impl TryInto for proto::requests::get_account_proofs_request::AccountRequest { type Error = ConversionError; fn try_into(self) -> Result { - let get_account_proofs_request::AccountRequest { account_id, storage_requests } = self; + let proto::requests::get_account_proofs_request::AccountRequest { + account_id, + storage_requests, + } = self; Ok(AccountProofRequest { account_id: account_id .clone() - .ok_or(get_account_proofs_request::AccountRequest::missing_field(stringify!( - account_id - )))? + .ok_or(proto::requests::get_account_proofs_request::AccountRequest::missing_field( + stringify!(account_id), + ))? .try_into()?, storage_requests: try_convert(storage_requests)?, }) @@ -133,11 +132,14 @@ pub struct StorageMapKeysProof { pub storage_keys: Vec, } -impl TryInto for get_account_proofs_request::StorageRequest { +impl TryInto for proto::requests::get_account_proofs_request::StorageRequest { type Error = ConversionError; fn try_into(self) -> Result { - let get_account_proofs_request::StorageRequest { storage_slot_index, map_keys } = self; + let proto::requests::get_account_proofs_request::StorageRequest { + storage_slot_index, + map_keys, + } = self; Ok(StorageMapKeysProof { storage_index: storage_slot_index.try_into()?, @@ -156,7 +158,7 @@ pub struct AccountInputRecord { pub proof: MerklePath, } -impl From for AccountBlockInputRecord { +impl From for proto::responses::AccountBlockInputRecord { fn from(from: AccountInputRecord) -> Self { Self { account_id: Some(from.account_id.into()), @@ -166,23 +168,29 @@ impl From for AccountBlockInputRecord { } } -impl TryFrom for AccountInputRecord { +impl TryFrom for AccountInputRecord { type Error = ConversionError; - fn try_from(account_input_record: AccountBlockInputRecord) -> Result { + fn try_from( + account_input_record: proto::responses::AccountBlockInputRecord, + ) -> Result { Ok(Self { account_id: account_input_record .account_id - .ok_or(AccountBlockInputRecord::missing_field(stringify!(account_id)))? + .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!( + account_id + )))? .try_into()?, account_hash: account_input_record .account_hash - .ok_or(AccountBlockInputRecord::missing_field(stringify!(account_hash)))? + .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!( + account_hash + )))? .try_into()?, proof: account_input_record .proof .as_ref() - .ok_or(AccountBlockInputRecord::missing_field(stringify!(proof)))? + .ok_or(proto::responses::AccountBlockInputRecord::missing_field(stringify!(proof)))? .try_into()?, }) } @@ -210,7 +218,7 @@ impl Display for AccountState { } } -impl From for AccountTransactionInputRecord { +impl From for proto::responses::AccountTransactionInputRecord { fn from(from: AccountState) -> Self { Self { account_id: Some(from.account_id.into()), @@ -219,7 +227,7 @@ impl From for AccountTransactionInputRecord { } } -impl From for proto::AccountHeader { +impl From for proto::account::AccountHeader { fn from(from: AccountHeader) -> Self { Self { vault_root: Some(from.vault_root().into()), @@ -230,18 +238,24 @@ impl From for proto::AccountHeader { } } -impl TryFrom for AccountState { +impl TryFrom for AccountState { type Error = ConversionError; - fn try_from(from: AccountTransactionInputRecord) -> Result { + fn try_from( + from: proto::responses::AccountTransactionInputRecord, + ) -> Result { let account_id = from .account_id - .ok_or(AccountTransactionInputRecord::missing_field(stringify!(account_id)))? + .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!( + account_id + )))? .try_into()?; let account_hash = from .account_hash - .ok_or(AccountTransactionInputRecord::missing_field(stringify!(account_hash)))? + .ok_or(proto::responses::AccountTransactionInputRecord::missing_field(stringify!( + account_hash + )))? .try_into()?; // If the hash is equal to `Digest::default()`, it signifies that this is a new account diff --git a/crates/proto/src/domain/digest.rs b/crates/proto/src/domain/digest.rs index fb396cb99..811ee872d 100644 --- a/crates/proto/src/domain/digest.rs +++ b/crates/proto/src/domain/digest.rs @@ -207,23 +207,23 @@ mod test { use hex::{FromHex, ToHex}; use proptest::prelude::*; - use crate::generated::digest::Digest; + use crate::generated::digest as proto; #[test] fn hex_digest() { - let digest = Digest { + let digest = proto::Digest { d0: 3488802789098113751, d1: 5271242459988994564, d2: 17816570245237064784, d3: 10910963388447438895, }; let encoded: String = ToHex::encode_hex(&digest); - let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); + let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); assert_eq!(digest, round_trip.unwrap()); - let digest = Digest { d0: 0, d1: 0, d2: 0, d3: 0 }; + let digest = proto::Digest { d0: 0, d1: 0, d2: 0, d3: 0 }; let encoded: String = ToHex::encode_hex(&digest); - let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); + let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); assert_eq!(digest, round_trip.unwrap()); } @@ -235,9 +235,9 @@ mod test { d2: u64, d3: u64, ) { - let digest = Digest { d0, d1, d2, d3 }; + let digest = proto::Digest { d0, d1, d2, d3 }; let encoded: String = ToHex::encode_hex(&digest); - let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); + let round_trip: Result = FromHex::from_hex::<&[u8]>(encoded.as_ref()); assert_eq!(digest, round_trip.unwrap()); } } diff --git a/crates/proto/src/domain/merkle.rs b/crates/proto/src/domain/merkle.rs index f21bec087..2cd1adf74 100644 --- a/crates/proto/src/domain/merkle.rs +++ b/crates/proto/src/domain/merkle.rs @@ -6,29 +6,29 @@ use miden_objects::{ use super::{convert, try_convert}; use crate::{ errors::{ConversionError, MissingFieldHelper}, - generated, + generated as proto, }; // MERKLE PATH // ================================================================================================ -impl From<&MerklePath> for generated::merkle::MerklePath { +impl From<&MerklePath> for proto::merkle::MerklePath { fn from(value: &MerklePath) -> Self { - let siblings = value.nodes().iter().map(generated::digest::Digest::from).collect(); - generated::merkle::MerklePath { siblings } + let siblings = value.nodes().iter().map(proto::digest::Digest::from).collect(); + proto::merkle::MerklePath { siblings } } } -impl From for generated::merkle::MerklePath { +impl From for proto::merkle::MerklePath { fn from(value: MerklePath) -> Self { (&value).into() } } -impl TryFrom<&generated::merkle::MerklePath> for MerklePath { +impl TryFrom<&proto::merkle::MerklePath> for MerklePath { type Error = ConversionError; - fn try_from(merkle_path: &generated::merkle::MerklePath) -> Result { + fn try_from(merkle_path: &proto::merkle::MerklePath) -> Result { merkle_path.siblings.iter().map(Digest::try_from).collect() } } @@ -36,17 +36,17 @@ impl TryFrom<&generated::merkle::MerklePath> for MerklePath { // MMR DELTA // ================================================================================================ -impl From for generated::mmr::MmrDelta { +impl From for proto::mmr::MmrDelta { fn from(value: MmrDelta) -> Self { - let data = value.data.into_iter().map(generated::digest::Digest::from).collect(); - generated::mmr::MmrDelta { forest: value.forest as u64, data } + let data = value.data.into_iter().map(proto::digest::Digest::from).collect(); + proto::mmr::MmrDelta { forest: value.forest as u64, data } } } -impl TryFrom for MmrDelta { +impl TryFrom for MmrDelta { type Error = ConversionError; - fn try_from(value: generated::mmr::MmrDelta) -> Result { + fn try_from(value: proto::mmr::MmrDelta) -> Result { let data: Result, ConversionError> = value.data.into_iter().map(Digest::try_from).collect(); @@ -63,22 +63,22 @@ impl TryFrom for MmrDelta { // SMT LEAF // ------------------------------------------------------------------------------------------------ -impl TryFrom for SmtLeaf { +impl TryFrom for SmtLeaf { type Error = ConversionError; - fn try_from(value: generated::smt::SmtLeaf) -> Result { - let leaf = value.leaf.ok_or(generated::smt::SmtLeaf::missing_field(stringify!(leaf)))?; + fn try_from(value: proto::smt::SmtLeaf) -> Result { + let leaf = value.leaf.ok_or(proto::smt::SmtLeaf::missing_field(stringify!(leaf)))?; match leaf { - generated::smt::smt_leaf::Leaf::Empty(leaf_index) => { + proto::smt::smt_leaf::Leaf::Empty(leaf_index) => { Ok(Self::new_empty(LeafIndex::new_max_depth(leaf_index))) }, - generated::smt::smt_leaf::Leaf::Single(entry) => { + proto::smt::smt_leaf::Leaf::Single(entry) => { let (key, value): (Digest, Word) = entry.try_into()?; Ok(SmtLeaf::new_single(key, value)) }, - generated::smt::smt_leaf::Leaf::Multiple(entries) => { + proto::smt::smt_leaf::Leaf::Multiple(entries) => { let domain_entries: Vec<(Digest, Word)> = try_convert(entries.entries)?; Ok(SmtLeaf::new_multiple(domain_entries)?) @@ -87,15 +87,15 @@ impl TryFrom for SmtLeaf { } } -impl From for generated::smt::SmtLeaf { +impl From for proto::smt::SmtLeaf { fn from(smt_leaf: SmtLeaf) -> Self { - use generated::smt::smt_leaf::Leaf; + use proto::smt::smt_leaf::Leaf; let leaf = match smt_leaf { SmtLeaf::Empty(leaf_index) => Leaf::Empty(leaf_index.value()), SmtLeaf::Single(entry) => Leaf::Single(entry.into()), SmtLeaf::Multiple(entries) => { - Leaf::Multiple(generated::smt::SmtLeafEntries { entries: convert(entries) }) + Leaf::Multiple(proto::smt::SmtLeafEntries { entries: convert(entries) }) }, }; @@ -106,24 +106,24 @@ impl From for generated::smt::SmtLeaf { // SMT LEAF ENTRY // ------------------------------------------------------------------------------------------------ -impl TryFrom for (Digest, Word) { +impl TryFrom for (Digest, Word) { type Error = ConversionError; - fn try_from(entry: generated::smt::SmtLeafEntry) -> Result { + fn try_from(entry: proto::smt::SmtLeafEntry) -> Result { let key: Digest = entry .key - .ok_or(generated::smt::SmtLeafEntry::missing_field(stringify!(key)))? + .ok_or(proto::smt::SmtLeafEntry::missing_field(stringify!(key)))? .try_into()?; let value: Word = entry .value - .ok_or(generated::smt::SmtLeafEntry::missing_field(stringify!(value)))? + .ok_or(proto::smt::SmtLeafEntry::missing_field(stringify!(value)))? .try_into()?; Ok((key, value)) } } -impl From<(Digest, Word)> for generated::smt::SmtLeafEntry { +impl From<(Digest, Word)> for proto::smt::SmtLeafEntry { fn from((key, value): (Digest, Word)) -> Self { Self { key: Some(key.into()), @@ -135,25 +135,25 @@ impl From<(Digest, Word)> for generated::smt::SmtLeafEntry { // SMT PROOF // ------------------------------------------------------------------------------------------------ -impl TryFrom for SmtProof { +impl TryFrom for SmtProof { type Error = ConversionError; - fn try_from(opening: generated::smt::SmtOpening) -> Result { + fn try_from(opening: proto::smt::SmtOpening) -> Result { let path: MerklePath = opening .path .as_ref() - .ok_or(generated::smt::SmtOpening::missing_field(stringify!(path)))? + .ok_or(proto::smt::SmtOpening::missing_field(stringify!(path)))? .try_into()?; let leaf: SmtLeaf = opening .leaf - .ok_or(generated::smt::SmtOpening::missing_field(stringify!(leaf)))? + .ok_or(proto::smt::SmtOpening::missing_field(stringify!(leaf)))? .try_into()?; Ok(SmtProof::new(path, leaf)?) } } -impl From for generated::smt::SmtOpening { +impl From for proto::smt::SmtOpening { fn from(proof: SmtProof) -> Self { let (ref path, leaf) = proof.into_parts(); Self { diff --git a/crates/proto/src/domain/notes.rs b/crates/proto/src/domain/notes.rs index 10cb5db4f..4dafb028f 100644 --- a/crates/proto/src/domain/notes.rs +++ b/crates/proto/src/domain/notes.rs @@ -9,20 +9,17 @@ use crate::{ convert, domain::blocks::BlockInclusionProof, errors::{ConversionError, MissingFieldHelper}, - generated::note::{ - NoteAuthenticationInfo as NoteAuthenticationInfoProto, - NoteInclusionInBlockProof as NoteInclusionInBlockProofPb, NoteMetadata as NoteMetadataPb, - }, + generated::note as proto, try_convert, }; -impl TryFrom for NoteMetadata { +impl TryFrom for NoteMetadata { type Error = ConversionError; - fn try_from(value: NoteMetadataPb) -> Result { + fn try_from(value: proto::NoteMetadata) -> Result { let sender = value .sender - .ok_or_else(|| NoteMetadataPb::missing_field(stringify!(sender)))? + .ok_or_else(|| proto::NoteMetadata::missing_field(stringify!(sender)))? .try_into()?; let note_type = NoteType::try_from(value.note_type as u64)?; let tag = NoteTag::from(value.tag); @@ -35,7 +32,7 @@ impl TryFrom for NoteMetadata { } } -impl From for NoteMetadataPb { +impl From for proto::NoteMetadata { fn from(val: NoteMetadata) -> Self { let sender = Some(val.sender().into()); let note_type = val.note_type() as u32; @@ -43,7 +40,7 @@ impl From for NoteMetadataPb { let execution_hint: u64 = val.execution_hint().into(); let aux = val.aux().into(); - crate::generated::note::NoteMetadata { + proto::NoteMetadata { sender, note_type, tag, @@ -53,7 +50,7 @@ impl From for NoteMetadataPb { } } -impl From<(&NoteId, &NoteInclusionProof)> for NoteInclusionInBlockProofPb { +impl From<(&NoteId, &NoteInclusionProof)> for proto::NoteInclusionInBlockProof { fn from((note_id, proof): (&NoteId, &NoteInclusionProof)) -> Self { Self { note_id: Some(note_id.into()), @@ -64,18 +61,18 @@ impl From<(&NoteId, &NoteInclusionProof)> for NoteInclusionInBlockProofPb { } } -impl TryFrom<&NoteInclusionInBlockProofPb> for (NoteId, NoteInclusionProof) { +impl TryFrom<&proto::NoteInclusionInBlockProof> for (NoteId, NoteInclusionProof) { type Error = ConversionError; fn try_from( - proof: &NoteInclusionInBlockProofPb, + proof: &proto::NoteInclusionInBlockProof, ) -> Result<(NoteId, NoteInclusionProof), Self::Error> { Ok(( Digest::try_from( proof .note_id .as_ref() - .ok_or(NoteInclusionInBlockProofPb::missing_field(stringify!(note_id)))?, + .ok_or(proto::NoteInclusionInBlockProof::missing_field(stringify!(note_id)))?, )? .into(), NoteInclusionProof::new( @@ -84,7 +81,9 @@ impl TryFrom<&NoteInclusionInBlockProofPb> for (NoteId, NoteInclusionProof) { proof .merkle_path .as_ref() - .ok_or(NoteInclusionInBlockProofPb::missing_field(stringify!(merkle_path)))? + .ok_or(proto::NoteInclusionInBlockProof::missing_field(stringify!( + merkle_path + )))? .try_into()?, )?, )) @@ -107,7 +106,7 @@ impl NoteAuthenticationInfo { } } -impl From for NoteAuthenticationInfoProto { +impl From for proto::NoteAuthenticationInfo { fn from(value: NoteAuthenticationInfo) -> Self { Self { note_proofs: convert(&value.note_proofs), @@ -116,10 +115,10 @@ impl From for NoteAuthenticationInfoProto { } } -impl TryFrom for NoteAuthenticationInfo { +impl TryFrom for NoteAuthenticationInfo { type Error = ConversionError; - fn try_from(value: NoteAuthenticationInfoProto) -> Result { + fn try_from(value: proto::NoteAuthenticationInfo) -> Result { let result = Self { block_proofs: try_convert(value.block_proofs)?, note_proofs: try_convert(&value.note_proofs)?, diff --git a/crates/proto/src/domain/nullifiers.rs b/crates/proto/src/domain/nullifiers.rs index 0b3cc0d0c..3f7bebc03 100644 --- a/crates/proto/src/domain/nullifiers.rs +++ b/crates/proto/src/domain/nullifiers.rs @@ -5,19 +5,19 @@ use miden_objects::{ use crate::{ errors::{ConversionError, MissingFieldHelper}, - generated::{digest::Digest, responses::NullifierBlockInputRecord}, + generated as proto, }; // FROM NULLIFIER // ================================================================================================ -impl From<&Nullifier> for Digest { +impl From<&Nullifier> for proto::digest::Digest { fn from(value: &Nullifier) -> Self { (*value).inner().into() } } -impl From for Digest { +impl From for proto::digest::Digest { fn from(value: Nullifier) -> Self { value.inner().into() } @@ -26,10 +26,10 @@ impl From for Digest { // INTO NULLIFIER // ================================================================================================ -impl TryFrom for Nullifier { +impl TryFrom for Nullifier { type Error = ConversionError; - fn try_from(value: Digest) -> Result { + fn try_from(value: proto::digest::Digest) -> Result { let digest: RpoDigest = value.try_into()?; Ok(digest.into()) } @@ -44,24 +44,30 @@ pub struct NullifierWitness { pub proof: SmtProof, } -impl TryFrom for NullifierWitness { +impl TryFrom for NullifierWitness { type Error = ConversionError; - fn try_from(nullifier_input_record: NullifierBlockInputRecord) -> Result { + fn try_from( + nullifier_input_record: proto::responses::NullifierBlockInputRecord, + ) -> Result { Ok(Self { nullifier: nullifier_input_record .nullifier - .ok_or(NullifierBlockInputRecord::missing_field(stringify!(nullifier)))? + .ok_or(proto::responses::NullifierBlockInputRecord::missing_field(stringify!( + nullifier + )))? .try_into()?, proof: nullifier_input_record .opening - .ok_or(NullifierBlockInputRecord::missing_field(stringify!(opening)))? + .ok_or(proto::responses::NullifierBlockInputRecord::missing_field(stringify!( + opening + )))? .try_into()?, }) } } -impl From for NullifierBlockInputRecord { +impl From for proto::responses::NullifierBlockInputRecord { fn from(value: NullifierWitness) -> Self { Self { nullifier: Some(value.nullifier.into()), diff --git a/crates/proto/src/domain/transactions.rs b/crates/proto/src/domain/transactions.rs index 5f22cad37..d4f71c32f 100644 --- a/crates/proto/src/domain/transactions.rs +++ b/crates/proto/src/domain/transactions.rs @@ -1,32 +1,29 @@ use miden_objects::{crypto::hash::rpo::RpoDigest, transaction::TransactionId}; -use crate::{ - errors::ConversionError, - generated::{digest::Digest, transaction::TransactionId as TransactionIdPb}, -}; +use crate::{errors::ConversionError, generated as proto}; // FROM TRANSACTION ID // ================================================================================================ -impl From<&TransactionId> for Digest { +impl From<&TransactionId> for proto::digest::Digest { fn from(value: &TransactionId) -> Self { (*value).inner().into() } } -impl From for Digest { +impl From for proto::digest::Digest { fn from(value: TransactionId) -> Self { value.inner().into() } } -impl From<&TransactionId> for TransactionIdPb { +impl From<&TransactionId> for proto::transaction::TransactionId { fn from(value: &TransactionId) -> Self { - TransactionIdPb { id: Some(value.into()) } + proto::transaction::TransactionId { id: Some(value.into()) } } } -impl From for TransactionIdPb { +impl From for proto::transaction::TransactionId { fn from(value: TransactionId) -> Self { (&value).into() } @@ -35,19 +32,19 @@ impl From for TransactionIdPb { // INTO TRANSACTION ID // ================================================================================================ -impl TryFrom for TransactionId { +impl TryFrom for TransactionId { type Error = ConversionError; - fn try_from(value: Digest) -> Result { + fn try_from(value: proto::digest::Digest) -> Result { let digest: RpoDigest = value.try_into()?; Ok(digest.into()) } } -impl TryFrom for TransactionId { +impl TryFrom for TransactionId { type Error = ConversionError; - fn try_from(value: TransactionIdPb) -> Result { + fn try_from(value: proto::transaction::TransactionId) -> Result { value .id .ok_or(ConversionError::MissingFieldInProtobufRepresentation { diff --git a/crates/proto/src/generated/requests.rs b/crates/proto/src/generated/requests.rs index 16b7ba794..b28e0e3cf 100644 --- a/crates/proto/src/generated/requests.rs +++ b/crates/proto/src/generated/requests.rs @@ -115,12 +115,6 @@ pub struct GetNoteAuthenticationInfoRequest { #[prost(message, repeated, tag = "1")] pub note_ids: ::prost::alloc::vec::Vec, } -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct ListNullifiersRequest {} -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct ListAccountsRequest {} -#[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct ListNotesRequest {} /// Returns the latest state of an account with the specified ID. #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetAccountDetailsRequest { @@ -156,7 +150,8 @@ pub struct GetAccountProofsRequest { pub account_requests: ::prost::alloc::vec::Vec< get_account_proofs_request::AccountRequest, >, - /// Optional flag to include header and account code in the response. False by default. + /// Optional flag to include account headers and account code in the response. If false, storage + /// requests are also ignored. False by default. #[prost(bool, optional, tag = "2")] pub include_headers: ::core::option::Option, /// Account code commitments corresponding to the last-known `AccountCode` for requested diff --git a/crates/proto/src/generated/responses.rs b/crates/proto/src/generated/responses.rs index a996cdda9..46ce0cd3d 100644 --- a/crates/proto/src/generated/responses.rs +++ b/crates/proto/src/generated/responses.rs @@ -160,24 +160,6 @@ pub struct GetNoteAuthenticationInfoResponse { pub proofs: ::core::option::Option, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ListNullifiersResponse { - /// Lists all nullifiers of the current chain - #[prost(message, repeated, tag = "1")] - pub nullifiers: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ListAccountsResponse { - /// Lists all accounts of the current chain - #[prost(message, repeated, tag = "1")] - pub accounts: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ListNotesResponse { - /// Lists all notes of the current chain - #[prost(message, repeated, tag = "1")] - pub notes: ::prost::alloc::vec::Vec, -} -#[derive(Clone, PartialEq, ::prost::Message)] pub struct GetAccountDetailsResponse { /// Account info (with details for public accounts) #[prost(message, optional, tag = "1")] @@ -233,7 +215,7 @@ pub struct AccountStateHeader { pub account_code: ::core::option::Option<::prost::alloc::vec::Vec>, /// Storage slots information for this account #[prost(message, repeated, tag = "4")] - pub storage_slots: ::prost::alloc::vec::Vec, + pub storage_maps: ::prost::alloc::vec::Vec, } /// Represents a single storage slot with the reuqested keys and their respective values. #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/proto/src/generated/store.rs b/crates/proto/src/generated/store.rs index 0a9055a9a..7d979f379 100644 --- a/crates/proto/src/generated/store.rs +++ b/crates/proto/src/generated/store.rs @@ -388,71 +388,6 @@ pub mod api_client { .insert(GrpcMethod::new("store.Api", "GetTransactionInputs")); self.inner.unary(req, path, codec).await } - pub async fn list_accounts( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static("/store.Api/ListAccounts"); - let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("store.Api", "ListAccounts")); - self.inner.unary(req, path, codec).await - } - pub async fn list_notes( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static("/store.Api/ListNotes"); - let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("store.Api", "ListNotes")); - self.inner.unary(req, path, codec).await - } - pub async fn list_nullifiers( - &mut self, - request: impl tonic::IntoRequest< - super::super::requests::ListNullifiersRequest, - >, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::unknown( - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static("/store.Api/ListNullifiers"); - let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("store.Api", "ListNullifiers")); - self.inner.unary(req, path, codec).await - } pub async fn sync_notes( &mut self, request: impl tonic::IntoRequest, @@ -600,27 +535,6 @@ pub mod api_server { tonic::Response, tonic::Status, >; - async fn list_accounts( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - async fn list_notes( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - async fn list_nullifiers( - &self, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; async fn sync_notes( &self, request: tonic::Request, @@ -1291,150 +1205,6 @@ pub mod api_server { }; Box::pin(fut) } - "/store.Api/ListAccounts" => { - #[allow(non_camel_case_types)] - struct ListAccountsSvc(pub Arc); - impl< - T: Api, - > tonic::server::UnaryService< - super::super::requests::ListAccountsRequest, - > for ListAccountsSvc { - type Response = super::super::responses::ListAccountsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::super::requests::ListAccountsRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::list_accounts(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = ListAccountsSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/store.Api/ListNotes" => { - #[allow(non_camel_case_types)] - struct ListNotesSvc(pub Arc); - impl< - T: Api, - > tonic::server::UnaryService< - super::super::requests::ListNotesRequest, - > for ListNotesSvc { - type Response = super::super::responses::ListNotesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::super::requests::ListNotesRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::list_notes(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = ListNotesSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/store.Api/ListNullifiers" => { - #[allow(non_camel_case_types)] - struct ListNullifiersSvc(pub Arc); - impl< - T: Api, - > tonic::server::UnaryService< - super::super::requests::ListNullifiersRequest, - > for ListNullifiersSvc { - type Response = super::super::responses::ListNullifiersResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - super::super::requests::ListNullifiersRequest, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::list_nullifiers(&inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let method = ListNullifiersSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } "/store.Api/SyncNotes" => { #[allow(non_camel_case_types)] struct SyncNotesSvc(pub Arc); diff --git a/crates/rpc-proto/proto/requests.proto b/crates/rpc-proto/proto/requests.proto index f427a8995..62ec750a1 100644 --- a/crates/rpc-proto/proto/requests.proto +++ b/crates/rpc-proto/proto/requests.proto @@ -104,12 +104,6 @@ message GetNoteAuthenticationInfoRequest { repeated digest.Digest note_ids = 1; } -message ListNullifiersRequest {} - -message ListAccountsRequest {} - -message ListNotesRequest {} - // Returns the latest state of an account with the specified ID. message GetAccountDetailsRequest { // Account ID to get details. @@ -156,7 +150,8 @@ message GetAccountProofsRequest { // A list of account requests, including map keys + values. repeated AccountRequest account_requests = 1; - // Optional flag to include header and account code in the response. False by default. + // Optional flag to include account headers and account code in the response. If false, storage + // requests are also ignored. False by default. optional bool include_headers = 2; // Account code commitments corresponding to the last-known `AccountCode` for requested diff --git a/crates/rpc-proto/proto/responses.proto b/crates/rpc-proto/proto/responses.proto index ccce92c84..530a5dc18 100644 --- a/crates/rpc-proto/proto/responses.proto +++ b/crates/rpc-proto/proto/responses.proto @@ -144,21 +144,6 @@ message GetNoteAuthenticationInfoResponse { note.NoteAuthenticationInfo proofs = 1; } -message ListNullifiersResponse { - // Lists all nullifiers of the current chain - repeated smt.SmtLeafEntry nullifiers = 1; -} - -message ListAccountsResponse { - // Lists all accounts of the current chain - repeated account.AccountInfo accounts = 1; -} - -message ListNotesResponse { - // Lists all notes of the current chain - repeated note.Note notes = 1; -} - message GetAccountDetailsResponse { // Account info (with details for public accounts) account.AccountInfo details = 1; @@ -204,7 +189,7 @@ message AccountStateHeader { optional bytes account_code = 3; // Storage slots information for this account - repeated StorageSlotMapProof storage_slots = 4; + repeated StorageSlotMapProof storage_maps = 4; } // Represents a single storage slot with the reuqested keys and their respective values. diff --git a/crates/rpc-proto/proto/store.proto b/crates/rpc-proto/proto/store.proto index ec5a11270..b51198b96 100644 --- a/crates/rpc-proto/proto/store.proto +++ b/crates/rpc-proto/proto/store.proto @@ -20,9 +20,6 @@ service Api { rpc GetNoteAuthenticationInfo(requests.GetNoteAuthenticationInfoRequest) returns (responses.GetNoteAuthenticationInfoResponse) {} rpc GetNotesById(requests.GetNotesByIdRequest) returns (responses.GetNotesByIdResponse) {} rpc GetTransactionInputs(requests.GetTransactionInputsRequest) returns (responses.GetTransactionInputsResponse) {} - rpc ListAccounts(requests.ListAccountsRequest) returns (responses.ListAccountsResponse) {} - rpc ListNotes(requests.ListNotesRequest) returns (responses.ListNotesResponse) {} - rpc ListNullifiers(requests.ListNullifiersRequest) returns (responses.ListNullifiersResponse) {} rpc SyncNotes(requests.SyncNoteRequest) returns (responses.SyncNoteResponse) {} rpc SyncState(requests.SyncStateRequest) returns (responses.SyncStateResponse) {} } diff --git a/crates/store/README.md b/crates/store/README.md index 036d3f272..a201956df 100644 --- a/crates/store/README.md +++ b/crates/store/README.md @@ -2,6 +2,8 @@ The **Store** maintains the state of the chain. It serves as the "source of truth" for the chain - i.e., if it is not in the store, the node does not consider it to be part of the chain. +Incoming requests to the store are trusted because they are validated in the RPC component. + **Store** is one of components of the [Miden node](..). ## Architecture @@ -16,7 +18,7 @@ The Store can be installed and run as part of [Miden node](../README.md#installi ## API -The **Store** serves connections using the [gRPC protocol](https://grpc.io) on a port, set in the previously mentioned configuration file. +The **Store** serves connections using the [gRPC protocol](https://grpc.io) on a port, set in the previously mentioned configuration file. Here is a brief description of supported methods. ### ApplyBlock @@ -157,44 +159,6 @@ contains excessive notes and nullifiers, client can make additional filtering of - `nullifiers`: `[NullifierUpdate]` – a list of nullifiers created between `request.block_num + 1` and `response.block_header.block_num`. - Each `NullifierUpdate` consists of the `nullifier` and `block_num` the block number in which the note corresponding to that nullifier was consumed. -## Methods for testing purposes - -### ListNullifiers - -Lists all nullifiers of the current chain. - -**Parameters** - -This request doesn't have any parameters. - -**Returns** - -- `nullifiers`: `[NullifierLeaf]` – lists of all nullifiers of the current chain. - -### ListAccounts - -Lists all accounts of the current chain. - -**Parameters** - -This request doesn't have any parameters. - -**Returns** - -- `accounts`: `[AccountInfo]` – list of all accounts of the current chain. - -### ListNotes - -Lists all notes of the current chain. - -**Parameters** - -This request doesn't have any parameters. - -**Returns** - -- `notes`: `[Note]` – list of all notes of the current chain. - ## License This project is [MIT licensed](../../LICENSE). diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index cc8a12be0..abdd88d21 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -7,7 +7,7 @@ use std::{ use deadpool_sqlite::{Config as SqliteConfig, Hook, HookError, Pool, Runtime}; use miden_node_proto::{ domain::accounts::{AccountInfo, AccountSummary}, - generated::note::{Note as NotePb, NoteSyncRecord as NoteSyncRecordPb}, + generated::note as proto, }; use miden_objects::{ accounts::{AccountDelta, AccountId}, @@ -68,7 +68,7 @@ pub struct NoteRecord { pub merkle_path: MerklePath, } -impl From for NotePb { +impl From for proto::Note { fn from(note: NoteRecord) -> Self { Self { block_num: note.block_num, @@ -105,7 +105,7 @@ pub struct NoteSyncRecord { pub merkle_path: MerklePath, } -impl From for NoteSyncRecordPb { +impl From for proto::NoteSyncRecord { fn from(note: NoteSyncRecord) -> Self { Self { note_index: note.note_index.leaf_index_value().into(), @@ -232,22 +232,6 @@ impl Db { })? } - /// Loads all the notes from the DB. - #[instrument(target = COMPONENT, skip_all, ret(level = "debug"), err)] - pub async fn select_all_notes(&self) -> Result> { - self.pool.get().await?.interact(sql::select_all_notes).await.map_err(|err| { - DatabaseError::InteractError(format!("Select notes task failed: {err}")) - })? - } - - /// Loads all the accounts from the DB. - #[instrument(target = COMPONENT, skip_all, ret(level = "debug"), err)] - pub async fn select_all_accounts(&self) -> Result> { - self.pool.get().await?.interact(sql::select_all_accounts).await.map_err(|err| { - DatabaseError::InteractError(format!("Select accounts task failed: {err}")) - })? - } - /// Search for a [BlockHeader] from the database by its `block_num`. /// /// When `block_number` is [None], the latest block header is returned. diff --git a/crates/store/src/db/sql/mod.rs b/crates/store/src/db/sql/mod.rs index 359f4f79a..fa93a26ec 100644 --- a/crates/store/src/db/sql/mod.rs +++ b/crates/store/src/db/sql/mod.rs @@ -47,6 +47,7 @@ use crate::{ /// # Returns /// /// A vector with accounts, or an error. +#[cfg(test)] pub fn select_all_accounts(conn: &mut Connection) -> Result> { let mut stmt = conn.prepare_cached( " @@ -700,6 +701,7 @@ pub fn select_nullifiers_by_prefix( /// # Returns /// /// A vector with notes, or an error. +#[cfg(test)] pub fn select_all_notes(conn: &mut Connection) -> Result> { let mut stmt = conn.prepare_cached( " diff --git a/crates/store/src/server/api.rs b/crates/store/src/server/api.rs index 3de3dd529..1eb5f3627 100644 --- a/crates/store/src/server/api.rs +++ b/crates/store/src/server/api.rs @@ -16,19 +16,16 @@ use miden_node_proto::{ GetAccountDetailsRequest, GetAccountProofsRequest, GetAccountStateDeltaRequest, GetBlockByNumberRequest, GetBlockHeaderByNumberRequest, GetBlockInputsRequest, GetNoteAuthenticationInfoRequest, GetNotesByIdRequest, GetTransactionInputsRequest, - ListAccountsRequest, ListNotesRequest, ListNullifiersRequest, SyncNoteRequest, - SyncStateRequest, + SyncNoteRequest, SyncStateRequest, }, responses::{ AccountTransactionInputRecord, ApplyBlockResponse, CheckNullifiersByPrefixResponse, CheckNullifiersResponse, GetAccountDetailsResponse, GetAccountProofsResponse, GetAccountStateDeltaResponse, GetBlockByNumberResponse, GetBlockHeaderByNumberResponse, GetBlockInputsResponse, GetNoteAuthenticationInfoResponse, GetNotesByIdResponse, - GetTransactionInputsResponse, ListAccountsResponse, ListNotesResponse, - ListNullifiersResponse, NullifierTransactionInputRecord, NullifierUpdate, + GetTransactionInputsResponse, NullifierTransactionInputRecord, NullifierUpdate, SyncNoteResponse, SyncStateResponse, }, - smt::SmtLeafEntry, store::api_server, transaction::TransactionSummary, }, @@ -40,7 +37,6 @@ use miden_objects::{ crypto::hash::rpo::RpoDigest, notes::{NoteId, Nullifier}, utils::{Deserializable, Serializable}, - Felt, ZERO, }; use tonic::{Request, Response, Status}; use tracing::{debug, info, instrument}; @@ -531,64 +527,6 @@ impl api_server::Api for StoreApi { Ok(Response::new(GetAccountStateDeltaResponse { delta })) } - - // TESTING ENDPOINTS - // -------------------------------------------------------------------------------------------- - - /// Returns a list of all nullifiers - #[instrument( - target = COMPONENT, - name = "store:list_nullifiers", - skip_all, - ret(level = "debug"), - err - )] - async fn list_nullifiers( - &self, - _request: Request, - ) -> Result, Status> { - let raw_nullifiers = self.state.list_nullifiers().await?; - let nullifiers = raw_nullifiers - .into_iter() - .map(|(key, block_num)| SmtLeafEntry { - key: Some(key.into()), - value: Some([Felt::from(block_num), ZERO, ZERO, ZERO].into()), - }) - .collect(); - Ok(Response::new(ListNullifiersResponse { nullifiers })) - } - - /// Returns a list of all notes - #[instrument( - target = COMPONENT, - name = "store:list_notes", - skip_all, - ret(level = "debug"), - err - )] - async fn list_notes( - &self, - _request: Request, - ) -> Result, Status> { - let notes = self.state.list_notes().await?.into_iter().map(Into::into).collect(); - Ok(Response::new(ListNotesResponse { notes })) - } - - /// Returns a list of all accounts - #[instrument( - target = COMPONENT, - name = "store:list_accounts", - skip_all, - ret(level = "debug"), - err - )] - async fn list_accounts( - &self, - _request: Request, - ) -> Result, Status> { - let accounts = self.state.list_accounts().await?.iter().map(Into::into).collect(); - Ok(Response::new(ListAccountsResponse { accounts })) - } } // UTILITIES diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index 47afe0a3e..4da0cac78 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -5,6 +5,7 @@ use std::{ collections::{BTreeMap, BTreeSet}, + ops::Not, sync::Arc, }; @@ -670,22 +671,6 @@ impl State { }) } - /// Lists all known nullifiers with their inclusion blocks, intended for testing. - pub async fn list_nullifiers(&self) -> Result, DatabaseError> { - self.db.select_all_nullifiers().await - } - - /// Lists all known accounts, with their ids, latest state hash, and block at which the account - /// was last modified, intended for testing. - pub async fn list_accounts(&self) -> Result, DatabaseError> { - self.db.select_all_accounts().await - } - - /// Lists all known notes, intended for testing. - pub async fn list_notes(&self) -> Result, DatabaseError> { - self.db.select_all_notes().await - } - /// Returns details for public (on-chain) account. pub async fn get_account_details(&self, id: AccountId) -> Result { self.db.select_account(id).await @@ -695,7 +680,7 @@ impl State { pub async fn get_account_proofs( &self, account_requests: Vec, - request_code_commitments: BTreeSet, + known_code_commitments: BTreeSet, include_headers: bool, ) -> Result<(BlockNumber, Vec), DatabaseError> { // Lock inner state for the whole operation. We need to hold this lock to prevent the @@ -719,14 +704,14 @@ impl State { let mut headers_map = BTreeMap::new(); - for account_info in infos { - if let Some(details) = account_info.details { - let request = - match account_requests.iter().find(|req| req.account_id == details.id()) { - Some(r) => r, - None => panic!(), - }; + // Iterate and build state headers for public accounts + for request in account_requests { + let account_info = infos + .iter() + .find(|info| info.summary.account_id == request.account_id) + .expect("retrieved accounts were validated against request"); + if let Some(details) = &account_info.details { let mut storage_slot_map_keys = Vec::new(); for StorageMapKeysProof { storage_index, storage_keys } in @@ -744,24 +729,22 @@ impl State { }; storage_slot_map_keys.push(slot_map_key); } + } else { + return Err(AccountError::StorageSlotNotMap(*storage_index).into()); } - // TODO: else error? } - // Only return code for commitments that the user did not know according ot - // the request - let maybe_account_code = - if !request_code_commitments.contains(&details.code().commitment()) { - Some(details.code().to_bytes()) - } else { - None - }; + // Only include unknown account codes + let account_code = known_code_commitments + .contains(&details.code().commitment()) + .not() + .then(|| details.code().to_bytes()); let state_header = AccountStateHeader { - header: Some(AccountHeader::from(&details).into()), + header: Some(AccountHeader::from(details).into()), storage_header: details.storage().get_header().to_bytes(), - account_code: maybe_account_code, - storage_slots: storage_slot_map_keys, + account_code, + storage_maps: storage_slot_map_keys, }; headers_map.insert(account_info.summary.account_id, state_header); @@ -776,7 +759,6 @@ impl State { .map(|account_id| { let acc_leaf_idx = LeafIndex::new_max_depth(account_id.prefix().into()); let opening = inner_state.account_tree.open(&acc_leaf_idx); - let state_header = state_headers.get(&account_id).cloned(); AccountProofsResponse { diff --git a/proto/requests.proto b/proto/requests.proto index f427a8995..62ec750a1 100644 --- a/proto/requests.proto +++ b/proto/requests.proto @@ -104,12 +104,6 @@ message GetNoteAuthenticationInfoRequest { repeated digest.Digest note_ids = 1; } -message ListNullifiersRequest {} - -message ListAccountsRequest {} - -message ListNotesRequest {} - // Returns the latest state of an account with the specified ID. message GetAccountDetailsRequest { // Account ID to get details. @@ -156,7 +150,8 @@ message GetAccountProofsRequest { // A list of account requests, including map keys + values. repeated AccountRequest account_requests = 1; - // Optional flag to include header and account code in the response. False by default. + // Optional flag to include account headers and account code in the response. If false, storage + // requests are also ignored. False by default. optional bool include_headers = 2; // Account code commitments corresponding to the last-known `AccountCode` for requested diff --git a/proto/responses.proto b/proto/responses.proto index ccce92c84..530a5dc18 100644 --- a/proto/responses.proto +++ b/proto/responses.proto @@ -144,21 +144,6 @@ message GetNoteAuthenticationInfoResponse { note.NoteAuthenticationInfo proofs = 1; } -message ListNullifiersResponse { - // Lists all nullifiers of the current chain - repeated smt.SmtLeafEntry nullifiers = 1; -} - -message ListAccountsResponse { - // Lists all accounts of the current chain - repeated account.AccountInfo accounts = 1; -} - -message ListNotesResponse { - // Lists all notes of the current chain - repeated note.Note notes = 1; -} - message GetAccountDetailsResponse { // Account info (with details for public accounts) account.AccountInfo details = 1; @@ -204,7 +189,7 @@ message AccountStateHeader { optional bytes account_code = 3; // Storage slots information for this account - repeated StorageSlotMapProof storage_slots = 4; + repeated StorageSlotMapProof storage_maps = 4; } // Represents a single storage slot with the reuqested keys and their respective values. diff --git a/proto/store.proto b/proto/store.proto index ec5a11270..b51198b96 100644 --- a/proto/store.proto +++ b/proto/store.proto @@ -20,9 +20,6 @@ service Api { rpc GetNoteAuthenticationInfo(requests.GetNoteAuthenticationInfoRequest) returns (responses.GetNoteAuthenticationInfoResponse) {} rpc GetNotesById(requests.GetNotesByIdRequest) returns (responses.GetNotesByIdResponse) {} rpc GetTransactionInputs(requests.GetTransactionInputsRequest) returns (responses.GetTransactionInputsResponse) {} - rpc ListAccounts(requests.ListAccountsRequest) returns (responses.ListAccountsResponse) {} - rpc ListNotes(requests.ListNotesRequest) returns (responses.ListNotesResponse) {} - rpc ListNullifiers(requests.ListNullifiersRequest) returns (responses.ListNullifiersResponse) {} rpc SyncNotes(requests.SyncNoteRequest) returns (responses.SyncNoteResponse) {} rpc SyncState(requests.SyncStateRequest) returns (responses.SyncStateResponse) {} }