diff --git a/CHANGELOG.md b/CHANGELOG.md index e4fd60ec9..575e6b3b6 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 6864a2071..5db357e3e 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 41710fc6a..1eb70bdde 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 3a79f4294..55e6d2dfd 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 08a153fe2..7781e891e 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 5217b37cd..4a0c38634 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 8705c2fb3..45dbb167e 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 4c85efba0..b30229650 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 6b01158bc..c586db8f8 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 5ec6e24f2..5a95cc4ec 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 cdfdfad9e..b732c65d5 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 41e328cce..01d8c21a1 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 385c2587d..137de6bcd 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 1ebabe66f..1e0e33074 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 7b42245ea..beae70c62 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 5f1b4d090..b106beddf 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 048116ef4..aa2c6c1be 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 d83315020..e19ba44db 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 fe59b189a..ba9bdb4d8 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 1dbbd6e9f..b0bd0688c 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 5d970de1d..3df13bc37 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 748686061..ed26ed6be 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::{ @@ -237,6 +238,38 @@ 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: impl IntoIterator, + ) -> Result<(), Status> { + let account_ids: Vec = + candidate_ids.into_iter().map(Into::into).collect(); + if account_ids.is_empty() { + return Ok(()); + } + + let response = self + .store + .clone() + .filter_network_accounts(tonic::Request::new(proto::account::AccountIdList { + account_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 +551,12 @@ 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() - { - 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", - )); - } - } + // 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_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| { @@ -615,36 +631,17 @@ 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. - let non_deployment_ids: Vec<_> = proposed_batch + // batch. One store round-trip classifies all the non-deployment, public-account ids; any + // match fails the entire 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(); - - 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", - )); - } - } + .map(|tx| tx.account_id()); + 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 f21124248..75c67500c 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 95c87860b..ec7f6f387 100644 --- a/crates/store/build.rs +++ b/crates/store/build.rs @@ -40,16 +40,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 +84,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 +94,8 @@ fn generate_agglayer_sample_accounts() { usdc_faucet_seed, "USDC", 6, - Felt::new_unchecked(10_000_000_000), - Felt::new_unchecked(0), + Felt::new_unchecked(10_000_000_000u64), + Felt::ZERO, bridge_account_id, ); diff --git a/crates/store/src/db/migrations.rs b/crates/store/src/db/migrations.rs index ac87105bd..5d56caf69 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 70f0d05f5..ca32aad11 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 ab36a1ef9..3a574f77a 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, @@ -452,7 +451,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 66524b307..abd26debc 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 c433457c8..e0b9038c4 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/db/models/queries/notes.rs b/crates/store/src/db/models/queries/notes.rs index 02524b580..4ed020503 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 3217abd94..2e421c803 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 e48e97976..468e38b64 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/errors.rs b/crates/store/src/errors.rs index 05b277e77..d16548804 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 b7c897a70..59556fcdc 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 50f85cd16..c24031d6a 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 1e6a756be..3a3049f6d 100644 Binary files a/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_eth.mac and b/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_eth.mac differ diff --git a/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_usdc.mac b/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_usdc.mac index 2c9886c6c..dee7fa660 100644 Binary files a/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_usdc.mac and b/crates/store/src/genesis/config/samples/02-with-account-files/agglayer_faucet_usdc.mac differ diff --git a/crates/store/src/genesis/config/samples/02-with-account-files/bridge.mac b/crates/store/src/genesis/config/samples/02-with-account-files/bridge.mac index ad6425b51..8412cfe5c 100644 Binary files a/crates/store/src/genesis/config/samples/02-with-account-files/bridge.mac and b/crates/store/src/genesis/config/samples/02-with-account-files/bridge.mac differ diff --git a/crates/store/src/genesis/config/tests.rs b/crates/store/src/genesis/config/tests.rs index 84858e8d5..2027522ba 100644 --- a/crates/store/src/genesis/config/tests.rs +++ b/crates/store/src/genesis/config/tests.rs @@ -103,7 +103,7 @@ fn parsing_account_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 = 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 ec6620328..5512e3aac 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/apply_block.rs b/crates/store/src/state/apply_block.rs index 98abe40e8..4c5f2ab73 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, diff --git a/crates/store/src/state/loader.rs b/crates/store/src/state/loader.rs index 6c64dc1d8..9d06f00e4 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 baa2229dc..22a94b82d 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 647fec0a8..87d35fede 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 8df287963..c608a8b26 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) {} @@ -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; -} diff --git a/proto/proto/types/note.proto b/proto/proto/types/note.proto index 78c890cea..a3525afbb 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;