From 845c773a4f568388eea28418883765664d7d7938 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Tue, 26 May 2026 11:41:26 -0300 Subject: [PATCH 1/5] feat: address #2095 review comments --- CHANGELOG.md | 1 + Cargo.lock | 100 +++++++++--------- bin/network-monitor/src/deploy/wallet.rs | 4 +- bin/ntx-builder/src/actor/mod.rs | 17 ++- bin/ntx-builder/src/builder.rs | 10 +- bin/ntx-builder/src/committed_block.rs | 7 +- bin/ntx-builder/src/coordinator.rs | 34 +++--- bin/ntx-builder/src/db/mod.rs | 14 +-- .../src/db/models/account_effect.rs | 18 +--- bin/ntx-builder/src/db/models/conv.rs | 12 +-- .../src/db/models/queries/accounts.rs | 11 +- bin/ntx-builder/src/db/models/queries/mod.rs | 23 ++-- .../src/db/models/queries/notes.rs | 9 +- bin/ntx-builder/src/test_utils.rs | 22 ++-- .../src/mempool/subscription.rs | 6 +- crates/db/src/conv.rs | 3 + .../large-smt-backend-rocksdb/src/rocksdb.rs | 4 + crates/proto/Cargo.toml | 3 +- crates/proto/src/domain/account.rs | 98 ----------------- crates/proto/src/domain/merkle.rs | 9 +- crates/proto/src/domain/note.rs | 38 ++++++- crates/rpc/src/server/api.rs | 85 ++++++++------- crates/rpc/src/tests.rs | 48 ++++----- crates/store/build.rs | 19 ++-- crates/store/src/db/mod.rs | 2 +- crates/store/src/db/models/conv.rs | 4 + .../store/src/db/models/queries/accounts.rs | 68 ++++-------- crates/store/src/errors.rs | 3 - crates/store/src/genesis/config/mod.rs | 34 +++--- .../src/genesis/config/samples/01-simple.toml | 8 +- .../agglayer_faucet_eth.mac | Bin 18053 -> 16279 bytes .../agglayer_faucet_usdc.mac | Bin 18053 -> 16279 bytes .../samples/02-with-account-files/bridge.mac | Bin 40163 -> 37762 bytes crates/store/src/genesis/config/tests.rs | 6 +- crates/store/src/server/rpc_api.rs | 10 +- crates/store/src/state/loader.rs | 5 +- crates/store/src/state/mod.rs | 4 +- crates/utils/src/crypto.rs | 4 +- docs/external/src/operator/usage.md | 8 +- proto/proto/internal/store.proto | 6 +- proto/proto/types/note.proto | 6 +- 41 files changed, 314 insertions(+), 449 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4fd60ec9f..575e6b3b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ - [BREAKING] Changed `SyncChainMmr` endpoint: the upper end of the block range we're syncing is now the chain tip with the requested finality level. Validator signature is also returned ([#2075](https://github.com/0xMiden/node/pull/2075)). - [BREAKING] Renamed `SubmitProvenTransaction` RPC endpoint to `SubmitProvenTx` ([#2094](https://github.com/0xMiden/node/pull/2094)). - [BREAKING] Renamed `SubmitProvenBatch` RPC endpoint to `SubmitProvenTxBatch` ([#2094](https://github.com/0xMiden/node/pull/2094)). +- [BREAKING] Updated `miden-protocol` to `v0.15.0` and `miden-crypto` to `v0.25`. Faucet-vs-wallet distinction is now component-based; the former `AccountStorageMode` is renamed to `AccountType` with only `Public`/`Private` variants. Genesis config `storage_mode` is renamed to `account_type`, the `Network` variant is removed, and the implicit default for wallets and faucets is now `Private`. The `has_updatable_code` wallet field is removed. `NetworkAccountId` is removed; network-account identity is now a plain `AccountId`. Fixed `proto::note::NoteHeader` so it round-trips losslessly (wire field is now `details_commitment` instead of `note_id`) ([#2095](https://github.com/0xMiden/node/pull/2095), [#2132](https://github.com/0xMiden/node/pull/2132)). - Updated `miden-protocol` and bumped `miden-crypto` to `v0.25`. `AccountId::is_network()` was removed upstream, so `SubmitProvenTx` and `SubmitProvenTxBatch` now consult the store to classify post-deployment public-account transactions as network accounts. - [BREAKING] Removed `Network` variant from genesis config `StorageMode`. The implicit default for wallets and fungible faucets is now `Private` (previously `Network`, which mapped to `Public` storage) ([#2095](https://github.com/0xMiden/node/pull/2095)). - [BREAKING] Updated `miden-protocol` family of crates to the published `v0.15.0` on crates.io (previously tracked the `next` branch). The published release removes the multi-variant `AccountType` (`RegularAccount*`, `FungibleFaucet`, `NonFungibleFaucet`) and renames the former `AccountStorageMode` to `AccountType` with only `Public`/`Private`. Faucet-vs-wallet distinction is now component-based. diff --git a/Cargo.lock b/Cargo.lock index 22a42da5d7..4ec36ac5ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2933,9 +2933,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miden-ace-codegen" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c5e3d08008b5f9dd3e6145e55e4d3c1a4e47a74383dd8d0d64003455ee94510" +checksum = "ed5cec5dea133d84f194fdb0e1fc1915f37ec3314cc4c847ef3d6ef1f91a29a1" dependencies = [ "miden-core", "miden-crypto", @@ -2966,9 +2966,9 @@ dependencies = [ [[package]] name = "miden-air" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d980fa23010e53c43077cf0182e6e88acd1a099abbf8a98cd298aa69b86688dc" +checksum = "a04e2a9fe12abe40a7a3b10f0184ead7105a081d9940d927d32120544a84c2b3" dependencies = [ "miden-ace-codegen", "miden-core", @@ -2982,9 +2982,9 @@ dependencies = [ [[package]] name = "miden-assembly" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb53abd723b7dd8ff1210b7d126f0d9e1d9127ea0e7f6a46471845d060cc43" +checksum = "d09158daf738e51eeb035ef4b71b9e9f9173d4b012d532034aca8d7b673e82fe" dependencies = [ "env_logger", "log", @@ -3000,9 +3000,9 @@ dependencies = [ [[package]] name = "miden-assembly-syntax" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02ead435c8dccfd3b9bd97779cfa0d74cc14c767a9740f162bf7cd49a0da26bd" +checksum = "fce9e789cfcf73408c792f116fda46f6e5bac4eebbcf6c8299c58cfc4629f677" dependencies = [ "aho-corasick", "env_logger", @@ -3035,9 +3035,9 @@ dependencies = [ [[package]] name = "miden-core" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7c7eecda385bcc66aea99ee3279ae7c78f38e3541f4c1d67d309ac32314ff4" +checksum = "c2315cc7abb7abee25889de16739685f584215dc0e4c77f873e7054d0e234712" dependencies = [ "derive_more", "log", @@ -3057,9 +3057,9 @@ dependencies = [ [[package]] name = "miden-core-lib" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99bc06c94dcb75e3e44220fe7623ab99585e7828f19b80534c48a9971d31bfd8" +checksum = "35bd6eafb6d904c09add69070c8c1ecc3dd5a954587a4a6f8fe0a32125dcbd6b" dependencies = [ "env_logger", "fs-err", @@ -3074,9 +3074,9 @@ dependencies = [ [[package]] name = "miden-crypto" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ae084a6d15d5d862760bcbdbb5caad41415ed455cd7ab2b53a012d21a431ed" +checksum = "35198bebd353cddc25ad4aafb5f4ef9e71b283d71c787b8938c575c16974135d" dependencies = [ "blake3", "cc", @@ -3119,9 +3119,9 @@ dependencies = [ [[package]] name = "miden-crypto-derive" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8523f6ac9b28782ca759920e536164e7eab7dbfaf9132721ca3e325c060df73" +checksum = "9068c6554db0e051f62913575de9949841a46b96ae92d4b7d28e1fed5d8f052b" dependencies = [ "quote", "syn 2.0.117", @@ -3129,9 +3129,9 @@ dependencies = [ [[package]] name = "miden-debug-types" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206f5346bef744da1ffae9874a22170a2d0c27dd20947d89182d16f5d86348f7" +checksum = "85e70c3163517092a462abb2ac502bb1d23c6717dcbc9f1358e712bcdcbc68f5" dependencies = [ "memchr", "miden-crypto", @@ -3148,9 +3148,9 @@ dependencies = [ [[package]] name = "miden-field" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8a48ba526c353bf583bba18ddf62ad4846945e85fcaf44614633276416b5d9" +checksum = "379a39db52cd932a95d4017a18b712ee53ed0f86cfedf8c63ed72d687a18a191" dependencies = [ "miden-serde-utils", "num-bigint", @@ -3205,9 +3205,9 @@ dependencies = [ [[package]] name = "miden-lifted-air" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00d97ada3bfd70d6cc4f1a13ced8cb509f72fb16b1b28c292366aee846d3220" +checksum = "789e0e469d1731012d8a018057317f31580611535c20d2a47c022213228cb733" dependencies = [ "p3-air", "p3-field", @@ -3218,9 +3218,9 @@ dependencies = [ [[package]] name = "miden-lifted-stark" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3601a30d39e08a31ecc5b5215c87b11623942d9610aebccda89a4a5bf757c4" +checksum = "f62cca91182917b22a47e150028b7c785df620a15b2974a39c64e2b1b7a889d3" dependencies = [ "miden-lifted-air", "miden-stark-transcript", @@ -3241,9 +3241,9 @@ dependencies = [ [[package]] name = "miden-mast-package" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae69cd4dc25b0993eb5fafcc99987abd3502d8a7a902248af4a597537dce600" +checksum = "c4b79ebfcb493b6a3be4e065eca5f9109bfdf83e3b4d894eb246a37af7711242" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -3610,9 +3610,9 @@ dependencies = [ [[package]] name = "miden-package-registry" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1d77c6f5942f3111fc56a19d638076ea77df3628efa97c7955259c609ca8dd" +checksum = "c8836d3302f2de24ca3a8299fbdbc9b4a90963825e7962ce198a8f84ffdbb0a6" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -3626,9 +3626,9 @@ dependencies = [ [[package]] name = "miden-processor" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70508a4a75989a47f03a0681df412b80040c76230c3d850f8f2b80f7a986d4aa" +checksum = "a29df38b4dce4b644862f4b314db80927f11fbd2d373221cf4d2cbb775e90360" dependencies = [ "itertools 0.14.0", "miden-air", @@ -3644,9 +3644,9 @@ dependencies = [ [[package]] name = "miden-project" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6338d92713845f57b137e4305db11ee8614b0cec9b1516470233ce64d53f6376" +checksum = "af05f1abc7e0a0ca5d28492e48534aab2265d5948330699d59a29a6e2bfcf072" dependencies = [ "miden-assembly-syntax", "miden-core", @@ -3691,9 +3691,9 @@ dependencies = [ [[package]] name = "miden-prover" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59b5dbdac3a8596fc201de028c17ed5060ad554901259222420afd186f2b467" +checksum = "f2800c1923060c0da2f6e8d45259cccf34c87d4636f7d76446bda51c0dc07d48" dependencies = [ "bincode", "miden-air", @@ -3755,9 +3755,9 @@ dependencies = [ [[package]] name = "miden-serde-utils" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d899afcfbf85c851522f2dff73db1064116486fb8e0ebce0ade44ce72dadc075" +checksum = "d78cd1d4fcad937312e544f7d53423485e453598aa4fb989d2b6374027a8c136" dependencies = [ "p3-field", "p3-goldilocks", @@ -3782,9 +3782,9 @@ dependencies = [ [[package]] name = "miden-stark-transcript" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0e1f55bdff89f12ec6fc3881660a0d51d4e77f66026ab0e6ddcbd098a16f2f" +checksum = "05901db2e30d3954243960fe21cea7fbec39f97c27774b56fd5031c28c4881ba" dependencies = [ "p3-challenger", "p3-field", @@ -3794,9 +3794,9 @@ dependencies = [ [[package]] name = "miden-stateful-hasher" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0069bab01139a5660c7189589c95dfedf8449514d03641fd3f9d049e1b031f9" +checksum = "faeb47a90c55c5d45051d23cf691588804dd531995b4582c79108b64e445a905" dependencies = [ "p3-field", "p3-symmetric", @@ -3849,9 +3849,9 @@ dependencies = [ [[package]] name = "miden-utils-core-derive" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f74ef56bd44d23c805f662cd4f4639b04df6e0204d78806ede0e6f93d9695a5" +checksum = "9dc6b702574184af27e29d4441c213327521447b9683bac5018e429c91619aef" dependencies = [ "proc-macro2", "quote", @@ -3860,9 +3860,9 @@ dependencies = [ [[package]] name = "miden-utils-diagnostics" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7b3aa61e0ee0f8a5b3f44250b73879437f9e10b6061c3ae743fd1cdb1f56321" +checksum = "d7d66bc24d1770ae5392f54a33d3df9fcc02ee93a07d358cc763493ba7c88280" dependencies = [ "miden-crypto", "miden-debug-types", @@ -3872,9 +3872,9 @@ dependencies = [ [[package]] name = "miden-utils-indexing" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57419a0557e580e04125542f4383910fb997660633e15ad4570ce1ff2ae80557" +checksum = "7f87e39461905a7b3145c8903840821fc55107e94e73932baef03bbb46d25de9" dependencies = [ "miden-crypto", "proptest", @@ -3884,9 +3884,9 @@ dependencies = [ [[package]] name = "miden-utils-sync" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8a579c0c7cf44c123129045339f53b1f26f0aa2dc508494e97360d3ccab80e" +checksum = "7cb89b92fa49b29c57c0f4edfc6acfbdfc617ab0ead60ad0a9409bcda2e5bec9" dependencies = [ "lock_api", "loom", @@ -3925,9 +3925,9 @@ dependencies = [ [[package]] name = "miden-verifier" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49cd4ca9a8b90c48d5fc6f5707c46cc44b17285f2f705cbe44e5b56c430a85c8" +checksum = "18bed960749c3c078f56a25f7c396770af3e08a7815ff86144fd63d3b619797a" dependencies = [ "bincode", "miden-air", diff --git a/bin/network-monitor/src/deploy/wallet.rs b/bin/network-monitor/src/deploy/wallet.rs index 41710fc6a1..1eb70bdde0 100644 --- a/bin/network-monitor/src/deploy/wallet.rs +++ b/bin/network-monitor/src/deploy/wallet.rs @@ -1,7 +1,7 @@ //! Wallet account creation functionality. use anyhow::Result; -use miden_node_utils::crypto::get_rpo_random_coin; +use miden_node_utils::crypto::get_random_coin; use miden_protocol::account::auth::AuthScheme; use miden_protocol::account::{Account, AccountType}; use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey; @@ -19,7 +19,7 @@ use crate::COMPONENT; #[instrument(target = COMPONENT, name = "create-wallet-account", skip_all, ret(level = "debug"))] pub fn create_wallet_account() -> Result<(Account, SecretKey)> { let mut rng = ChaCha20Rng::from_seed(rand::random()); - let secret_key = SecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); + let secret_key = SecretKey::with_rng(&mut get_random_coin(&mut rng)); let auth = AuthMethod::SingleSig { approver: (secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2), }; diff --git a/bin/ntx-builder/src/actor/mod.rs b/bin/ntx-builder/src/actor/mod.rs index 3a79f4294c..55e6d2dfda 100644 --- a/bin/ntx-builder/src/actor/mod.rs +++ b/bin/ntx-builder/src/actor/mod.rs @@ -10,10 +10,10 @@ use allowlist::{NoteScriptNotAllowlisted, partition_by_allowlist}; use anyhow::Context; use candidate::TransactionCandidate; use futures::FutureExt; -use miden_node_proto::domain::account::NetworkAccountId; use miden_node_utils::ErrorReport; use miden_node_utils::lru_cache::LruCache; use miden_protocol::Word; +use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; use miden_protocol::note::{NoteScript, Nullifier}; use miden_protocol::transaction::TransactionId; @@ -196,7 +196,7 @@ enum ActorMode { /// actor exits of its own accord when idle for longer than [`ActorConfig::idle_timeout`]. pub struct AccountActor { /// The network account this actor is responsible for. - account_id: NetworkAccountId, + account_id: AccountId, /// gRPC clients used by the actor. clients: GrpcClients, /// Shared state accessed by the actor. @@ -213,7 +213,7 @@ pub struct AccountActor { impl AccountActor { /// Constructs a new account actor with the given configuration. pub fn new( - account_id: NetworkAccountId, + account_id: AccountId, actor_context: &AccountActorContext, notify: Arc, ) -> Self { @@ -328,7 +328,7 @@ impl AccountActor { /// Selects a transaction candidate by querying the DB. async fn select_candidate_from_db( &self, - account_id: NetworkAccountId, + account_id: AccountId, chain_state: ChainState, ) -> anyhow::Result> { let block_num = chain_state.chain_tip_header.block_num(); @@ -388,10 +388,7 @@ impl AccountActor { /// the coordinator will respawn a new actor when the account reappears through /// [`Coordinator::send_targeted`](crate::coordinator::Coordinator::send_targeted) or the /// account loader. - async fn wait_for_committed_account( - &self, - account_id: NetworkAccountId, - ) -> anyhow::Result { + async fn wait_for_committed_account(&self, account_id: AccountId) -> anyhow::Result { // Check if the account is already committed. if self .state @@ -439,7 +436,7 @@ impl AccountActor { #[tracing::instrument(name = "ntx.actor.execute_transactions", skip(self, tx_candidate))] async fn execute_transactions( &self, - account_id: NetworkAccountId, + account_id: AccountId, tx_candidate: TransactionCandidate, ) -> ActorMode { let block_num = tx_candidate.chain_tip_header.block_num(); @@ -606,7 +603,7 @@ end"; fn actor_with_request_handler( db: &Db, - account_id: NetworkAccountId, + account_id: AccountId, ) -> (AccountActor, AccountActorContext) { let (request_tx, request_rx) = mpsc::channel(8); let mut context = AccountActorContext::test(db); diff --git a/bin/ntx-builder/src/builder.rs b/bin/ntx-builder/src/builder.rs index 08a153fe2d..7781e891e5 100644 --- a/bin/ntx-builder/src/builder.rs +++ b/bin/ntx-builder/src/builder.rs @@ -25,12 +25,12 @@ use crate::server::NtxBuilderRpcServer; pub(crate) type BlockStream = Pin> + Send>>; -/// Network transaction builder component (PR 1: subscription-driven sync only). +/// Network transaction builder component. /// /// The builder consumes the RPC committed-block subscription and applies each block's -/// network-relevant effects to its local database. The actor execution path is wired back in a -/// subsequent PR; in this PR the binary stays up and keeps the local DB caught up to the live -/// chain tip without scheduling any network transactions. +/// network-relevant effects to its local database. The actor execution path is currently unwired; +/// the builder keeps the local DB caught up to the live chain tip without scheduling any network +/// transactions. pub struct NetworkTransactionBuilder { /// Configuration for the builder. config: NtxBuilderConfig, @@ -45,7 +45,7 @@ pub struct NetworkTransactionBuilder { /// restart. chain: ChainState, /// `false` until the first applied block whose `committed_chain_tip` matches the just-applied - /// block number. Stays `true` afterwards. Exposed so the gRPC status surface and PR 2's actor + /// block number. Stays `true` afterwards. Exposed so the gRPC status surface and future actor /// spawn gating can read it. is_synced: bool, } diff --git a/bin/ntx-builder/src/committed_block.rs b/bin/ntx-builder/src/committed_block.rs index 5217b37cd4..4a0c386341 100644 --- a/bin/ntx-builder/src/committed_block.rs +++ b/bin/ntx-builder/src/committed_block.rs @@ -1,4 +1,4 @@ -use miden_node_proto::domain::account::NetworkAccountId; +use miden_protocol::account::AccountId; use miden_protocol::account::delta::AccountUpdateDetails; use miden_protocol::block::{BlockHeader, SignedBlock}; use miden_protocol::note::Nullifier; @@ -14,7 +14,7 @@ pub struct CommittedBlockEffects { pub header: BlockHeader, pub network_notes: Vec, pub nullifiers: Vec, - pub network_account_updates: Vec<(NetworkAccountId, AccountUpdateDetails)>, + pub network_account_updates: Vec<(AccountId, AccountUpdateDetails)>, } impl CommittedBlockEffects { @@ -52,8 +52,7 @@ impl CommittedBlockEffects { if !account_id.is_public() { return None; } - let network_id = NetworkAccountId::new_unchecked(account_id); - Some((network_id, update.details().clone())) + Some((account_id, update.details().clone())) }) .collect(); diff --git a/bin/ntx-builder/src/coordinator.rs b/bin/ntx-builder/src/coordinator.rs index 8705c2fb36..45dbb167e9 100644 --- a/bin/ntx-builder/src/coordinator.rs +++ b/bin/ntx-builder/src/coordinator.rs @@ -2,8 +2,8 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; use miden_node_proto::domain::mempool::MempoolEvent; +use miden_protocol::account::AccountId; use miden_protocol::account::delta::AccountUpdateDetails; use tokio::sync::{Notify, Semaphore}; use tokio::task::JoinSet; @@ -17,7 +17,7 @@ use crate::db::Db; /// Result of writing a mempool event to the database. pub struct WriteEventResult { /// Accounts that should be notified of state changes. - pub accounts_to_notify: Vec, + pub accounts_to_notify: Vec, } // ACTOR HANDLE @@ -104,14 +104,14 @@ pub struct Coordinator { /// When actors are spawned, they register their notification handle here. When events need /// to be broadcast, this registry is used to locate the appropriate actors. The registry is /// automatically cleaned up when actors complete their execution. - actor_registry: HashMap, + actor_registry: HashMap, /// Join set for managing actor tasks and monitoring their completion status. /// /// This join set allows the coordinator to wait for actor task completion and handle /// different shutdown scenarios. When an actor task completes (either successfully or /// due to an error), the corresponding entry is removed from the actor registry. - actor_join_set: JoinSet<(NetworkAccountId, anyhow::Result<()>)>, + actor_join_set: JoinSet<(AccountId, anyhow::Result<()>)>, /// Semaphore for controlling the maximum number of concurrent transactions across all network /// accounts. @@ -130,7 +130,7 @@ pub struct Coordinator { /// When an actor shuts down due to a DB error, its crash count is incremented. Once /// the count reaches `max_account_crashes`, the account is deactivated and no new actor /// will be spawned for it. - crash_counts: HashMap, + crash_counts: HashMap, /// Maximum number of crashes an account actor is allowed before being deactivated. max_account_crashes: usize, @@ -156,11 +156,7 @@ impl Coordinator { /// and adds it to the coordinator's management system. The actor will be responsible for /// processing transactions and managing state for the network account. #[tracing::instrument(name = "ntx.builder.spawn_actor", skip(self, actor_context))] - pub fn spawn_actor( - &mut self, - account_id: NetworkAccountId, - actor_context: &AccountActorContext, - ) { + pub fn spawn_actor(&mut self, account_id: AccountId, actor_context: &AccountActorContext) { // Skip spawning if the account has been deactivated due to repeated crashes. if let Some(&count) = self.crash_counts.get(&account_id) { if count >= self.max_account_crashes { @@ -201,7 +197,7 @@ impl Coordinator { /// Only actors that are currently active are notified. Each actor will re-evaluate its state /// from the DB on the next iteration of its run loop. Notifications are coalesced: multiple /// notifications while an actor is busy result in a single wake-up. - pub fn notify_accounts(&self, account_ids: &[NetworkAccountId]) { + pub fn notify_accounts(&self, account_ids: &[AccountId]) { for account_id in account_ids { if let Some(handle) = self.actor_registry.get(account_id) { handle.notify(); @@ -220,7 +216,7 @@ impl Coordinator { /// /// Returns `Some(account_id)` if an actor should be respawned (because a /// notification arrived just as it shut down), or `None` otherwise. - pub async fn next(&mut self) -> anyhow::Result> { + pub async fn next(&mut self) -> anyhow::Result> { let actor_result = self.actor_join_set.join_next().await; match actor_result { Some(Ok((account_id, Ok(())))) => { @@ -265,7 +261,7 @@ impl Coordinator { /// Returns account IDs of note targets that do not have active actors (e.g. previously /// deactivated due to sterility). The caller can use this to re-activate actors for those /// accounts. - pub fn send_targeted(&self, event: &MempoolEvent) -> Vec { + pub fn send_targeted(&self, event: &MempoolEvent) -> Vec { let mut target_account_ids = HashSet::new(); let mut inactive_targets = Vec::new(); @@ -275,18 +271,16 @@ impl Coordinator { // external network transactions (once these are allowed). if let Some(AccountUpdateDetails::Delta(delta)) = account_delta { // The actor registry only contains accounts the builder has already classified as - // network. Wrap the id unconditionally and let the registry lookup filter for us; - // unknown accounts simply won't match. - let network_account_id = NetworkAccountId::new_unchecked(delta.id()); - if self.actor_registry.contains_key(&network_account_id) { - target_account_ids.insert(network_account_id); + // network, so the lookup itself filters out non-network ids. + let account_id = delta.id(); + if self.actor_registry.contains_key(&account_id) { + target_account_ids.insert(account_id); } } // Determine target actors for each note. for note in network_notes { let account = note.target_account_id(); - let account = NetworkAccountId::new_unchecked(account); if self.actor_registry.contains_key(&account) { target_account_ids.insert(account); @@ -369,7 +363,7 @@ mod tests { use crate::test_utils::*; /// Registers a dummy actor handle (no real actor task) in the coordinator's registry. - fn register_dummy_actor(coordinator: &mut Coordinator, account_id: NetworkAccountId) { + fn register_dummy_actor(coordinator: &mut Coordinator, account_id: AccountId) { let notify = Arc::new(Notify::new()); coordinator.actor_registry.insert(account_id, ActorHandle::new(notify)); } diff --git a/bin/ntx-builder/src/db/mod.rs b/bin/ntx-builder/src/db/mod.rs index 4c85efba0b..b302296509 100644 --- a/bin/ntx-builder/src/db/mod.rs +++ b/bin/ntx-builder/src/db/mod.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use anyhow::Context; use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; use miden_protocol::Word; +use miden_protocol::account::AccountId; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::crypto::merkle::mmr::PartialMmr; use miden_protocol::note::{NoteId, NoteScript, Nullifier}; @@ -81,7 +81,7 @@ impl Db { &self, effects: CommittedBlockEffects, chain_mmr: PartialMmr, - ) -> Result> { + ) -> Result> { self.inner .transact("apply_committed_block", move |conn| { queries::apply_committed_block(conn, &effects, &chain_mmr) @@ -101,7 +101,7 @@ impl Db { /// Returns `true` if there are notes available for consumption by the given account. pub async fn has_available_notes( &self, - account_id: NetworkAccountId, + account_id: AccountId, block_num: BlockNumber, max_attempts: usize, ) -> Result { @@ -114,7 +114,7 @@ impl Db { } /// Returns `true` if a committed account state exists for the given account. - pub async fn has_committed_account(&self, account_id: NetworkAccountId) -> Result { + pub async fn has_committed_account(&self, account_id: AccountId) -> Result { self.inner .query("has_committed_account", move |conn| { Ok(queries::get_account(conn, account_id)?.is_some()) @@ -125,7 +125,7 @@ impl Db { /// Returns the latest account state and available notes for the given account. pub async fn select_candidate( &self, - account_id: NetworkAccountId, + account_id: AccountId, block_num: BlockNumber, max_note_attempts: usize, ) -> Result<(Option, Vec)> { @@ -215,7 +215,7 @@ impl Db { _txs: Vec, _block_num: BlockNumber, _header: BlockHeader, - ) -> Result> { + ) -> Result> { unimplemented!("handle_block_committed is rewired in PR 2 of the ntx-builder refactor") } @@ -223,7 +223,7 @@ impl Db { pub async fn handle_transactions_reverted( &self, _tx_ids: Vec, - ) -> Result> { + ) -> Result> { unimplemented!( "handle_transactions_reverted is rewired in PR 2 of the ntx-builder refactor" ) diff --git a/bin/ntx-builder/src/db/models/account_effect.rs b/bin/ntx-builder/src/db/models/account_effect.rs index 6b01158bca..c586db8f8d 100644 --- a/bin/ntx-builder/src/db/models/account_effect.rs +++ b/bin/ntx-builder/src/db/models/account_effect.rs @@ -1,6 +1,5 @@ -use miden_node_proto::domain::account::NetworkAccountId; use miden_protocol::account::delta::AccountUpdateDetails; -use miden_protocol::account::{Account, AccountDelta, AccountId}; +use miden_protocol::account::{Account, AccountDelta}; use miden_standards::account::auth::NetworkAccount; // NETWORK ACCOUNT EFFECT @@ -33,19 +32,4 @@ impl NetworkAccountEffect { }, } } - - #[expect(dead_code)] - pub fn network_account_id(&self) -> NetworkAccountId { - // Trusted: constructors only produce this enum for accounts already classified as network - // (via the allowlist check above) or for updates that the caller filters through the actor - // registry. - NetworkAccountId::new_unchecked(self.protocol_account_id()) - } - - fn protocol_account_id(&self) -> AccountId { - match self { - NetworkAccountEffect::Created(acc) => acc.id(), - NetworkAccountEffect::Updated(delta) => delta.id(), - } - } } diff --git a/bin/ntx-builder/src/db/models/conv.rs b/bin/ntx-builder/src/db/models/conv.rs index 5ec6e24f2b..5a95cc4ec5 100644 --- a/bin/ntx-builder/src/db/models/conv.rs +++ b/bin/ntx-builder/src/db/models/conv.rs @@ -1,7 +1,6 @@ //! Conversions between Miden domain types and database column types. use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; use miden_protocol::Word; use miden_protocol::account::{Account, AccountId}; use miden_protocol::block::{BlockHeader, BlockNumber}; @@ -20,8 +19,8 @@ pub fn block_header_to_bytes(header: &BlockHeader) -> Vec { header.to_bytes() } -pub fn network_account_id_to_bytes(id: NetworkAccountId) -> Vec { - id.inner().to_bytes() +pub fn account_id_to_bytes(id: AccountId) -> Vec { + id.to_bytes() } #[expect(dead_code)] @@ -53,16 +52,11 @@ pub fn account_from_bytes(bytes: &[u8]) -> Result { Account::read_from_bytes(bytes).map_err(|e| DatabaseError::deserialization("account", e)) } +#[expect(dead_code)] pub fn account_id_from_bytes(bytes: &[u8]) -> Result { AccountId::read_from_bytes(bytes).map_err(|e| DatabaseError::deserialization("account id", e)) } -#[expect(dead_code)] -pub fn network_account_id_from_bytes(bytes: &[u8]) -> Result { - let account_id = account_id_from_bytes(bytes)?; - Ok(NetworkAccountId::new_unchecked(account_id)) -} - pub fn word_to_bytes(word: &Word) -> Vec { word.to_bytes() } diff --git a/bin/ntx-builder/src/db/models/queries/accounts.rs b/bin/ntx-builder/src/db/models/queries/accounts.rs index cdfdfad9e4..b732c65d57 100644 --- a/bin/ntx-builder/src/db/models/queries/accounts.rs +++ b/bin/ntx-builder/src/db/models/queries/accounts.rs @@ -2,8 +2,7 @@ use diesel::prelude::*; use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; -use miden_protocol::account::Account; +use miden_protocol::account::{Account, AccountId}; use crate::db::models::conv as conversions; use crate::db::schema; @@ -39,11 +38,11 @@ pub struct AccountRow { /// ``` pub fn upsert_account( conn: &mut SqliteConnection, - account_id: NetworkAccountId, + account_id: AccountId, account: &Account, ) -> Result<(), DatabaseError> { let row = AccountInsert { - account_id: conversions::network_account_id_to_bytes(account_id), + account_id: conversions::account_id_to_bytes(account_id), account_data: conversions::account_to_bytes(account), }; diesel::replace_into(schema::accounts::table).values(&row).execute(conn)?; @@ -59,9 +58,9 @@ pub fn upsert_account( /// ``` pub fn get_account( conn: &mut SqliteConnection, - account_id: NetworkAccountId, + account_id: AccountId, ) -> Result, DatabaseError> { - let account_id_bytes = conversions::network_account_id_to_bytes(account_id); + let account_id_bytes = conversions::account_id_to_bytes(account_id); let row: Option = schema::accounts::table .find(&account_id_bytes) diff --git a/bin/ntx-builder/src/db/models/queries/mod.rs b/bin/ntx-builder/src/db/models/queries/mod.rs index 41e328ccef..01d8c21a16 100644 --- a/bin/ntx-builder/src/db/models/queries/mod.rs +++ b/bin/ntx-builder/src/db/models/queries/mod.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use diesel::prelude::*; use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_protocol::account::AccountId; use miden_protocol::crypto::merkle::mmr::PartialMmr; use super::account_effect::NetworkAccountEffect; @@ -45,36 +45,33 @@ pub fn apply_committed_block( conn: &mut SqliteConnection, effects: &CommittedBlockEffects, chain_mmr: &PartialMmr, -) -> Result, DatabaseError> { - let mut affected_accounts: HashSet = HashSet::new(); +) -> Result, DatabaseError> { + let mut affected_accounts: HashSet = HashSet::new(); - for (network_id, details) in &effects.network_account_updates { + for (account_id, details) in &effects.network_account_updates { let Some(effect) = NetworkAccountEffect::from_protocol(details) else { continue; }; match effect { NetworkAccountEffect::Created(account) => { - upsert_account(conn, *network_id, &account)?; + upsert_account(conn, *account_id, &account)?; }, NetworkAccountEffect::Updated(delta) => { - // Partial deltas carry no storage to verify network-ness. If the account is not - // already tracked locally, treat the update as a non-network public account that - // leaked through the upstream filter and skip it. - let Some(mut current) = get_account(conn, *network_id)? else { + // If the account is not already tracked locally, skip it. + let Some(mut current) = get_account(conn, *account_id)? else { continue; }; current .apply_delta(&delta) .expect("network account delta should apply since the block was committed"); - upsert_account(conn, *network_id, ¤t)?; + upsert_account(conn, *account_id, ¤t)?; }, } - affected_accounts.insert(*network_id); + affected_accounts.insert(*account_id); } for note in &effects.network_notes { - let target = NetworkAccountId::new_unchecked(note.target_account_id()); - affected_accounts.insert(target); + affected_accounts.insert(note.target_account_id()); } insert_network_notes(conn, &effects.network_notes)?; diff --git a/bin/ntx-builder/src/db/models/queries/notes.rs b/bin/ntx-builder/src/db/models/queries/notes.rs index 385c2587d4..137de6bcd4 100644 --- a/bin/ntx-builder/src/db/models/queries/notes.rs +++ b/bin/ntx-builder/src/db/models/queries/notes.rs @@ -2,7 +2,7 @@ use diesel::prelude::*; use miden_node_db::DatabaseError; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; use miden_protocol::note::{Note, Nullifier}; use miden_protocol::utils::serde::{Deserializable, Serializable}; @@ -63,10 +63,9 @@ pub fn insert_network_notes( notes: &[AccountTargetNetworkNote], ) -> Result<(), DatabaseError> { for note in notes { - let target_id = NetworkAccountId::new_unchecked(note.target_account_id()); let row = NoteInsert { nullifier: conversions::nullifier_to_bytes(¬e.as_note().nullifier()), - account_id: conversions::network_account_id_to_bytes(target_id), + account_id: conversions::account_id_to_bytes(note.target_account_id()), note_data: note.as_note().to_bytes(), note_id: Some(conversions::note_id_to_bytes(¬e.as_note().id())), attempt_count: 0, @@ -108,11 +107,11 @@ pub fn mark_notes_consumed( #[expect(clippy::cast_possible_wrap)] pub fn available_notes( conn: &mut SqliteConnection, - account_id: NetworkAccountId, + account_id: AccountId, block_num: BlockNumber, max_attempts: usize, ) -> Result, DatabaseError> { - let account_id_bytes = conversions::network_account_id_to_bytes(account_id); + let account_id_bytes = conversions::account_id_to_bytes(account_id); let rows: Vec = schema::notes::table .filter(schema::notes::account_id.eq(&account_id_bytes)) diff --git a/bin/ntx-builder/src/test_utils.rs b/bin/ntx-builder/src/test_utils.rs index 1ebabe66fa..1e0e330746 100644 --- a/bin/ntx-builder/src/test_utils.rs +++ b/bin/ntx-builder/src/test_utils.rs @@ -1,6 +1,5 @@ //! Shared test helpers for the NTX builder crate. -use miden_node_proto::domain::account::NetworkAccountId; use miden_protocol::Word; use miden_protocol::account::{Account, AccountComponent, AccountId, AccountType}; use miden_protocol::block::BlockNumber; @@ -15,18 +14,15 @@ use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::SeedableRng; /// Creates a network account ID from a test constant. -pub fn mock_network_account_id() -> NetworkAccountId { - let account_id: AccountId = - ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap(); - NetworkAccountId::new_unchecked(account_id) +pub fn mock_network_account_id() -> AccountId { + ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE.try_into().unwrap() } /// Creates a distinct network account ID using a seeded RNG. -pub fn mock_network_account_id_seeded(seed: u8) -> NetworkAccountId { - let account_id = AccountIdBuilder::new() +pub fn mock_network_account_id_seeded(seed: u8) -> AccountId { + AccountIdBuilder::new() .account_type(AccountType::Public) - .build_with_seed([seed; 32]); - NetworkAccountId::new_unchecked(account_id) + .build_with_seed([seed; 32]) } /// Creates a unique `TransactionId` from a seed value. @@ -41,7 +37,7 @@ pub fn mock_tx_id(seed: u64) -> TransactionId { /// Creates a `AccountTargetNetworkNote` targeting the given network account. pub fn mock_single_target_note( - network_account_id: NetworkAccountId, + network_account_id: AccountId, seed: u8, ) -> AccountTargetNetworkNote { mock_single_target_note_with_code(network_account_id, seed, None) @@ -49,7 +45,7 @@ pub fn mock_single_target_note( /// Creates a `AccountTargetNetworkNote` with optional custom note script code. pub fn mock_single_target_note_with_code( - network_account_id: NetworkAccountId, + network_account_id: AccountId, seed: u8, code: Option<&str>, ) -> AccountTargetNetworkNote { @@ -58,7 +54,7 @@ pub fn mock_single_target_note_with_code( .account_type(AccountType::Private) .build_with_rng(&mut rng); - let target = NetworkAccountTarget::new(network_account_id.inner(), NoteExecutionHint::Always) + let target = NetworkAccountTarget::new(network_account_id, NoteExecutionHint::Always) .expect("network account should be valid target"); let mut builder = NoteBuilder::new(sender, rng).attachment(target); @@ -74,7 +70,7 @@ pub fn mock_single_target_note_with_code( /// Creates a mock `Account` for a network account. /// /// Uses `AccountBuilder` with minimal components needed for serialization. -pub fn mock_account(_account_id: NetworkAccountId) -> miden_protocol::account::Account { +pub fn mock_account(_account_id: AccountId) -> miden_protocol::account::Account { use miden_protocol::account::AccountBuilder; use miden_protocol::testing::noop_auth_component::NoopAuthComponent; use miden_standards::testing::account_component::MockAccountComponent; diff --git a/crates/block-producer/src/mempool/subscription.rs b/crates/block-producer/src/mempool/subscription.rs index 7b42245ea9..beae70c62b 100644 --- a/crates/block-producer/src/mempool/subscription.rs +++ b/crates/block-producer/src/mempool/subscription.rs @@ -87,10 +87,8 @@ impl SubscriptionProvider { OutputNote::Private(_) => None, }) .collect(); - // The classifier `is_network()` is gone from the protocol; network-ness now lives in - // account storage and cannot be determined from an AccountId alone. We send the delta for - // every non-private update and let the subscriber (which keeps its own list of network - // accounts) filter. Private accounts carry no payload, so the extra envelopes are cheap. + + // Private accounts cannot be managed by the network, so filter them out let account_delta = match tx.account_update().details() { miden_protocol::account::delta::AccountUpdateDetails::Private => None, details @ miden_protocol::account::delta::AccountUpdateDetails::Delta(_) => { diff --git a/crates/db/src/conv.rs b/crates/db/src/conv.rs index 5f1b4d0903..b106beddf9 100644 --- a/crates/db/src/conv.rs +++ b/crates/db/src/conv.rs @@ -147,6 +147,9 @@ pub(crate) fn nullifier_prefix_to_raw_sql(prefix: u16) -> i32 { #[inline(always)] pub(crate) fn raw_sql_to_nonce(raw: i64) -> Felt { debug_assert!(raw >= 0); + // SAFETY: This is a sound cast. In the store we write `Felt::as_canonical_u64() as i64`, so + // `raw` is the bit reinterpretation of a u64 in the field. Casting back via `raw as u64` + // recovers that same canonical value, which is always a valid field element Felt::new_unchecked(raw as u64) } #[inline(always)] diff --git a/crates/large-smt-backend-rocksdb/src/rocksdb.rs b/crates/large-smt-backend-rocksdb/src/rocksdb.rs index 048116ef4e..aa2c6c1be5 100644 --- a/crates/large-smt-backend-rocksdb/src/rocksdb.rs +++ b/crates/large-smt-backend-rocksdb/src/rocksdb.rs @@ -1,3 +1,7 @@ +//! Ported from `miden-crypto`'s `merkle::smt::large::storage::rocksdb` so we can expose RocksDB +//! tuning knobs (per-CF bloom bits, WAL size, shared `WriteBufferManager`) that upstream's +//! `RocksDbConfig` hardcodes. Keep in sync with upstream. + use alloc::boxed::Box; use alloc::vec::Vec; use std::mem::ManuallyDrop; diff --git a/crates/proto/Cargo.toml b/crates/proto/Cargo.toml index d83315020e..e19ba44db6 100644 --- a/crates/proto/Cargo.toml +++ b/crates/proto/Cargo.toml @@ -29,7 +29,8 @@ tonic-prost = { workspace = true } url = { workspace = true } [dev-dependencies] -proptest = { version = "1.7" } +miden-protocol = { features = ["testing"], workspace = true } +proptest = { version = "1.7" } [build-dependencies] build-rs = { workspace = true } diff --git a/crates/proto/src/domain/account.rs b/crates/proto/src/domain/account.rs index fe59b189ae..ba9bdb4d8f 100644 --- a/crates/proto/src/domain/account.rs +++ b/crates/proto/src/domain/account.rs @@ -19,10 +19,7 @@ use miden_protocol::block::BlockNumber; use miden_protocol::block::account_tree::AccountWitness; use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::crypto::merkle::smt::SmtProof; -use miden_protocol::note::NoteAttachment; use miden_protocol::utils::serde::{Deserializable, DeserializationError, Serializable}; -use miden_standards::note::{NetworkAccountTarget, NetworkAccountTargetError}; -use thiserror::Error; use super::try_convert; use crate::decode; @@ -971,98 +968,3 @@ impl From for proto::primitives::Asset { } } } - -// NETWORK ACCOUNT PREFIX -// ================================================================================================ - -pub type AccountPrefix = u32; - -/// Newtype wrapper for network account IDs. -/// -/// Provides type safety for accounts that are meant for network execution. -/// This wraps the full `AccountId` of a network account, typically extracted -/// from a `NetworkAccountTarget` attachment. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct NetworkAccountId(AccountId); - -impl std::fmt::Display for NetworkAccountId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(&self.0, f) - } -} - -impl NetworkAccountId { - /// Wraps an `AccountId` known by the caller to belong to a network account. - /// - /// # Preconditions - /// - /// Callers must ensure that: - /// - The account's storage contains a valid `NetworkAccountNoteAllowlist` slot - /// (verified e.g. via `miden_standards::account::auth::network_account::NetworkAccount` - /// or the store's `network_account_type` column). - /// - The account ID has public storage mode (network accounts cannot be private). - /// - /// The public-mode precondition is asserted in debug builds. - pub fn new_unchecked(id: AccountId) -> Self { - debug_assert!( - id.is_public(), - "NetworkAccountId requires a public AccountId, got account type {:?}", - id.account_type() - ); - Self(id) - } - - /// Returns the inner `AccountId`. - pub fn inner(&self) -> AccountId { - self.0 - } - - /// Gets the 30-bit prefix of the account ID used for tag matching. - pub fn prefix(&self) -> AccountPrefix { - get_account_id_tag_prefix(self.0) - } -} - -impl TryFrom<&NoteAttachment> for NetworkAccountId { - type Error = NetworkAccountError; - - fn try_from(attachment: &NoteAttachment) -> Result { - let target = NetworkAccountTarget::try_from(attachment) - .map_err(NetworkAccountError::InvalidAttachment)?; - Ok(NetworkAccountId(target.target_id())) - } -} - -impl TryFrom for NetworkAccountId { - type Error = NetworkAccountError; - - fn try_from(attachment: NoteAttachment) -> Result { - NetworkAccountId::try_from(&attachment) - } -} - -impl From for AccountId { - fn from(value: NetworkAccountId) -> Self { - value.inner() - } -} - -impl From for u32 { - /// Returns the 30-bit prefix of the network account ID. This is used for note tag matching. - fn from(value: NetworkAccountId) -> Self { - value.prefix() - } -} - -#[derive(Debug, Error)] -pub enum NetworkAccountError { - #[error("account ID {0} is not a valid network account ID")] - NotNetworkAccount(AccountId), - #[error("invalid network account attachment: {0}")] - InvalidAttachment(#[source] NetworkAccountTargetError), -} - -/// Gets the 30-bit prefix of the account ID. -fn get_account_id_tag_prefix(id: AccountId) -> AccountPrefix { - (id.prefix().as_u64() >> 34) as AccountPrefix -} diff --git a/crates/proto/src/domain/merkle.rs b/crates/proto/src/domain/merkle.rs index 1dbbd6e9ff..b0bd0688cf 100644 --- a/crates/proto/src/domain/merkle.rs +++ b/crates/proto/src/domain/merkle.rs @@ -93,10 +93,11 @@ impl TryFrom for MmrDelta { .collect::>() .context("data")?; - Ok(MmrDelta { - forest: Forest::new(value.forest as usize).context("forest")?, - data, - }) + let forest_size: usize = + value.forest.try_into().context("forest size does not fit in usize")?; + let forest = Forest::new(forest_size).context("forest size out of range")?; + + Ok(MmrDelta { forest, data }) } } diff --git a/crates/proto/src/domain/note.rs b/crates/proto/src/domain/note.rs index 5d970de1d0..3df13bc37f 100644 --- a/crates/proto/src/domain/note.rs +++ b/crates/proto/src/domain/note.rs @@ -259,7 +259,7 @@ impl TryFrom<&proto::note::NoteInclusionInBlockProof> for (NoteId, NoteInclusion impl From for proto::note::NoteHeader { fn from(header: NoteHeader) -> Self { Self { - note_id: Some((&header.id()).into()), + details_commitment: Some(header.details_commitment().as_word().into()), metadata: Some(header.into_metadata().into()), } } @@ -270,11 +270,11 @@ impl TryFrom for NoteHeader { fn try_from(value: proto::note::NoteHeader) -> Result { let decoder = value.decoder(); - let note_details_commitment: Word = decode!(decoder, value.note_id)?; + let details_commitment_word: Word = decode!(decoder, value.details_commitment)?; let metadata: NoteMetadata = decode!(decoder, value.metadata)?; Ok(NoteHeader::new( - NoteDetailsCommitment::from_raw(note_details_commitment), + NoteDetailsCommitment::from_raw(details_commitment_word), metadata, )) } @@ -335,3 +335,35 @@ fn decode_attachments(bytes: &[u8]) -> Result NoteAttachments::decode_bytes(bytes, "NoteAttachments") } } + +#[cfg(test)] +mod tests { + use miden_protocol::account::{AccountId, AccountIdVersion, AccountType}; + + use super::*; + + #[test] + fn note_header_roundtrip_preserves_id() { + // Build a NoteHeader with a known details_commitment and metadata. + let details_commitment = + NoteDetailsCommitment::from_raw(Word::try_from([1u64, 2, 3, 4]).unwrap()); + let sender = AccountId::dummy([1; 15], AccountIdVersion::Version1, AccountType::Public); + let metadata = NoteMetadata::new( + PartialNoteMetadata::new(sender, NoteType::Public).with_tag(NoteTag::from(7u32)), + &NoteAttachments::default(), + ); + + let original = NoteHeader::new(details_commitment, metadata); + + // Round-trip through proto. + let proto_header: proto::note::NoteHeader = original.into(); + let decoded = NoteHeader::try_from(proto_header).expect("proto NoteHeader should decode"); + + // Both the derived id and the details_commitment must match — guards against the historical + // bug where the encoder wrote `id` into the same wire field the decoder interpreted as + // `details_commitment`. + assert_eq!(decoded.id(), original.id()); + assert_eq!(decoded.details_commitment(), original.details_commitment()); + assert_eq!(decoded.metadata(), original.metadata()); + } +} diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 7486860610..6cb688cb9f 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -237,6 +237,36 @@ impl RpcService { Ok(()) } + + /// Errors if any of `candidate_ids` is classified as a network account by the store. Callers + /// should pre-filter to post-deployment, public-account ids; `Ok(())` on empty. + async fn reject_if_any_network_accounts( + &self, + candidate_ids: Vec, + ) -> Result<(), Status> { + if candidate_ids.is_empty() { + return Ok(()); + } + + let response = self + .store + .clone() + .filter_network_accounts(tonic::Request::new(proto::account::AccountIdList { + account_ids: candidate_ids, + })) + .await + .map_err(|err| { + Status::internal(format!("network-account classification failed: {err}")) + })?; + + if !response.into_inner().account_ids.is_empty() { + return Err(Status::invalid_argument( + "Network transactions may not be submitted by users yet", + )); + } + + Ok(()) + } } // API IMPLEMENTATION @@ -518,29 +548,16 @@ impl api_server::Api for RpcService { request.transaction = rebuilt_tx.to_bytes(); // Block post-deployment network-account transactions from user RPC. First-deployment txs - // are allowed because the protocol-level allowlist only kicks in once the account exists. - // For non-deployment txs, ask the store whether the account is classified as a network - // account; the store is the source of truth because network-ness now lives in account - // storage and isn't derivable from an AccountId alone. Network accounts must be public, so - // private-account txs short-circuit and skip the store roundtrip. - if !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() + // are exempt because the protocol-level allowlist only kicks in once the account exists, + // and network accounts must be public, so private-account txs are filtered out up front. + let candidate_ids = if !tx.account_update().initial_state_commitment().is_empty() + && tx.account_id().is_public() { - let response = self - .store - .clone() - .are_network_accounts(tonic::Request::new(proto::account::AccountIdList { - account_ids: vec![tx.account_id().into()], - })) - .await - .map_err(|err| { - Status::internal(format!("network-account classification failed: {err}")) - })?; - if !response.into_inner().network_account_ids.is_empty() { - return Err(Status::invalid_argument( - "Network transactions may not be submitted by users yet", - )); - } - } + vec![tx.account_id().into()] + } else { + Vec::new() + }; + self.reject_if_any_network_accounts(candidate_ids).await?; let tx_verifier = TransactionVerifier::new(MIN_PROOF_SECURITY_LEVEL); tx_verifier.verify(&tx).map_err(|err| { @@ -615,9 +632,8 @@ impl api_server::Api for RpcService { } // Same gate as `submit_proven_transaction`, applied to every post-deployment tx in the - // batch. One store round-trip filters the non-deployment account ids; any match fails the - // entire batch (matches the original loop semantics). Network accounts must be public, so - // private-account txs are excluded up front to skip the store roundtrip when possible. + // batch. One store round-trip classifies all the non-deployment, public-account ids; any + // match fails the entire batch. let non_deployment_ids: Vec<_> = proposed_batch .transactions() .iter() @@ -627,24 +643,7 @@ impl api_server::Api for RpcService { }) .map(|tx| proto::account::AccountId::from(tx.account_id())) .collect(); - - if !non_deployment_ids.is_empty() { - let response = self - .store - .clone() - .are_network_accounts(tonic::Request::new(proto::account::AccountIdList { - account_ids: non_deployment_ids, - })) - .await - .map_err(|err| { - Status::internal(format!("network-account classification failed: {err}")) - })?; - if !response.into_inner().network_account_ids.is_empty() { - return Err(Status::invalid_argument( - "Network transactions may not be submitted by users yet", - )); - } - } + self.reject_if_any_network_accounts(non_deployment_ids).await?; // Verify batch transaction proofs. // diff --git a/crates/rpc/src/tests.rs b/crates/rpc/src/tests.rs index f211242486..75c67500c5 100644 --- a/crates/rpc/src/tests.rs +++ b/crates/rpc/src/tests.rs @@ -302,9 +302,7 @@ async fn rpc_rate_limits_per_ip() { #[tokio::test] async fn rpc_server_accepts_requests_with_accept_header() { - // Start the RPC. - let (mut rpc_client, _, store_listener) = start_rpc().await; - let _store = TestStore::start(store_listener).await; + let (mut rpc_client, _, _store) = start_rpc_and_store_ready().await; // Send any request to the RPC. let response = send_request(&mut rpc_client).await; @@ -316,9 +314,7 @@ async fn rpc_server_accepts_requests_with_accept_header() { #[tokio::test] async fn rpc_server_rejects_requests_with_accept_header_invalid_version() { for version in ["1.9.0", "0.8.1", "0.8.0", "0.999.0", "99.0.0"] { - // Start the RPC. - let (_, rpc_addr, store_listener) = start_rpc().await; - let _store = TestStore::start(store_listener).await; + let (_, rpc_addr, _store) = start_rpc_and_store_ready().await; // Recreate the RPC client with an invalid version. let url = rpc_addr.to_string(); @@ -423,14 +419,9 @@ async fn rpc_server_has_web_support() { #[tokio::test] async fn rpc_server_rejects_proven_transactions_with_invalid_commitment() { - // Start the RPC. - let (_, rpc_addr, store_listener) = start_rpc().await; - let store = TestStore::start(store_listener).await; + let (_, rpc_addr, store) = start_rpc_and_store_ready().await; let genesis = store.genesis_commitment(); - // Wait for the store to be ready before sending requests. - tokio::time::sleep(Duration::from_millis(100)).await; - // Override the client so that the ACCEPT header is not set. let mut rpc_client = miden_node_proto::clients::Builder::new(Url::parse(&format!("http://{rpc_addr}")).unwrap()) @@ -475,14 +466,9 @@ async fn rpc_server_rejects_proven_transactions_with_invalid_commitment() { #[tokio::test] async fn rpc_server_rejects_proven_transactions_with_invalid_reference_block() { - // Start the RPC. - let (_, rpc_addr, store_listener) = start_rpc().await; - let store = TestStore::start(store_listener).await; + let (_, rpc_addr, store) = start_rpc_and_store_ready().await; let genesis = store.genesis_commitment(); - // Wait for the store to be ready before sending requests. - tokio::time::sleep(Duration::from_millis(100)).await; - // Override the client so that the ACCEPT header is not set. let mut rpc_client = miden_node_proto::clients::Builder::new(Url::parse(&format!("http://{rpc_addr}")).unwrap()) @@ -518,13 +504,9 @@ async fn rpc_server_rejects_proven_transactions_with_invalid_reference_block() { #[tokio::test] async fn rpc_rejects_post_deployment_network_account_tx() { - let (_, rpc_addr, store_listener) = start_rpc().await; - let store = TestStore::start(store_listener).await; + let (_, rpc_addr, store) = start_rpc_and_store_ready().await; let genesis = store.genesis_commitment(); - // Wait for the store to be ready before sending requests. - tokio::time::sleep(Duration::from_millis(100)).await; - // Build a client that advertises the right `application/vnd.miden` content type so // tx-submission requests reach the handler. let mut rpc_client = @@ -570,9 +552,7 @@ async fn rpc_rejects_post_deployment_network_account_tx() { #[tokio::test] async fn rpc_server_rejects_tx_submissions_without_genesis() { - // Start the RPC. - let (_, rpc_addr, store_listener) = start_rpc().await; - let store = TestStore::start(store_listener).await; + let (_, rpc_addr, store) = start_rpc_and_store_ready().await; let genesis = store.genesis_commitment(); // Override the client so that the ACCEPT header is not set. @@ -655,6 +635,18 @@ async fn start_rpc() -> (RpcClient, std::net::SocketAddr, TcpListener) { start_rpc_with_options(GrpcOptionsExternal::test()).await } +/// Starts the RPC + store pair and blocks until the RPC server has finished its genesis-fetch +/// bootstrap and is accepting requests. Use this in tests that send a single one-shot request and +/// would otherwise race the RPC component's startup under high test parallelism. +async fn start_rpc_and_store_ready() -> (RpcClient, std::net::SocketAddr, TestStore) { + let (mut rpc_client, rpc_addr, store_listener) = start_rpc().await; + let store = TestStore::start(store_listener).await; + send_request_until_success(&mut rpc_client) + .await + .expect("RPC should become ready after store starts"); + (rpc_client, rpc_addr, store) +} + async fn start_rpc_with_options( grpc_options: GrpcOptionsExternal, ) -> (RpcClient, std::net::SocketAddr, TcpListener) { @@ -700,9 +692,7 @@ async fn start_rpc_with_options( #[tokio::test] async fn get_limits_endpoint() { - // Start the RPC and store - let (mut rpc_client, _rpc_addr, store_listener) = start_rpc().await; - let _store = TestStore::start(store_listener).await; + let (mut rpc_client, _rpc_addr, _store) = start_rpc_and_store_ready().await; // Call the get_limits endpoint let response = rpc_client.get_limits(()).await.expect("get_limits should succeed"); diff --git a/crates/store/build.rs b/crates/store/build.rs index 95c87860be..cd6da178d9 100644 --- a/crates/store/build.rs +++ b/crates/store/build.rs @@ -6,6 +6,7 @@ use miden_protocol::account::auth::AuthScheme; use miden_protocol::account::{Account, AccountCode, AccountFile, AccountType}; use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey; use miden_protocol::crypto::rand::RandomCoin; +use miden_protocol::field::PrimeCharacteristicRing; use miden_protocol::{Felt, Word}; use miden_standards::AuthMethod; use miden_standards::account::wallets::create_basic_wallet; @@ -40,16 +41,16 @@ fn generate_agglayer_sample_accounts() { fs_err::create_dir_all(&samples_dir).expect("Failed to create samples directory"); // Use deterministic seeds for reproducible builds. WARNING: DO NOT USE THESE IN PRODUCTION - let bridge_seed: Word = Word::new([Felt::new_unchecked(1u64); 4]); - let eth_faucet_seed: Word = Word::new([Felt::new_unchecked(2u64); 4]); - let usdc_faucet_seed: Word = Word::new([Felt::new_unchecked(3u64); 4]); + let bridge_seed: Word = Word::new([Felt::from_u32(1u32); 4]); + let eth_faucet_seed: Word = Word::new([Felt::from_u32(2u32); 4]); + let usdc_faucet_seed: Word = Word::new([Felt::from_u32(3u32); 4]); // Create bridge admin and GER manager as proper wallet accounts. WARNING: DO NOT USE THESE IN // PRODUCTION let bridge_admin_key = - SecretKey::with_rng(&mut RandomCoin::new(Word::new([Felt::new_unchecked(4u64); 4]))); + SecretKey::with_rng(&mut RandomCoin::new(Word::new([Felt::from_u32(4u32); 4]))); let ger_manager_key = - SecretKey::with_rng(&mut RandomCoin::new(Word::new([Felt::new_unchecked(5u64); 4]))); + SecretKey::with_rng(&mut RandomCoin::new(Word::new([Felt::from_u32(5u32); 4]))); let bridge_admin = create_basic_wallet( [4u8; 32], @@ -84,8 +85,8 @@ fn generate_agglayer_sample_accounts() { eth_faucet_seed, "ETH", 8, - Felt::new_unchecked(1_000_000_000), - Felt::new_unchecked(0), + Felt::from_u32(1_000_000_000u32), + Felt::ZERO, bridge_account_id, ); @@ -94,8 +95,8 @@ fn generate_agglayer_sample_accounts() { usdc_faucet_seed, "USDC", 6, - Felt::new_unchecked(10_000_000_000), - Felt::new_unchecked(0), + Felt::from_u64(10_000_000_000u64), + Felt::ZERO, bridge_account_id, ); diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index ab36a1ef99..380985f47a 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -452,7 +452,7 @@ impl Db { .await } - /// Returns the subset of the provided account IDs that currently classify as network accounts. + /// Returns the subset of the provided account IDs that classify as network accounts. #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] pub async fn select_network_accounts_subset( &self, diff --git a/crates/store/src/db/models/conv.rs b/crates/store/src/db/models/conv.rs index 66524b307b..abd26debca 100644 --- a/crates/store/src/db/models/conv.rs +++ b/crates/store/src/db/models/conv.rs @@ -200,6 +200,10 @@ pub(crate) fn nullifier_prefix_to_raw_sql(prefix: u16) -> i32 { #[inline(always)] pub(crate) fn raw_sql_to_nonce(raw: i64) -> Felt { debug_assert!(raw >= 0); + // SAFETY: In the store we write `Felt::as_canonical_u64() as i64`, so `raw` is the bit + // reinterpretation of a u64 in the field. Casting back via `raw as u64` recovers that same + // canonical value, which is always a valid (already reduced) field element, so + // `Felt::new_unchecked` is sound. Felt::new_unchecked(raw as u64) } #[inline(always)] diff --git a/crates/store/src/db/models/queries/accounts.rs b/crates/store/src/db/models/queries/accounts.rs index c433457c87..e0b9038c49 100644 --- a/crates/store/src/db/models/queries/accounts.rs +++ b/crates/store/src/db/models/queries/accounts.rs @@ -1288,30 +1288,6 @@ fn prepare_partial_account_update( Ok((AccountStateForInsert::PartialState(account_state), storage, assets)) } -/// Reads the network-account classification of the latest row for `account_id_bytes`. -/// -/// Returns `Ok(None)` if no row exists for this account. -fn select_latest_network_account_type( - conn: &mut SqliteConnection, - account_id_bytes: &[u8], -) -> Result, DatabaseError> { - let raw: Option = QueryDsl::select( - schema::accounts::table.filter( - schema::accounts::account_id - .eq(account_id_bytes) - .and(schema::accounts::is_latest.eq(true)), - ), - schema::accounts::network_account_type, - ) - .first::(conn) - .optional() - .map_err(DatabaseError::Diesel)?; - - raw.map(NetworkAccountType::from_raw_sql) - .transpose() - .map_err(DatabaseError::from) -} - /// Returns the subset of `account_ids` whose latest committed state is a network account. /// /// Unknown ids and non-network accounts are silently omitted. @@ -1360,22 +1336,25 @@ pub(crate) fn upsert_accounts( for update in accounts { let account_id = update.account_id(); let account_id_bytes = account_id.to_bytes(); - let block_num_raw = block_num.to_raw_sql(); - // Preserve the original creation block when updating existing accounts. - let created_at_block_raw = QueryDsl::select( + // Pull the latest row (if any) so we can carry forward `created_at_block` and the + // `network_account_type` classification, both of which are fixed at account creation. + let existing: Option<(i64, i32)> = QueryDsl::select( schema::accounts::table.filter( schema::accounts::account_id .eq(&account_id_bytes) .and(schema::accounts::is_latest.eq(true)), ), - schema::accounts::created_at_block, + (schema::accounts::created_at_block, schema::accounts::network_account_type), ) - .first::(conn) + .first(conn) .optional() - .map_err(DatabaseError::Diesel)? - .unwrap_or(block_num_raw); - let created_at_block = BlockNumber::from_raw_sql(created_at_block_raw)?; + .map_err(DatabaseError::Diesel)?; + + let created_at_block = match existing { + Some((raw, _)) => BlockNumber::from_raw_sql(raw)?, + None => block_num, + }; // NOTE: we collect storage / asset inserts to apply them only after the account row is // written. The storage and vault tables have FKs pointing to accounts `(account_id, @@ -1400,22 +1379,17 @@ pub(crate) fn upsert_accounts( }, }; - // Classify the account as network or not by looking for the standardized - // `NetworkAccountNoteAllowlist` slot in storage. Only full account states let us inspect - // storage directly; for partial updates we inherit the latest classification from the DB. - let network_account_type = match &account_state { - AccountStateForInsert::Private => NetworkAccountType::None, - AccountStateForInsert::FullAccount(account) => { - if NetworkAccount::new(account.clone()).is_ok() { + // Inherit the classification when the account already exists; otherwise classify it once at + // creation based on the new state. + let network_account_type = match existing { + Some((_, raw)) => NetworkAccountType::from_raw_sql(raw)?, + None => match &account_state { + AccountStateForInsert::FullAccount(account) + if NetworkAccount::new(account.clone()).is_ok() => + { NetworkAccountType::Network - } else { - NetworkAccountType::None - } - }, - AccountStateForInsert::PartialState(_) => { - // We do not have full storage here; carry the previous classification forward. - select_latest_network_account_type(conn, &account_id_bytes)? - .unwrap_or(NetworkAccountType::None) + }, + _ => NetworkAccountType::None, }, }; diff --git a/crates/store/src/errors.rs b/crates/store/src/errors.rs index 05b277e775..d165488041 100644 --- a/crates/store/src/errors.rs +++ b/crates/store/src/errors.rs @@ -1,6 +1,5 @@ use std::io; -use miden_node_proto::domain::account::NetworkAccountError; use miden_node_proto::domain::block::InvalidBlockRange; use miden_node_proto::errors::{ConversionError, GrpcError}; use miden_node_utils::ErrorReport; @@ -589,7 +588,6 @@ mod compile_tests { AccountError, DatabaseError, DeserializationError, - NetworkAccountError, NoteError, RecvError, StateInitializationError, @@ -609,7 +607,6 @@ mod compile_tests { ensure_is_error::(PhantomData); ensure_is_error::(PhantomData); ensure_is_error::(PhantomData); - ensure_is_error::(PhantomData); ensure_is_error::(PhantomData); ensure_is_error::(PhantomData); ensure_is_error::>(PhantomData); diff --git a/crates/store/src/genesis/config/mod.rs b/crates/store/src/genesis/config/mod.rs index b7c897a701..59556fcdc2 100644 --- a/crates/store/src/genesis/config/mod.rs +++ b/crates/store/src/genesis/config/mod.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use indexmap::IndexMap; -use miden_node_utils::crypto::get_rpo_random_coin; +use miden_node_utils::crypto::get_random_coin; use miden_protocol::account::auth::{AuthScheme, AuthSecretKey}; use miden_protocol::account::{ Account, @@ -219,18 +219,18 @@ impl GenesisConfig { let zero_padding_width = usize::ilog10(std::cmp::max(10, wallet_configs.len())) as usize; // Setup all wallet accounts, which reference the faucet's for their provided assets. - for (index, WalletConfig { storage_mode, assets }) in wallet_configs.into_iter().enumerate() + for (index, WalletConfig { account_type, assets }) in wallet_configs.into_iter().enumerate() { tracing::debug!(index, assets = ?assets, "Adding wallet account"); let mut rng = ChaCha20Rng::from_seed(rand::random()); - let secret_key = RpoSecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); + let secret_key = RpoSecretKey::with_rng(&mut get_random_coin(&mut rng)); let auth = AuthMethod::SingleSig { approver: (secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2), }; let init_seed: [u8; 32] = rng.random(); - let mut wallet_account = create_basic_wallet(init_seed, auth, storage_mode.into())?; + let mut wallet_account = create_basic_wallet(init_seed, auth, account_type.into())?; // Add fungible assets and track the faucet adjustments per faucet/asset. let wallet_fungible_asset_update = @@ -383,7 +383,7 @@ impl NativeFaucetConfig { symbol: symbol.clone(), decimals: DEFAULT_NATIVE_FAUCET_DECIMALS, max_supply: DEFAULT_NATIVE_FAUCET_MAX_SUPPLY, - storage_mode: StorageMode::Public, + account_type: AccountTypeConfig::Public, }; let (account, secret_key) = faucet_config.build_account()?; Ok((account, symbol, Some(secret_key))) @@ -419,7 +419,7 @@ pub struct FungibleFaucetConfig { /// using based `10.powi(decimals)` as a multiplier. max_supply: u64, #[serde(default)] - storage_mode: StorageMode, + account_type: AccountTypeConfig, } impl FungibleFaucetConfig { @@ -429,10 +429,10 @@ impl FungibleFaucetConfig { symbol, decimals, max_supply, - storage_mode, + account_type, } = self; let mut rng = ChaCha20Rng::from_seed(rand::random()); - let secret_key = RpoSecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); + let secret_key = RpoSecretKey::with_rng(&mut get_random_coin(&mut rng)); let auth = AuthSingleSig::new(secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2); let init_seed: [u8; 32] = rng.random(); @@ -449,7 +449,7 @@ impl FungibleFaucetConfig { // It's similar to `fn create_basic_fungible_faucet`, but we need to cover more cases. let faucet_account = AccountBuilder::new(init_seed) - .account_type(storage_mode.into()) + .account_type(account_type.into()) .with_auth_component(auth) .with_component(faucet) .with_components( @@ -473,7 +473,7 @@ impl FungibleFaucetConfig { #[serde(deny_unknown_fields)] pub struct WalletConfig { #[serde(default)] - storage_mode: StorageMode, + account_type: AccountTypeConfig, assets: Vec, } @@ -484,13 +484,13 @@ struct AssetEntry { amount: u64, } -// STORAGE MODE +// ACCOUNT TYPE CONFIG // ================================================================================================ /// See the [full description](https://0xmiden.github.io/miden-protocol/account.html?highlight=Accoun#account-storage-mode) /// for details #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Default)] -pub enum StorageMode { +pub enum AccountTypeConfig { /// A publicly stored account, lives on-chain. #[serde(alias = "public")] Public, @@ -500,11 +500,11 @@ pub enum StorageMode { Private, } -impl From for AccountType { - fn from(mode: StorageMode) -> AccountType { - match mode { - StorageMode::Public => AccountType::Public, - StorageMode::Private => AccountType::Private, +impl From for AccountType { + fn from(value: AccountTypeConfig) -> AccountType { + match value { + AccountTypeConfig::Public => AccountType::Public, + AccountTypeConfig::Private => AccountType::Private, } } } diff --git a/crates/store/src/genesis/config/samples/01-simple.toml b/crates/store/src/genesis/config/samples/01-simple.toml index 50f85cd164..c24031d6a2 100644 --- a/crates/store/src/genesis/config/samples/01-simple.toml +++ b/crates/store/src/genesis/config/samples/01-simple.toml @@ -5,19 +5,19 @@ version = 1 verification_base_fee = 0 [[fungible_faucet]] +account_type = "public" decimals = 3 max_supply = 100_000_000 -storage_mode = "public" symbol = "WHAT" [[wallet]] +account_type = "private" assets = [{ amount = 999_000, symbol = "MIDEN" }] -storage_mode = "private" [[wallet]] +account_type = "private" assets = [{ amount = 777, symbol = "MIDEN" }] -storage_mode = "private" [[wallet]] +account_type = "private" assets = [{ amount = 1, symbol = "WHAT" }] -storage_mode = "private" diff --git a/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_eth.mac b/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_eth.mac index 1e6a756be93478aba81fcf09d5a12d36bbd9584b..3a3049f6dc0d4beb1e517504c4d40275ea21e80c 100644 GIT binary patch delta 1892 zcmah}ZBSHY6u!F)LZF7rmqM_sh{p1@Kr6c7bRj5*nT$%&5gBweF&Js^8?p8p;RK|m zuZTrXhDGjsR?mg5UI@ekAczNjg1=?4`9yf$!#^&@7Xm;!{s1Lmr z&7ya7v5`@dBxM1Gz#3pZPyq;_8mIw&1QLNxUtKO^K_5X{A*tJog{RA>qVL0S`qHQ zR2=cQiJSnN@C?+e*1!?1?YNFklgwsGat0!p2}}f*N|HPdJX?|!(#yb<@8)`hw3HR7 zn}i?_AYKVgbvL*!4K+c~1@L{qw1R!+cgh2F*b zOT-aC9d1hgR{(J>=k17106v*Q+{pexoCEDskN(c-NE|`|*yk{2A>9CA`FxFy^bx)R znOMphKBy5Q+GV*mrI-C4$lb^SFo<3j~az_m5ZfJqjuK0gWgz1EAN~ zf(K*3v?c&2P)ELL3^|hdBsOZ0%K9`n{t0z)@Xv7L`J6{%XFU83tlQ9EftK2V1Rxg$ z#J|F)LNt`016T3`=MYoF{j4u=!{6XpfQ#Jl+b0cWy38fk-Q4don0(tHCwkB*5ylmO z5@_uDFTiaT^1VE;;l_VrqXDL{zQzMwhc^qj!2?)0j~mNY_zPGM@Bo8g_(@U$CvKrI z35B--dVmH<2512C%ejvJ7!80Vsec)!X;sq{#Y?nC$-)#Z3rDnUtGl_dv*_4pUSwx> zLhjkad)vAqUd$~x{>4zo@{jvYZ`>sf`V|l4Pf(qe9S4kA^`>BdyKU8@M&)FxDRy3c z;qvHnL0ht_+8^qQ7J29GN_}lpW|&8nuJo06M=Bm<8=K3{4trFe7}fqBH82=n-+t?U z-@>}ZO`jdS>)U#&U0yRs^S5xs7;OPBb=hlELalSU-*{^C)t)Ou=ru`#(##e1EslBvk zan?{>#m!GQmD&zG8r6T`Q6GG5BtPA9p-Mk%<!Vz~>IO2_Jf4rg<4vSIrTa2n{#y*YOp9rVIDy#k0eH!6N zmPJXrQO%g`)QIkMqsWGEJP+|5L=xbYDhr#J(M!XtN2j)=j?sv&7gCJ>Gh3OOl$N3W E2NM68dH?_b delta 2522 zcmah~dr(w$6ux&+dCM-KWDt81Mcyt5BC^1uhW zl!D@;nrvDPH84z_P;*8U--ueHDS`!wf-c&3;Arb#-I;IC`M&R*bIX zgoH|xlnP`6i-D!UMxYod1$G0MfEb`2cn^3Cv;*B`NzwxDfCtb6SOI-UUrV*?6+1D` zE!N)78=@qO{cgR*eqEAl5FS#x0Fr3ch4yiD3Uc~S>e^{*$o|g}q3!_=&n)qVyDvKR zj?ZU_Def!95qIC%t`RZ~@9chV(=7XlG;-+iqr{Ffn+dKS3+?&UVwuMRVRZ9#vaug} zd?429UXaFm=Gs;q@GQ2agL*{y=x76a=!Fh1zR)tIhgG9SNs`$aMn%UulEHxdCEyD4 zNs?qDK3S3~y%71CXoTJiBR_cHwH0L2;LHg&35&xM&7;EjTK z=5vs{09k+!u$c9ak?VlDScvp_7)X7eu}=r^1$HxE2%mTv^BnXMKi0v$B*PLesOADQ z3iKkWr-z!$rFmFQLCZC+SdI=q@Mh*KQAb?viVsOLEJA&6B(BU?!zb>|oQm+6*8%=W zP%rfX@-Y`&+pn`uH6){vj90m_0C`{F3pA2mhz1(`Hv5}UuLqyOd^744@FK(j#QCg$ zg*wtPr?Ma7R?MNOg403Pb=0YnfeHd4Y{vkKU-JN5vN;I6iuo?o5x-#L2ZOgTm%D-B zX52XT;kbui7|DDX$8?I=#$e$SWU%3eO~c?VXI=)M_0Qa=V2M$~fpOQ0le*AJufo#;EkoY+iP{T0RFCfOs!%03p61_8y)f5-dH;L;!1oQ!>J+(aG=+`^z3#Ivm5Mox>| zflmGn*6;EnZOj{ak$b3#1ODPgTpU2?X9VvvA_-Sn_y+~#+(#s12AbM}xk3Pa0te95yw`9-v7-bz0H@18ENcs3hkG=0KNr;J5 zH|qmav`^L-#LsC-9XRG{RHAP|hIzzEQ%Xxs{FtYsmE@+92dSA>Vbv=lCt5Mx%Zj{t z_x-8GTk_o_UxN>x&eiT z4nbHuqs1p;t4tl1@<^(n?sR?mg5UI@ekAczNjg1=?4`9yf$!#^&@7Xm;!{s1Lmr z&7ya7v5`@dBxM1Gz#3pZPyq;_8mIw&1QLNxUtKO^K_5X{A*tJog{RA>qVL0S`qHQ zR2=cQiJSnN@C?+e*1!?1?YNFklgwsGat0!p2}}f*N|HPdJX?|!(#yb<@8)`hw3HR7 zn}i?_AYKVgbvL*!4K+c~1@L{qw1R!+cgh2F*b zOT-aC9d1hgR{(J>=k17106v*Q+{pexoCEDskN(c-NE|`|*yk{2A>9CA`FxFy^bx)R znOMphKBy5Q+GV*mrI-C4$lb^SFo<3j~az_m5ZfJqjuK0gWgz1EAN~ zf(K*3v?c&2P)ELL3^|hdBsOZ0%K9`n{t0z)@Xv7L`J6{%XFU83tlQ9EftK2V1Rxg$ z#J|F)LNt`016T3`=MYoF{j4u=!{6XpfQ#Jl+b0cWy38fk-Q4don0(tHCwkB*5ylmO z5@_uDFTiaT^1VE;;l_VrqXDL{zQzMwhc^qj!2?)0j~mNY_zPGM@Bo8g_(@U$CvKrI z35B--dVmH<2512C%ejvJ7!80Vsec)!X;sq{#Y?nC$-)#Z3rDnUtGl_dv*_4pUSwx> zLhjkad)vAqUd$~x{>4zo@{jvYZ`>sf`V|l4Pf(qe9S4kA^`>BdyKU8@M&)FxDRy3c z;qvHnL0ht_+8^qQ7J29GN_}lpW|&8nuJo06M=Bm<8=K3{4trFe7}fqBH82=n-+t?U z-@>}ZO`jdS>)U#&U0yRs^S5xs7;OPBb=hlELalSU-*{^C)t)Ou=ru`#(##e1EslBvk zan?{>#m!GQmD&zG8r6T`Q6GG5BtPA9p-Mk%<!Vz~>IO2_Jf4rg<4vSIrTa2n{#y*YOp9rVIDy#k0eH!6N zmPJXrQO%g`)QIkMqsWGEJP+|5L=xbYDhr#J(M!XtN2j)=j?sv&7gCJ>Gh3OOl$N3W E2NM68dH?_b delta 2522 zcmah~dr(w$6ux&+dCM-KWDt81Mcyt5BC^1uhW zl!D@;nrvDPH84z_P;*8U--ueHDS`!wf-c&3;Arb#-I;IC`M&R*bIX zgoH|xlnP`6i-D!UMxYod1$G0MfEb`2cn^3Cv;*B`NzwxDfCtb6SOI-UUrV*?6+1D` zE!N)78=@qO{cgR*eqEAl5FS#x0Fr3ch4yiD3Uc~S>e^{*$o|g}q3!_=&n)qVyDvKR zj?ZU_Def!95qIC%t`RZ~@9chV(=7XlG;-+iqr{Ffn+dKS3+?&UVwuMRVRZ9#vaug} zd?429UXaFm=Gs;q@GQ2agL*{y=x76a=!Fh1zR)tIhgG9SNs`$aMn%UulEHxdCEyD4 zNs?qDK3S3~y%71CXoTJiBR_cHwH0L2;LHg&35&xM&7;EjTK z=5vs{09k+!u$c9ak?VlDScvp_7)X7eu}=r^1$HxE2%mTv^BnXMKi0v$B*PLesOADQ z3iKkWr-z!$rFmFQLCZC+SdI=q@Mh*KQAb?viVsOLEJA&6B(BU?!zb>|oQm+6*8%=W zP%rfX@-Y`&+pn`uH6){vj90m_0C`{F3pA2mhz1(`Hv5}UuLqyOd^744@FK(j#QCg$ zg*wtPr?Ma7R?MNOg403Pb=0YnfeHd4Y{vkKU-JN5vN;I6iuo?o5x-#L2ZOgTm%D-B zX52XT;kbui7|DDX$8?I=#$e$SWU%3eO~c?VXI=)M_0Qa=V2M$~fpOQ0le*AJufo#;EkoY+iP{T0RFCfOs!%03p61_8y)f5-dH;L;!1oQ!>J+(aG=+`^z3#Ivm5Mox>| zflmGn*6;EnZOj{ak$b3#1ODPgTpU2?X9VvvA_-Sn_y+~#+(#s12AbM}xk3Pa0te95yw`9-v7-bz0H@18ENcs3hkG=0KNr;J5 zH|qmav`^L-#LsC-9XRG{RHAP|hIzzEQ%Xxs{FtYsmE@+92dSA>Vbv=lCt5Mx%Zj{t z_x-8GTk_o_UxN>x&eiT z4nbHuqs1p;t4tl1@<^(n?7Z3AwSv|W&`MPt3UmM*cx#8#=llBS9lvwdUVH7m&p!9uOFr+O z6n}VFT-7XAe>L*3Z@^{8XZHdZ&kAZkFFh9(?%3c!~Tt4fU&p8eu zoJ7fSa-mT^O4MJ_YFvczeKIWHF^_UVDh;XNW0`7*vWS<6+@X&&ysmIIH;l49D-i}e zxEdlIlFWsxs!}1-`7=X^i&kPNb^S;rzUxD2LiNB(fp@nbVva)c9B#FolRIMKpG+y| za-un2$#W8GYJ(DLP&e4$Jby+Fo1wM7Pidx(o$w{Ng=)rLHHf>>!)5`pP1f`d=@Mvs+*uw#J zCJS1f)Q0w=L7m7bO)F^Rw$ukg?4;zD=S<bXjgaRjO&)c@JBodKNp^M~bJrMXhhH zxZyn2xG!q6DoeI(c^O>dw&5pe@J zoc}XC6b$cVG@yd978}w_ca`}`#Nbn)JHtrDB-{W#0GNhJ*vjHjl6L{`VH^$V)Zng` zSg_A1hN#DB==o=0Lp5+S5(y`>fUV&4=wcbqM4SYCI|{l2i* z=@sw~1wYGp32ZOS02?gW2k|^{1L6qh*_ig{ypMwn!~6mTQ9voMgfGF#e-eqb(J62Z z@N=e*fvv^7&v*#ph+Fo>f(?y`C;k;SB&-1+4s>I>miG6-4Eo!F0>sZEfp7uye-GOi zb2s$Ak*=l|1&Oo4Uu5~)SRObx0`n5${Cb0U;+IjMxV|2OH;e{MHRcrv^!Z%HBs|Ir zwK2Socm@0lTP!%?O>hZ}TfmV(74tWu4!w^}XlN9$8Ju)KrjP4Iy*Cx}$Bbq|3j&G1 z&-i^5Ce8+boB3N&cr=_37=O(86Zir!p&JQZ9I>F$Ht1xuW74(xn*4gsdkG6z&9H>w zUlfgp(uu@C7=zK+SYS7h_&F8_lLr3+%Q(!J*qGuP{?M@=?w_7M z4c;69K`_KlChiSxBrX$iJfZTHpAQCiX512S#H~b}j_`GG9Z-Qh!jUYl8;y?#k7OLC z>HFhtkwAhS5(w?l2;o8&=m32JxFe7rof9@14eSs8CU5}q2vx|VJ5FMGxLRW<_-@8E z&?kbsvbK$MS~NBpaZUrU zppFmnr@-(@chZ}WC76(;iAEhLV++CGL?z{_o_ytz*M*bP>|4;>4D(k;A5dv z-Z*eV9U7*_1OHJcM2`)oK}co71SCuce+vc39|}$r-+VGX?t#D+?o z*f1k$H5T~ekw2F#4K4Wo*Pvr^B%f$2yj3}FFQ^c#!>Ek2_d7(*F&Fd?~OHd>L>*;)nxv#EVde zp^@-o#4QK<;Nh4Oi&%nH>D7F$RWBB5@g(4w)QIn9p*jOC5#H=_wRw`7k)(MJu-cT9O zOAO&jI+39=S!ZTYr1EBlN_~@9oo;>1{&8GTYR9;oPuv1(fAhOB#O}x-<})S=(h*2*&X_WxtqUW7g&B{ZDmL2=|2+e$KTl&@oVhY zRlj{z(PJyS-`D254Wm5(&~8<$vua$g9k|eNFspK}tncRd;6D<^$btgOYOOSJW%IpC(}nP@ zYD4zcCXpdK^Ps3W{3Ed;E-P9rJk5y~vUjS5x}CfbmuobH3p$Zdxr>jkUb4MAGyG;?J8GCME`O*V)NiC^g8$YyK~;Y8)~ik=;NS${nNf~IO(f!sot~p zQtm_dy?*eS`b@_40 zh5XtL=55oDl*alN_pct~ytzCgCwtcSyK*dA#$P!!;$~@^HgZz5eM?1Ta!YpTp#4#f z>s_5X?vD-7d{8#-Q1`j3c|Z8hu>7oI$IYC6OEdHP3xBph?;P>k&FxqB3*q_d6`RVK<<>V!P66 z#+iWut)DLc(4p(Pw#(;mPWpQ{oOWKV-;``M^T&SrM5CKg;`xnD=kYtLJ0domWQOK^ VJF(zUs8RpF96|e`T3BDW;y;m*JHr3~ delta 6066 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 TestResult { let init_seed: [u8; 32] = rand::random(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(rand::random()); let secret_key = miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey::with_rng( - &mut miden_node_utils::crypto::get_rpo_random_coin(&mut rng), + &mut miden_node_utils::crypto::get_random_coin(&mut rng), ); let auth = AuthMethod::SingleSig { approver: (secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2), @@ -164,7 +164,7 @@ fn parsing_native_faucet_from_file() -> TestResult { let init_seed: [u8; 32] = rand::random(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(rand::random()); let secret_key = miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey::with_rng( - &mut miden_node_utils::crypto::get_rpo_random_coin(&mut rng), + &mut miden_node_utils::crypto::get_random_coin(&mut rng), ); let auth = AuthSingleSig::new(secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2); @@ -235,7 +235,7 @@ fn native_faucet_from_file_must_be_faucet_type() -> TestResult { let init_seed: [u8; 32] = rand::random(); let mut rng = rand_chacha::ChaCha20Rng::from_seed(rand::random()); let secret_key = miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey::with_rng( - &mut miden_node_utils::crypto::get_rpo_random_coin(&mut rng), + &mut miden_node_utils::crypto::get_random_coin(&mut rng), ); let auth = AuthMethod::SingleSig { approver: (secret_key.public_key().into(), AuthScheme::Falcon512Poseidon2), diff --git a/crates/store/src/server/rpc_api.rs b/crates/store/src/server/rpc_api.rs index ec66203288..5512e3aac6 100644 --- a/crates/store/src/server/rpc_api.rs +++ b/crates/store/src/server/rpc_api.rs @@ -426,14 +426,14 @@ impl rpc_server::Rpc for StoreApi { })) } - async fn are_network_accounts( + async fn filter_network_accounts( &self, request: Request, - ) -> Result, Status> { + ) -> Result, Status> { let ids = read_account_ids::(request.into_inner().account_ids)?; - let subset = self.state.network_accounts_subset(&ids).await?; - let network_account_ids = subset.into_iter().map(proto::account::AccountId::from).collect(); - Ok(Response::new(proto::store::NetworkAccountIdSubset { network_account_ids })) + let subset = self.state.filter_network_accounts(&ids).await?; + let account_ids = subset.into_iter().map(proto::account::AccountId::from).collect(); + Ok(Response::new(proto::account::AccountIdList { account_ids })) } async fn sync_transactions( diff --git a/crates/store/src/state/loader.rs b/crates/store/src/state/loader.rs index 6c64dc1d8b..9d06f00e49 100644 --- a/crates/store/src/state/loader.rs +++ b/crates/store/src/state/loader.rs @@ -465,9 +465,8 @@ pub async fn load_mmr(db: &mut Db) -> Result Result, DatabaseError> { diff --git a/crates/utils/src/crypto.rs b/crates/utils/src/crypto.rs index baa2229dc2..22a94b82dd 100644 --- a/crates/utils/src/crypto.rs +++ b/crates/utils/src/crypto.rs @@ -2,8 +2,8 @@ use miden_protocol::crypto::rand::RandomCoin; use miden_protocol::{Felt, Word}; use rand::Rng; -/// Creates a new RPO Random Coin with random seed -pub fn get_rpo_random_coin(rng: &mut T) -> RandomCoin { +/// Creates a new [`RandomCoin`] with random seed. +pub fn get_random_coin(rng: &mut T) -> RandomCoin { let auth_seed: [u64; 4] = rng.random(); let rng_seed = Word::from(auth_seed.map(Felt::new_unchecked)); diff --git a/docs/external/src/operator/usage.md b/docs/external/src/operator/usage.md index 647fec0a88..87d35fedeb 100644 --- a/docs/external/src/operator/usage.md +++ b/docs/external/src/operator/usage.md @@ -74,8 +74,8 @@ decimals = 6 # e.g. a max supply of `1e15` _base units_ and decimals set to `6`, will yield you a total supply # of `1e15/1e6 = 1e9` `FUZZY`s. max_supply = 1_000_000_000_000_000 -# Storage mode of the faucet account. -storage_mode = "public" +# Account type of the faucet account. +account_type = "public" [[wallet]] @@ -83,8 +83,8 @@ storage_mode = "public" # The number is in _base units_, e.g. specifying `999 FUZZY` at 6 decimals would become # `999_000_000`. assets = [{ amount = 999_000_000, symbol = "FUZZY" }] -# Storage mode of the wallet account. -storage_mode = "private" +# Account type of the wallet account. +account_type = "private" ``` To include pre-built accounts (e.g. bridge or wrapped-asset faucets) in the genesis block, use diff --git a/proto/proto/internal/store.proto b/proto/proto/internal/store.proto index 8df2879630..ad6cd65db3 100644 --- a/proto/proto/internal/store.proto +++ b/proto/proto/internal/store.proto @@ -23,12 +23,12 @@ service Rpc { // Filters the provided account ids down to the subset that are currently // classified as network accounts. Ids that don't exist in the store, or that - // aren't network accounts, are omitted. The order of `network_account_ids` - // is unspecified. + // aren't network accounts, are omitted. The order of the returned ids is + // unspecified. // // Callers checking a single id should pass a one-element list and check whether // the response is non-empty. - rpc AreNetworkAccounts(account.AccountIdList) returns (NetworkAccountIdSubset) {} + rpc FilterNetworkAccounts(account.AccountIdList) returns (account.AccountIdList) {} // Returns raw block data for the specified block number, optionally including the block proof. rpc GetBlockByNumber(blockchain.BlockRequest) returns (blockchain.MaybeBlock) {} diff --git a/proto/proto/types/note.proto b/proto/proto/types/note.proto index 78c890ceaa..a3525afbb9 100644 --- a/proto/proto/types/note.proto +++ b/proto/proto/types/note.proto @@ -138,10 +138,10 @@ message NoteSyncRecord { // Represents a note header. // -// A note header consists of a note's ID and its metadata. +// A note header consists of a commitment to the note's details and its metadata. message NoteHeader { - // A unique identifier of the note. - NoteId note_id = 1; + // Commitment to the note's details (recipient + assets), excluding metadata. + primitives.Digest details_commitment = 1; // The note's metadata. NoteMetadata metadata = 2; From fd1f458cc0f1ad2cb61ff8150cc34e69896c1dfc Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 26 May 2026 15:42:27 -0300 Subject: [PATCH 2/5] chore: remove `details_commitment` from store --- crates/store/src/db/migrations.rs | 2 +- crates/store/src/db/migrations/001_initial.sql | 2 -- crates/store/src/db/mod.rs | 1 - crates/store/src/db/models/queries/notes.rs | 12 +----------- crates/store/src/db/schema.rs | 1 - crates/store/src/db/tests.rs | 15 --------------- crates/store/src/state/apply_block.rs | 1 - 7 files changed, 2 insertions(+), 32 deletions(-) diff --git a/crates/store/src/db/migrations.rs b/crates/store/src/db/migrations.rs index ac87105bdd..5d56caf695 100644 --- a/crates/store/src/db/migrations.rs +++ b/crates/store/src/db/migrations.rs @@ -50,7 +50,7 @@ mod tests { use super::*; const EXPECTED_SCHEMA_HASHES: [SchemaHash; 1] = [SchemaHash::from_hex( - "66ca90d8f2681eee5418a30ba67c003b51ab7185eb120b36c6081e5628c603a6", + "d8f0b2f5c2d7011c2a806ebdb7ddf3d957a6edeed065ccf21019205ebc1a01a4", )]; #[test] diff --git a/crates/store/src/db/migrations/001_initial.sql b/crates/store/src/db/migrations/001_initial.sql index 70f0d05f5e..ca32aad111 100644 --- a/crates/store/src/db/migrations/001_initial.sql +++ b/crates/store/src/db/migrations/001_initial.sql @@ -54,7 +54,6 @@ CREATE TABLE notes ( committed_at BIGINT NOT NULL, -- Block number when the note was committed batch_index INTEGER NOT NULL, -- Index of batch in block, starting from 0 note_index INTEGER NOT NULL, -- Index of note in batch, starting from 0 - details_commitment BLOB NOT NULL, note_id BLOB NOT NULL, note_type INTEGER NOT NULL, -- 0-Private, 1-Public sender BLOB NOT NULL, @@ -78,7 +77,6 @@ CREATE TABLE notes ( CONSTRAINT notes_note_index_is_u32 CHECK (note_index BETWEEN 0 AND 0xFFFFFFFF) ); -CREATE INDEX idx_notes_details_commitment ON notes(details_commitment); CREATE INDEX idx_notes_note_id ON notes(note_id); CREATE INDEX idx_notes_sender ON notes(sender, committed_at); CREATE INDEX idx_notes_tag ON notes(tag, committed_at); diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 380985f47a..3a574f77af 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -170,7 +170,6 @@ impl TransactionRecord { pub struct NoteRecord { pub block_num: BlockNumber, pub note_index: BlockNoteIndex, - pub details_commitment: Word, pub note_id: Word, pub metadata: NoteMetadata, pub details: Option, diff --git a/crates/store/src/db/models/queries/notes.rs b/crates/store/src/db/models/queries/notes.rs index 02524b5805..4ed0205033 100644 --- a/crates/store/src/db/models/queries/notes.rs +++ b/crates/store/src/db/models/queries/notes.rs @@ -599,8 +599,7 @@ pub struct NoteSyncRecordRawRow { pub committed_at: i64, // BlockNumber #[diesel(embed)] pub block_note_index: BlockNoteIndexRawRow, - pub details_commitment: Vec, // BlobDigest - pub note_id: Vec, // BlobDigest + pub note_id: Vec, // BlobDigest #[diesel(embed)] pub metadata: NoteMetadataRawRow, pub inclusion_path: Vec, // SparseMerklePath @@ -644,7 +643,6 @@ pub struct NoteRecordWithScriptRawJoined { pub note_index: i32, // index within batch // #[diesel(embed)] // pub note_index: BlockNoteIndexRaw, - pub details_commitment: Vec, pub note_id: Vec, pub note_type: i32, @@ -669,7 +667,6 @@ impl From<(NoteRecordRawRow, Option>)> for NoteRecordWithScriptRawJoined committed_at, batch_index, note_index, - details_commitment, note_id, note_type, sender, @@ -684,7 +681,6 @@ impl From<(NoteRecordRawRow, Option>)> for NoteRecordWithScriptRawJoined committed_at, batch_index, note_index, - details_commitment, note_id, note_type, sender, @@ -710,7 +706,6 @@ impl TryInto for NoteRecordWithScriptRawJoined { batch_index, note_index, // block note index ^^^ - details_commitment, note_id, note_type, @@ -732,7 +727,6 @@ impl TryInto for NoteRecordWithScriptRawJoined { let (metadata, attachments) = metadata.try_into()?; let committed_at = BlockNumber::from_raw_sql(committed_at)?; - let details_commitment = Word::read_from_bytes(&details_commitment[..])?; let note_id = Word::read_from_bytes(¬e_id[..])?; let script = script.map(|script| NoteScript::read_from_bytes(&script[..])).transpose()?; let details = if let NoteDetailsRawRow { @@ -762,7 +756,6 @@ impl TryInto for NoteRecordWithScriptRawJoined { Ok(NoteRecord { block_num: committed_at, note_index, - details_commitment, note_id, metadata, details, @@ -780,7 +773,6 @@ pub struct NoteRecordRawRow { pub batch_index: i32, pub note_index: i32, // index within batch - pub details_commitment: Vec, pub note_id: Vec, pub note_type: i32, @@ -919,7 +911,6 @@ pub struct NoteInsertRow { pub batch_index: i32, pub note_index: i32, // index within batch - pub details_commitment: Vec, pub note_id: Vec, pub note_type: i32, @@ -953,7 +944,6 @@ impl From<(NoteRecord, Option)> for NoteInsertRow { committed_at: note.block_num.to_raw_sql(), batch_index: idx_to_raw_sql(note.note_index.batch_idx()), note_index: idx_to_raw_sql(note.note_index.note_idx_in_batch()), - details_commitment: note.details_commitment.to_bytes(), note_id: note.note_id.to_bytes(), note_type: note_type_to_raw_sql(note.metadata.note_type() as u8), sender: note.metadata.sender().to_bytes(), diff --git a/crates/store/src/db/schema.rs b/crates/store/src/db/schema.rs index 3217abd94b..2e421c8034 100644 --- a/crates/store/src/db/schema.rs +++ b/crates/store/src/db/schema.rs @@ -64,7 +64,6 @@ diesel::table! { committed_at -> BigInt, batch_index -> Integer, note_index -> Integer, - details_commitment -> Binary, note_id -> Binary, note_type -> Integer, sender -> Binary, diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index e48e979768..468e38b645 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -237,7 +237,6 @@ fn sql_select_notes() { let note = NoteRecord { block_num, note_index: BlockNoteIndex::new(0, i.try_into().unwrap()).unwrap(), - details_commitment: num_to_word(u64::try_from(i).unwrap()), note_id: num_to_word(u64::try_from(i).unwrap()), metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), @@ -282,7 +281,6 @@ fn sql_select_note_script_by_root() { let note = NoteRecord { block_num, note_index: BlockNoteIndex::new(0, 0.try_into().unwrap()).unwrap(), - details_commitment: num_to_word(0), note_id: num_to_word(0), metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), @@ -358,7 +356,6 @@ fn sql_unconsumed_network_notes() { let note = NoteRecord { block_num: 0.into(), // Created on same block. note_index: BlockNoteIndex::new(0, i as usize).unwrap(), - details_commitment: num_to_word(i.into()), note_id: num_to_word(i.into()), metadata, details: None, @@ -826,7 +823,6 @@ fn notes() { let note = NoteRecord { block_num: block_num_1, note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: note_metadata, details: Some(NoteDetails::from(&new_note)), @@ -857,7 +853,6 @@ fn notes() { let note2 = NoteRecord { block_num: block_num_2, note_index: note.note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: note.metadata, details: None, @@ -932,7 +927,6 @@ fn note_sync_across_multiple_blocks() { let note = NoteRecord { block_num, note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: note_metadata, details: Some(NoteDetails::from(&new_note)), @@ -1014,7 +1008,6 @@ fn note_sync_multi_respects_payload_limit() { let note = NoteRecord { block_num, note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: note_metadata, details: Some(NoteDetails::from(&new_note)), @@ -1072,7 +1065,6 @@ fn note_sync_no_matching_tags() { let note = NoteRecord { block_num, note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: note_metadata, details: Some(NoteDetails::from(&new_note)), @@ -2502,7 +2494,6 @@ fn db_roundtrip_notes() { let note = NoteRecord { block_num, note_index, - details_commitment: new_note.details_commitment().as_word(), note_id: new_note.id().as_word(), metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), @@ -2522,10 +2513,6 @@ fn db_roundtrip_notes() { let retrieved_note = &retrieved[0]; assert_eq!(note.note_id, retrieved_note.note_id, "NoteId DB roundtrip must be symmetric"); - assert_eq!( - note.details_commitment, retrieved_note.details_commitment, - "Details commitment DB roundtrip must be symmetric" - ); assert_eq!( note.metadata, retrieved_note.metadata, "Metadata DB roundtrip must be symmetric" @@ -2770,7 +2757,6 @@ fn db_roundtrip_note_metadata_attachment() { let note = NoteRecord { block_num, note_index: BlockNoteIndex::new(0, 0).unwrap(), - details_commitment: num_to_word(1), note_id: num_to_word(1), metadata, details: None, @@ -3612,7 +3598,6 @@ fn db_roundtrip_transactions() { NoteRecord { block_num, note_index: BlockNoteIndex::new(0, idx).unwrap(), - details_commitment: note.details_commitment().as_word(), note_id: note.id().as_word(), metadata: *note.metadata(), details: None, diff --git a/crates/store/src/state/apply_block.rs b/crates/store/src/state/apply_block.rs index 98abe40e85..4c5f2ab731 100644 --- a/crates/store/src/state/apply_block.rs +++ b/crates/store/src/state/apply_block.rs @@ -318,7 +318,6 @@ impl State { let note_record = NoteRecord { block_num, note_index, - details_commitment: note.details_commitment().as_word(), note_id: note.id().as_word(), metadata: *note.metadata(), details, From 69aa66586705a28740359c78a5238a43600e141f Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 26 May 2026 18:18:18 -0300 Subject: [PATCH 3/5] refactor: add network account cache for `RpcService` --- CHANGELOG.md | 1 + crates/rpc/src/server/api.rs | 41 ++++++++++++++++++++++++++++-------- crates/rpc/src/server/mod.rs | 1 + 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 575e6b3b6e..b4345e1b27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - [BREAKING] Renamed `SubmitProvenBatch` RPC endpoint to `SubmitProvenTxBatch` ([#2094](https://github.com/0xMiden/node/pull/2094)). - [BREAKING] Updated `miden-protocol` to `v0.15.0` and `miden-crypto` to `v0.25`. Faucet-vs-wallet distinction is now component-based; the former `AccountStorageMode` is renamed to `AccountType` with only `Public`/`Private` variants. Genesis config `storage_mode` is renamed to `account_type`, the `Network` variant is removed, and the implicit default for wallets and faucets is now `Private`. The `has_updatable_code` wallet field is removed. `NetworkAccountId` is removed; network-account identity is now a plain `AccountId`. Fixed `proto::note::NoteHeader` so it round-trips losslessly (wire field is now `details_commitment` instead of `note_id`) ([#2095](https://github.com/0xMiden/node/pull/2095), [#2132](https://github.com/0xMiden/node/pull/2132)). - Updated `miden-protocol` and bumped `miden-crypto` to `v0.25`. `AccountId::is_network()` was removed upstream, so `SubmitProvenTx` and `SubmitProvenTxBatch` now consult the store to classify post-deployment public-account transactions as network accounts. +- The RPC service now memoizes store-confirmed network-account classifications in memory, so repeat post-deployment submissions for a known network account are rejected by `SubmitProvenTx`/`SubmitProvenTxBatch` without a store round-trip. Classification is fixed at account creation, so cached entries never go stale ([#2132](https://github.com/0xMiden/node/pull/2132)). - [BREAKING] Removed `Network` variant from genesis config `StorageMode`. The implicit default for wallets and fungible faucets is now `Private` (previously `Network`, which mapped to `Public` storage) ([#2095](https://github.com/0xMiden/node/pull/2095)). - [BREAKING] Updated `miden-protocol` family of crates to the published `v0.15.0` on crates.io (previously tracked the `next` branch). The published release removes the multi-variant `AccountType` (`RegularAccount*`, `FungibleFaucet`, `NonFungibleFaucet`) and renames the former `AccountStorageMode` to `AccountType` with only `Public`/`Private`. Faucet-vs-wallet distinction is now component-based. - [BREAKING] Removed the `has_updatable_code` field from genesis `[[wallet]]` config entries. Updatable/immutable code is no longer modeled by the protocol, so any genesis config that explicitly sets this field will fail to parse — remove the field. diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 6cb688cb9f..7023bc0d5e 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -28,6 +28,7 @@ use miden_node_utils::limiter::{ }; use miden_node_utils::lru_cache::LruCache; use miden_node_utils::tracing::OpenTelemetrySpanExt; +use miden_protocol::account::AccountId; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::transaction::{ @@ -49,6 +50,9 @@ use crate::COMPONENT; // RPC SERVICE // ================================================================================================ +/// Error returned when a user submits a transaction for an account classified as a network account. +const NETWORK_TX_REJECTION_MSG: &str = "Network transactions may not be submitted by users yet"; + pub struct RpcService { store: StoreRpcClient, block_producer: Option, @@ -56,6 +60,7 @@ pub struct RpcService { ntx_builder: Option, genesis_commitment: Option, block_commitment_cache: LruCache, + network_account_cache: LruCache, } impl RpcService { @@ -65,6 +70,7 @@ impl RpcService { validator_url: Url, ntx_builder_url: Option, commitment_cache_capacity: NonZeroUsize, + network_account_cache_capacity: NonZeroUsize, ) -> Self { let store = { info!(target: COMPONENT, store_endpoint = %store_url, "Initializing store client"); @@ -129,6 +135,7 @@ impl RpcService { ntx_builder, genesis_commitment: None, block_commitment_cache: LruCache::new(commitment_cache_capacity), + network_account_cache: LruCache::new(network_account_cache_capacity), } } @@ -242,30 +249,46 @@ impl RpcService { /// should pre-filter to post-deployment, public-account ids; `Ok(())` on empty. async fn reject_if_any_network_accounts( &self, - candidate_ids: Vec, + candidate_ids: Vec, ) -> Result<(), Status> { if candidate_ids.is_empty() { return Ok(()); } + // A cached id is a known network account, so the gate fails without touching the store. + if self + .network_account_cache + .get_many(candidate_ids.iter()) + .iter() + .any(Option::is_some) + { + return Err(Status::invalid_argument(NETWORK_TX_REJECTION_MSG)); + } + let response = self .store .clone() .filter_network_accounts(tonic::Request::new(proto::account::AccountIdList { - account_ids: candidate_ids, + account_ids: candidate_ids.iter().map(|id| (*id).into()).collect(), })) .await .map_err(|err| { Status::internal(format!("network-account classification failed: {err}")) })?; - if !response.into_inner().account_ids.is_empty() { - return Err(Status::invalid_argument( - "Network transactions may not be submitted by users yet", - )); + let network_ids: Vec = read_account_ids(response.into_inner().account_ids) + .map_err(|err: ConversionError| { + Status::internal(format!("malformed network-account response: {err}")) + })?; + + if network_ids.is_empty() { + return Ok(()); } - Ok(()) + // Memoize the confirmed network accounts so subsequent gate checks skip the store. + self.network_account_cache.put_many(network_ids.into_iter().map(|id| (id, ()))); + + Err(Status::invalid_argument(NETWORK_TX_REJECTION_MSG)) } } @@ -553,7 +576,7 @@ impl api_server::Api for RpcService { let candidate_ids = if !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() { - vec![tx.account_id().into()] + vec![tx.account_id()] } else { Vec::new() }; @@ -641,7 +664,7 @@ impl api_server::Api for RpcService { !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() }) - .map(|tx| proto::account::AccountId::from(tx.account_id())) + .map(|tx| tx.account_id()) .collect(); self.reject_if_any_network_accounts(non_deployment_ids).await?; diff --git a/crates/rpc/src/server/mod.rs b/crates/rpc/src/server/mod.rs index 6a8c3acc98..edd6646ffe 100644 --- a/crates/rpc/src/server/mod.rs +++ b/crates/rpc/src/server/mod.rs @@ -51,6 +51,7 @@ impl Rpc { self.validator_url, self.ntx_builder_url.clone(), NonZeroUsize::new(1_000_000).unwrap(), + NonZeroUsize::new(65_536).unwrap(), ); let genesis = api From 3b57c9e3a7c15759cb2c1a2c8a9dc64d618b2678 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 26 May 2026 20:56:30 -0300 Subject: [PATCH 4/5] Revert "refactor: add network account cache for `RpcService`" This reverts commit 69aa66586705a28740359c78a5238a43600e141f. --- CHANGELOG.md | 1 - crates/rpc/src/server/api.rs | 41 ++++++++---------------------------- crates/rpc/src/server/mod.rs | 1 - 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4345e1b27..575e6b3b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,6 @@ - [BREAKING] Renamed `SubmitProvenBatch` RPC endpoint to `SubmitProvenTxBatch` ([#2094](https://github.com/0xMiden/node/pull/2094)). - [BREAKING] Updated `miden-protocol` to `v0.15.0` and `miden-crypto` to `v0.25`. Faucet-vs-wallet distinction is now component-based; the former `AccountStorageMode` is renamed to `AccountType` with only `Public`/`Private` variants. Genesis config `storage_mode` is renamed to `account_type`, the `Network` variant is removed, and the implicit default for wallets and faucets is now `Private`. The `has_updatable_code` wallet field is removed. `NetworkAccountId` is removed; network-account identity is now a plain `AccountId`. Fixed `proto::note::NoteHeader` so it round-trips losslessly (wire field is now `details_commitment` instead of `note_id`) ([#2095](https://github.com/0xMiden/node/pull/2095), [#2132](https://github.com/0xMiden/node/pull/2132)). - Updated `miden-protocol` and bumped `miden-crypto` to `v0.25`. `AccountId::is_network()` was removed upstream, so `SubmitProvenTx` and `SubmitProvenTxBatch` now consult the store to classify post-deployment public-account transactions as network accounts. -- The RPC service now memoizes store-confirmed network-account classifications in memory, so repeat post-deployment submissions for a known network account are rejected by `SubmitProvenTx`/`SubmitProvenTxBatch` without a store round-trip. Classification is fixed at account creation, so cached entries never go stale ([#2132](https://github.com/0xMiden/node/pull/2132)). - [BREAKING] Removed `Network` variant from genesis config `StorageMode`. The implicit default for wallets and fungible faucets is now `Private` (previously `Network`, which mapped to `Public` storage) ([#2095](https://github.com/0xMiden/node/pull/2095)). - [BREAKING] Updated `miden-protocol` family of crates to the published `v0.15.0` on crates.io (previously tracked the `next` branch). The published release removes the multi-variant `AccountType` (`RegularAccount*`, `FungibleFaucet`, `NonFungibleFaucet`) and renames the former `AccountStorageMode` to `AccountType` with only `Public`/`Private`. Faucet-vs-wallet distinction is now component-based. - [BREAKING] Removed the `has_updatable_code` field from genesis `[[wallet]]` config entries. Updatable/immutable code is no longer modeled by the protocol, so any genesis config that explicitly sets this field will fail to parse — remove the field. diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 7023bc0d5e..6cb688cb9f 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -28,7 +28,6 @@ use miden_node_utils::limiter::{ }; use miden_node_utils::lru_cache::LruCache; use miden_node_utils::tracing::OpenTelemetrySpanExt; -use miden_protocol::account::AccountId; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::transaction::{ @@ -50,9 +49,6 @@ use crate::COMPONENT; // RPC SERVICE // ================================================================================================ -/// Error returned when a user submits a transaction for an account classified as a network account. -const NETWORK_TX_REJECTION_MSG: &str = "Network transactions may not be submitted by users yet"; - pub struct RpcService { store: StoreRpcClient, block_producer: Option, @@ -60,7 +56,6 @@ pub struct RpcService { ntx_builder: Option, genesis_commitment: Option, block_commitment_cache: LruCache, - network_account_cache: LruCache, } impl RpcService { @@ -70,7 +65,6 @@ impl RpcService { validator_url: Url, ntx_builder_url: Option, commitment_cache_capacity: NonZeroUsize, - network_account_cache_capacity: NonZeroUsize, ) -> Self { let store = { info!(target: COMPONENT, store_endpoint = %store_url, "Initializing store client"); @@ -135,7 +129,6 @@ impl RpcService { ntx_builder, genesis_commitment: None, block_commitment_cache: LruCache::new(commitment_cache_capacity), - network_account_cache: LruCache::new(network_account_cache_capacity), } } @@ -249,46 +242,30 @@ impl RpcService { /// should pre-filter to post-deployment, public-account ids; `Ok(())` on empty. async fn reject_if_any_network_accounts( &self, - candidate_ids: Vec, + candidate_ids: Vec, ) -> Result<(), Status> { if candidate_ids.is_empty() { return Ok(()); } - // A cached id is a known network account, so the gate fails without touching the store. - if self - .network_account_cache - .get_many(candidate_ids.iter()) - .iter() - .any(Option::is_some) - { - return Err(Status::invalid_argument(NETWORK_TX_REJECTION_MSG)); - } - let response = self .store .clone() .filter_network_accounts(tonic::Request::new(proto::account::AccountIdList { - account_ids: candidate_ids.iter().map(|id| (*id).into()).collect(), + account_ids: candidate_ids, })) .await .map_err(|err| { Status::internal(format!("network-account classification failed: {err}")) })?; - let network_ids: Vec = read_account_ids(response.into_inner().account_ids) - .map_err(|err: ConversionError| { - Status::internal(format!("malformed network-account response: {err}")) - })?; - - if network_ids.is_empty() { - return Ok(()); + if !response.into_inner().account_ids.is_empty() { + return Err(Status::invalid_argument( + "Network transactions may not be submitted by users yet", + )); } - // Memoize the confirmed network accounts so subsequent gate checks skip the store. - self.network_account_cache.put_many(network_ids.into_iter().map(|id| (id, ()))); - - Err(Status::invalid_argument(NETWORK_TX_REJECTION_MSG)) + Ok(()) } } @@ -576,7 +553,7 @@ impl api_server::Api for RpcService { let candidate_ids = if !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() { - vec![tx.account_id()] + vec![tx.account_id().into()] } else { Vec::new() }; @@ -664,7 +641,7 @@ impl api_server::Api for RpcService { !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() }) - .map(|tx| tx.account_id()) + .map(|tx| proto::account::AccountId::from(tx.account_id())) .collect(); self.reject_if_any_network_accounts(non_deployment_ids).await?; diff --git a/crates/rpc/src/server/mod.rs b/crates/rpc/src/server/mod.rs index edd6646ffe..6a8c3acc98 100644 --- a/crates/rpc/src/server/mod.rs +++ b/crates/rpc/src/server/mod.rs @@ -51,7 +51,6 @@ impl Rpc { self.validator_url, self.ntx_builder_url.clone(), NonZeroUsize::new(1_000_000).unwrap(), - NonZeroUsize::new(65_536).unwrap(), ); let genesis = api From 363ef8102c76a7b806ae3c286f7895691ee537e4 Mon Sep 17 00:00:00 2001 From: Ignacio Amigo Date: Tue, 26 May 2026 23:02:48 -0300 Subject: [PATCH 5/5] reviews: remove unnecessary protobuf message, remove felt import --- crates/rpc/src/server/api.rs | 26 ++++++++++++-------------- crates/store/build.rs | 3 +-- proto/proto/internal/store.proto | 10 ---------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index 6cb688cb9f..ed26ed6be4 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -28,6 +28,7 @@ use miden_node_utils::limiter::{ }; use miden_node_utils::lru_cache::LruCache; use miden_node_utils::tracing::OpenTelemetrySpanExt; +use miden_protocol::account::AccountId; use miden_protocol::batch::{ProposedBatch, ProvenBatch}; use miden_protocol::block::{BlockHeader, BlockNumber}; use miden_protocol::transaction::{ @@ -242,9 +243,11 @@ impl RpcService { /// should pre-filter to post-deployment, public-account ids; `Ok(())` on empty. async fn reject_if_any_network_accounts( &self, - candidate_ids: Vec, + candidate_ids: impl IntoIterator, ) -> Result<(), Status> { - if candidate_ids.is_empty() { + let account_ids: Vec = + candidate_ids.into_iter().map(Into::into).collect(); + if account_ids.is_empty() { return Ok(()); } @@ -252,7 +255,7 @@ impl RpcService { .store .clone() .filter_network_accounts(tonic::Request::new(proto::account::AccountIdList { - account_ids: candidate_ids, + account_ids, })) .await .map_err(|err| { @@ -550,14 +553,10 @@ impl api_server::Api for RpcService { // Block post-deployment network-account transactions from user RPC. First-deployment txs // are exempt because the protocol-level allowlist only kicks in once the account exists, // and network accounts must be public, so private-account txs are filtered out up front. - let candidate_ids = if !tx.account_update().initial_state_commitment().is_empty() - && tx.account_id().is_public() - { - vec![tx.account_id().into()] - } else { - Vec::new() - }; - self.reject_if_any_network_accounts(candidate_ids).await?; + let candidate_id = (!tx.account_update().initial_state_commitment().is_empty() + && tx.account_id().is_public()) + .then(|| tx.account_id()); + self.reject_if_any_network_accounts(candidate_id).await?; let tx_verifier = TransactionVerifier::new(MIN_PROOF_SECURITY_LEVEL); tx_verifier.verify(&tx).map_err(|err| { @@ -634,15 +633,14 @@ impl api_server::Api for RpcService { // Same gate as `submit_proven_transaction`, applied to every post-deployment tx in the // batch. One store round-trip classifies all the non-deployment, public-account ids; any // match fails the entire batch. - let non_deployment_ids: Vec<_> = proposed_batch + let non_deployment_ids = proposed_batch .transactions() .iter() .filter(|tx| { !tx.account_update().initial_state_commitment().is_empty() && tx.account_id().is_public() }) - .map(|tx| proto::account::AccountId::from(tx.account_id())) - .collect(); + .map(|tx| tx.account_id()); self.reject_if_any_network_accounts(non_deployment_ids).await?; // Verify batch transaction proofs. diff --git a/crates/store/build.rs b/crates/store/build.rs index cd6da178d9..ec7f6f3879 100644 --- a/crates/store/build.rs +++ b/crates/store/build.rs @@ -6,7 +6,6 @@ use miden_protocol::account::auth::AuthScheme; use miden_protocol::account::{Account, AccountCode, AccountFile, AccountType}; use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey; use miden_protocol::crypto::rand::RandomCoin; -use miden_protocol::field::PrimeCharacteristicRing; use miden_protocol::{Felt, Word}; use miden_standards::AuthMethod; use miden_standards::account::wallets::create_basic_wallet; @@ -95,7 +94,7 @@ fn generate_agglayer_sample_accounts() { usdc_faucet_seed, "USDC", 6, - Felt::from_u64(10_000_000_000u64), + Felt::new_unchecked(10_000_000_000u64), Felt::ZERO, bridge_account_id, ); diff --git a/proto/proto/internal/store.proto b/proto/proto/internal/store.proto index ad6cd65db3..c608a8b263 100644 --- a/proto/proto/internal/store.proto +++ b/proto/proto/internal/store.proto @@ -244,13 +244,3 @@ message TransactionInputs { // Whether the account ID prefix is unique. Only relevant for account creation requests. optional bool new_account_id_prefix_is_unique = 5; // TODO: Replace this with an error. When a general error message exists. } - -// ARE NETWORK ACCOUNTS -// ================================================================================================ - -// Represents the subset of the provided account ids that are currently classified -// as network accounts. The order of `network_account_ids` is unspecified. -message NetworkAccountIdSubset { - // The network-account subset of the requested ids. - repeated account.AccountId network_account_ids = 1; -}