diff --git a/CHANGELOG.md b/CHANGELOG.md index fcbd2a16c..d26af6568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ - [BREAKING] Remove public store API `GetAccountStateDelta` ([#1162](https://github.com/0xMiden/miden-node/pull/1162)). - Removed `faucet` binary ([#1172](https://github.com/0xMiden/miden-node/pull/1172)). - Add `genesis_commitment` in `Status` response ([#1181](https://github.com/0xMiden/miden-node/pull/1181)). +- [BREAKING] `GetAccountProofs` endpoint uses a `PartialSmt` for proofs. ([#1158](https://github.com/0xMiden/miden-node/pull/1158)). ### Fixes diff --git a/crates/proto/src/generated/rpc_store.rs b/crates/proto/src/generated/rpc_store.rs index dd6679f0a..ab5cc0d47 100644 --- a/crates/proto/src/generated/rpc_store.rs +++ b/crates/proto/src/generated/rpc_store.rs @@ -99,14 +99,26 @@ pub mod account_proofs { /// 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_maps: ::prost::alloc::vec::Vec< - account_state_header::StorageSlotMapProof, + /// A sparse merkle tree per storage slot, including all relevant merkle proofs for storage entries. + #[prost(message, repeated, tag = "5")] + pub partial_storage_smts: ::prost::alloc::vec::Vec< + account_state_header::StorageSlotMap, >, } /// Nested message and enum types in `AccountStateHeader`. pub mod account_state_header { + /// Represents a single storage slot with the requested keys and their respective values. + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct StorageSlotMap { + /// The storage slot index (\[0..255\]). + #[prost(uint32, tag = "1")] + pub storage_slot: u32, + /// Merkle proofs of the map value as partial sparse merkle tree for compression. + /// The respective rust types is `SparseMerkleTree` and the transformation to and from + /// bytes is done via the traits `Serializable::to_bytes` and `Deserializable::from_bytes`. + #[prost(bytes = "vec", tag = "2")] + pub partial_smt: ::prost::alloc::vec::Vec, + } /// Represents a single storage slot with the requested keys and their respective values. #[derive(Clone, PartialEq, ::prost::Message)] pub struct StorageSlotMapProof { diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs index d9858d33d..472fd164f 100644 --- a/crates/store/src/state.rs +++ b/crates/store/src/state.rs @@ -37,6 +37,7 @@ use miden_objects::crypto::merkle::{ MmrPeaks, MmrProof, PartialMmr, + PartialSmt, SmtProof, }; use miden_objects::note::{NoteDetails, NoteId, Nullifier}; @@ -919,8 +920,7 @@ impl State { .expect("retrieved accounts were validated against request"); if let Some(details) = &account_info.details { - let mut storage_slot_map_keys = Vec::new(); - + let mut partials = BTreeMap::::default(); for StorageMapKeysProof { storage_index, storage_keys } in &request.storage_requests { @@ -928,13 +928,12 @@ impl State { details.storage().slots().get(*storage_index as usize) { for map_key in storage_keys { + // only add the required storage keys to the partial representation let proof = storage_map.open(map_key); - - let slot_map_key = proto::rpc_store::account_proofs::account_proof::account_state_header::StorageSlotMapProof { - storage_slot: u32::from(*storage_index), - smt_proof: proof.to_bytes(), - }; - storage_slot_map_keys.push(slot_map_key); + partials + .entry(*storage_index) + .or_insert_with(PartialSmt::new) + .add_proof(proof)?; } } else { return Err(AccountError::StorageSlotNotMap(*storage_index).into()); @@ -947,12 +946,19 @@ impl State { .not() .then(|| details.code().to_bytes()); + let partial_storage_smts = Vec::from_iter( + partials.into_iter() + .map(|(slot, partial_smt)| proto::rpc_store::account_proofs::account_proof::account_state_header::StorageSlotMap { + storage_slot: u32::from(slot), + partial_smt: partial_smt.to_bytes(), + }) + ); let state_header = proto::rpc_store::account_proofs::account_proof::AccountStateHeader { header: Some(AccountHeader::from(details).into()), storage_header: details.storage().to_header().to_bytes(), account_code, - storage_maps: storage_slot_map_keys, + partial_storage_smts, }; headers_map.insert(account_info.summary.account_id, state_header); diff --git a/proto/proto/store/rpc.proto b/proto/proto/store/rpc.proto index eb7ce863d..220b7f8f9 100644 --- a/proto/proto/store/rpc.proto +++ b/proto/proto/store/rpc.proto @@ -138,6 +138,17 @@ message AccountProofs { message AccountProof { // State header for public accounts. message AccountStateHeader { + // Represents a single storage slot with the requested keys and their respective values. + message StorageSlotMap { + // The storage slot index ([0..255]). + uint32 storage_slot = 1; + + // Merkle proofs of the map value as partial sparse merkle tree for compression. + // The respective rust types is `SparseMerkleTree` and the transformation to and from + // bytes is done via the traits `Serializable::to_bytes` and `Deserializable::from_bytes`. + bytes partial_smt = 2; + } + // Represents a single storage slot with the requested keys and their respective values. message StorageSlotMapProof { // The storage slot index ([0..255]). @@ -157,8 +168,8 @@ message AccountProofs { // the current one. optional bytes account_code = 3; - // Storage slots information for this account - repeated StorageSlotMapProof storage_maps = 4; + // A sparse merkle tree per storage slot, including all relevant merkle proofs for storage entries. + repeated StorageSlotMap partial_storage_smts = 5; } // The account witness for the current state commitment of one account ID.