From 73c57785d868cdc0b82b8619e34f94a34d2d46f9 Mon Sep 17 00:00:00 2001 From: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Date: Tue, 20 Jan 2026 16:16:22 -0300 Subject: [PATCH] Revert "chore: update with latest miden-base (#1526)" This reverts commit 5478bad3405986a5e81b6154213a7eaa61324830. --- CHANGELOG.md | 3 +- Cargo.lock | 85 ++---- bin/network-monitor/src/counter.rs | 17 +- bin/network-monitor/src/deploy/wallet.rs | 4 +- bin/stress-test/src/seeding/mod.rs | 11 +- bin/stress-test/src/store/mod.rs | 4 +- .../block-producer/src/domain/transaction.rs | 5 +- crates/block-producer/src/errors.rs | 3 +- .../src/test_utils/proven_tx.rs | 2 +- crates/ntx-builder/src/actor/account_state.rs | 53 ++-- crates/ntx-builder/src/actor/execute.rs | 3 +- crates/ntx-builder/src/actor/inflight_note.rs | 11 +- crates/ntx-builder/src/actor/mod.rs | 24 +- crates/ntx-builder/src/actor/note_state.rs | 16 +- crates/ntx-builder/src/builder.rs | 6 +- crates/ntx-builder/src/coordinator.rs | 25 +- crates/ntx-builder/src/store.rs | 10 +- crates/proto/Cargo.toml | 1 - crates/proto/src/domain/account.rs | 82 +++--- crates/proto/src/domain/note.rs | 128 +++++---- crates/proto/src/errors/mod.rs | 4 +- crates/proto/src/generated/note.rs | 11 +- crates/rpc/src/server/api.rs | 5 +- crates/store/src/accounts/mod.rs | 3 +- .../db/migrations/2025062000000_setup/up.sql | 39 +-- crates/store/src/db/mod.rs | 6 +- crates/store/src/db/models/conv.rs | 66 ++++- .../store/src/db/models/queries/accounts.rs | 16 +- .../src/db/models/queries/accounts/tests.rs | 10 +- crates/store/src/db/models/queries/notes.rs | 82 +++--- crates/store/src/db/schema.rs | 5 +- crates/store/src/db/tests.rs | 245 +++++++++++------- crates/store/src/errors.rs | 8 +- crates/store/src/genesis/config/errors.rs | 10 +- crates/store/src/genesis/config/mod.rs | 11 +- crates/store/src/server/ntx_builder.rs | 12 +- crates/store/src/state/mod.rs | 7 +- crates/validator/src/block_validation/mod.rs | 2 +- .../validator/src/tx_validation/data_store.rs | 34 ++- proto/proto/types/note.proto | 9 +- 40 files changed, 612 insertions(+), 466 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8fdd662..ff897a298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,9 +49,8 @@ - Increased the maximum query limit for the store ([#1443](https://github.com/0xMiden/miden-node/pull/1443)). - [BREAKING] Migrated to version `v0.20` of the VM ([#1476](https://github.com/0xMiden/miden-node/pull/1476)). - [BREAKING] Change account in database representation ([#1481](https://github.com/0xMiden/miden-node/pull/1481)). -- Remove the cyclic database optimization ([#1497](https://github.com/0xMiden/miden-node/pull/1497)). +- Remove the cyclic database optimization ([#1497](https://github.com/0xMiden/miden-node/pull/1497)). - Fix race condition at DB shutdown in tests ([#1503](https://github.com/0xMiden/miden-node/pull/1503)). -- [BREAKING] Updated to new miden-base protocol: removed `aux` and `execution_hint` from `NoteMetadata`, removed `NoteExecutionMode`, and `NoteMetadata::new()` is now infallible ([#1526](https://github.com/0xMiden/miden-node/pull/1526)). ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 15ad3a6f4..2d9c82f3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,17 +27,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.12" @@ -1366,7 +1355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1711,9 +1700,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -2144,7 +2130,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2511,22 +2497,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miden-agglayer" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" -dependencies = [ - "fs-err", - "miden-assembly", - "miden-core", - "miden-core-lib", - "miden-protocol", - "miden-standards", - "miden-utils-sync", - "regex", - "walkdir", -] - [[package]] name = "miden-air" version = "0.20.2" @@ -2581,8 +2551,8 @@ dependencies = [ [[package]] name = "miden-block-prover" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "miden-protocol", "thiserror 2.0.17", @@ -2882,7 +2852,6 @@ dependencies = [ "miden-node-proto-build", "miden-node-utils", "miden-protocol", - "miden-standards", "miette", "proptest", "prost", @@ -3075,8 +3044,8 @@ dependencies = [ [[package]] name = "miden-protocol" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "bech32", "fs-err", @@ -3105,8 +3074,8 @@ dependencies = [ [[package]] name = "miden-protocol-macros" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "proc-macro2", "quote", @@ -3195,8 +3164,8 @@ dependencies = [ [[package]] name = "miden-standards" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "fs-err", "miden-assembly", @@ -3212,15 +3181,12 @@ dependencies = [ [[package]] name = "miden-testing" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "anyhow", "itertools 0.14.0", - "miden-agglayer", - "miden-assembly", "miden-block-prover", - "miden-core-lib", "miden-processor", "miden-protocol", "miden-standards", @@ -3228,14 +3194,13 @@ dependencies = [ "miden-tx-batch-prover", "rand 0.9.2", "rand_chacha 0.9.0", - "thiserror 2.0.17", "winterfell", ] [[package]] name = "miden-tx" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "miden-processor", "miden-protocol", @@ -3247,8 +3212,8 @@ dependencies = [ [[package]] name = "miden-tx-batch-prover" -version = "0.14.0" -source = "git+https://github.com/0xMiden/miden-base.git?branch=next#99c6b5116a88cb3efc54bd8250a06ae082e9648f" +version = "0.13.0" +source = "git+https://github.com/0xMiden/miden-base.git?branch=next#20014df8b7e64648cf771a56572e47f78911b712" dependencies = [ "miden-protocol", "miden-tx", @@ -3501,7 +3466,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3897,7 +3862,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef622051fbb2cb98a524df3a8112f02d0919ccda600a44d705ec550f1a28fe2" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "blake2", "bytes", @@ -3933,7 +3898,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76f63d3f67d99c95a1f85623fc43242fd644dd12ccbaa18c38a54e1580c6846a" dependencies = [ - "ahash 0.8.12", + "ahash", "async-trait", "brotli", "bytes", @@ -4023,7 +3988,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b93c897e8cc04ff0d077ee2a655142910618222aeefc83f7f99f5b9fc59ccb13" dependencies = [ - "ahash 0.8.12", + "ahash", ] [[package]] @@ -4055,7 +4020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba89e4400cb978f0d7be1c14bd7ab4168c8e2c00d97ff19f964fc0048780237c" dependencies = [ "arrayvec", - "hashbrown 0.12.3", + "hashbrown 0.16.1", "parking_lot", "rand 0.8.5", ] @@ -4875,7 +4840,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5508,7 +5473,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5517,7 +5482,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6522,7 +6487,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/bin/network-monitor/src/counter.rs b/bin/network-monitor/src/counter.rs index 86eb7a2f2..b64a85f2b 100644 --- a/bin/network-monitor/src/counter.rs +++ b/bin/network-monitor/src/counter.rs @@ -20,6 +20,7 @@ use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey; use miden_protocol::note::{ Note, NoteAssets, + NoteExecutionHint, NoteInputs, NoteMetadata, NoteRecipient, @@ -29,7 +30,7 @@ use miden_protocol::note::{ }; use miden_protocol::transaction::{InputNotes, PartialBlockchain, TransactionArgs}; use miden_protocol::utils::Deserializable; -use miden_protocol::{Felt, Word}; +use miden_protocol::{Felt, Word, ZERO}; use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt}; use miden_standards::code_builder::CodeBuilder; use miden_tx::auth::BasicAuthenticator; @@ -317,7 +318,7 @@ async fn setup_increment_task( .await? .unwrap_or(wallet_account_file.account.clone()); - let AuthSecretKey::Falcon512Rpo(secret_key) = wallet_account_file + let AuthSecretKey::RpoFalcon512(secret_key) = wallet_account_file .auth_secret_keys .first() .expect("wallet account file should have one auth secret key") @@ -769,7 +770,7 @@ async fn create_and_submit_network_note( rng: &mut ChaCha20Rng, ) -> Result<(String, AccountHeader, BlockNumber)> { // Create authenticator for transaction signing - let authenticator = BasicAuthenticator::new(&[AuthSecretKey::Falcon512Rpo(secret_key.clone())]); + let authenticator = BasicAuthenticator::new(&[AuthSecretKey::RpoFalcon512(secret_key.clone())]); let account_interface = AccountInterface::from_account(wallet_account); @@ -793,8 +794,6 @@ async fn create_and_submit_network_note( .await .context("Failed to execute transaction")?; - let tx_inputs = executed_tx.tx_inputs().to_bytes(); - let final_account = executed_tx.final_account().clone(); // Prove the transaction @@ -804,7 +803,7 @@ async fn create_and_submit_network_note( // Submit the proven transaction let request = ProvenTransaction { transaction: proven_tx.to_bytes(), - transaction_inputs: Some(tx_inputs), + transaction_inputs: None, }; let block_height: BlockNumber = rpc_client @@ -852,8 +851,10 @@ fn create_network_note( let metadata = NoteMetadata::new( wallet_account.id(), NoteType::Public, - NoteTag::with_account_target(counter_account.id()), - ); + NoteTag::from_account_id(counter_account.id()), + NoteExecutionHint::Always, + ZERO, + )?; let serial_num = Word::new([ Felt::new(rng.random()), diff --git a/bin/network-monitor/src/deploy/wallet.rs b/bin/network-monitor/src/deploy/wallet.rs index de687ab6d..89c616c17 100644 --- a/bin/network-monitor/src/deploy/wallet.rs +++ b/bin/network-monitor/src/deploy/wallet.rs @@ -22,7 +22,7 @@ use crate::COMPONENT; 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 auth = AuthScheme::Falcon512Rpo { pub_key: secret_key.public_key().into() }; + let auth = AuthScheme::RpoFalcon512 { pub_key: secret_key.public_key().into() }; let init_seed: [u8; 32] = rng.random(); let wallet_account = create_basic_wallet( @@ -41,7 +41,7 @@ pub fn save_wallet_account( secret_key: &SecretKey, file_path: &Path, ) -> Result<()> { - let auth_secret_key = AuthSecretKey::Falcon512Rpo(secret_key.clone()); + let auth_secret_key = AuthSecretKey::RpoFalcon512(secret_key.clone()); let account_file = AccountFile::new(account.clone(), vec![auth_secret_key]); account_file.write(file_path)?; Ok(()) diff --git a/bin/stress-test/src/seeding/mod.rs b/bin/stress-test/src/seeding/mod.rs index e0fe79338..a3a258892 100644 --- a/bin/stress-test/src/seeding/mod.rs +++ b/bin/stress-test/src/seeding/mod.rs @@ -34,7 +34,6 @@ use miden_protocol::block::{ use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey as EcdsaSecretKey; use miden_protocol::crypto::dsa::falcon512_rpo::{PublicKey, SecretKey}; use miden_protocol::crypto::rand::RpoRandomCoin; -use miden_protocol::errors::AssetError; use miden_protocol::note::{Note, NoteHeader, NoteId, NoteInclusionProof}; use miden_protocol::transaction::{ InputNote, @@ -46,8 +45,8 @@ use miden_protocol::transaction::{ TransactionHeader, }; use miden_protocol::utils::Serializable; -use miden_protocol::{Felt, ONE, Word}; -use miden_standards::account::auth::AuthFalcon512Rpo; +use miden_protocol::{AssetError, Felt, ONE, Word}; +use miden_standards::account::auth::AuthRpoFalcon512; use miden_standards::account::faucets::BasicFungibleFaucet; use miden_standards::account::wallets::BasicWallet; use miden_standards::note::create_p2id_note; @@ -315,7 +314,7 @@ fn create_note(faucet_id: AccountId, target_id: AccountId, rng: &mut RpoRandomCo target_id, vec![asset], miden_protocol::note::NoteType::Public, - miden_protocol::note::NoteAttachment::default(), + Felt::default(), rng, ) .expect("note creation failed") @@ -328,7 +327,7 @@ fn create_account(public_key: PublicKey, index: u64, storage_mode: AccountStorag AccountBuilder::new(init_seed.try_into().unwrap()) .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(storage_mode) - .with_auth_component(AuthFalcon512Rpo::new(public_key.into())) + .with_auth_component(AuthRpoFalcon512::new(public_key.into())) .with_component(BasicWallet) .build() .unwrap() @@ -346,7 +345,7 @@ fn create_faucet() -> Account { .account_type(AccountType::FungibleFaucet) .storage_mode(AccountStorageMode::Private) .with_component(BasicFungibleFaucet::new(token_symbol, 2, Felt::new(u64::MAX)).unwrap()) - .with_auth_component(AuthFalcon512Rpo::new(key_pair.public_key().into())) + .with_auth_component(AuthRpoFalcon512::new(key_pair.public_key().into())) .build() .unwrap() } diff --git a/bin/stress-test/src/store/mod.rs b/bin/stress-test/src/store/mod.rs index e4960bb7e..59e094ba5 100644 --- a/bin/stress-test/src/store/mod.rs +++ b/bin/stress-test/src/store/mod.rs @@ -92,7 +92,7 @@ pub async fn sync_state( ) -> (Duration, proto::rpc::SyncStateResponse) { let note_tags = account_ids .iter() - .map(|id| u32::from(NoteTag::with_account_target(*id))) + .map(|id| u32::from(NoteTag::from_account_id(*id))) .collect::>(); let account_ids = account_ids @@ -158,7 +158,7 @@ pub async fn sync_notes( ) -> Duration { let note_tags = account_ids .iter() - .map(|id| u32::from(NoteTag::with_account_target(*id))) + .map(|id| u32::from(NoteTag::from_account_id(*id))) .collect::>(); let sync_request = proto::rpc::SyncNotesRequest { block_range: Some(proto::rpc::BlockRange { block_from: 0, block_to: None }), diff --git a/crates/block-producer/src/domain/transaction.rs b/crates/block-producer/src/domain/transaction.rs index 5b2ab30b3..0c819d06f 100644 --- a/crates/block-producer/src/domain/transaction.rs +++ b/crates/block-producer/src/domain/transaction.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; -use miden_protocol::note::{NoteHeader, Nullifier}; +use miden_protocol::note::Nullifier; use miden_protocol::transaction::{OutputNote, ProvenTransaction, TransactionId, TxAccountUpdate}; use crate::errors::VerifyTxError; @@ -119,7 +119,8 @@ impl AuthenticatedTransaction { pub fn unauthenticated_note_commitments(&self) -> impl Iterator + '_ { self.inner .unauthenticated_notes() - .map(NoteHeader::commitment) + .copied() + .map(|header| header.commitment()) .filter(|commitment| !self.notes_authenticated_by_store.contains(commitment)) } diff --git a/crates/block-producer/src/errors.rs b/crates/block-producer/src/errors.rs index 40c74c99f..38ac06716 100644 --- a/crates/block-producer/src/errors.rs +++ b/crates/block-producer/src/errors.rs @@ -2,12 +2,11 @@ use core::error::Error as CoreError; use miden_block_prover::BlockProverError; use miden_node_proto::errors::{ConversionError, GrpcError}; -use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; -use miden_protocol::errors::{ProposedBatchError, ProposedBlockError, ProvenBatchError}; use miden_protocol::note::Nullifier; use miden_protocol::transaction::TransactionId; +use miden_protocol::{ProposedBatchError, ProposedBlockError, ProvenBatchError, Word}; use miden_remote_prover_client::RemoteProverClientError; use thiserror::Error; use tokio::task::JoinError; diff --git a/crates/block-producer/src/test_utils/proven_tx.rs b/crates/block-producer/src/test_utils/proven_tx.rs index b8d67e9fb..aa6ec310e 100644 --- a/crates/block-producer/src/test_utils/proven_tx.rs +++ b/crates/block-producer/src/test_utils/proven_tx.rs @@ -131,7 +131,7 @@ impl MockProvenTxBuilder { .map(|note_index| { let note = Note::mock_noop(Word::from([0, 0, 0, note_index])); - OutputNote::Header(note.header().clone()) + OutputNote::Header(*note.header()) }) .collect(); diff --git a/crates/ntx-builder/src/actor/account_state.rs b/crates/ntx-builder/src/actor/account_state.rs index 3a9015a26..bfc5a41b7 100644 --- a/crates/ntx-builder/src/actor/account_state.rs +++ b/crates/ntx-builder/src/actor/account_state.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::num::NonZeroUsize; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::mempool::MempoolEvent; use miden_node_proto::domain::note::{NetworkNote, SingleTargetNetworkNote}; use miden_node_utils::tracing::OpenTelemetrySpanExt; @@ -49,8 +49,8 @@ pub struct TransactionCandidate { /// The current state of a network account. #[derive(Clone)] pub struct NetworkAccountState { - /// The network account ID corresponding to the network account this state represents. - account_id: NetworkAccountId, + /// The network account prefix corresponding to the network account this state represents. + account_prefix: NetworkAccountPrefix, /// Component of this state which Contains the committed and inflight account updates as well /// as available and nullified notes. @@ -74,23 +74,26 @@ impl NetworkAccountState { #[instrument(target = COMPONENT, name = "ntx.state.load", skip_all)] pub async fn load( account: Account, - account_id: NetworkAccountId, + account_prefix: NetworkAccountPrefix, store: &StoreClient, block_num: BlockNumber, ) -> Result { - let notes = store.get_unconsumed_network_notes(account_id, block_num.as_u32()).await?; + let notes = store.get_unconsumed_network_notes(account_prefix, block_num.as_u32()).await?; let notes = notes .into_iter() - .map(|note| { - let NetworkNote::SingleTarget(note) = note; - note + .filter_map(|note| { + if let NetworkNote::SingleTarget(note) = note { + Some(note) + } else { + None + } }) .collect::>(); let account = NetworkAccountNoteState::new(account, notes); let state = Self { account, - account_id, + account_prefix, inflight_txs: BTreeMap::default(), nullifier_idx: HashSet::default(), }; @@ -163,7 +166,7 @@ impl NetworkAccountState { } => { // Filter network notes relevant to this account. let network_notes = filter_by_prefix_and_map_to_single_target( - self.account_id, + self.account_prefix, network_notes.clone(), ); self.add_transaction(*id, nullifiers, &network_notes, account_delta.as_ref()); @@ -206,12 +209,12 @@ impl NetworkAccountState { let mut tx_impact = TransactionImpact::default(); if let Some(update) = account_delta.and_then(NetworkAccountEffect::from_protocol) { - let account_id = update.network_account_id(); - if account_id == self.account_id { + let account_prefix = update.prefix(); + if account_prefix == self.account_prefix { match update { NetworkAccountEffect::Updated(account_delta) => { self.account.add_delta(&account_delta); - tx_impact.account_delta = Some(account_id); + tx_impact.account_delta = Some(account_prefix); }, NetworkAccountEffect::Created(_) => {}, } @@ -219,9 +222,9 @@ impl NetworkAccountState { } for note in network_notes { assert_eq!( - note.account_id(), - self.account_id, - "note's account ID does not match network account actor's account ID" + note.account_prefix(), + self.account_prefix, + "transaction note prefix does not match network account actor's prefix" ); tx_impact.notes.insert(note.nullifier()); self.nullifier_idx.insert(note.nullifier()); @@ -250,7 +253,7 @@ impl NetworkAccountState { }; if let Some(prefix) = impact.account_delta { - if prefix == self.account_id { + if prefix == self.account_prefix { self.account.commit_delta(); } } @@ -273,10 +276,10 @@ impl NetworkAccountState { }; // Revert account creation. - if let Some(account_id) = impact.account_delta { + if let Some(account_prefix) = impact.account_delta { // Account creation reverted, actor must stop. - if account_id == self.account_id && self.account.revert_delta() { - return Some(ActorShutdownReason::AccountReverted(account_id)); + if account_prefix == self.account_prefix && self.account.revert_delta() { + return Some(ActorShutdownReason::AccountReverted(account_prefix)); } } @@ -315,7 +318,7 @@ impl NetworkAccountState { #[derive(Clone, Default)] struct TransactionImpact { /// The network account this transaction added an account delta to. - account_delta: Option, + account_delta: Option, /// Network notes this transaction created. notes: BTreeSet, @@ -332,14 +335,16 @@ impl TransactionImpact { /// Filters network notes by prefix and maps them to single target network notes. fn filter_by_prefix_and_map_to_single_target( - account_id: NetworkAccountId, + account_prefix: NetworkAccountPrefix, notes: Vec, ) -> Vec { notes .into_iter() .filter_map(|note| match note { - NetworkNote::SingleTarget(note) if note.account_id() == account_id => Some(note), - NetworkNote::SingleTarget(_) => None, + NetworkNote::SingleTarget(note) if note.account_prefix() == account_prefix => { + Some(note) + }, + _ => None, }) .collect::>() } diff --git a/crates/ntx-builder/src/actor/execute.rs b/crates/ntx-builder/src/actor/execute.rs index 66f22f8c0..cb38dc89a 100644 --- a/crates/ntx-builder/src/actor/execute.rs +++ b/crates/ntx-builder/src/actor/execute.rs @@ -4,7 +4,6 @@ use miden_node_proto::clients::ValidatorClient; use miden_node_proto::generated::{self as proto}; use miden_node_utils::lru_cache::LruCache; use miden_node_utils::tracing::OpenTelemetrySpanExt; -use miden_protocol::Word; use miden_protocol::account::{ Account, AccountId, @@ -14,7 +13,6 @@ use miden_protocol::account::{ }; use miden_protocol::asset::{AssetVaultKey, AssetWitness}; use miden_protocol::block::{BlockHeader, BlockNumber}; -use miden_protocol::errors::TransactionInputError; use miden_protocol::note::{Note, NoteScript}; use miden_protocol::transaction::{ AccountInputs, @@ -28,6 +26,7 @@ use miden_protocol::transaction::{ TransactionInputs, }; use miden_protocol::vm::FutureMaybeSend; +use miden_protocol::{TransactionInputError, Word}; use miden_remote_prover_client::remote_prover::tx_prover::RemoteTransactionProver; use miden_tx::auth::UnreachableAuth; use miden_tx::utils::Serializable; diff --git a/crates/ntx-builder/src/actor/inflight_note.rs b/crates/ntx-builder/src/actor/inflight_note.rs index 23c7d06d7..626b474ac 100644 --- a/crates/ntx-builder/src/actor/inflight_note.rs +++ b/crates/ntx-builder/src/actor/inflight_note.rs @@ -46,10 +46,15 @@ impl InflightNetworkNote { /// Checks if the network note is available for execution. /// - /// The note is available if the backoff period has passed. + /// The note is available if it can be consumed and the backoff period has passed. pub fn is_available(&self, block_num: BlockNumber) -> bool { - self.note.can_be_consumed(block_num).unwrap_or(true) - && has_backoff_passed(block_num, self.last_attempt, self.attempt_count) + let can_consume = self + .to_inner() + .metadata() + .execution_hint() + .can_be_consumed(block_num) + .unwrap_or(true); + can_consume && has_backoff_passed(block_num, self.last_attempt, self.attempt_count) } /// Registers a failed attempt to execute the network note at the specified block number. diff --git a/crates/ntx-builder/src/actor/mod.rs b/crates/ntx-builder/src/actor/mod.rs index f743d7908..602dde11b 100644 --- a/crates/ntx-builder/src/actor/mod.rs +++ b/crates/ntx-builder/src/actor/mod.rs @@ -9,7 +9,7 @@ use std::time::Duration; use account_state::{NetworkAccountState, TransactionCandidate}; use futures::FutureExt; use miden_node_proto::clients::{Builder, ValidatorClient}; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::mempool::MempoolEvent; use miden_node_utils::ErrorReport; use miden_node_utils::lru_cache::LruCache; @@ -33,7 +33,7 @@ use crate::store::StoreClient; /// The reason an actor has shut down. pub enum ActorShutdownReason { /// Occurs when the transaction that created the actor is reverted. - AccountReverted(NetworkAccountId), + AccountReverted(NetworkAccountPrefix), /// Occurs when an account actor detects failure in the messaging channel used by the /// coordinator. EventChannelClosed, @@ -42,7 +42,7 @@ pub enum ActorShutdownReason { /// Occurs when an account actor detects its corresponding cancellation token has been triggered /// by the coordinator. Cancellation tokens are triggered by the coordinator to initiate /// graceful shutdown of actors. - Cancelled(NetworkAccountId), + Cancelled(NetworkAccountPrefix), } // ACCOUNT ACTOR CONFIG @@ -78,7 +78,7 @@ pub enum AccountOrigin { /// store yet. Transaction(Box), /// Accounts that already exist in the store. - Store(NetworkAccountId), + Store(NetworkAccountPrefix), } impl AccountOrigin { @@ -93,16 +93,16 @@ impl AccountOrigin { } /// Returns an [`AccountOrigin::Store`]. - pub fn store(account_id: NetworkAccountId) -> Self { - AccountOrigin::Store(account_id) + pub fn store(prefix: NetworkAccountPrefix) -> Self { + AccountOrigin::Store(prefix) } - /// Returns the [`NetworkAccountId`] of the account. - pub fn id(&self) -> NetworkAccountId { + /// Returns the [`NetworkAccountPrefix`] of the account. + pub fn prefix(&self) -> NetworkAccountPrefix { match self { - AccountOrigin::Transaction(account) => NetworkAccountId::try_from(account.id()) + AccountOrigin::Transaction(account) => NetworkAccountPrefix::try_from(account.id()) .expect("actor accounts are always network accounts"), - AccountOrigin::Store(account_id) => *account_id, + AccountOrigin::Store(prefix) => *prefix, } } } @@ -213,7 +213,7 @@ impl AccountActor { }; let block_num = self.chain_state.read().await.chain_tip_header.block_num(); let mut state = - NetworkAccountState::load(account, self.origin.id(), &self.store, block_num) + NetworkAccountState::load(account, self.origin.prefix(), &self.store, block_num) .await .expect("actor should be able to load account state"); @@ -229,7 +229,7 @@ impl AccountActor { }; tokio::select! { _ = self.cancel_token.cancelled() => { - return ActorShutdownReason::Cancelled(self.origin.id()); + return ActorShutdownReason::Cancelled(self.origin.prefix()); } // Handle mempool events. event = self.event_rx.recv() => { diff --git a/crates/ntx-builder/src/actor/note_state.rs b/crates/ntx-builder/src/actor/note_state.rs index 87b91fc21..9de85dd6a 100644 --- a/crates/ntx-builder/src/actor/note_state.rs +++ b/crates/ntx-builder/src/actor/note_state.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, VecDeque}; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::note::SingleTargetNetworkNote; use miden_protocol::account::delta::AccountUpdateDetails; use miden_protocol::account::{Account, AccountDelta, AccountId}; @@ -33,7 +33,7 @@ pub struct NetworkAccountNoteState { impl NetworkAccountNoteState { /// Creates a new account state from the supplied account and notes. pub fn new(account: Account, notes: Vec) -> Self { - let account_id = NetworkAccountId::try_from(account.id()) + let account_prefix = NetworkAccountPrefix::try_from(account.id()) .expect("only network accounts are used for account state"); let mut state = Self { @@ -46,8 +46,8 @@ impl NetworkAccountNoteState { for note in notes { // Currently only support single target network notes in NTB. assert!( - note.account_id() == account_id, - "Notes supplied into account state must match expected account ID" + note.account_prefix() == account_prefix, + "Notes supplied into account state must match expected account prefix" ); state.add_note(note); } @@ -210,15 +210,15 @@ impl NetworkAccountEffect { AccountUpdateDetails::Delta(update) => NetworkAccountEffect::Updated(update.clone()), }; - update.protocol_account_id().is_network().then_some(update) + update.account_id().is_network().then_some(update) } - pub fn network_account_id(&self) -> NetworkAccountId { + pub fn prefix(&self) -> NetworkAccountPrefix { // SAFETY: This is a network account by construction. - self.protocol_account_id().try_into().unwrap() + self.account_id().try_into().unwrap() } - fn protocol_account_id(&self) -> AccountId { + fn account_id(&self) -> AccountId { match self { NetworkAccountEffect::Created(acc) => acc.id(), NetworkAccountEffect::Updated(delta) => delta.id(), diff --git a/crates/ntx-builder/src/builder.rs b/crates/ntx-builder/src/builder.rs index 84c711385..5a1b091a6 100644 --- a/crates/ntx-builder/src/builder.rs +++ b/crates/ntx-builder/src/builder.rs @@ -4,7 +4,7 @@ use std::time::Duration; use anyhow::Context; use futures::TryStreamExt; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::mempool::MempoolEvent; use miden_node_utils::lru_cache::LruCache; use miden_protocol::Word; @@ -155,9 +155,9 @@ impl NetworkTransactionBuilder { // Create initial set of actors based on all known network accounts. let account_ids = store.get_network_account_ids().await?; for account_id in account_ids { - if let Ok(account_id) = NetworkAccountId::try_from(account_id) { + if let Ok(account_prefix) = NetworkAccountPrefix::try_from(account_id) { self.coordinator - .spawn_actor(AccountOrigin::store(account_id), &actor_context) + .spawn_actor(AccountOrigin::store(account_prefix), &actor_context) .await?; } } diff --git a/crates/ntx-builder/src/coordinator.rs b/crates/ntx-builder/src/coordinator.rs index f6c038911..7b5a588a9 100644 --- a/crates/ntx-builder/src/coordinator.rs +++ b/crates/ntx-builder/src/coordinator.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Context; use indexmap::IndexMap; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::mempool::MempoolEvent; use miden_node_proto::domain::note::NetworkNote; use miden_protocol::account::delta::AccountUpdateDetails; @@ -69,7 +69,7 @@ pub struct Coordinator { /// When actors are spawned, they register their communication channel 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. /// @@ -89,7 +89,7 @@ pub struct Coordinator { /// Cache of events received from the mempool that predate corresponding network accounts. /// Grouped by account prefix to allow targeted event delivery to actors upon creation. - predating_events: HashMap>>, + predating_events: HashMap>>, } impl Coordinator { @@ -118,7 +118,7 @@ impl Coordinator { origin: AccountOrigin, actor_context: &AccountActorContext, ) -> Result<(), SendError>> { - let account_prefix = origin.id(); + let account_prefix = origin.prefix(); // If an actor already exists for this account prefix, something has gone wrong. if let Some(handle) = self.actor_registry.remove(&account_prefix) { @@ -248,14 +248,15 @@ impl Coordinator { // Determine target actors for each note. for note in network_notes { - let NetworkNote::SingleTarget(note) = note; - let prefix = note.account_id(); - if let Some(actor) = self.actor_registry.get(&prefix) { - // Register actor as target. - target_actors.insert(prefix, actor); - } else { - // Cache event for every note that doesn't have a corresponding actor. - self.predating_events.entry(prefix).or_default().insert(*id, event.clone()); + if let NetworkNote::SingleTarget(note) = note { + let prefix = note.account_prefix(); + if let Some(actor) = self.actor_registry.get(&prefix) { + // Register actor as target. + target_actors.insert(prefix, actor); + } else { + // Cache event for every note that doesn't have a corresponding actor. + self.predating_events.entry(prefix).or_default().insert(*id, event.clone()); + } } } } diff --git a/crates/ntx-builder/src/store.rs b/crates/ntx-builder/src/store.rs index 784a27101..447571a5a 100644 --- a/crates/ntx-builder/src/store.rs +++ b/crates/ntx-builder/src/store.rs @@ -1,7 +1,7 @@ use std::time::Duration; use miden_node_proto::clients::{Builder, StoreNtxBuilderClient}; -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_node_proto::domain::note::NetworkNote; use miden_node_proto::errors::ConversionError; use miden_node_proto::generated::rpc::BlockRange; @@ -109,9 +109,9 @@ impl StoreClient { #[instrument(target = COMPONENT, name = "store.client.get_network_account", skip_all, err)] pub async fn get_network_account( &self, - account_id: NetworkAccountId, + prefix: NetworkAccountPrefix, ) -> Result, StoreError> { - let request = proto::store::AccountIdPrefix { account_id_prefix: account_id.prefix() }; + let request = proto::store::AccountIdPrefix { account_id_prefix: prefix.inner() }; let store_response = self .inner @@ -140,7 +140,7 @@ impl StoreClient { #[instrument(target = COMPONENT, name = "store.client.get_unconsumed_network_notes", skip_all, err)] pub async fn get_unconsumed_network_notes( &self, - network_account_id: NetworkAccountId, + network_account_prefix: NetworkAccountPrefix, block_num: u32, ) -> Result, StoreError> { // Upper bound of each note is ~10KB. Limit page size to ~10MB. @@ -154,7 +154,7 @@ impl StoreClient { let req = proto::store::UnconsumedNetworkNotesRequest { page_token, page_size: PAGE_SIZE, - network_account_id_prefix: network_account_id.prefix(), + network_account_id_prefix: network_account_prefix.inner(), block_num, }; let resp = store_client.get_unconsumed_network_notes(req).await?.into_inner(); diff --git a/crates/proto/Cargo.toml b/crates/proto/Cargo.toml index 6d3589ca3..255b27c9d 100644 --- a/crates/proto/Cargo.toml +++ b/crates/proto/Cargo.toml @@ -21,7 +21,6 @@ http = { workspace = true } miden-node-grpc-error-macro = { workspace = true } miden-node-utils = { workspace = true } miden-protocol = { workspace = true } -miden-standards = { workspace = true } prost = { workspace = true } thiserror = { workspace = true } tonic = { default-features = true, workspace = true } diff --git a/crates/proto/src/domain/account.rs b/crates/proto/src/domain/account.rs index 3bd3aa87c..57e9a4de8 100644 --- a/crates/proto/src/domain/account.rs +++ b/crates/proto/src/domain/account.rs @@ -18,9 +18,8 @@ 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::note::{NoteExecutionMode, NoteTag}; use miden_protocol::utils::{Deserializable, DeserializationError, Serializable}; -use miden_standards::note::NetworkAccountTarget; use thiserror::Error; use super::try_convert; @@ -1023,95 +1022,76 @@ impl From for proto::primitives::Asset { pub type AccountPrefix = u32; -/// Newtype wrapper for network account IDs. -/// +/// Newtype wrapper for network account prefix. /// 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); +pub struct NetworkAccountPrefix(u32); -impl std::fmt::Display for NetworkAccountId { +impl std::fmt::Display for NetworkAccountPrefix { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(&self.0, f) } } -impl NetworkAccountId { - /// Returns the inner `AccountId`. - pub fn inner(&self) -> AccountId { +impl NetworkAccountPrefix { + pub fn inner(&self) -> u32 { 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 for NetworkAccountPrefix { + type Error = NetworkAccountError; + + fn try_from(value: u32) -> Result { + if value >> 30 != 0 { + return Err(NetworkAccountError::InvalidPrefix(value)); + } + Ok(NetworkAccountPrefix(value)) } } -impl TryFrom for NetworkAccountId { +impl TryFrom for NetworkAccountPrefix { type Error = NetworkAccountError; fn try_from(id: AccountId) -> Result { if !id.is_network() { return Err(NetworkAccountError::NotNetworkAccount(id)); } - Ok(NetworkAccountId(id)) + let prefix = get_account_id_tag_prefix(id); + Ok(NetworkAccountPrefix(prefix)) } } -impl TryFrom<&NoteAttachment> for NetworkAccountId { +impl TryFrom for NetworkAccountPrefix { type Error = NetworkAccountError; - fn try_from(attachment: &NoteAttachment) -> Result { - let target = NetworkAccountTarget::try_from(attachment) - .map_err(|e| NetworkAccountError::InvalidAttachment(e.to_string()))?; - Ok(NetworkAccountId(target.target_id())) - } -} - -impl TryFrom for NetworkAccountId { - type Error = NetworkAccountError; + fn try_from(tag: NoteTag) -> Result { + if tag.execution_mode() != NoteExecutionMode::Network || !tag.is_single_target() { + return Err(NetworkAccountError::InvalidExecutionMode(tag)); + } - fn try_from(attachment: NoteAttachment) -> Result { - NetworkAccountId::try_from(&attachment) + let tag_inner: u32 = tag.into(); + assert!(tag_inner >> 30 == 0, "first 2 bits have to be 0"); + Ok(NetworkAccountPrefix(tag_inner)) } } -impl From for AccountId { - fn from(value: NetworkAccountId) -> Self { +impl From for u32 { + fn from(value: NetworkAccountPrefix) -> 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(String), - #[error("invalid network account prefix: {0}")] + #[error("note tag {0} is not valid for network account execution")] + InvalidExecutionMode(NoteTag), + #[error("note prefix should be 30-bit long ({0} has non-zero in the 2 most significant bits)")] InvalidPrefix(u32), } -/// Validates that a u32 represents a valid network account prefix. -/// -/// Network accounts have a 30-bit prefix (top 2 bits must be 0). -pub fn validate_network_account_prefix(prefix: u32) -> Result { - if prefix >> 30 != 0 { - return Err(NetworkAccountError::InvalidPrefix(prefix)); - } - Ok(prefix) -} - /// 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/note.rs b/crates/proto/src/domain/note.rs index 5098ef41f..c4065b298 100644 --- a/crates/proto/src/domain/note.rs +++ b/crates/proto/src/domain/note.rs @@ -1,10 +1,8 @@ -use miden_protocol::Word; -use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::note::{ Note, - NoteAttachment, NoteDetails, + NoteExecutionHint, NoteId, NoteInclusionProof, NoteMetadata, @@ -14,10 +12,10 @@ use miden_protocol::note::{ Nullifier, }; use miden_protocol::utils::{Deserializable, Serializable}; -use miden_standards::note::NetworkAccountTarget; +use miden_protocol::{Felt, Word}; use thiserror::Error; -use super::account::NetworkAccountId; +use super::account::NetworkAccountPrefix; use crate::errors::{ConversionError, MissingFieldHelper}; use crate::generated as proto; @@ -30,24 +28,20 @@ impl TryFrom for NoteMetadata { .ok_or_else(|| proto::note::NoteMetadata::missing_field(stringify!(sender)))? .try_into()?; let note_type = NoteType::try_from(u64::from(value.note_type))?; - let tag = NoteTag::new(value.tag); + let tag = NoteTag::from(value.tag); - // Deserialize attachment if present - let attachment = if value.attachment.is_empty() { - NoteAttachment::default() - } else { - NoteAttachment::read_from_bytes(&value.attachment) - .map_err(|err| ConversionError::deserialization_error("NoteAttachment", err))? - }; + let execution_hint = NoteExecutionHint::try_from(value.execution_hint)?; + + let aux = Felt::try_from(value.aux).map_err(|_| ConversionError::NotAValidFelt)?; - Ok(NoteMetadata::new(sender, note_type, tag).with_attachment(attachment)) + Ok(NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?) } } impl From for proto::note::NetworkNote { fn from(note: Note) -> Self { Self { - metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())), + metadata: Some(proto::note::NoteMetadata::from(*note.metadata())), details: NoteDetails::from(note).to_bytes(), } } @@ -56,7 +50,7 @@ impl From for proto::note::NetworkNote { impl From for proto::note::Note { fn from(note: Note) -> Self { Self { - metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())), + metadata: Some(proto::note::NoteMetadata::from(*note.metadata())), details: Some(NoteDetails::from(note).to_bytes()), } } @@ -66,7 +60,7 @@ impl From for proto::note::NetworkNote { fn from(note: NetworkNote) -> Self { let note = Note::from(note); Self { - metadata: Some(proto::note::NoteMetadata::from(note.metadata().clone())), + metadata: Some(proto::note::NoteMetadata::from(*note.metadata())), details: NoteDetails::from(note).to_bytes(), } } @@ -76,10 +70,17 @@ impl From for proto::note::NoteMetadata { fn from(val: NoteMetadata) -> Self { let sender = Some(val.sender().into()); let note_type = val.note_type() as u32; - let tag = val.tag().as_u32(); - let attachment = val.attachment().to_bytes(); - - proto::note::NoteMetadata { sender, note_type, tag, attachment } + let tag = val.tag().into(); + let execution_hint: u64 = val.execution_hint().into(); + let aux = val.aux().into(); + + proto::note::NoteMetadata { + sender, + note_type, + tag, + execution_hint, + aux, + } } } @@ -183,12 +184,14 @@ impl TryFrom for Note { #[derive(Clone, Debug, PartialEq, Eq)] pub enum NetworkNote { SingleTarget(SingleTargetNetworkNote), + MultiTarget(MultiTargetNetworkNote), } impl NetworkNote { pub fn inner(&self) -> &Note { match self { - NetworkNote::SingleTarget(note) => note.inner(), + NetworkNote::SingleTarget(note) => ¬e.0, + NetworkNote::MultiTarget(note) => ¬e.0, } } @@ -208,7 +211,8 @@ impl NetworkNote { impl From for Note { fn from(value: NetworkNote) -> Self { match value { - NetworkNote::SingleTarget(note) => note.into(), + NetworkNote::SingleTarget(note) => note.0, + NetworkNote::MultiTarget(note) => note.0, } } } @@ -217,7 +221,15 @@ impl TryFrom for NetworkNote { type Error = NetworkNoteError; fn try_from(note: Note) -> Result { - SingleTargetNetworkNote::try_from(note).map(NetworkNote::SingleTarget) + if note.is_network_note() { + if note.metadata().tag().is_single_target() { + Ok(NetworkNote::SingleTarget(SingleTargetNetworkNote(note))) + } else { + Ok(NetworkNote::MultiTarget(MultiTargetNetworkNote(note))) + } + } else { + Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag())) + } } } @@ -229,22 +241,43 @@ impl TryFrom for NetworkNote { } } -// SINGLE TARGET NETWORK NOTE +// MULTI TARGET NETWORK NOTE // ================================================================================================ -/// A newtype that wraps around notes targeting a single network account. -/// -/// A note is considered a single-target network note if its attachment -/// is a valid `NetworkAccountTarget`. +/// A newtype that wraps around notes having multiple targets to be used in a network mode. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct SingleTargetNetworkNote { - note: Note, - account_target: NetworkAccountTarget, +pub struct MultiTargetNetworkNote(Note); + +impl TryFrom for MultiTargetNetworkNote { + type Error = NetworkNoteError; + + fn try_from(note: Note) -> Result { + if note.is_network_note() && !note.metadata().tag().is_single_target() { + Ok(Self(note)) + } else { + Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag())) + } + } } +impl TryFrom for MultiTargetNetworkNote { + type Error = ConversionError; + + fn try_from(proto_note: proto::note::NetworkNote) -> Result { + from_proto(proto_note) + } +} + +// SINGLE TARGET NETWORK NOTE +// ================================================================================================ + +/// A newtype that wraps around notes targeting a single account to be used in a network mode. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SingleTargetNetworkNote(Note); + impl SingleTargetNetworkNote { pub fn inner(&self) -> &Note { - &self.note + &self.0 } pub fn metadata(&self) -> &NoteMetadata { @@ -259,19 +292,18 @@ impl SingleTargetNetworkNote { self.inner().id() } - /// The network account ID that this note targets. - pub fn account_id(&self) -> NetworkAccountId { - self.account_target.target_id().try_into().expect("always a network account ID") - } - - pub fn can_be_consumed(&self, block_num: BlockNumber) -> Option { - self.account_target.execution_hint().can_be_consumed(block_num) + /// The account prefix that this note targets. + pub fn account_prefix(&self) -> NetworkAccountPrefix { + self.metadata() + .tag() + .try_into() + .expect("Single target network note's tag should contain an account prefix") } } impl From for Note { fn from(value: SingleTargetNetworkNote) -> Self { - value.note + value.0 } } @@ -279,11 +311,11 @@ impl TryFrom for SingleTargetNetworkNote { type Error = NetworkNoteError; fn try_from(note: Note) -> Result { - // Single-target network notes are identified by having a NetworkAccountTarget attachment - let attachment = note.metadata().attachment(); - let account_target = NetworkAccountTarget::try_from(attachment) - .map_err(|e| NetworkNoteError::InvalidAttachment(e.to_string()))?; - Ok(Self { note, account_target }) + if note.is_network_note() && note.metadata().tag().is_single_target() { + Ok(Self(note)) + } else { + Err(NetworkNoteError::InvalidExecutionMode(note.metadata().tag())) + } } } @@ -314,8 +346,8 @@ where #[derive(Debug, Error)] pub enum NetworkNoteError { - #[error("note does not have a valid NetworkAccountTarget attachment: {0}")] - InvalidAttachment(String), + #[error("note tag {0} is not a valid network note tag")] + InvalidExecutionMode(NoteTag), } // NOTE SCRIPT diff --git a/crates/proto/src/errors/mod.rs b/crates/proto/src/errors/mod.rs index d65414188..49f0f30bd 100644 --- a/crates/proto/src/errors/mod.rs +++ b/crates/proto/src/errors/mod.rs @@ -4,8 +4,8 @@ use std::num::TryFromIntError; // Re-export the GrpcError derive macro for convenience pub use miden_node_grpc_error_macro::GrpcError; use miden_protocol::crypto::merkle::smt::{SmtLeafError, SmtProofError}; -use miden_protocol::errors::{AccountError, AssetError, FeeError, NoteError, StorageSlotNameError}; use miden_protocol::utils::DeserializationError; +use miden_protocol::{AccountError, AssetError, FeeError, StorageSlotNameError}; use thiserror::Error; use crate::domain::note::NetworkNoteError; @@ -24,7 +24,7 @@ pub enum ConversionError { #[error("hex error")] HexError(#[from] hex::FromHexError), #[error("note error")] - NoteError(#[from] NoteError), + NoteError(#[from] miden_protocol::NoteError), #[error("network note error")] NetworkNoteError(#[from] NetworkNoteError), #[error("SMT leaf error")] diff --git a/crates/proto/src/generated/note.rs b/crates/proto/src/generated/note.rs index 83d56aeb6..239d6f6d0 100644 --- a/crates/proto/src/generated/note.rs +++ b/crates/proto/src/generated/note.rs @@ -27,11 +27,14 @@ pub struct NoteMetadata { /// See `miden_protocol::note::note_tag` for more info. #[prost(fixed32, tag = "3")] pub tag: u32, - /// Serialized note attachment + /// Specifies when a note is ready to be consumed. /// - /// See `miden_protocol::note::NoteAttachment` for more info. - #[prost(bytes = "vec", tag = "4")] - pub attachment: ::prost::alloc::vec::Vec, + /// See `miden_protocol::note::execution_hint` for more info. + #[prost(fixed64, tag = "4")] + pub execution_hint: u64, + /// An arbitrary user-defined value. + #[prost(fixed64, tag = "5")] + pub aux: u64, } /// Represents a note. /// diff --git a/crates/rpc/src/server/api.rs b/crates/rpc/src/server/api.rs index fd3ee97c6..42889ef63 100644 --- a/crates/rpc/src/server/api.rs +++ b/crates/rpc/src/server/api.rs @@ -360,7 +360,7 @@ impl api_server::Api for RpcService { let script = NoteScript::from_parts(mast, note.script().entrypoint()); let recipient = NoteRecipient::new(note.serial_num(), script, note.inputs().clone()); - let new_note = Note::new(note.assets().clone(), note.metadata().clone(), recipient); + let new_note = Note::new(note.assets().clone(), *note.metadata(), recipient); OutputNote::Full(new_note) }, other => other.clone(), @@ -423,8 +423,7 @@ impl api_server::Api for RpcService { let script = NoteScript::from_parts(mast, note.script().entrypoint()); let recipient = NoteRecipient::new(note.serial_num(), script, note.inputs().clone()); - let new_note = - Note::new(note.assets().clone(), note.metadata().clone(), recipient); + let new_note = Note::new(note.assets().clone(), *note.metadata(), recipient); OutputNote::Full(new_note) }, other => other.clone(), diff --git a/crates/store/src/accounts/mod.rs b/crates/store/src/accounts/mod.rs index d015408ad..7fa5be8e7 100644 --- a/crates/store/src/accounts/mod.rs +++ b/crates/store/src/accounts/mod.rs @@ -21,8 +21,7 @@ use miden_protocol::crypto::merkle::{ NodeIndex, SparseMerklePath, }; -use miden_protocol::errors::AccountTreeError; -use miden_protocol::{EMPTY_WORD, Word}; +use miden_protocol::{AccountTreeError, EMPTY_WORD, Word}; #[cfg(test)] mod tests; diff --git a/crates/store/src/db/migrations/2025062000000_setup/up.sql b/crates/store/src/db/migrations/2025062000000_setup/up.sql index 32c618aab..adf06e2a3 100644 --- a/crates/store/src/db/migrations/2025062000000_setup/up.sql +++ b/crates/store/src/db/migrations/2025062000000_setup/up.sql @@ -43,27 +43,28 @@ CREATE INDEX idx_accounts_block_num ON accounts(block_num); CREATE INDEX idx_accounts_code_commitment ON accounts(code_commitment) WHERE code_commitment IS NOT NULL; CREATE TABLE notes ( - committed_at INTEGER 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 - note_id BLOB NOT NULL, - note_commitment BLOB NOT NULL, - note_type INTEGER NOT NULL, -- 1-Public (0b01), 2-Private (0b10), 3-Encrypted (0b11) - sender BLOB NOT NULL, - tag INTEGER NOT NULL, - is_single_target_network_note INTEGER NOT NULL, -- 1 if note has NetworkAccountTarget attachment, 0 otherwise - attachment BLOB NOT NULL, -- Serialized note attachment data - inclusion_path BLOB NOT NULL, -- Serialized sparse Merkle path of the note in the block's note tree - consumed_at INTEGER, -- Block number when the note was consumed - nullifier BLOB, -- Only known for public notes, null for private notes - assets BLOB, - inputs BLOB, - script_root BLOB, - serial_num BLOB, + committed_at INTEGER 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 + note_id BLOB NOT NULL, + note_commitment BLOB NOT NULL, + note_type INTEGER NOT NULL, -- 1-Public (0b01), 2-Private (0b10), 3-Encrypted (0b11) + sender BLOB NOT NULL, + tag INTEGER NOT NULL, + execution_mode INTEGER NOT NULL, -- 0-Network, 1-Local + aux INTEGER NOT NULL, + execution_hint INTEGER NOT NULL, + inclusion_path BLOB NOT NULL, -- Serialized sparse Merkle path of the note in the block's note tree + consumed_at INTEGER, -- Block number when the note was consumed + nullifier BLOB, -- Only known for public notes, null for private notes + assets BLOB, + inputs BLOB, + script_root BLOB, + serial_num BLOB, PRIMARY KEY (committed_at, batch_index, note_index), CONSTRAINT notes_type_in_enum CHECK (note_type BETWEEN 1 AND 3), - CONSTRAINT notes_is_single_target_network_note_is_bool CHECK (is_single_target_network_note BETWEEN 0 AND 1), + CONSTRAINT notes_execution_mode_in_enum CHECK (execution_mode BETWEEN 0 AND 1), CONSTRAINT notes_consumed_at_is_u32 CHECK (consumed_at BETWEEN 0 AND 0xFFFFFFFF), CONSTRAINT notes_batch_index_is_u32 CHECK (batch_index BETWEEN 0 AND 0xFFFFFFFF), CONSTRAINT notes_note_index_is_u32 CHECK (note_index BETWEEN 0 AND 0xFFFFFFFF) @@ -74,7 +75,7 @@ CREATE INDEX idx_notes_note_commitment ON notes(note_commitment); CREATE INDEX idx_notes_sender ON notes(sender, committed_at); CREATE INDEX idx_notes_tag ON notes(tag, committed_at); CREATE INDEX idx_notes_nullifier ON notes(nullifier); -CREATE INDEX idx_unconsumed_network_notes ON notes(is_single_target_network_note, consumed_at); +CREATE INDEX idx_unconsumed_network_notes ON notes(execution_mode, consumed_at); -- Index for joining with block_headers on committed_at CREATE INDEX idx_notes_committed_at ON notes(committed_at); -- Index for joining with note_scripts diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 7fe9c7497..fc96212b5 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use anyhow::Context; use diesel::{Connection, QueryableByName, RunQueryDsl, SqliteConnection}; -use miden_node_proto::domain::account::{AccountInfo, AccountSummary}; +use miden_node_proto::domain::account::{AccountInfo, AccountSummary, NetworkAccountPrefix}; use miden_node_proto::generated as proto; use miden_node_utils::tracing::OpenTelemetrySpanExt; use miden_protocol::Word; @@ -652,7 +652,7 @@ impl Db { /// Pagination is used to limit the number of notes returned. pub(crate) async fn select_unconsumed_network_notes( &self, - network_account_prefix: u32, + network_account_id_prefix: NetworkAccountPrefix, block_num: BlockNumber, page: Page, ) -> Result<(Vec, Page)> { @@ -662,7 +662,7 @@ impl Db { self.transact("unconsumed network notes for account", move |conn| { models::queries::select_unconsumed_network_notes_by_tag( conn, - network_account_prefix, + network_account_id_prefix.into(), block_num, page, ) diff --git a/crates/store/src/db/models/conv.rs b/crates/store/src/db/models/conv.rs index 4dcd012ef..37a9b019f 100644 --- a/crates/store/src/db/models/conv.rs +++ b/crates/store/src/db/models/conv.rs @@ -32,11 +32,11 @@ on relevant platforms" )] -use miden_node_proto::domain::account::NetworkAccountId; +use miden_node_proto::domain::account::NetworkAccountPrefix; use miden_protocol::Felt; use miden_protocol::account::{StorageSlotName, StorageSlotType}; use miden_protocol::block::BlockNumber; -use miden_protocol::note::NoteTag; +use miden_protocol::note::{NoteExecutionMode, NoteTag}; #[derive(Debug, thiserror::Error)] #[error("failed to convert from database type {from_type} into {into_type}")] @@ -78,10 +78,42 @@ impl SqlTypeConvert for BlockNumber { } } -/// Converts a network account ID to its 30-bit prefix for database indexing. -#[inline(always)] -pub(crate) fn network_account_id_to_prefix_sql(id: NetworkAccountId) -> i64 { - i64::from(id.prefix()) +impl SqlTypeConvert for NetworkAccountPrefix { + type Raw = i64; + + fn from_raw_sql(raw: Self::Raw) -> Result { + NetworkAccountPrefix::try_from(raw as u32).map_err(Self::map_err) + } + fn to_raw_sql(self) -> Self::Raw { + i64::from(self.inner()) + } +} + +impl SqlTypeConvert for NoteExecutionMode { + type Raw = i32; + + #[inline(always)] + fn from_raw_sql(raw: Self::Raw) -> Result { + #[derive(Debug, thiserror::Error)] + #[error("valid values are 0 or 1 but found {0}")] + struct ValueError(i32); + + Ok(match raw { + 0 => Self::Network, + 1 => Self::Local, + invalid => { + return Err(Self::map_err(ValueError(invalid))); + }, + }) + } + + #[inline(always)] + fn to_raw_sql(self) -> Self::Raw { + match self { + NoteExecutionMode::Network => 0, + NoteExecutionMode::Local => 1, + } + } } impl SqlTypeConvert for NoteTag { @@ -90,12 +122,12 @@ impl SqlTypeConvert for NoteTag { #[inline(always)] fn from_raw_sql(raw: Self::Raw) -> Result { #[allow(clippy::cast_sign_loss)] - Ok(NoteTag::new(raw as u32)) + Ok(NoteTag::from(raw as u32)) } #[inline(always)] fn to_raw_sql(self) -> Self::Raw { - self.as_u32() as i32 + u32::from(self) as i32 } } @@ -180,6 +212,24 @@ pub(crate) fn note_type_to_raw_sql(note_type: u8) -> i32 { i32::from(note_type) } +#[inline(always)] +pub(crate) fn raw_sql_to_execution_hint(raw: i64) -> u64 { + raw as u64 +} +#[inline(always)] +pub(crate) fn execution_hint_to_raw_sql(hint: u64) -> i64 { + hint as i64 +} + +#[inline(always)] +pub(crate) fn raw_sql_to_aux(raw: i64) -> Felt { + Felt::try_from(raw as u64).unwrap() +} +#[inline(always)] +pub(crate) fn aux_to_raw_sql(hint: Felt) -> i64 { + hint.inner() as i64 +} + #[inline(always)] pub(crate) fn raw_sql_to_idx(raw: i32) -> usize { raw as usize diff --git a/crates/store/src/db/models/queries/accounts.rs b/crates/store/src/db/models/queries/accounts.rs index 5c049916e..f23ecf793 100644 --- a/crates/store/src/db/models/queries/accounts.rs +++ b/crates/store/src/db/models/queries/accounts.rs @@ -44,12 +44,7 @@ use miden_protocol::block::{BlockAccountUpdate, BlockNumber}; use miden_protocol::utils::{Deserializable, Serializable}; use crate::COMPONENT; -use crate::db::models::conv::{ - SqlTypeConvert, - network_account_id_to_prefix_sql, - nonce_to_raw_sql, - raw_sql_to_nonce, -}; +use crate::db::models::conv::{SqlTypeConvert, nonce_to_raw_sql, raw_sql_to_nonce}; use crate::db::models::{serialize_vec, vec_raw_try_into}; use crate::db::{AccountVaultValue, schema}; use crate::errors::DatabaseError; @@ -922,7 +917,7 @@ pub(crate) fn upsert_accounts( accounts: &[BlockAccountUpdate], block_num: BlockNumber, ) -> Result { - use proto::domain::account::NetworkAccountId; + use proto::domain::account::NetworkAccountPrefix; let mut count = 0; for update in accounts { @@ -930,8 +925,8 @@ pub(crate) fn upsert_accounts( let account_id_bytes = account_id.to_bytes(); let block_num_raw = block_num.to_raw_sql(); - let network_account_id = if account_id.is_network() { - Some(NetworkAccountId::try_from(account_id)?) + let network_account_id_prefix = if account_id.is_network() { + Some(NetworkAccountPrefix::try_from(account_id)?) } else { None }; @@ -1061,7 +1056,8 @@ pub(crate) fn upsert_accounts( let account_value = AccountRowInsert { account_id: account_id_bytes, - network_account_id_prefix: network_account_id.map(network_account_id_to_prefix_sql), + network_account_id_prefix: network_account_id_prefix + .map(NetworkAccountPrefix::to_raw_sql), account_commitment: update.final_state_commitment().to_bytes(), block_num: block_num_raw, nonce: full_account.as_ref().map(|account| nonce_to_raw_sql(account.nonce())), diff --git a/crates/store/src/db/models/queries/accounts/tests.rs b/crates/store/src/db/models/queries/accounts/tests.rs index 9206311a1..2df630987 100644 --- a/crates/store/src/db/models/queries/accounts/tests.rs +++ b/crates/store/src/db/models/queries/accounts/tests.rs @@ -35,7 +35,7 @@ use miden_protocol::block::{BlockAccountUpdate, BlockHeader, BlockNumber}; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SecretKey; use miden_protocol::utils::{Deserializable, Serializable}; use miden_protocol::{EMPTY_WORD, Felt, Word}; -use miden_standards::account::auth::AuthFalcon512Rpo; +use miden_standards::account::auth::AuthRpoFalcon512; use miden_standards::code_builder::CodeBuilder; use super::*; @@ -151,7 +151,7 @@ fn create_test_account_with_storage() -> (Account, AccountId) { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -410,7 +410,7 @@ fn test_upsert_accounts_updates_is_latest_flag() { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(component_2) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -503,7 +503,7 @@ fn test_upsert_accounts_with_multiple_storage_slots() { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -565,7 +565,7 @@ fn test_upsert_accounts_with_empty_storage() { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); diff --git a/crates/store/src/db/models/queries/notes.rs b/crates/store/src/db/models/queries/notes.rs index f68d5447b..9204adea7 100644 --- a/crates/store/src/db/models/queries/notes.rs +++ b/crates/store/src/db/models/queries/notes.rs @@ -31,14 +31,14 @@ use miden_node_utils::limiter::{ QueryParamNoteCommitmentLimit, QueryParamNoteTagLimit, }; -use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::{BlockNoteIndex, BlockNumber}; use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::note::{ NoteAssets, - NoteAttachment, NoteDetails, + NoteExecutionHint, + NoteExecutionMode, NoteId, NoteInclusionProof, NoteInputs, @@ -50,11 +50,13 @@ use miden_protocol::note::{ Nullifier, }; use miden_protocol::utils::{Deserializable, Serializable}; -use miden_standards::note::NetworkAccountTarget; +use miden_protocol::{Felt, Word}; use crate::COMPONENT; use crate::db::models::conv::{ SqlTypeConvert, + aux_to_raw_sql, + execution_hint_to_raw_sql, idx_to_raw_sql, note_type_to_raw_sql, raw_sql_to_idx, @@ -95,7 +97,8 @@ use crate::errors::NoteSyncError; /// note_type, /// sender, /// tag, -/// attachment, +/// aux, +/// execution_hint, /// inclusion_path /// FROM /// notes @@ -182,7 +185,8 @@ pub(crate) fn select_notes_since_block_by_tag_and_sender( /// notes.note_type, /// notes.sender, /// notes.tag, -/// notes.attachment, +/// notes.aux, +/// notes.execution_hint, /// notes.assets, /// notes.inputs, /// notes.serial_num, @@ -262,7 +266,8 @@ pub(crate) fn select_existing_note_commitments( /// notes.note_type, /// notes.sender, /// notes.tag, -/// notes.attachment, +/// notes.aux, +/// notes.execution_hint, /// notes.assets, /// notes.inputs, /// notes.serial_num, @@ -406,7 +411,8 @@ pub(crate) fn select_note_script_by_root( /// notes.note_type, /// notes.sender, /// notes.tag, -/// notes.attachment, +/// notes.aux, +/// notes.execution_hint, /// notes.assets, /// notes.inputs, /// notes.serial_num, @@ -416,7 +422,7 @@ pub(crate) fn select_note_script_by_root( /// FROM notes /// LEFT JOIN note_scripts ON notes.script_root = note_scripts.script_root /// WHERE -/// is_single_target_network_note = TRUE AND tag = ?1 AND +/// execution_mode = 0 AND tag = ?1 AND /// committed_at <= ?2 AND /// (consumed_at IS NULL OR consumed_at > ?2) AND notes.rowid >= ?3 /// ORDER BY notes.rowid ASC @@ -436,6 +442,12 @@ pub(crate) fn select_unconsumed_network_notes_by_tag( block_num: BlockNumber, mut page: Page, ) -> Result<(Vec, Page), DatabaseError> { + assert_eq!( + NoteExecutionMode::Network as u8, + 0, + "Hardcoded execution value must match query" + ); + let rowid_sel = diesel::dsl::sql::("notes.rowid"); let rowid_sel_ge = diesel::dsl::sql::("notes.rowid >= ") @@ -474,7 +486,7 @@ pub(crate) fn select_unconsumed_network_notes_by_tag( rowid_sel.clone(), ), ) - .filter(schema::notes::is_single_target_network_note.eq(true)) + .filter(schema::notes::execution_mode.eq(NoteExecutionMode::Network.to_raw_sql())) .filter(schema::notes::tag.eq(tag as i32)) .filter(schema::notes::committed_at.le(block_num.to_raw_sql())) .filter( @@ -578,7 +590,8 @@ pub struct NoteRecordWithScriptRawJoined { pub note_type: i32, pub sender: Vec, // AccountId pub tag: i32, - pub attachment: Vec, + pub aux: i64, + pub execution_hint: i64, // #[diesel(embed)] // pub metadata: NoteMetadataRaw, pub assets: Option>, @@ -602,7 +615,8 @@ impl From<(NoteRecordRawRow, Option>)> for NoteRecordWithScriptRawJoined note_type, sender, tag, - attachment, + aux, + execution_hint, assets, inputs, serial_num, @@ -617,7 +631,8 @@ impl From<(NoteRecordRawRow, Option>)> for NoteRecordWithScriptRawJoined note_type, sender, tag, - attachment, + aux, + execution_hint, assets, inputs, serial_num, @@ -644,7 +659,8 @@ impl TryInto for NoteRecordWithScriptRawJoined { note_type, sender, tag, - attachment, + execution_hint, + aux, // metadata ^^^, assets, inputs, @@ -655,7 +671,13 @@ impl TryInto for NoteRecordWithScriptRawJoined { .. } = raw; let index = BlockNoteIndexRawRow { batch_index, note_index }; - let metadata = NoteMetadataRawRow { note_type, sender, tag, attachment }; + let metadata = NoteMetadataRawRow { + note_type, + sender, + tag, + aux, + execution_hint, + }; let details = NoteDetailsRawRow { assets, inputs, serial_num }; let metadata = metadata.try_into()?; @@ -708,7 +730,8 @@ pub struct NoteRecordRawRow { pub note_type: i32, pub sender: Vec, // AccountId pub tag: i32, - pub attachment: Vec, + pub aux: i64, + pub execution_hint: i64, pub assets: Option>, pub inputs: Option>, @@ -724,7 +747,8 @@ pub struct NoteMetadataRawRow { note_type: i32, sender: Vec, // AccountId tag: i32, - attachment: Vec, + aux: i64, + execution_hint: i64, } #[allow(clippy::cast_sign_loss)] @@ -734,9 +758,11 @@ impl TryInto for NoteMetadataRawRow { let sender = AccountId::read_from_bytes(&self.sender[..])?; let note_type = NoteType::try_from(self.note_type as u32) .map_err(DatabaseError::conversiont_from_sql::)?; - let tag = NoteTag::new(self.tag as u32); - let attachment = NoteAttachment::read_from_bytes(&self.attachment)?; - Ok(NoteMetadata::new(sender, note_type, tag).with_attachment(attachment)) + let tag = NoteTag::from(self.tag as u32); + let execution_hint = NoteExecutionHint::try_from(self.execution_hint as u64) + .map_err(DatabaseError::conversiont_from_sql::)?; + let aux = Felt::new(self.aux as u64); + Ok(NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?) } } @@ -841,26 +867,21 @@ pub struct NoteInsertRowInsert { pub note_type: i32, pub sender: Vec, // AccountId pub tag: i32, + pub aux: i64, + pub execution_hint: i64, - pub attachment: Vec, pub consumed_at: Option, pub assets: Option>, pub inputs: Option>, pub serial_num: Option>, pub nullifier: Option>, pub script_root: Option>, - pub is_single_target_network_note: bool, + pub execution_mode: i32, pub inclusion_path: Vec, } impl From<(NoteRecord, Option)> for NoteInsertRowInsert { fn from((note, nullifier): (NoteRecord, Option)) -> Self { - let attachment = note.metadata.attachment(); - - let is_single_target_network_note = NetworkAccountTarget::try_from(attachment).is_ok(); - - let attachment_bytes = attachment.to_bytes(); - Self { committed_at: note.block_num.to_raw_sql(), batch_index: idx_to_raw_sql(note.note_index.batch_idx()), @@ -870,11 +891,12 @@ impl From<(NoteRecord, Option)> for NoteInsertRowInsert { note_type: note_type_to_raw_sql(note.metadata.note_type() as u8), sender: note.metadata.sender().to_bytes(), tag: note.metadata.tag().to_raw_sql(), - is_single_target_network_note, - attachment: attachment_bytes, + execution_mode: note.metadata.tag().execution_mode().to_raw_sql(), + aux: aux_to_raw_sql(note.metadata.aux()), + execution_hint: execution_hint_to_raw_sql(note.metadata.execution_hint().into()), inclusion_path: note.inclusion_path.to_bytes(), consumed_at: None::, // New notes are always unconsumed. - nullifier: nullifier.as_ref().map(Nullifier::to_bytes), + nullifier: nullifier.as_ref().map(Nullifier::to_bytes), /* Beware: `Option` also implements `to_bytes`, but this is not what you want. */ assets: note.details.as_ref().map(|d| d.assets().to_bytes()), inputs: note.details.as_ref().map(|d| d.inputs().to_bytes()), script_root: note.details.as_ref().map(|d| d.script().root().to_bytes()), diff --git a/crates/store/src/db/schema.rs b/crates/store/src/db/schema.rs index e9333057c..6bf6af3cf 100644 --- a/crates/store/src/db/schema.rs +++ b/crates/store/src/db/schema.rs @@ -67,8 +67,9 @@ diesel::table! { note_type -> Integer, sender -> Binary, tag -> Integer, - is_single_target_network_note -> Bool, - attachment -> Binary, + execution_mode -> Integer, + aux -> BigInt, + execution_hint -> BigInt, inclusion_path -> Binary, consumed_at -> Nullable, nullifier -> Nullable, diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index 5d3785b45..3988e160d 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -39,7 +39,6 @@ use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::crypto::rand::RpoRandomCoin; use miden_protocol::note::{ Note, - NoteAttachment, NoteDetails, NoteExecutionHint, NoteHeader, @@ -65,10 +64,10 @@ use miden_protocol::transaction::{ TransactionId, }; use miden_protocol::utils::{Deserializable, Serializable}; -use miden_protocol::{EMPTY_WORD, Felt, FieldElement, Word}; -use miden_standards::account::auth::AuthFalcon512Rpo; +use miden_protocol::{EMPTY_WORD, Felt, FieldElement, Word, ZERO}; +use miden_standards::account::auth::AuthRpoFalcon512; use miden_standards::code_builder::CodeBuilder; -use miden_standards::note::{NetworkAccountTarget, create_p2id_note}; +use miden_standards::note::create_p2id_note; use pretty_assertions::assert_eq; use rand::Rng; @@ -226,7 +225,7 @@ pub fn create_note(account_id: AccountId) -> Note { FungibleAsset::new(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET.try_into().unwrap(), 10).unwrap(), )], NoteType::Public, - NoteAttachment::default(), + Felt::default(), &mut *rng, ) .expect("Failed to create note") @@ -258,7 +257,7 @@ fn sql_select_notes() { note_index: BlockNoteIndex::new(0, i.try_into().unwrap()).unwrap(), note_id: num_to_word(u64::try_from(i).unwrap()), note_commitment: num_to_word(u64::try_from(i).unwrap()), - metadata: new_note.metadata().clone(), + metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), inclusion_path: SparseMerklePath::default(), }; @@ -281,6 +280,108 @@ fn sql_select_notes() { } } +#[test] +#[miden_node_test_macro::enable_logging] +fn sql_select_notes_different_execution_hints() { + let mut conn = create_db(); + let conn = &mut conn; + + let block_num = 1.into(); + create_block(conn, block_num); + + // test querying empty table + let notes = queries::select_all_notes(conn).unwrap(); + assert!(notes.is_empty()); + + let sender = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap(); + + queries::upsert_accounts(conn, &[mock_block_account_update(sender, 0)], block_num).unwrap(); + + // test multiple entries + let mut state = vec![]; + + let new_note = create_note(sender); + + let note_none = NoteRecord { + block_num, + note_index: BlockNoteIndex::new(0, 0).unwrap(), + note_id: num_to_word(0), + note_commitment: num_to_word(0), + metadata: NoteMetadata::new( + sender, + NoteType::Public, + 0.into(), + NoteExecutionHint::none(), + Felt::default(), + ) + .unwrap(), + details: Some(NoteDetails::from(&new_note)), + inclusion_path: SparseMerklePath::default(), + }; + state.push(note_none.clone()); + + queries::insert_scripts(conn, [¬e_none]).unwrap(); // only necessary for the first note + let res = queries::insert_notes(conn, &[(note_none, None)]); + assert_eq!(res.unwrap(), 1, "One element must have been inserted"); + + let note_id = NoteId::from_raw(num_to_word(0)); + let note = &queries::select_notes_by_id(conn, &[note_id]).unwrap()[0]; + + assert_eq!(note.metadata.execution_hint(), NoteExecutionHint::none()); + + let note_always = NoteRecord { + block_num, + note_index: BlockNoteIndex::new(0, 1).unwrap(), + note_id: num_to_word(1), + note_commitment: num_to_word(1), + metadata: NoteMetadata::new( + sender, + NoteType::Public, + 0.into(), + NoteExecutionHint::always(), + Felt::default(), + ) + .unwrap(), + details: Some(NoteDetails::from(&new_note)), + inclusion_path: SparseMerklePath::default(), + }; + state.push(note_always.clone()); + + let res = queries::insert_notes(conn, &[(note_always, None)]); + assert_eq!(res.unwrap(), 1, "One element must have been inserted"); + + let note_id = NoteId::from_raw(num_to_word(1)); + let note = &queries::select_notes_by_id(conn, &[note_id]).unwrap()[0]; + assert_eq!(note.metadata.execution_hint(), NoteExecutionHint::always()); + + let note_after_block = NoteRecord { + block_num, + note_index: BlockNoteIndex::new(0, 2).unwrap(), + note_id: num_to_word(2), + note_commitment: num_to_word(2), + metadata: NoteMetadata::new( + sender, + NoteType::Public, + 2.into(), + NoteExecutionHint::after_block(12.into()).unwrap(), + Felt::default(), + ) + .unwrap(), + details: Some(NoteDetails::from(&new_note)), + inclusion_path: SparseMerklePath::default(), + }; + state.push(note_after_block.clone()); + + let res = queries::insert_notes(conn, &[(note_after_block, None)]); + assert_eq!(res.unwrap(), 1, "One element must have been inserted"); + let note_id = NoteId::from_raw(num_to_word(2)); + let note = &queries::select_notes_by_id(conn, &[note_id]).unwrap()[0]; + assert_eq!( + note.metadata.execution_hint(), + NoteExecutionHint::after_block(12.into()).unwrap() + ); +} + #[test] #[miden_node_test_macro::enable_logging] fn sql_select_note_script_by_root() { @@ -302,7 +403,7 @@ fn sql_select_note_script_by_root() { note_index: BlockNoteIndex::new(0, 0.try_into().unwrap()).unwrap(), note_id: num_to_word(0), note_commitment: num_to_word(0), - metadata: new_note.metadata().clone(), + metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), inclusion_path: SparseMerklePath::default(), }; @@ -365,11 +466,6 @@ fn sql_unconsumed_network_notes() { create_block(&mut conn, 0.into()); create_block(&mut conn, 1.into()); - // Create a NetworkAccountTarget attachment for the network account - let target = NetworkAccountTarget::new(account_note.0, NoteExecutionHint::Always) - .expect("NetworkAccountTarget creation should succeed for network account"); - let attachment: NoteAttachment = target.into(); - // Create an unconsumed note in each block. let notes = Vec::from_iter((0..2).map(|i: u32| { let note = NoteRecord { @@ -380,9 +476,11 @@ fn sql_unconsumed_network_notes() { metadata: NoteMetadata::new( account_note.0, NoteType::Public, - NoteTag::with_account_target(account_note.0), + NoteTag::from_account_id(account_note.0), + NoteExecutionHint::none(), + Felt::default(), ) - .with_attachment(attachment.clone()), + .unwrap(), details: None, inclusion_path: SparseMerklePath::default(), }; @@ -395,7 +493,7 @@ fn sql_unconsumed_network_notes() { (0..2).for_each(|i: u32| { let (result, _) = queries::select_unconsumed_network_notes_by_tag( &mut conn, - NoteTag::with_account_target(account_note.0).into(), + NoteTag::from_account_id(account_note.0).into(), i.into(), Page { token: None, @@ -412,7 +510,7 @@ fn sql_unconsumed_network_notes() { // Query against first block should return both notes. let (result, _) = queries::select_unconsumed_network_notes_by_tag( &mut conn, - NoteTag::with_account_target(account_note.0).into(), + NoteTag::from_account_id(account_note.0).into(), 0.into(), Page { token: None, @@ -425,7 +523,7 @@ fn sql_unconsumed_network_notes() { // Query against second block should return only first note. let (result, _) = queries::select_unconsumed_network_notes_by_tag( &mut conn, - NoteTag::with_account_target(account_note.0).into(), + NoteTag::from_account_id(account_note.0).into(), 1.into(), Page { token: None, @@ -918,10 +1016,12 @@ fn notes() { let new_note = create_note(sender); let note_index = BlockNoteIndex::new(0, 2).unwrap(); let tag = 5u32; - let note_metadata = NoteMetadata::new(sender, NoteType::Public, tag.into()); + let note_metadata = + NoteMetadata::new(sender, NoteType::Public, tag.into(), NoteExecutionHint::none(), ZERO) + .unwrap(); - let values = [(note_index, new_note.id(), ¬e_metadata)]; - let notes_db = BlockNoteTree::with_entries(values).unwrap(); + let values = [(note_index, new_note.id(), note_metadata)]; + let notes_db = BlockNoteTree::with_entries(values.iter().copied()).unwrap(); let inclusion_path = notes_db.open(note_index); let note = NoteRecord { @@ -929,7 +1029,14 @@ fn notes() { note_index, note_id: new_note.id().as_word(), note_commitment: new_note.commitment(), - metadata: NoteMetadata::new(sender, NoteType::Public, tag.into()), + metadata: NoteMetadata::new( + sender, + NoteType::Public, + tag.into(), + NoteExecutionHint::none(), + Felt::default(), + ) + .unwrap(), details: Some(NoteDetails::from(&new_note)), inclusion_path: inclusion_path.clone(), }; @@ -969,7 +1076,7 @@ fn notes() { note_index: note.note_index, note_id: new_note.id().as_word(), note_commitment: new_note.commitment(), - metadata: note.metadata.clone(), + metadata: note.metadata, details: None, inclusion_path: inclusion_path.clone(), }; @@ -1238,7 +1345,7 @@ fn create_account_with_code(code_str: &str, seed: [u8; 32]) -> Account { .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Public) .with_component(component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap() } @@ -1257,7 +1364,14 @@ fn mock_block_transaction(account_id: AccountId, num: u64) -> TransactionHeader Word::try_from([num, num, 0, 0]).unwrap(), Word::try_from([0, 0, num, num]).unwrap(), ), - NoteMetadata::new(account_id, NoteType::Public, NoteTag::new(num as u32)), + NoteMetadata::new( + account_id, + NoteType::Public, + NoteTag::LocalAny(num as u32), + NoteExecutionHint::None, + Felt::default(), + ) + .unwrap(), )]; TransactionHeader::new_unchecked( @@ -1333,7 +1447,7 @@ fn mock_account_code_and_storage( .storage_mode(storage_mode) .with_assets(assets) .with_component(account_component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap() } @@ -1494,7 +1608,7 @@ fn genesis_with_account_assets() { .storage_mode(AccountStorageMode::Public) .with_component(account_component) .with_assets([fungible_asset.into()]) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1543,7 +1657,7 @@ fn genesis_with_account_storage_map() { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(account_component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1590,7 +1704,7 @@ fn genesis_with_account_assets_and_storage() { .storage_mode(AccountStorageMode::Public) .with_component(account_component) .with_assets([fungible_asset.into()]) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1621,7 +1735,7 @@ fn genesis_with_multiple_accounts() { .account_type(AccountType::RegularAccountImmutableCode) .storage_mode(AccountStorageMode::Public) .with_component(account_component1) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1640,7 +1754,7 @@ fn genesis_with_multiple_accounts() { .storage_mode(AccountStorageMode::Public) .with_component(account_component2) .with_assets([fungible_asset.into()]) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1663,7 +1777,7 @@ fn genesis_with_multiple_accounts() { .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Public) .with_component(account_component3) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -1826,8 +1940,15 @@ fn serialization_symmetry_note_metadata() { let sender = AccountId::try_from(ACCOUNT_ID_PRIVATE_SENDER).unwrap(); // Use a tag that roundtrips properly - NoteTag::LocalAny stores the full u32 including type // bits - let tag = NoteTag::with_account_target(sender); - let metadata = NoteMetadata::new(sender, NoteType::Public, tag); + let tag = NoteTag::from_account_id(sender); + let metadata = NoteMetadata::new( + sender, + NoteType::Public, + tag, + NoteExecutionHint::always(), + Felt::new(42), + ) + .unwrap(); let bytes = metadata.to_bytes(); let restored = NoteMetadata::read_from_bytes(&bytes).unwrap(); @@ -1965,7 +2086,7 @@ fn db_roundtrip_notes() { note_index, note_id: new_note.id().as_word(), note_commitment: new_note.commitment(), - metadata: new_note.metadata().clone(), + metadata: *new_note.metadata(), details: Some(NoteDetails::from(&new_note)), inclusion_path: SparseMerklePath::default(), }; @@ -2159,7 +2280,7 @@ fn db_roundtrip_account_storage_with_maps() { .account_type(AccountType::RegularAccountUpdatableCode) .storage_mode(AccountStorageMode::Public) .with_component(account_component) - .with_auth_component(AuthFalcon512Rpo::new(PublicKeyCommitment::from(EMPTY_WORD))) + .with_auth_component(AuthRpoFalcon512::new(PublicKeyCommitment::from(EMPTY_WORD))) .build_existing() .unwrap(); @@ -2227,57 +2348,3 @@ fn db_roundtrip_account_storage_with_maps() { "Full account commitment must match after DB roundtrip" ); } - -#[test] -#[miden_node_test_macro::enable_logging] -fn test_note_metadata_with_attachment_roundtrip() { - let mut conn = create_db(); - let block_num = BlockNumber::from(1); - create_block(&mut conn, block_num); - - let (account_id, _) = - make_account_and_note(&mut conn, block_num, [1u8; 32], AccountStorageMode::Network); - - let target = NetworkAccountTarget::new(account_id, NoteExecutionHint::Always) - .expect("NetworkAccountTarget creation should succeed for network account"); - let attachment: NoteAttachment = target.into(); - - // Create NoteMetadata with the attachment - let metadata = - NoteMetadata::new(account_id, NoteType::Public, NoteTag::with_account_target(account_id)) - .with_attachment(attachment.clone()); - - let note = NoteRecord { - block_num, - note_index: BlockNoteIndex::new(0, 0).unwrap(), - note_id: num_to_word(1), - note_commitment: num_to_word(1), - metadata: metadata.clone(), - details: None, - inclusion_path: SparseMerklePath::default(), - }; - - queries::insert_scripts(&mut conn, [¬e]).unwrap(); - queries::insert_notes(&mut conn, &[(note.clone(), None)]).unwrap(); - - // Fetch the note back and verify the attachment is preserved - let retrieved = queries::select_notes_by_id(&mut conn, &[NoteId::from_raw(note.note_id)]) - .expect("select_notes_by_id should succeed"); - - assert_eq!(retrieved.len(), 1, "Should retrieve exactly one note"); - - let retrieved_metadata = &retrieved[0].metadata; - assert_eq!( - retrieved_metadata.attachment(), - metadata.attachment(), - "Attachment should be preserved after DB roundtrip" - ); - - let retrieved_target = NetworkAccountTarget::try_from(retrieved_metadata.attachment()) - .expect("Should be able to parse NetworkAccountTarget from retrieved attachment"); - assert_eq!( - retrieved_target.target_id(), - account_id, - "NetworkAccountTarget should have the correct target account ID" - ); -} diff --git a/crates/store/src/errors.rs b/crates/store/src/errors.rs index a842c449d..32c345a98 100644 --- a/crates/store/src/errors.rs +++ b/crates/store/src/errors.rs @@ -6,13 +6,14 @@ 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::limiter::QueryLimitError; -use miden_protocol::Word; use miden_protocol::account::AccountId; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::MerkleError; use miden_protocol::crypto::merkle::mmr::MmrError; use miden_protocol::crypto::utils::DeserializationError; -use miden_protocol::errors::{ +use miden_protocol::note::{NoteId, Nullifier}; +use miden_protocol::transaction::OutputNote; +use miden_protocol::{ AccountDeltaError, AccountError, AccountTreeError, @@ -22,9 +23,8 @@ use miden_protocol::errors::{ NoteError, NullifierTreeError, StorageMapError, + Word, }; -use miden_protocol::note::{NoteId, Nullifier}; -use miden_protocol::transaction::OutputNote; use thiserror::Error; use tokio::sync::oneshot::error::RecvError; use tonic::Status; diff --git a/crates/store/src/genesis/config/errors.rs b/crates/store/src/genesis/config/errors.rs index b39495c87..e4eb4810e 100644 --- a/crates/store/src/genesis/config/errors.rs +++ b/crates/store/src/genesis/config/errors.rs @@ -1,12 +1,6 @@ use miden_protocol::account::AccountId; -use miden_protocol::errors::{ - AccountDeltaError, - AccountError, - AssetError, - FeeError, - TokenSymbolError, -}; use miden_protocol::utils::DeserializationError; +use miden_protocol::{AccountError, AssetError, FeeError, TokenSymbolError}; use miden_standards::account::faucets::FungibleFaucetError; use miden_standards::account::wallets::BasicWalletError; @@ -22,7 +16,7 @@ pub enum GenesisConfigError { #[error("asset translation from config to state failed")] Asset(#[from] AssetError), #[error("adding assets to account failed")] - AccountDelta(#[from] AccountDeltaError), + AccountDelta(#[from] miden_protocol::AccountDeltaError), #[error("the defined asset {symbol:?} has no corresponding faucet")] MissingFaucetDefinition { symbol: TokenSymbolStr }, #[error("account with id {account_id} was referenced but is not part of given genesis state")] diff --git a/crates/store/src/genesis/config/mod.rs b/crates/store/src/genesis/config/mod.rs index 8d1a83437..ed0c0077c 100644 --- a/crates/store/src/genesis/config/mod.rs +++ b/crates/store/src/genesis/config/mod.rs @@ -23,10 +23,9 @@ use miden_protocol::account::{ use miden_protocol::asset::{FungibleAsset, TokenSymbol}; use miden_protocol::block::FeeParameters; use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey as RpoSecretKey; -use miden_protocol::errors::TokenSymbolError; -use miden_protocol::{Felt, FieldElement, ONE, ZERO}; +use miden_protocol::{Felt, FieldElement, ONE, TokenSymbolError, ZERO}; use miden_standards::AuthScheme; -use miden_standards::account::auth::AuthFalcon512Rpo; +use miden_standards::account::auth::AuthRpoFalcon512; use miden_standards::account::faucets::BasicFungibleFaucet; use miden_standards::account::wallets::create_basic_wallet; use rand::distr::weighted::Weight; @@ -160,7 +159,7 @@ impl GenesisConfig { let mut rng = ChaCha20Rng::from_seed(rand::random()); let secret_key = RpoSecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); - let auth = AuthScheme::Falcon512Rpo { pub_key: secret_key.public_key().into() }; + let auth = AuthScheme::RpoFalcon512 { pub_key: secret_key.public_key().into() }; let init_seed: [u8; 32] = rng.random(); let account_type = if has_updatable_code { @@ -347,7 +346,7 @@ impl FungibleFaucetConfig { } = self; let mut rng = ChaCha20Rng::from_seed(rand::random()); let secret_key = RpoSecretKey::with_rng(&mut get_rpo_random_coin(&mut rng)); - let auth = AuthFalcon512Rpo::new(secret_key.public_key().into()); + let auth = AuthRpoFalcon512::new(secret_key.public_key().into()); let init_seed: [u8; 32] = rng.random(); let max_supply = Felt::try_from(max_supply) @@ -452,7 +451,7 @@ impl AccountSecrets { .get(&account_id) .ok_or(GenesisConfigError::MissingGenesisAccount { account_id })?; let account_file = - AccountFile::new(account.clone(), vec![AuthSecretKey::Falcon512Rpo(secret_key)]); + AccountFile::new(account.clone(), vec![AuthSecretKey::RpoFalcon512(secret_key)]); Ok(AccountFileWithName { name, account_file }) }) } diff --git a/crates/store/src/server/ntx_builder.rs b/crates/store/src/server/ntx_builder.rs index f08e32728..40f1ae5b3 100644 --- a/crates/store/src/server/ntx_builder.rs +++ b/crates/store/src/server/ntx_builder.rs @@ -1,6 +1,6 @@ use std::num::{NonZero, TryFromIntError}; -use miden_node_proto::domain::account::{AccountInfo, validate_network_account_prefix}; +use miden_node_proto::domain::account::{AccountInfo, NetworkAccountPrefix}; use miden_node_proto::generated::rpc::BlockRange; use miden_node_proto::generated::store::ntx_builder_server; use miden_node_proto::generated::{self as proto}; @@ -91,13 +91,13 @@ impl ntx_builder_server::NtxBuilder for StoreApi { let request = request.into_inner(); // Validate that the call is for a valid network account prefix - let prefix = validate_network_account_prefix(request.account_id_prefix).map_err(|err| { + let prefix = NetworkAccountPrefix::try_from(request.account_id_prefix).map_err(|err| { Status::invalid_argument( err.as_report_context("request does not contain a valid network account prefix"), ) })?; let account_info: Option = - self.state.get_network_account_details_by_prefix(prefix).await?; + self.state.get_network_account_details_by_prefix(prefix.inner()).await?; Ok(Response::new(proto::store::MaybeAccountDetails { details: account_info.map(|acc| (&acc).into()), @@ -117,8 +117,8 @@ impl ntx_builder_server::NtxBuilder for StoreApi { ) -> Result, Status> { let request = request.into_inner(); let block_num = BlockNumber::from(request.block_num); - let network_account_prefix = - validate_network_account_prefix(request.network_account_id_prefix).map_err(|err| { + let network_account_id_prefix = + NetworkAccountPrefix::try_from(request.network_account_id_prefix).map_err(|err| { invalid_argument(err.as_report_context("invalid network_account_id_prefix")) })?; @@ -132,7 +132,7 @@ impl ntx_builder_server::NtxBuilder for StoreApi { // TODO: no need to get the whole NoteRecord here, a NetworkNote wrapper should be created // instead let (notes, next_page) = state - .get_unconsumed_network_notes_for_account(network_account_prefix, block_num, page) + .get_unconsumed_network_notes_for_account(network_account_id_prefix, block_num, page) .await .map_err(internal_error)?; diff --git a/crates/store/src/state/mod.rs b/crates/store/src/state/mod.rs index d33a9aaf4..a798defcd 100644 --- a/crates/store/src/state/mod.rs +++ b/crates/store/src/state/mod.rs @@ -17,6 +17,7 @@ use miden_node_proto::domain::account::{ AccountStorageDetails, AccountStorageMapDetails, AccountVaultDetails, + NetworkAccountPrefix, SlotData, StorageMapRequest, }; @@ -366,7 +367,7 @@ impl State { note_index, note_id: note.id().as_word(), note_commitment: note.commitment(), - metadata: note.metadata().clone(), + metadata: *note.metadata(), details, inclusion_path, }; @@ -1206,12 +1207,12 @@ impl State { /// along with the next pagination token. pub async fn get_unconsumed_network_notes_for_account( &self, - network_account_prefix: u32, + network_account_id_prefix: NetworkAccountPrefix, block_num: BlockNumber, page: Page, ) -> Result<(Vec, Page), DatabaseError> { self.db - .select_unconsumed_network_notes(network_account_prefix, block_num, page) + .select_unconsumed_network_notes(network_account_id_prefix, block_num, page) .await } diff --git a/crates/validator/src/block_validation/mod.rs b/crates/validator/src/block_validation/mod.rs index 416b2beb9..68744010f 100644 --- a/crates/validator/src/block_validation/mod.rs +++ b/crates/validator/src/block_validation/mod.rs @@ -1,8 +1,8 @@ use std::sync::Arc; +use miden_protocol::ProposedBlockError; use miden_protocol::block::{BlockNumber, BlockSigner, ProposedBlock}; use miden_protocol::crypto::dsa::ecdsa_k256_keccak::Signature; -use miden_protocol::errors::ProposedBlockError; use miden_protocol::transaction::TransactionId; use crate::server::ValidatedTransactions; diff --git a/crates/validator/src/tx_validation/data_store.rs b/crates/validator/src/tx_validation/data_store.rs index 749ddaac1..a48c2e8e6 100644 --- a/crates/validator/src/tx_validation/data_store.rs +++ b/crates/validator/src/tx_validation/data_store.rs @@ -58,11 +58,37 @@ impl DataStore for TransactionInputsDataStore { fn get_vault_asset_witnesses( &self, - _account_id: AccountId, - _vault_root: Word, - _vault_keys: BTreeSet, + account_id: AccountId, + vault_root: Word, + vault_keys: BTreeSet, ) -> impl FutureMaybeSend, DataStoreError>> { - std::future::ready(Ok(vec![])) + async move { + if self.tx_inputs.account().id() != account_id { + return Err(DataStoreError::AccountNotFound(account_id)); + } + + if self.tx_inputs.account().vault().root() != vault_root { + return Err(DataStoreError::Other { + error_msg: "vault root mismatch".into(), + source: None, + }); + } + + Result::, _>::from_iter(vault_keys.into_iter().map(|vault_key| { + match self.tx_inputs.account().vault().open(vault_key) { + Ok(vault_proof) => { + AssetWitness::new(vault_proof.into()).map_err(|err| DataStoreError::Other { + error_msg: "failed to open vault asset tree".into(), + source: Some(err.into()), + }) + }, + Err(err) => Err(DataStoreError::Other { + error_msg: "failed to open vault".into(), + source: Some(err.into()), + }), + } + })) + } } fn get_storage_map_witness( diff --git a/proto/proto/types/note.proto b/proto/proto/types/note.proto index ac125daa0..1177f350c 100644 --- a/proto/proto/types/note.proto +++ b/proto/proto/types/note.proto @@ -32,10 +32,13 @@ message NoteMetadata { // See `miden_protocol::note::note_tag` for more info. fixed32 tag = 3; - // Serialized note attachment + // Specifies when a note is ready to be consumed. // - // See `miden_protocol::note::NoteAttachment` for more info. - bytes attachment = 4; + // See `miden_protocol::note::execution_hint` for more info. + fixed64 execution_hint = 4; + + // An arbitrary user-defined value. + fixed64 aux = 5; } // Represents a note.