diff --git a/Cargo.toml b/Cargo.toml index c25ea8a35..df1889166 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,31 +17,26 @@ required-features = ["integration"] [features] concurrent = [ - "miden_lib/concurrent", - "objects/concurrent", - "miden_tx/concurrent", + "miden-lib/concurrent", + "miden-objects/concurrent", + "miden-tx/concurrent", ] default = ["std"] integration = ["testing", "concurrent", "uuid"] -mock = [] -std = ["crypto/std", "objects/std"] -testing = ["objects/testing", "miden_lib/testing"] +mock = ["miden-objects/testing"] +std = ["miden-objects/std"] +testing = ["miden-objects/testing", "miden-lib/testing"] [dependencies] async-trait = { version = "0.1" } clap = { version = "4.3", features = ["derive"] } comfy-table = "7.1.0" -crypto = { package = "miden-crypto", version = "0.8", default-features = false } figment = { version = "0.10", features = ["toml", "env"] } lazy_static = "1.4.0" -miden_lib = { package = "miden-lib", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", default-features = false } -miden_node_store = { package = "miden-node-store", git = "https://github.com/0xPolygonMiden/miden-node.git", branch = "main" } -miden_node_proto = { package = "miden-node-proto", git = "https://github.com/0xPolygonMiden/miden-node.git", branch = "main", default-features = false } -miden_tx = { package = "miden-tx", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", default-features = false } -mock = { package = "miden-mock", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main" } -objects = { package = "miden-objects", git = "https://github.com/0xPolygonMiden/miden-base", branch = "main", features = [ - "serde", -] } +miden-lib = { version= "0.1", default-features = false } +miden-node-proto = { version= "0.1", default-features = false } +miden-tx = { version= "0.1", default-features = false } +miden-objects = { version = "0.1", features = ["serde"] } rand = { version = "0.8.5" } rusqlite = { version = "0.30.0", features = ["bundled"] } rusqlite_migration = { version = "1.0" } diff --git a/Makefile b/Makefile index 785ece2ab..87a96da42 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ node: rm -rf miden-node/miden-store.sqlite3 miden-node/miden-store.sqlite3-wal miden-node/miden-store.sqlite3-shm rm -rf miden-node/accounts rm -f miden-node/genesis.dat - cd miden-node && cargo run --release $(NODE_BINARY) $(NODE_FEATURES_TESTING) -- make-genesis --inputs-path node/genesis.toml + cd miden-node && cargo run $(NODE_BINARY) $(NODE_FEATURES_TESTING) -- make-genesis --inputs-path node/genesis.toml start-node: node - cd miden-node && cargo run --release $(NODE_BINARY) $(NODE_FEATURES_TESTING) -- start --config node/miden-node.toml + cd miden-node && cargo run $(NODE_BINARY) $(NODE_FEATURES_TESTING) -- start --config node/miden-node.toml kill-node: pkill miden-node diff --git a/README.md b/README.md index 4f2de924b..49ca6bfea 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ After creating the note with the minted asset, the regular account can now consu miden-client tx new consume-notes ``` -This will consume the input note identified by its ID, which you can get by listing them as explained in the previous step. Note that you can consume more than one note in a single transaction. +This will consume the input note identified by its ID, which you can get by listing them as explained in the previous step. Note that you can consume more than one note in a single transaction. Additionally, it's possible to provide just a prefix of a note's ID. For example, instead of `miden-client tx new consume-notes 0x70b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0` you can do `miden-client tx new consume-notes 0x70b7ec`. You will now be able to see the asset in the account's vault by running: diff --git a/docs/cli.md b/docs/cli.md index 9622b1bf2..52b3d3ba1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -44,6 +44,8 @@ Once an account gets created with the `new` command, it will be automatically st | `export` | Export input note data to a binary file | -e | | `import` | Import input note data from a binary file | -i | +For `show` subcommand, you can also provide a partial ID instead of the full ID. So instead of `miden-client input-notes show 0x70b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0` you can do `miden-client input-notes show 0x70b7ec` + ### `tags` command | Command | Description | Aliases | @@ -73,3 +75,4 @@ You can list them with their respective commands. | `mint ` | Creates a note that contains a specific amount tokens minted by a faucet, that the target Account ID can consume| | `consume-notes [NOTES]` | Account ID consumes a list of notes, specified by their Note ID | +For `consume-notes` subcommand, you can also provide a partial ID instead of the full ID for each note. So instead of `miden-client consume-notes 0x70b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0 0x80b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0` you can do `miden-client consume-notes 0x70b7ecb 0x80b7ecb` diff --git a/src/cli/account.rs b/src/cli/account.rs index 38a409865..cf6a33e9b 100644 --- a/src/cli/account.rs +++ b/src/cli/account.rs @@ -1,19 +1,19 @@ use clap::Parser; use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table}; -use crypto::{ - dsa::rpo_falcon512::KeyPair, - utils::{bytes_to_hex_string, Deserializable, Serializable}, - ZERO, -}; use miden_client::{ client::{accounts, rpc::NodeRpcClient, Client}, store::Store, }; -use miden_tx::DataStore; -use objects::{ +use miden_objects::{ accounts::{AccountData, AccountId, AccountStorage, AccountType, StorageSlotType}, assets::{Asset, TokenSymbol}, + crypto::dsa::rpo_falcon512::KeyPair, + ZERO, +}; +use miden_tx::{ + utils::{bytes_to_hex_string, Deserializable, Serializable}, + DataStore, }; use std::{fs, path::PathBuf}; use tracing::info; diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index ac9481988..308a08052 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -1,17 +1,19 @@ use super::{Client, Parser}; -use crate::cli::create_dynamic_table; +use crate::cli::{create_dynamic_table, get_note_with_id_prefix}; use clap::ValueEnum; use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table}; -use crypto::utils::{Deserializable, Serializable}; use miden_client::{ client::rpc::NodeRpcClient, store::{InputNoteRecord, NoteFilter as ClientNoteFilter, Store}, }; -use miden_tx::DataStore; -use objects::{ +use miden_objects::{ notes::{NoteId, NoteInputs, NoteScript}, Digest, }; +use miden_tx::{ + utils::{Deserializable, Serializable}, + DataStore, +}; use std::{ fs::File, io::{Read, Write}, @@ -182,11 +184,8 @@ fn show_input_note( show_vault: bool, show_inputs: bool, ) -> Result<(), String> { - let note_id = Digest::try_from(note_id) - .map_err(|err| format!("Failed to parse input note with ID: {}", err))? - .into(); - - let input_note_record = client.get_input_note(note_id)?; + let input_note_record = + get_note_with_id_prefix(&client, ¬e_id).map_err(|err| err.to_string())?; // print note summary print_notes_summary(core::iter::once(&input_note_record))?; @@ -296,16 +295,19 @@ where #[cfg(test)] mod tests { - use crate::cli::input_notes::{export_note, import_note}; + use crate::cli::{ + get_note_with_id_prefix, + input_notes::{export_note, import_note}, + }; use miden_client::{ config::{ClientConfig, Endpoint}, - mock::{MockClient, MockDataStore, MockRpcApi}, + errors::NoteIdPrefixFetchError, + mock::{mock_full_chain_mmr_and_notes, mock_notes, MockClient, MockDataStore, MockRpcApi}, store::{sqlite_store::SqliteStore, InputNoteRecord}, }; - use mock::mock::{ - account::MockAccountType, notes::AssetPreservationStatus, transaction::mock_inputs, - }; + use miden_lib::transaction::TransactionKernel; + use std::env::temp_dir; use uuid::Uuid; @@ -328,20 +330,17 @@ mod tests { let mut client = MockClient::new( MockRpcApi::new(&Endpoint::default().to_string()), store, - MockDataStore::new(), + MockDataStore::default(), ) .unwrap(); // generate test data - let transaction_inputs = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); + let assembler = TransactionKernel::assembler(); + let (consumed_notes, created_notes) = mock_notes(&assembler); + let (_, commited_notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); - let committed_note: InputNoteRecord = - transaction_inputs.input_notes().get_note(0).clone().into(); - let pending_note = - InputNoteRecord::from(transaction_inputs.input_notes().get_note(1).note().clone()); + let committed_note: InputNoteRecord = commited_notes.first().unwrap().clone().into(); + let pending_note = InputNoteRecord::from(created_notes.first().unwrap().clone()); client.import_input_note(committed_note.clone()).unwrap(); client.import_input_note(pending_note.clone()).unwrap(); @@ -388,7 +387,7 @@ mod tests { let mut client = MockClient::new( MockRpcApi::new(&Endpoint::default().to_string()), store, - MockDataStore::new(), + MockDataStore::default(), ) .unwrap(); @@ -403,4 +402,66 @@ mod tests { assert_eq!(imported_pending_note_record.id(), pending_note.id()); } + + #[tokio::test] + async fn get_input_note_with_prefix() { + // generate test client + let mut path = temp_dir(); + path.push(Uuid::new_v4().to_string()); + let client_config = ClientConfig::new( + path.into_os_string() + .into_string() + .unwrap() + .try_into() + .unwrap(), + Endpoint::default().into(), + ); + + let store = SqliteStore::new((&client_config).into()).unwrap(); + + let mut client = MockClient::new( + MockRpcApi::new(&Endpoint::default().to_string()), + store, + MockDataStore::default(), + ) + .unwrap(); + + // Ensure we get an error if no note is found + let non_existent_note_id = "0x123456"; + assert_eq!( + get_note_with_id_prefix(&client, non_existent_note_id), + Err(NoteIdPrefixFetchError::NoMatch( + non_existent_note_id.to_string() + )) + ); + + // generate test data + let assembler = TransactionKernel::assembler(); + let (consumed_notes, created_notes) = mock_notes(&assembler); + let (_, notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); + + let committed_note: InputNoteRecord = notes.first().unwrap().clone().into(); + let pending_note = InputNoteRecord::from(created_notes.first().unwrap().clone()); + + client.import_input_note(committed_note.clone()).unwrap(); + client.import_input_note(pending_note.clone()).unwrap(); + assert!(pending_note.inclusion_proof().is_none()); + assert!(committed_note.inclusion_proof().is_some()); + + // Check that we can fetch Both notes + let note = get_note_with_id_prefix(&client, &committed_note.id().to_hex()).unwrap(); + assert_eq!(note.id(), committed_note.id()); + + let note = get_note_with_id_prefix(&client, &pending_note.id().to_hex()).unwrap(); + assert_eq!(note.id(), pending_note.id()); + + // Check that we get an error if many match + let note_id_with_many_matches = "0x"; + assert_eq!( + get_note_with_id_prefix(&client, note_id_with_many_matches), + Err(NoteIdPrefixFetchError::MultipleMatches( + note_id_with_many_matches.to_string() + )) + ); + } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 4b86639a9..9b4a73800 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -7,7 +7,10 @@ use figment::{ Figment, }; use miden_client::{ - client::Client, config::ClientConfig, errors::ClientError, store::sqlite_store::SqliteStore, + client::{rpc::NodeRpcClient, Client}, + config::ClientConfig, + errors::{ClientError, NoteIdPrefixFetchError}, + store::{sqlite_store::SqliteStore, InputNoteRecord, NoteFilter as ClientNoteFilter, Store}, }; #[cfg(feature = "mock")] @@ -21,6 +24,7 @@ use miden_client::mock::MockRpcApi; use miden_client::client::rpc::TonicRpcClient; #[cfg(not(feature = "mock"))] use miden_client::store::data_store::SqliteDataStore; +use miden_tx::DataStore; mod account; mod info; @@ -93,8 +97,11 @@ impl Cli { }; #[cfg(feature = "mock")] - let client: MockClient = - Client::new(MockRpcApi::new(&rpc_endpoint), store, MockDataStore::new())?; + let client: MockClient = Client::new( + MockRpcApi::new(&rpc_endpoint), + store, + MockDataStore::default(), + )?; // Execute cli command match &self.action { @@ -146,3 +153,46 @@ pub fn create_dynamic_table(headers: &[&str]) -> Table { table } + +/// Returns all client's notes whose ID starts with `note_id_prefix` +/// +/// # Errors +/// +/// - Returns [NoteIdPrefixFetchError::NoMatch] if we were unable to find any note where +/// `note_id_prefix` is a prefix of its id. +/// - Returns [NoteIdPrefixFetchError::MultipleMatches] if there were more than one note found +/// where `note_id_prefix` is a prefix of its id. +pub(crate) fn get_note_with_id_prefix( + client: &Client, + note_id_prefix: &str, +) -> Result { + let input_note_records = client + .get_input_notes(ClientNoteFilter::All) + .map_err(|err| { + tracing::error!("Error when fetching all notes from the store: {err}"); + NoteIdPrefixFetchError::NoMatch(note_id_prefix.to_string()) + })? + .into_iter() + .filter(|note_record| note_record.id().to_hex().starts_with(note_id_prefix)) + .collect::>(); + + if input_note_records.is_empty() { + return Err(NoteIdPrefixFetchError::NoMatch(note_id_prefix.to_string())); + } + if input_note_records.len() > 1 { + let input_note_record_ids = input_note_records + .iter() + .map(|input_note_record| input_note_record.id()) + .collect::>(); + tracing::error!( + "Multiple notes found for the prefix {}: {:?}", + note_id_prefix, + input_note_record_ids + ); + return Err(NoteIdPrefixFetchError::MultipleMatches( + note_id_prefix.to_string(), + )); + } + + Ok(input_note_records[0].clone()) +} diff --git a/src/cli/transactions.rs b/src/cli/transactions.rs index 336006f9b..12a8af9c6 100644 --- a/src/cli/transactions.rs +++ b/src/cli/transactions.rs @@ -6,98 +6,41 @@ use miden_client::{ store::{Store, TransactionFilter}, }; +use miden_objects::{accounts::AccountId, assets::FungibleAsset, notes::NoteId}; use miden_tx::DataStore; -use objects::{accounts::AccountId, assets::FungibleAsset, notes::NoteId}; use tracing::info; use crate::cli::create_dynamic_table; -use super::{Client, Parser}; +use super::{get_note_with_id_prefix, Client, Parser}; #[derive(Clone, Debug, Parser)] #[clap()] pub enum TransactionType { + /// Create a Pay To ID transaction. P2ID { sender_account_id: String, target_account_id: String, faucet_id: String, amount: u64, }, + /// Mint `amount` tokens from the specified fungible faucet (corresponding to `faucet_id`). The created note can then be then consumed by + /// `target_account_id`. Mint { target_account_id: String, faucet_id: String, amount: u64, }, + /// Create a Pay To ID with Recall transaction. P2IDR, + /// Consume with the account corresponding to `account_id` all of the notes from `list_of_notes`. ConsumeNotes { account_id: String, + /// A list of note IDs or the hex prefixes of their corresponding IDs list_of_notes: Vec, }, } -impl TryInto for &TransactionType { - type Error = String; - - fn try_into(self) -> Result { - match self { - TransactionType::P2ID { - sender_account_id, - target_account_id, - faucet_id, - amount, - } => { - let faucet_id = AccountId::from_hex(faucet_id).map_err(|err| err.to_string())?; - let fungible_asset = FungibleAsset::new(faucet_id, *amount) - .map_err(|err| err.to_string())? - .into(); - let sender_account_id = - AccountId::from_hex(sender_account_id).map_err(|err| err.to_string())?; - let target_account_id = - AccountId::from_hex(target_account_id).map_err(|err| err.to_string())?; - let payment_transaction = PaymentTransactionData::new( - fungible_asset, - sender_account_id, - target_account_id, - ); - - Ok(TransactionTemplate::PayToId(payment_transaction)) - } - TransactionType::P2IDR => { - todo!() - } - TransactionType::Mint { - faucet_id, - target_account_id, - amount, - } => { - let faucet_id = AccountId::from_hex(faucet_id).map_err(|err| err.to_string())?; - let fungible_asset = - FungibleAsset::new(faucet_id, *amount).map_err(|err| err.to_string())?; - let target_account_id = - AccountId::from_hex(target_account_id).map_err(|err| err.to_string())?; - - Ok(TransactionTemplate::MintFungibleAsset { - asset: fungible_asset, - target_account_id, - }) - } - TransactionType::ConsumeNotes { - account_id, - list_of_notes, - } => { - let list_of_notes = list_of_notes - .iter() - .map(|n| NoteId::try_from_hex(n).map_err(|err| err.to_string())) - .collect::, _>>()?; - - let account_id = AccountId::from_hex(account_id).map_err(|err| err.to_string())?; - - Ok(TransactionTemplate::ConsumeNotes(account_id, list_of_notes)) - } - } - } -} - #[derive(Debug, Parser, Clone)] #[clap(about = "Execute and view transactions")] pub enum Transaction { @@ -123,19 +66,97 @@ impl Transaction { list_transactions(client)?; } Transaction::New { transaction_type } => { - let transaction_template: TransactionTemplate = transaction_type.try_into()?; + new_transaction(&mut client, transaction_type).await?; + } + } + Ok(()) + } +} - let transaction_execution_result = - client.new_transaction(transaction_template.clone())?; +// NEW TRANSACTION +// ================================================================================================ +async fn new_transaction( + client: &mut Client, + transaction_type: &TransactionType, +) -> Result<(), String> { + let transaction_template: TransactionTemplate = + build_transaction_template(client, transaction_type)?; - info!("Executed transaction, proving and then submitting..."); + let transaction_execution_result = client.new_transaction(transaction_template.clone())?; - client - .send_transaction(transaction_execution_result) - .await? - } + info!("Executed transaction, proving and then submitting..."); + + client + .send_transaction(transaction_execution_result) + .await?; + + Ok(()) +} + +/// Builds a [TransactionTemplate] based on the transaction type provided via cli args +/// +/// For [TransactionTemplate::ConsumeNotes], it'll try to find the corresponding notes by using the +/// provided IDs as prefixes +fn build_transaction_template( + client: &Client, + transaction_type: &TransactionType, +) -> Result { + match transaction_type { + TransactionType::P2ID { + sender_account_id, + target_account_id, + faucet_id, + amount, + } => { + let faucet_id = AccountId::from_hex(faucet_id).map_err(|err| err.to_string())?; + let fungible_asset = FungibleAsset::new(faucet_id, *amount) + .map_err(|err| err.to_string())? + .into(); + let sender_account_id = + AccountId::from_hex(sender_account_id).map_err(|err| err.to_string())?; + let target_account_id = + AccountId::from_hex(target_account_id).map_err(|err| err.to_string())?; + let payment_transaction = + PaymentTransactionData::new(fungible_asset, sender_account_id, target_account_id); + + Ok(TransactionTemplate::PayToId(payment_transaction)) + } + TransactionType::P2IDR => { + todo!() + } + TransactionType::Mint { + faucet_id, + target_account_id, + amount, + } => { + let faucet_id = AccountId::from_hex(faucet_id).map_err(|err| err.to_string())?; + let fungible_asset = + FungibleAsset::new(faucet_id, *amount).map_err(|err| err.to_string())?; + let target_account_id = + AccountId::from_hex(target_account_id).map_err(|err| err.to_string())?; + + Ok(TransactionTemplate::MintFungibleAsset { + asset: fungible_asset, + target_account_id, + }) + } + TransactionType::ConsumeNotes { + account_id, + list_of_notes, + } => { + let list_of_notes = list_of_notes + .iter() + .map(|note_id| { + get_note_with_id_prefix(client, note_id) + .map(|note_record| note_record.id()) + .map_err(|err| err.to_string()) + }) + .collect::, _>>()?; + + let account_id = AccountId::from_hex(account_id).map_err(|err| err.to_string())?; + + Ok(TransactionTemplate::ConsumeNotes(account_id, list_of_notes)) } - Ok(()) } } diff --git a/src/client/accounts.rs b/src/client/accounts.rs index c2631d1a6..c497661db 100644 --- a/src/client/accounts.rs +++ b/src/client/accounts.rs @@ -1,10 +1,11 @@ -use crypto::{dsa::rpo_falcon512::KeyPair, Felt, Word}; use miden_lib::AuthScheme; -use miden_tx::DataStore; -use objects::{ +use miden_objects::{ accounts::{Account, AccountData, AccountId, AccountStub, AccountType, AuthData}, assets::TokenSymbol, + crypto::dsa::rpo_falcon512::KeyPair, + Felt, Word, }; +use miden_tx::DataStore; use rand::{rngs::ThreadRng, Rng}; use crate::{ @@ -115,8 +116,7 @@ impl Client { todo!("Recording the account on chain is not supported yet"); } - let key_pair: objects::crypto::dsa::rpo_falcon512::KeyPair = - objects::crypto::dsa::rpo_falcon512::KeyPair::new()?; + let key_pair: KeyPair = KeyPair::new()?; let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key: key_pair.public_key(), @@ -155,8 +155,7 @@ impl Client { todo!("On-chain accounts are not supported yet"); } - let key_pair: objects::crypto::dsa::rpo_falcon512::KeyPair = - objects::crypto::dsa::rpo_falcon512::KeyPair::new()?; + let key_pair: KeyPair = KeyPair::new()?; let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key: key_pair.public_key(), @@ -244,47 +243,35 @@ impl Client { #[cfg(test)] pub mod tests { - use crypto::{dsa::rpo_falcon512::KeyPair, Felt, FieldElement}; + use miden_objects::{ + accounts::{Account, AccountData, AccountId, AuthData}, + crypto::dsa::rpo_falcon512::KeyPair, + Word, + }; - use miden_lib::transaction::TransactionKernel; - use mock::{ - constants::{generate_account_seed, AccountSeedType}, - mock::account::{self, mock_account}, + use crate::{ + mock::{ + get_account_with_default_account_code, get_new_account_with_default_account_code, + ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_REGULAR, + }, + store::{sqlite_store::tests::create_test_client, AuthInfo}, }; - use objects::accounts::{AccountData, AuthData}; - use rand::{rngs::ThreadRng, thread_rng, Rng}; - - use crate::store::{sqlite_store::tests::create_test_client, AuthInfo}; - - fn create_account_data(rng: &mut ThreadRng, seed_type: AccountSeedType) -> AccountData { - // Create an account and save it to a file - let (account_id, account_seed) = generate_account_seed(seed_type); - let assembler = TransactionKernel::assembler(); - let account = account::mock_account(Some(account_id.into()), Felt::ZERO, None, &assembler); - - let key_pair_seed: [u32; 10] = rng.gen(); - let mut key_pair_seed_u8: [u8; 40] = [0; 40]; - for (dest_c, source_e) in key_pair_seed_u8 - .chunks_exact_mut(4) - .zip(key_pair_seed.iter()) - { - dest_c.copy_from_slice(&source_e.to_le_bytes()) - } - let auth_data = AuthData::RpoFalcon512Seed(key_pair_seed_u8); - AccountData::new(account.clone(), Some(account_seed), auth_data) + fn create_account_data(account_id: u64) -> AccountData { + let account_id = AccountId::try_from(account_id).unwrap(); + let account = get_account_with_default_account_code(account_id, Word::default(), None); + + AccountData::new( + account.clone(), + Some(Word::default()), + AuthData::RpoFalcon512Seed([0; 40]), + ) } pub fn create_initial_accounts_data() -> Vec { - let mut rng = thread_rng(); - let account = create_account_data( - &mut rng, - AccountSeedType::RegularAccountUpdatableCodeOnChain, - ); + let account = create_account_data(ACCOUNT_ID_REGULAR); - // Create a Faucet and save it to a file - let faucet_account = - create_account_data(&mut rng, AccountSeedType::FungibleFaucetValidInitialBalance); + let faucet_account = create_account_data(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN); // Create Genesis state and save it to a file let accounts = vec![account, faucet_account]; @@ -296,11 +283,12 @@ pub mod tests { pub fn try_import_new_account() { // generate test client let mut client = create_test_client(); - let assembler = TransactionKernel::assembler(); - let (account_id, account_seed) = - generate_account_seed(AccountSeedType::RegularAccountUpdatableCodeOnChain); - let account = mock_account(Some(u64::from(account_id)), Felt::ZERO, None, &assembler); + let account = get_new_account_with_default_account_code( + AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(), + Word::default(), + None, + ); let key_pair: KeyPair = KeyPair::new() .map_err(|err| format!("Error generating KeyPair: {}", err)) @@ -312,7 +300,7 @@ pub mod tests { assert!(client .insert_account( &account, - Some(account_seed), + Some(Word::default()), &AuthInfo::RpoFalcon512(key_pair) ) .is_ok()); @@ -329,41 +317,15 @@ pub mod tests { client.import_account(account_data).unwrap(); } - let expected_accounts: Vec<_> = created_accounts_data + let expected_accounts: Vec = created_accounts_data .into_iter() .map(|account_data| account_data.account) .collect(); let accounts = client.get_accounts().unwrap(); assert_eq!(accounts.len(), 2); - assert_eq!(accounts[0].0.id(), expected_accounts[0].id()); - assert_eq!(accounts[0].0.nonce(), expected_accounts[0].nonce()); - assert_eq!( - accounts[0].0.vault_root(), - expected_accounts[0].vault().commitment() - ); - assert_eq!( - accounts[0].0.storage_root(), - expected_accounts[0].storage().root() - ); - assert_eq!( - accounts[0].0.code_root(), - expected_accounts[0].code().root() - ); - - assert_eq!(accounts[1].0.id(), expected_accounts[1].id()); - assert_eq!(accounts[1].0.nonce(), expected_accounts[1].nonce()); - assert_eq!( - accounts[1].0.vault_root(), - expected_accounts[1].vault().commitment() - ); - assert_eq!( - accounts[1].0.storage_root(), - expected_accounts[1].storage().root() - ); - assert_eq!( - accounts[1].0.code_root(), - expected_accounts[1].code().root() - ); + for (client_acc, expected_acc) in accounts.iter().zip(expected_accounts.iter()) { + assert_eq!(client_acc.0.hash(), expected_acc.hash()); + } } } diff --git a/src/client/chain_data.rs b/src/client/chain_data.rs index f92a82a5c..a47bc58d6 100644 --- a/src/client/chain_data.rs +++ b/src/client/chain_data.rs @@ -1,13 +1,12 @@ -use super::{rpc::NodeRpcClient, Client, Store}; +use crate::{ + client::{rpc::NodeRpcClient, Client}, + errors::ClientError, + store::Store, +}; +use miden_objects::BlockHeader; use miden_tx::DataStore; -#[cfg(test)] -use crate::errors::ClientError; -#[cfg(test)] -use objects::BlockHeader; - impl Client { - #[cfg(test)] pub fn get_block_headers_in_range( &self, start: u32, @@ -18,7 +17,6 @@ impl Client { .map_err(ClientError::StoreError) } - #[cfg(test)] pub fn get_block_headers( &self, block_numbers: &[u32], diff --git a/src/client/mod.rs b/src/client/mod.rs index e57d0a9dd..47f95ef62 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -5,6 +5,7 @@ pub mod rpc; use rpc::NodeRpcClient; pub mod accounts; +#[cfg(test)] mod chain_data; mod notes; pub(crate) mod sync; diff --git a/src/client/notes.rs b/src/client/notes.rs index c7151463b..bac276ba4 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -4,8 +4,8 @@ use crate::{ errors::ClientError, store::{InputNoteRecord, NoteFilter, Store}, }; +use miden_objects::notes::NoteId; use miden_tx::DataStore; -use objects::notes::NoteId; impl Client { // INPUT NOTE DATA RETRIEVAL diff --git a/src/client/rpc/mod.rs b/src/client/rpc/mod.rs index 013385eaa..82b9f42e9 100644 --- a/src/client/rpc/mod.rs +++ b/src/client/rpc/mod.rs @@ -1,9 +1,9 @@ use crate::errors::NodeRpcClientError; use async_trait::async_trait; use core::fmt; -use crypto::merkle::{MerklePath, MmrDelta}; -use objects::{ +use miden_objects::{ accounts::AccountId, + crypto::merkle::{MerklePath, MmrDelta}, notes::{NoteId, NoteMetadata}, transaction::ProvenTransaction, BlockHeader, Digest, diff --git a/src/client/rpc/tonic_client.rs b/src/client/rpc/tonic_client.rs index 1a88319f7..91aa60dd9 100644 --- a/src/client/rpc/tonic_client.rs +++ b/src/client/rpc/tonic_client.rs @@ -1,7 +1,6 @@ use super::{CommittedNote, NodeRpcClient, NodeRpcClientEndpoint, StateSyncInfo}; use crate::errors::NodeRpcClientError; use async_trait::async_trait; -use crypto::utils::Serializable; use miden_node_proto::{ errors::ParseError, generated::{ @@ -12,12 +11,13 @@ use miden_node_proto::{ rpc::api_client::ApiClient, }, }; -use objects::{ +use miden_objects::{ accounts::AccountId, notes::{NoteId, NoteMetadata}, transaction::ProvenTransaction, BlockHeader, Digest, Felt, }; +use miden_tx::utils::Serializable; use tonic::transport::Channel; // TONIC RPC CLIENT diff --git a/src/client/sync.rs b/src/client/sync.rs index b1e914aa6..79dee22cd 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -11,14 +11,14 @@ use crate::{ use crypto::merkle::{InOrderIndex, MmrDelta, MmrPeaks, PartialMmr}; use crate::store::{ChainMmrNodeFilter, NoteFilter, Store}; -use miden_tx::DataStore; -use objects::{ +use miden_objects::{ accounts::{AccountId, AccountStub}, crypto, notes::{NoteId, NoteInclusionProof}, transaction::TransactionId, BlockHeader, Digest, }; +use miden_tx::DataStore; use tracing::warn; pub enum SyncStatus { diff --git a/src/client/transactions.rs b/src/client/transactions.rs index 4ae0f64b8..d52daf05b 100644 --- a/src/client/transactions.rs +++ b/src/client/transactions.rs @@ -1,20 +1,18 @@ -use crypto::{rand::RpoRandomCoin, utils::Serializable, Felt, Word}; -use miden_lib::notes::create_p2id_note; - use crate::store::Store; -use miden_tx::{DataStore, ProvingOptions, TransactionProver}; -use mock::procedures::prepare_word; -use objects::{ +use miden_lib::notes::create_p2id_note; +use miden_objects::{ accounts::{AccountDelta, AccountId}, assembly::ProgramAst, assets::{Asset, FungibleAsset}, + crypto::rand::RpoRandomCoin, notes::{Note, NoteId}, transaction::{ ExecutedTransaction, OutputNote, OutputNotes, ProvenTransaction, TransactionArgs, TransactionId, TransactionScript, }, - Digest, + Digest, Felt, Word, }; +use miden_tx::{utils::Serializable, DataStore, ProvingOptions, TransactionProver}; use rand::Rng; use tracing::info; @@ -431,3 +429,13 @@ impl Client { RpoRandomCoin::new(coin_seed.map(Felt::new)) } } + +// HELPERS +// ================================================================================================ + +pub fn prepare_word(word: &Word) -> String { + word.iter() + .map(|x| x.as_int().to_string()) + .collect::>() + .join(".") +} diff --git a/src/errors.rs b/src/errors.rs index 2b15b5907..b13739ace 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,14 +1,14 @@ use core::fmt; -use crypto::{ - dsa::rpo_falcon512::FalconError, - merkle::MmrError, - utils::{DeserializationError, HexParseError}, -}; use miden_node_proto::errors::ParseError; -use miden_tx::{DataStoreError, TransactionExecutorError, TransactionProverError}; -use objects::{ - accounts::AccountId, notes::NoteId, AccountError, AssetVaultError, Digest, NoteError, - TransactionScriptError, +use miden_objects::{ + accounts::AccountId, + crypto::{dsa::rpo_falcon512::FalconError, merkle::MmrError}, + notes::NoteId, + AccountError, AssetVaultError, Digest, NoteError, TransactionScriptError, +}; +use miden_tx::{ + utils::{DeserializationError, HexParseError}, + DataStoreError, TransactionExecutorError, TransactionProverError, }; // CLIENT ERROR @@ -353,3 +353,29 @@ impl From for NodeRpcClientError { Self::ConversionFailure(err.to_string()) } } + +// NOTE ID PREFIX FETCH ERROR +// ================================================================================================ + +/// Error when Looking for a specific note ID from a partial ID +#[derive(Debug, Eq, PartialEq)] +pub enum NoteIdPrefixFetchError { + NoMatch(String), + MultipleMatches(String), +} + +impl fmt::Display for NoteIdPrefixFetchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + NoteIdPrefixFetchError::NoMatch(note_id) => { + write!(f, "No matches were found with the input prefix {note_id}.") + } + NoteIdPrefixFetchError::MultipleMatches(note_id) => { + write!( + f, + "found more than one note for the provided ID {note_id} and only one match is expected." + ) + } + } + } +} diff --git a/src/mock.rs b/src/mock.rs index 24823de5e..006c5c667 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -4,18 +4,13 @@ use crate::{ client::{ rpc::{NodeRpcClient, NodeRpcClientEndpoint, StateSyncInfo}, sync::FILTER_ID_SHIFT, - transactions::{PaymentTransactionData, TransactionTemplate}, + transactions::{prepare_word, PaymentTransactionData, TransactionTemplate}, Client, }, errors::NodeRpcClientError, store::{sqlite_store::SqliteStore, AuthInfo}, }; use async_trait::async_trait; -use crypto::{ - dsa::rpo_falcon512::KeyPair, - merkle::{NodeIndex, SimpleSmt}, - Felt, FieldElement, -}; use miden_lib::{transaction::TransactionKernel, AuthScheme}; use miden_node_proto::generated::{ account::AccountId as ProtoAccountId, @@ -24,28 +19,24 @@ use miden_node_proto::generated::{ requests::{GetBlockHeaderByNumberRequest, SyncStateRequest}, responses::{NullifierUpdate, SyncStateResponse}, }; -#[cfg(test)] -use miden_tx::DataStore; -use mock::{ - constants::{generate_account_seed, AccountSeedType}, - mock::{account::mock_account, block::mock_block_header}, -}; - -use mock::mock::{ - block, - notes::{mock_notes, AssetPreservationStatus}, -}; -use objects::{ - accounts::{Account, AccountStorage, StorageSlotType}, - accounts::{AccountId, AccountType}, - assets::FungibleAsset, - assets::{AssetVault, TokenSymbol}, - crypto::merkle::{Mmr, MmrDelta}, - notes::{Note, NoteInclusionProof}, +use miden_objects::{ + accounts::{ + get_account_seed_single, Account, AccountCode, AccountId, AccountStorage, AccountType, + StorageSlotType, + }, + assembly::{Assembler, ModuleAst, ProgramAst}, + assets::{Asset, AssetVault, FungibleAsset, TokenSymbol}, + crypto::{ + dsa::rpo_falcon512::KeyPair, + merkle::{Mmr, MmrDelta, NodeIndex, SimpleSmt}, + }, + notes::{Note, NoteAssets, NoteInclusionProof, NoteScript}, transaction::{InputNote, ProvenTransaction}, utils::collections::BTreeMap, - BlockHeader, NOTE_TREE_DEPTH, + BlockHeader, Felt, Word, NOTE_TREE_DEPTH, ZERO, }; +#[cfg(test)] +use miden_tx::DataStore; use rand::Rng; use tonic::{IntoRequest, Response, Status}; @@ -53,6 +44,21 @@ pub use crate::store::mock_executor_data_store::MockDataStore; pub type MockClient = Client; +// MOCK CONSTS +// ================================================================================================ + +pub const ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN: u64 = 3238098370154045919; +pub const ACCOUNT_ID_REGULAR: u64 = 0b0110111011u64 << 54; +pub const ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN: u64 = 0b1010011100 << 54; +pub const DEFAULT_ACCOUNT_CODE: &str = " + use.miden::contracts::wallets::basic->basic_wallet + use.miden::contracts::auth::basic->basic_eoa + + export.basic_wallet::receive_asset + export.basic_wallet::send_asset + export.basic_eoa::auth_tx_rpo_falcon512 +"; + /// Mock RPC API /// /// This struct implements the RPC API used by the client to communicate with the node. It is @@ -75,6 +81,14 @@ impl MockRpcApi { } } +#[cfg(test)] +impl Client { + /// Helper function to set a data store to conveniently mock data for tests + pub fn set_data_store(&mut self, data_store: D) { + self.set_tx_executor(miden_tx::TransactionExecutor::new(data_store)); + } +} + #[async_trait] impl NodeRpcClient for MockRpcApi { /// Executes the specified sync state request and returns the response. @@ -114,7 +128,7 @@ impl NodeRpcClient for MockRpcApi { let request: GetBlockHeaderByNumberRequest = request.into_request().into_inner(); if request.block_num == Some(0) { - let block_header: objects::BlockHeader = block::mock_block_header(0, None, None, &[]); + let block_header = BlockHeader::mock(0, None, None, &[]); return Ok(block_header); } panic!("get_block_header_by_number is supposed to be only used for genesis block") @@ -129,6 +143,9 @@ impl NodeRpcClient for MockRpcApi { } } +// HELPERS +// ================================================================================================ + /// Generates mock sync state requests and responses fn create_mock_sync_state_request_for_account_and_notes( account_id: AccountId, @@ -148,12 +165,11 @@ fn create_mock_sync_state_request_for_account_and_notes( .map(|note| (note.note().nullifier().as_elements()[3].as_int() >> FILTER_ID_SHIFT) as u32) .collect(); - let assembler = TransactionKernel::assembler(); - let account = mock_account(None, Felt::ONE, None, &assembler); + let account = get_account_with_default_account_code(account_id, Word::default(), None); let tracked_block_headers = tracked_block_headers.unwrap_or(vec![ - block::mock_block_header(8, None, None, &[]), - block::mock_block_header(10, None, None, &[]), + BlockHeader::mock(8, None, None, &[]), + BlockHeader::mock(10, None, None, &[]), ]); let chain_tip = tracked_block_headers @@ -211,32 +227,23 @@ fn create_mock_sync_state_request_for_account_and_notes( /// Generates mock sync state requests and responses fn generate_state_sync_mock_requests() -> BTreeMap { - use mock::mock::{account::MockAccountType, transaction::mock_inputs}; - - // generate test data - let transaction_inputs = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); + let account_id = AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(); // create sync state requests - let requests = create_mock_sync_state_request_for_account_and_notes( - transaction_inputs.account().id(), - &transaction_inputs - .input_notes() - .clone() - .into_iter() - .map(|input_note| input_note.note().clone()) - .collect::>(), - &transaction_inputs.input_notes().clone().into_vec(), + let assembler = TransactionKernel::assembler(); + let (consumed_notes, created_notes) = mock_notes(&assembler); + let (_, input_notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); + + create_mock_sync_state_request_for_account_and_notes( + account_id, + &created_notes, + &input_notes, None, None, - ); - - requests + ) } -fn mock_full_chain_mmr_and_notes( +pub fn mock_full_chain_mmr_and_notes( consumed_notes: Vec, ) -> (Mmr, Vec, Vec, Vec) { let mut note_trees = Vec::new(); @@ -258,13 +265,13 @@ fn mock_full_chain_mmr_and_notes( // create a dummy chain of block headers let block_chain = vec![ - mock_block_header(0, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(1, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(2, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(3, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(4, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(5, None, note_tree_iter.next().map(|x| x.root()), &[]), - mock_block_header(6, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(0, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(1, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(2, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(3, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(4, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(5, None, note_tree_iter.next().map(|x| x.root()), &[]), + BlockHeader::mock(6, None, note_tree_iter.next().map(|x| x.root()), &[]), ]; // instantiate and populate MMR @@ -315,14 +322,26 @@ fn mock_full_chain_mmr_and_notes( /// chain pub async fn insert_mock_data(client: &mut MockClient) -> Vec { // mock notes - let assembler = TransactionKernel::assembler(); - let (account_id, account_seed) = - generate_account_seed(AccountSeedType::RegularAccountUpdatableCodeOnChain); - let account = mock_account(Some(u64::from(account_id)), Felt::ONE, None, &assembler); - let (input_notes, created_notes) = mock_notes(&assembler, &AssetPreservationStatus::Preserved); + let account = get_account_with_default_account_code( + AccountId::try_from(ACCOUNT_ID_REGULAR_ACCOUNT_UPDATABLE_CODE_ON_CHAIN).unwrap(), + Word::default(), + None, + ); + + let init_seed: [u8; 32] = [0; 32]; + let account_seed = get_account_seed_single( + init_seed, + account.account_type(), + true, + account.code().root(), + account.storage().root(), + ) + .unwrap(); + let assembler = TransactionKernel::assembler(); + let (consumed_notes, created_notes) = mock_notes(&assembler); let (_mmr, consumed_notes, tracked_block_headers, mmr_deltas) = - mock_full_chain_mmr_and_notes(input_notes); + mock_full_chain_mmr_and_notes(consumed_notes); // insert notes into database for note in consumed_notes.clone() { @@ -425,9 +444,9 @@ pub async fn create_mock_transaction(client: &mut MockClient) { let (faucet, seed) = miden_lib::accounts::faucets::create_basic_fungible_faucet( init_seed, - objects::assets::TokenSymbol::new("MOCK").unwrap(), + miden_objects::assets::TokenSymbol::new("MOCK").unwrap(), 4u8, - crypto::Felt::try_from(max_supply.as_slice()).unwrap(), + Felt::try_from(max_supply.as_slice()).unwrap(), auth_scheme, ) .unwrap(); @@ -436,7 +455,7 @@ pub async fn create_mock_transaction(client: &mut MockClient) { .insert_account(&faucet, Some(seed), &AuthInfo::RpoFalcon512(key_pair)) .unwrap(); - let asset: objects::assets::Asset = FungibleAsset::new(faucet.id(), 5u64).unwrap().into(); + let asset: miden_objects::assets::Asset = FungibleAsset::new(faucet.id(), 5u64).unwrap().into(); // Insert a P2ID transaction object @@ -508,10 +527,201 @@ pub fn mock_fungible_faucet_account( ) } -#[cfg(test)] -impl Client { - /// Helper function to set a data store to conveniently mock data for tests - pub fn set_data_store(&mut self, data_store: D) { - self.set_tx_executor(miden_tx::TransactionExecutor::new(data_store)); +pub fn mock_notes(assembler: &Assembler) -> (Vec, Vec) { + const ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_1: u64 = + 0b1010010001111111010110100011011110101011010001101111110110111100u64; + const ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_2: u64 = + 0b1010000101101010101101000110111101010110100011011110100011011101u64; + const ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_3: u64 = + 0b1010011001011010101101000110111101010110100011011101000110111100u64; + // Note Assets + let faucet_id_1 = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_1).unwrap(); + let faucet_id_2 = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_2).unwrap(); + let faucet_id_3 = AccountId::try_from(ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN_3).unwrap(); + let fungible_asset_1: Asset = FungibleAsset::new(faucet_id_1, 100).unwrap().into(); + let fungible_asset_2: Asset = FungibleAsset::new(faucet_id_2, 150).unwrap().into(); + let fungible_asset_3: Asset = FungibleAsset::new(faucet_id_3, 7).unwrap().into(); + + // Sender account + let sender = AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(); + + // CREATED NOTES + // -------------------------------------------------------------------------------------------- + // create note script + let note_program_ast = ProgramAst::parse("begin push.1 drop end").unwrap(); + let (note_script, _) = NoteScript::new(note_program_ast, assembler).unwrap(); + + // Created Notes + const SERIAL_NUM_4: Word = [Felt::new(13), Felt::new(14), Felt::new(15), Felt::new(16)]; + let created_note_1 = Note::new( + note_script.clone(), + &[Felt::new(1)], + &[fungible_asset_1], + SERIAL_NUM_4, + sender, + ZERO, + ) + .unwrap(); + + const SERIAL_NUM_5: Word = [Felt::new(17), Felt::new(18), Felt::new(19), Felt::new(20)]; + let created_note_2 = Note::new( + note_script.clone(), + &[Felt::new(2)], + &[fungible_asset_2], + SERIAL_NUM_5, + sender, + ZERO, + ) + .unwrap(); + + const SERIAL_NUM_6: Word = [Felt::new(21), Felt::new(22), Felt::new(23), Felt::new(24)]; + let created_note_3 = Note::new( + note_script, + &[Felt::new(2)], + &[fungible_asset_3], + SERIAL_NUM_6, + sender, + ZERO, + ) + .unwrap(); + + let created_notes = vec![created_note_1, created_note_2, created_note_3]; + + // CONSUMED NOTES + // -------------------------------------------------------------------------------------------- + + // create note 1 script + let note_1_script_src = format!( + "\ + begin + # create note 0 + push.{created_note_0_recipient} + push.{created_note_0_tag} + push.{created_note_0_asset} + # MAST root of the `create_note` mock account procedure + # call.0xacb46cadec8d1721934827ed161b851f282f1f4b88b72391a67fed668b1a00ba + drop dropw dropw + + # create note 1 + push.{created_note_1_recipient} + push.{created_note_1_tag} + push.{created_note_1_asset} + # MAST root of the `create_note` mock account procedure + # call.0xacb46cadec8d1721934827ed161b851f282f1f4b88b72391a67fed668b1a00ba + drop dropw dropw + end + ", + created_note_0_recipient = prepare_word(&created_notes[0].recipient()), + created_note_0_tag = created_notes[0].metadata().tag(), + created_note_0_asset = prepare_assets(created_notes[0].assets())[0], + created_note_1_recipient = prepare_word(&created_notes[1].recipient()), + created_note_1_tag = created_notes[1].metadata().tag(), + created_note_1_asset = prepare_assets(created_notes[1].assets())[0], + ); + let note_1_script_ast = ProgramAst::parse(¬e_1_script_src).unwrap(); + let (note_1_script, _) = NoteScript::new(note_1_script_ast, assembler).unwrap(); + + // create note 2 script + let note_2_script_src = format!( + "\ + begin + # create note 2 + push.{created_note_2_recipient} + push.{created_note_2_tag} + push.{created_note_2_asset} + # MAST root of the `create_note` mock account procedure + # call.0xacb46cadec8d1721934827ed161b851f282f1f4b88b72391a67fed668b1a00ba + drop dropw dropw + end + ", + created_note_2_recipient = prepare_word(&created_notes[2].recipient()), + created_note_2_tag = created_notes[2].metadata().tag(), + created_note_2_asset = prepare_assets(created_notes[2].assets())[0], + ); + let note_2_script_ast = ProgramAst::parse(¬e_2_script_src).unwrap(); + let (note_2_script, _) = NoteScript::new(note_2_script_ast, assembler).unwrap(); + + // Consumed Notes + const SERIAL_NUM_1: Word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]; + let consumed_note_1 = Note::new( + note_1_script, + &[Felt::new(1)], + &[fungible_asset_1], + SERIAL_NUM_1, + sender, + ZERO, + ) + .unwrap(); + + const SERIAL_NUM_2: Word = [Felt::new(5), Felt::new(6), Felt::new(7), Felt::new(8)]; + let consumed_note_2 = Note::new( + note_2_script, + &[Felt::new(2)], + &[fungible_asset_2, fungible_asset_3], + SERIAL_NUM_2, + sender, + ZERO, + ) + .unwrap(); + + let consumed_notes = vec![consumed_note_1, consumed_note_2]; + + (consumed_notes, created_notes) +} + +fn get_account_with_nonce( + account_id: AccountId, + public_key: Word, + assets: Option, + nonce: u64, +) -> Account { + let account_code_src = DEFAULT_ACCOUNT_CODE; + let account_code_ast = ModuleAst::parse(account_code_src).unwrap(); + let account_assembler = TransactionKernel::assembler(); + + let account_code = AccountCode::new(account_code_ast, &account_assembler).unwrap(); + let account_storage = AccountStorage::new(vec![( + 0, + (StorageSlotType::Value { value_arity: 0 }, public_key), + )]) + .unwrap(); + + let asset_vault = match assets { + Some(asset) => AssetVault::new(&[asset]).unwrap(), + None => AssetVault::new(&[]).unwrap(), + }; + + Account::new( + account_id, + asset_vault, + account_storage, + account_code, + Felt::new(nonce), + ) +} + +pub fn get_account_with_default_account_code( + account_id: AccountId, + public_key: Word, + assets: Option, +) -> Account { + get_account_with_nonce(account_id, public_key, assets, 1) +} + +pub fn get_new_account_with_default_account_code( + account_id: AccountId, + public_key: Word, + assets: Option, +) -> Account { + get_account_with_nonce(account_id, public_key, assets, 0) +} + +fn prepare_assets(note_assets: &NoteAssets) -> Vec { + let mut assets = Vec::new(); + for &asset in note_assets.iter() { + let asset_word: Word = asset.into(); + let asset_str = prepare_word(&asset_word); + assets.push(asset_str); } + assets } diff --git a/src/store/data_store.rs b/src/store/data_store.rs index 5eb25afd4..acd568cfd 100644 --- a/src/store/data_store.rs +++ b/src/store/data_store.rs @@ -1,16 +1,15 @@ -use crate::errors::{ClientError, StoreError}; -use objects::utils::collections::BTreeSet; - use super::{sqlite_store::SqliteStore, ChainMmrNodeFilter, NoteFilter, Store}; -use crypto::merkle::{InOrderIndex, MerklePath, PartialMmr}; -use miden_tx::{DataStore, DataStoreError, TransactionInputs}; - -use objects::{ +use crate::errors::{ClientError, StoreError}; +use miden_objects::{ accounts::AccountId, assembly::ModuleAst, + crypto::merkle::{InOrderIndex, MerklePath, PartialMmr}, + notes::NoteId, transaction::{ChainMmr, InputNote, InputNotes}, + utils::collections::BTreeSet, BlockHeader, }; +use miden_tx::{DataStore, DataStoreError, TransactionInputs}; // DATA STORE // ================================================================================================ @@ -31,7 +30,7 @@ impl DataStore for SqliteDataStore { &self, account_id: AccountId, block_num: u32, - notes: &[objects::notes::NoteId], + notes: &[NoteId], ) -> Result { // First validate that no note has already been consumed let unspent_notes = self @@ -72,7 +71,7 @@ impl DataStore for SqliteDataStore { } } - let notes_blocks: Vec = self + let notes_blocks: Vec = self .store .get_block_headers(¬es_blocks)? .iter() diff --git a/src/store/mock_executor_data_store.rs b/src/store/mock_executor_data_store.rs index 044bb91f0..97c9b0d30 100644 --- a/src/store/mock_executor_data_store.rs +++ b/src/store/mock_executor_data_store.rs @@ -2,24 +2,21 @@ use miden_lib::{ transaction::{memory::FAUCET_STORAGE_DATA_SLOT, TransactionKernel}, MidenLib, }; -use miden_tx::{DataStore, DataStoreError, TransactionInputs}; -use mock::{ - constants::{ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_SENDER, DEFAULT_ACCOUNT_CODE}, - mock::{ - account::MockAccountType, - notes::AssetPreservationStatus, - transaction::{mock_inputs, mock_inputs_with_existing}, - }, -}; -use objects::{ +use miden_objects::{ accounts::{Account, AccountCode, AccountId, AccountStorage, StorageSlotType}, assembly::{Library, LibraryPath, ModuleAst, ProgramAst}, - assets::{Asset, AssetVault, FungibleAsset}, - crypto::{dsa::rpo_falcon512::KeyPair, utils::Serializable}, + assets::{AssetVault, FungibleAsset}, + crypto::{dsa::rpo_falcon512::KeyPair, merkle::PartialMmr, utils::Serializable}, notes::{Note, NoteId, NoteScript}, - transaction::{ChainMmr, InputNotes}, + transaction::{ChainMmr, InputNote, InputNotes}, BlockHeader, Felt, Word, }; +use miden_tx::{DataStore, DataStoreError, TransactionInputs}; + +use crate::mock::{ + get_account_with_default_account_code, mock_full_chain_mmr_and_notes, + ACCOUNT_ID_FUNGIBLE_FAUCET_ON_CHAIN, ACCOUNT_ID_REGULAR, +}; // MOCK DATA STORE // ================================================================================================ @@ -34,39 +31,36 @@ pub struct MockDataStore { } impl MockDataStore { - pub fn new() -> Self { - let transaction_data = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); - Self { - account: transaction_data.account().clone(), - block_header: *transaction_data.block_header(), - block_chain: transaction_data.block_chain().clone(), - input_notes: transaction_data.input_notes().clone(), - account_seed: None, - } - } - - pub fn with_existing( + pub fn new( account: Account, account_seed: Option, - input_notes: Option>, + input_notes: Option>, ) -> Self { - let (_mocked_account, block_header, block_chain, input_notes, _auxiliary_data_inputs) = - // NOTE: Currently this disregards the mocked account and uses the passed account - mock_inputs_with_existing( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - Some(account.clone()), - input_notes, - ); + let (mmr, _notes, headers, _) = mock_full_chain_mmr_and_notes(vec![]); + let partial_mmr_peaks = mmr.peaks(mmr.forest()).unwrap(); + let mut partial_mmr = PartialMmr::from_peaks(partial_mmr_peaks); + + for block in headers.iter() { + let merkle_path = mmr + .open(block.block_num() as usize, mmr.forest()) + .unwrap() + .merkle_path; + partial_mmr + .track(block.block_num() as usize, block.hash(), &merkle_path) + .unwrap(); + } Self { account, - block_header, - block_chain, - input_notes: InputNotes::new(input_notes).unwrap(), + // NOTE: This last block header is ahead of the mocked chain MMR view in order to correctly build transaction inputs + block_header: BlockHeader::mock( + 7, + Some(mmr.peaks(mmr.forest()).unwrap().hash_peaks()), + None, + &[], + ), + block_chain: ChainMmr::new(partial_mmr, headers).unwrap(), + input_notes: InputNotes::new(input_notes.unwrap_or_default()).unwrap(), account_seed, } } @@ -74,7 +68,12 @@ impl MockDataStore { impl Default for MockDataStore { fn default() -> Self { - Self::new() + let account = get_account_with_default_account_code( + ACCOUNT_ID_REGULAR.try_into().unwrap(), + Word::default(), + None, + ); + Self::new(account, Some(Word::default()), None) } } @@ -122,43 +121,13 @@ pub fn get_new_key_pair_with_advice_map() -> (Word, Vec) { (pk, pk_sk_felts) } -#[allow(dead_code)] -pub fn get_account_with_default_account_code( - account_id: AccountId, - public_key: Word, - assets: Option, -) -> Account { - let account_code_src = DEFAULT_ACCOUNT_CODE; - let account_code_ast = ModuleAst::parse(account_code_src).unwrap(); - let account_assembler = TransactionKernel::assembler(); - - let account_code = AccountCode::new(account_code_ast, &account_assembler).unwrap(); - let account_storage = AccountStorage::new(vec![( - 0, - (StorageSlotType::Value { value_arity: 0 }, public_key), - )]) - .unwrap(); - - let asset_vault = match assets { - Some(asset) => AssetVault::new(&[asset]).unwrap(), - None => AssetVault::new(&[]).unwrap(), - }; - - Account::new( - account_id, - asset_vault, - account_storage, - account_code, - Felt::new(1), - ) -} - #[allow(dead_code)] pub fn get_note_with_fungible_asset_and_script( fungible_asset: FungibleAsset, note_script: ProgramAst, ) -> Note { let note_assembler = TransactionKernel::assembler(); + const ACCOUNT_ID_SENDER: u64 = 0b0110111011u64 << 54; let (note_script, _) = NoteScript::new(note_script, ¬e_assembler).unwrap(); const SERIAL_NUM: Word = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)]; diff --git a/src/store/mod.rs b/src/store/mod.rs index 18f9d8e27..59215f295 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -3,24 +3,24 @@ use crate::{ errors::{ClientError, StoreError}, }; use clap::error::Result; -use crypto::{ - dsa::rpo_falcon512::KeyPair, - merkle::{InOrderIndex, MmrPeaks}, - utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, - Word, -}; -use objects::{ +use miden_objects::{ accounts::{Account, AccountId, AccountStub}, + crypto::{ + dsa::rpo_falcon512::KeyPair, + merkle::{InOrderIndex, MmrPeaks}, + }, notes::{ Note, NoteAssets, NoteId, NoteInclusionProof, NoteInputs, NoteMetadata, NoteScript, Nullifier, }, transaction::{InputNote, TransactionId}, utils::collections::BTreeMap, - BlockHeader, Digest, NoteError, + BlockHeader, Digest, NoteError, Word, }; use serde::{Deserialize, Serialize}; +use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; + pub mod data_store; pub mod sqlite_store; @@ -250,7 +250,7 @@ impl AuthInfo { } impl Serializable for AuthInfo { - fn write_into(&self, target: &mut W) { + fn write_into(&self, target: &mut W) { let mut bytes = vec![self.type_byte()]; match self { AuthInfo::RpoFalcon512(key_pair) => { @@ -262,18 +262,14 @@ impl Serializable for AuthInfo { } impl Deserializable for AuthInfo { - fn read_from( - source: &mut R, - ) -> Result { + fn read_from(source: &mut R) -> Result { let auth_type: u8 = source.read_u8()?; match auth_type { RPO_FALCON512_AUTH => { let key_pair = KeyPair::read_from(source)?; Ok(AuthInfo::RpoFalcon512(key_pair)) } - val => Err(crypto::utils::DeserializationError::InvalidValue( - val.to_string(), - )), + val => Err(DeserializationError::InvalidValue(val.to_string())), } } } @@ -489,12 +485,12 @@ impl TryInto for InputNoteRecord { } // TODO: should we use a better Error for these two? (None, _) => Err(ClientError::NoteError( - objects::NoteError::invalid_origin_index( - "Input Note Record contains no proof".to_string(), + miden_objects::NoteError::invalid_origin_index( + "Input Note Record contains no inclusion proof".to_string(), ), )), (_, None) => Err(ClientError::NoteError( - objects::NoteError::invalid_origin_index( + miden_objects::NoteError::invalid_origin_index( "Input Note Record contains no metadata".to_string(), ), )), diff --git a/src/store/sqlite_store/accounts.rs b/src/store/sqlite_store/accounts.rs index a32f80a64..f9a8dfa95 100644 --- a/src/store/sqlite_store/accounts.rs +++ b/src/store/sqlite_store/accounts.rs @@ -3,18 +3,14 @@ use super::SqliteStore; use crate::{errors::StoreError, store::AuthInfo}; use clap::error::Result; -use crypto::{ - hash::rpo::RpoDigest, - utils::{Deserializable, Serializable}, - Felt, Word, -}; use miden_lib::transaction::TransactionKernel; -use objects::{ +use miden_objects::{ accounts::{Account, AccountCode, AccountId, AccountStorage, AccountStub}, assembly::{AstSerdeOptions, ModuleAst}, assets::{Asset, AssetVault}, - Digest, + Digest, Felt, Word, }; +use miden_tx::utils::{Deserializable, Serializable}; use rusqlite::{params, Transaction}; // TYPES @@ -138,7 +134,7 @@ impl SqliteStore { pub(super) fn get_account_code( &self, root: Digest, - ) -> Result<(Vec, ModuleAst), StoreError> { + ) -> Result<(Vec, ModuleAst), StoreError> { let root_serialized = root.to_string(); const QUERY: &str = "SELECT root, procedures, module FROM account_code WHERE root = ?"; @@ -151,10 +147,7 @@ impl SqliteStore { } /// Retrieve account storage data by vault root - pub(super) fn get_account_storage( - &self, - root: RpoDigest, - ) -> Result { + pub(super) fn get_account_storage(&self, root: Digest) -> Result { let root_serialized = &root.to_string(); const QUERY: &str = "SELECT root, slots FROM account_storage WHERE root = ?"; @@ -366,7 +359,7 @@ fn parse_account_code_columns( /// Parse an account_code from the provided parts. fn parse_account_code( serialized_account_code_parts: SerializedAccountCodeParts, -) -> Result<(Vec, ModuleAst), StoreError> { +) -> Result<(Vec, ModuleAst), StoreError> { let (_, procedures, module) = serialized_account_code_parts; let procedures = @@ -450,12 +443,15 @@ fn serialize_account_asset_vault( #[cfg(test)] mod tests { - use crate::store::sqlite_store::{accounts::insert_account_code, tests::create_test_store}; - use crypto::{ - dsa::rpo_falcon512::KeyPair, - utils::{Deserializable, Serializable}, + use miden_objects::{ + accounts::AccountCode, assembly::ModuleAst, crypto::dsa::rpo_falcon512::KeyPair, + }; + use miden_tx::utils::{Deserializable, Serializable}; + + use crate::{ + mock::DEFAULT_ACCOUNT_CODE, + store::sqlite_store::{accounts::insert_account_code, tests::create_test_store}, }; - use mock::mock::account; use super::AuthInfo; @@ -463,7 +459,8 @@ mod tests { fn test_account_code_insertion_no_duplicates() { let mut store = create_test_store(); let assembler = miden_lib::transaction::TransactionKernel::assembler(); - let account_code = account::mock_account_code(&assembler); + let module_ast = ModuleAst::parse(DEFAULT_ACCOUNT_CODE).unwrap(); + let account_code = AccountCode::new(module_ast, &assembler).unwrap(); let tx = store.db.transaction().unwrap(); // Table is empty at the beginning diff --git a/src/store/sqlite_store/chain_data.rs b/src/store/sqlite_store/chain_data.rs index b4f93476c..d82212e3b 100644 --- a/src/store/sqlite_store/chain_data.rs +++ b/src/store/sqlite_store/chain_data.rs @@ -5,14 +5,15 @@ use crate::errors::StoreError; use crate::store::ChainMmrNodeFilter; use clap::error::Result; -use crypto::merkle::{InOrderIndex, MmrPeaks}; - -use objects::utils::collections::BTreeMap; -use objects::{BlockHeader, Digest}; +use miden_objects::{ + crypto::merkle::{InOrderIndex, MmrPeaks}, + utils::collections::BTreeMap, + BlockHeader, Digest, +}; use rusqlite::{params, OptionalExtension, Transaction}; -type SerializedBlockHeaderData = (i64, String, String, String, String, bool); -type SerializedBlockHeaderParts = (u64, String, String, String, String, bool); +type SerializedBlockHeaderData = (i64, String, String, bool); +type SerializedBlockHeaderParts = (u64, String, String, bool); type SerializedChainMmrNodeData = (i64, String); type SerializedChainMmrNodeParts = (u64, String); @@ -45,23 +46,16 @@ impl SqliteStore { has_client_notes: bool, ) -> Result<(), StoreError> { let chain_mmr_peaks = chain_mmr_peaks.peaks().to_vec(); - let (block_num, header, notes_root, sub_hash, chain_mmr, has_client_notes) = + let (block_num, header, chain_mmr, has_client_notes) = serialize_block_header(block_header, chain_mmr_peaks, has_client_notes)?; const QUERY: &str = "\ INSERT INTO block_headers - (block_num, header, notes_root, sub_hash, chain_mmr_peaks, has_client_notes) - VALUES (?, ?, ?, ?, ?, ?)"; + (block_num, header, chain_mmr_peaks, has_client_notes) + VALUES (?, ?, ?, ?)"; self.db.execute( QUERY, - params![ - block_num, - header, - notes_root, - sub_hash, - chain_mmr, - has_client_notes - ], + params![block_num, header, chain_mmr, has_client_notes], )?; Ok(()) @@ -77,7 +71,7 @@ impl SqliteStore { .collect::>() .join(","); let query = format!( - "SELECT block_num, header, notes_root, sub_hash, chain_mmr_peaks, has_client_notes FROM block_headers WHERE block_num IN ({})", + "SELECT block_num, header, chain_mmr_peaks, has_client_notes FROM block_headers WHERE block_num IN ({})", formatted_block_numbers_list ); self.db @@ -88,7 +82,7 @@ impl SqliteStore { } pub(crate) fn get_tracked_block_headers(&self) -> Result, StoreError> { - const QUERY: &str = "SELECT block_num, header, notes_root, sub_hash, chain_mmr_peaks, has_client_notes FROM block_headers WHERE has_client_notes=true"; + const QUERY: &str = "SELECT block_num, header, chain_mmr_peaks, has_client_notes FROM block_headers WHERE has_client_notes=true"; self.db .prepare(QUERY)? .query_map(params![], parse_block_headers_columns)? @@ -152,22 +146,15 @@ impl SqliteStore { has_client_notes: bool, ) -> Result<(), StoreError> { let chain_mmr_peaks = chain_mmr_peaks.peaks().to_vec(); - let (block_num, header, notes_root, sub_hash, chain_mmr, has_client_notes) = + let (block_num, header, chain_mmr, has_client_notes) = serialize_block_header(block_header, chain_mmr_peaks, has_client_notes)?; const QUERY: &str = "\ INSERT INTO block_headers - (block_num, header, notes_root, sub_hash, chain_mmr_peaks, has_client_notes) - VALUES (?, ?, ?, ?, ?, ?)"; + (block_num, header, chain_mmr_peaks, has_client_notes) + VALUES (?, ?, ?, ?)"; tx.execute( QUERY, - params![ - block_num, - header, - notes_root, - sub_hash, - chain_mmr, - has_client_notes - ], + params![block_num, header, chain_mmr, has_client_notes], )?; Ok(()) } @@ -203,21 +190,10 @@ fn serialize_block_header( let block_num = block_header.block_num(); let header = serde_json::to_string(&block_header).map_err(StoreError::InputSerializationError)?; - let notes_root = serde_json::to_string(&block_header.note_root()) - .map_err(StoreError::InputSerializationError)?; - let sub_hash = serde_json::to_string(&block_header.sub_hash()) - .map_err(StoreError::InputSerializationError)?; let chain_mmr_peaks = serde_json::to_string(&chain_mmr_peaks).map_err(StoreError::InputSerializationError)?; - Ok(( - block_num as i64, - header, - notes_root, - sub_hash, - chain_mmr_peaks, - has_client_notes, - )) + Ok((block_num as i64, header, chain_mmr_peaks, has_client_notes)) } fn parse_block_headers_columns( @@ -225,25 +201,16 @@ fn parse_block_headers_columns( ) -> Result { let block_num: i64 = row.get(0)?; let header: String = row.get(1)?; - let notes_root: String = row.get(2)?; - let sub_hash: String = row.get(3)?; - let chain_mmr: String = row.get(4)?; - let has_client_notes: bool = row.get(5)?; + let chain_mmr: String = row.get(2)?; + let has_client_notes: bool = row.get(3)?; - Ok(( - block_num as u64, - header, - notes_root, - sub_hash, - chain_mmr, - has_client_notes, - )) + Ok((block_num as u64, header, chain_mmr, has_client_notes)) } fn parse_block_header( serialized_block_header_parts: SerializedBlockHeaderParts, ) -> Result<(BlockHeader, bool), StoreError> { - let (_, header, _, _, _, has_client_notes) = serialized_block_header_parts; + let (_, header, _, has_client_notes) = serialized_block_header_parts; Ok(( serde_json::from_str(&header).map_err(StoreError::JsonDataDeserializationError)?, @@ -281,9 +248,7 @@ fn parse_chain_mmr_nodes( #[cfg(test)] mod test { - use crypto::merkle::MmrPeaks; - use mock::mock::block::mock_block_header; - use objects::BlockHeader; + use miden_objects::{crypto::merkle::MmrPeaks, BlockHeader}; use crate::store::{ sqlite_store::{tests::create_test_store, SqliteStore}, @@ -292,7 +257,7 @@ mod test { fn insert_dummy_block_headers(store: &mut SqliteStore) -> Vec { let block_headers: Vec = (0..5) - .map(|block_num| mock_block_header(block_num, None, None, &[])) + .map(|block_num| BlockHeader::mock(block_num, None, None, &[])) .collect(); let tx = store.db.transaction().unwrap(); let dummy_peaks = MmrPeaks::new(0, Vec::new()).unwrap(); diff --git a/src/store/sqlite_store/mod.rs b/src/store/sqlite_store/mod.rs index 1e61206be..204c9688b 100644 --- a/src/store/sqlite_store/mod.rs +++ b/src/store/sqlite_store/mod.rs @@ -1,20 +1,16 @@ -use objects::{ +use miden_objects::{ accounts::{Account, AccountId, AccountStub}, - notes::NoteInclusionProof, + crypto::merkle::{InOrderIndex, MmrPeaks}, + notes::{NoteId, NoteInclusionProof}, transaction::TransactionId, utils::collections::BTreeMap, - Digest, + BlockHeader, Digest, Word, }; use super::{ AuthInfo, ChainMmrNodeFilter, InputNoteRecord, NoteFilter, OutputNoteRecord, Store, TransactionFilter, }; -use crypto::{ - merkle::{InOrderIndex, MmrPeaks}, - Word, -}; -use objects::{notes::NoteId, BlockHeader}; use rusqlite::Connection; @@ -270,7 +266,12 @@ pub mod tests { let rpc_endpoint = client_config.rpc.endpoint.to_string(); let store = SqliteStore::new((&client_config).into()).unwrap(); - MockClient::new(MockRpcApi::new(&rpc_endpoint), store, MockDataStore::new()).unwrap() + MockClient::new( + MockRpcApi::new(&rpc_endpoint), + store, + MockDataStore::default(), + ) + .unwrap() } pub(crate) fn create_test_store_path() -> std::path::PathBuf { diff --git a/src/store/sqlite_store/notes.rs b/src/store/sqlite_store/notes.rs index 227f28a78..6104d0c8b 100644 --- a/src/store/sqlite_store/notes.rs +++ b/src/store/sqlite_store/notes.rs @@ -1,17 +1,19 @@ use std::fmt; -use crate::errors::StoreError; -use crate::store::{InputNoteRecord, NoteFilter, NoteRecordDetails, NoteStatus, OutputNoteRecord}; +use crate::{ + errors::StoreError, + store::{InputNoteRecord, NoteFilter, NoteRecordDetails, NoteStatus, OutputNoteRecord}, +}; use super::SqliteStore; use clap::error::Result; -use crypto::utils::{Deserializable, Serializable}; - -use objects::notes::{NoteAssets, NoteId, NoteInclusionProof, NoteMetadata, Nullifier}; - -use objects::Digest; +use miden_objects::{ + crypto::utils::{Deserializable, Serializable}, + notes::{NoteAssets, NoteId, NoteInclusionProof, NoteMetadata, Nullifier}, + Digest, +}; use rusqlite::{named_params, params, Transaction}; fn insert_note_query(table_name: NoteTable) -> String { @@ -86,13 +88,13 @@ impl NoteFilter { fn to_query(&self, notes_table: NoteTable) -> String { let base = format!( "SELECT - assets, - details, - recipient, - status, - metadata, - inclusion_proof - from {notes_table}" + assets, + details, + recipient, + status, + metadata, + inclusion_proof + from {notes_table}" ); match self { @@ -138,7 +140,7 @@ impl SqliteStore { const QUERY: &str = "SELECT assets, - details, + details, recipient, status, metadata, @@ -308,6 +310,7 @@ pub(crate) fn serialize_input_note( ) -> Result { let note_id = note.id().inner().to_string(); let note_assets = note.assets().to_bytes(); + let (inclusion_proof, status) = match note.inclusion_proof() { Some(proof) => { // FIXME: This removal is to accomodate a problem with how the node constructs paths where diff --git a/src/store/sqlite_store/store.sql b/src/store/sqlite_store/store.sql index afa42b6be..927387e21 100644 --- a/src/store/sqlite_store/store.sql +++ b/src/store/sqlite_store/store.sql @@ -171,8 +171,6 @@ WHERE ( CREATE TABLE block_headers ( block_num UNSIGNED BIG INT NOT NULL, -- block number header BLOB NOT NULL, -- serialized block header - notes_root BLOB NOT NULL, -- root of the notes Merkle tree in this block - sub_hash BLOB NOT NULL, -- hash of all other header fields in the block chain_mmr_peaks BLOB NOT NULL, -- serialized peaks of the chain MMR at this block has_client_notes BOOL NOT NULL, -- whether the block has notes relevant to the client PRIMARY KEY (block_num) diff --git a/src/store/sqlite_store/sync.rs b/src/store/sqlite_store/sync.rs index 027cd96c9..046ffefe1 100644 --- a/src/store/sqlite_store/sync.rs +++ b/src/store/sqlite_store/sync.rs @@ -1,6 +1,6 @@ -use crypto::merkle::{InOrderIndex, MmrPeaks}; - -use objects::{ +use crate::errors::StoreError; +use miden_objects::{ + crypto::merkle::{InOrderIndex, MmrPeaks}, notes::{NoteId, NoteInclusionProof}, transaction::TransactionId, BlockHeader, Digest, @@ -8,7 +8,6 @@ use objects::{ use rusqlite::{named_params, params}; use super::SqliteStore; -use crate::errors::StoreError; impl SqliteStore { pub(crate) fn get_note_tags(&self) -> Result, StoreError> { diff --git a/src/store/sqlite_store/transactions.rs b/src/store/sqlite_store/transactions.rs index 3dbe6aeaa..e459a1455 100644 --- a/src/store/sqlite_store/transactions.rs +++ b/src/store/sqlite_store/transactions.rs @@ -1,27 +1,23 @@ -use crate::{ - client::transactions::{TransactionRecord, TransactionResult, TransactionStatus}, - errors::StoreError, - store::{InputNoteRecord, OutputNoteRecord, TransactionFilter}, -}; -use crypto::{ - utils::{collections::BTreeMap, Deserializable, Serializable}, - Felt, -}; - -use tracing::info; - use super::{ accounts::{insert_account_asset_vault, insert_account_record, insert_account_storage}, notes::{insert_input_note_tx, insert_output_note_tx}, SqliteStore, }; -use objects::{ +use crate::{ + client::transactions::{TransactionRecord, TransactionResult, TransactionStatus}, + errors::StoreError, + store::{InputNoteRecord, OutputNoteRecord, TransactionFilter}, +}; +use miden_objects::{ accounts::{Account, AccountId}, assembly::{AstSerdeOptions, ProgramAst}, + crypto::utils::{Deserializable, Serializable}, transaction::{OutputNote, OutputNotes, TransactionId, TransactionScript}, - Digest, + utils::collections::BTreeMap, + Digest, Felt, }; use rusqlite::{params, Transaction}; +use tracing::info; pub(crate) const INSERT_TRANSACTION_QUERY: &str = "INSERT INTO transactions (id, account_id, init_account_state, final_account_state, \ diff --git a/src/tests.rs b/src/tests.rs index f5bacd590..329a5c2fc 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -5,29 +5,21 @@ use crate::{ accounts::{AccountStorageMode, AccountTemplate}, transactions::TransactionTemplate, }, - mock::mock_fungible_faucet_account, - store::{ - mock_executor_data_store::MockDataStore, sqlite_store::tests::create_test_client, AuthInfo, - InputNoteRecord, NoteFilter, + mock::{ + get_account_with_default_account_code, mock_full_chain_mmr_and_notes, + mock_fungible_faucet_account, mock_notes, MockDataStore, ACCOUNT_ID_REGULAR, }, + store::{sqlite_store::tests::create_test_client, AuthInfo, InputNoteRecord, NoteFilter}, }; -use crypto::{dsa::rpo_falcon512::KeyPair, Felt, FieldElement, Word}; use miden_lib::transaction::TransactionKernel; -use mock::{ - constants::{generate_account_seed, AccountSeedType}, - mock::{ - account::{self, MockAccountType}, - notes::AssetPreservationStatus, - transaction::mock_inputs, - }, -}; -use objects::{ +use miden_objects::{ accounts::{AccountId, AccountStub}, assembly::{AstSerdeOptions, ModuleAst}, assets::{FungibleAsset, TokenSymbol}, - transaction::InputNotes, + crypto::dsa::rpo_falcon512::KeyPair, + Felt, FieldElement, Word, }; #[tokio::test] @@ -36,25 +28,22 @@ async fn test_input_notes_round_trip() { let mut client = create_test_client(); // generate test data - let transaction_inputs = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); - let _recorded_notes = transaction_inputs.input_notes(); + + let assembler = TransactionKernel::assembler(); + let (consumed_notes, _created_notes) = mock_notes(&assembler); + let (_, consumed_notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); // insert notes into database - for note in transaction_inputs.input_notes().iter().cloned() { + for note in consumed_notes.iter().cloned() { client.import_input_note(note.into()).unwrap(); } // retrieve notes from database let retrieved_notes = client.get_input_notes(NoteFilter::Committed).unwrap(); + assert_eq!(retrieved_notes.len(), consumed_notes.len()); - let recorded_notes: Vec = transaction_inputs - .input_notes() - .iter() - .map(|n| n.clone().into()) - .collect(); + let recorded_notes: Vec = + consumed_notes.iter().map(|n| n.clone().into()).collect(); // compare notes for (recorded_note, retrieved_note) in recorded_notes.iter().zip(retrieved_notes) { assert_eq!(recorded_note.id(), retrieved_note.id()); @@ -66,26 +55,21 @@ async fn test_get_input_note() { // generate test client with a random store name let mut client = create_test_client(); - // generate test data - let transaction_inputs = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); - let _recorded_notes: InputNotes = transaction_inputs.input_notes().clone(); - let recorded_notes: InputNotes = transaction_inputs.input_notes().clone(); + let assembler = TransactionKernel::assembler(); + let (_consumed_notes, created_notes) = mock_notes(&assembler); - // insert note into database + // insert Note into database client - .import_input_note(recorded_notes.get_note(0).clone().into()) + .import_input_note(created_notes.first().unwrap().clone().into()) .unwrap(); // retrieve note from database let retrieved_note = client - .get_input_note(recorded_notes.get_note(0).note().id()) + .get_input_note(created_notes.first().unwrap().clone().id()) .unwrap(); - let recorded_note: InputNoteRecord = recorded_notes.get_note(0).clone().into(); - assert_eq!(recorded_note.id(), retrieved_note.id()) + let recorded_note: InputNoteRecord = created_notes.first().unwrap().clone().into(); + assert_eq!(recorded_note.id(), retrieved_note.id()); } #[tokio::test] @@ -159,11 +143,11 @@ async fn insert_same_account_twice_fails() { // generate test client with a random store name let mut client = create_test_client(); - let assembler = TransactionKernel::assembler(); - - let (account_id, account_seed) = - generate_account_seed(AccountSeedType::RegularAccountUpdatableCodeOnChain); - let account = account::mock_account(Some(account_id.into()), Felt::ZERO, None, &assembler); + let account = get_account_with_default_account_code( + AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(), + Word::default(), + None, + ); let key_pair: KeyPair = KeyPair::new() .map_err(|err| format!("Error generating KeyPair: {}", err)) @@ -172,33 +156,33 @@ async fn insert_same_account_twice_fails() { assert!(client .insert_account( &account, - Some(account_seed), + Some(Word::default()), &AuthInfo::RpoFalcon512(key_pair) ) .is_ok()); assert!(client .insert_account( &account, - Some(account_seed), + Some(Word::default()), &AuthInfo::RpoFalcon512(key_pair) ) .is_err()); } #[tokio::test] -async fn test_acc_code() { +async fn test_account_code() { // generate test client with a random store name let mut client = create_test_client(); - let assembler = TransactionKernel::assembler(); let key_pair: KeyPair = KeyPair::new() .map_err(|err| format!("Error generating KeyPair: {}", err)) .unwrap(); - let (account_id, account_seed) = - generate_account_seed(AccountSeedType::RegularAccountUpdatableCodeOnChain); - - let account = account::mock_account(Some(account_id.into()), Felt::ZERO, None, &assembler); + let account = get_account_with_default_account_code( + AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(), + Word::default(), + None, + ); let mut account_module = account.code().module().clone(); @@ -215,11 +199,11 @@ async fn test_acc_code() { client .insert_account( &account, - Some(account_seed), + Some(Word::default()), &AuthInfo::RpoFalcon512(key_pair), ) .unwrap(); - let (retrieved_acc, _) = client.get_account(account_id).unwrap(); + let (retrieved_acc, _) = client.get_account(account.id()).unwrap(); let mut account_module = account.code().module().clone(); account_module.clear_locations(); @@ -235,11 +219,11 @@ async fn test_get_account_by_id() { // generate test client with a random store name let mut client = create_test_client(); - let assembler = TransactionKernel::assembler(); - - let (account_id, account_seed) = - generate_account_seed(AccountSeedType::RegularAccountUpdatableCodeOnChain); - let account = account::mock_account(Some(account_id.into()), Felt::ZERO, None, &assembler); + let account = get_account_with_default_account_code( + AccountId::try_from(ACCOUNT_ID_REGULAR).unwrap(), + Word::default(), + None, + ); let key_pair: KeyPair = KeyPair::new() .map_err(|err| format!("Error generating KeyPair: {}", err)) @@ -248,7 +232,7 @@ async fn test_get_account_by_id() { client .insert_account( &account, - Some(account_seed), + Some(Word::default()), &AuthInfo::RpoFalcon512(key_pair), ) .unwrap(); @@ -450,12 +434,7 @@ async fn test_mint_transaction() { &AuthInfo::RpoFalcon512(key_pair), ) .unwrap(); - - client.set_data_store(MockDataStore::with_existing( - faucet.clone(), - None, - Some(vec![]), - )); + client.set_data_store(MockDataStore::new(faucet.clone(), None, Some(vec![]))); // Test submitting a mint transaction let transaction_template = TransactionTemplate::MintFungibleAsset { @@ -470,38 +449,3 @@ async fn test_mint_transaction() { .nonce() .is_some()); } - -#[tokio::test] -#[ignore = "currently fails because executor's DuplicateProcName error, see https://github.com/0xPolygonMiden/miden-base/issues/443"] -async fn test_consume_all_transaction() { - // generate test client with a random store name - let mut client = create_test_client(); - - // generate test data - let transaction_inputs = mock_inputs( - MockAccountType::StandardExisting, - AssetPreservationStatus::Preserved, - ); - - let recorded_notes: InputNotes = transaction_inputs.input_notes().clone(); - let mut notes_for_data_store = vec![]; - for note in recorded_notes.iter() { - notes_for_data_store.push(note.note().clone()); - } - - let (account, seed) = client - .new_account(AccountTemplate::BasicWallet { - mutable_code: false, - storage_mode: AccountStorageMode::Local, - }) - .unwrap(); - - let data_store = - MockDataStore::with_existing(account.clone(), Some(seed), Some(notes_for_data_store)); - - client.set_data_store(data_store); - - let note_list = recorded_notes.iter().map(|x| x.note().id()).collect(); - let transaction_template = TransactionTemplate::ConsumeNotes(account.id(), note_list); - client.new_transaction(transaction_template).unwrap(); -} diff --git a/tests/integration/main.rs b/tests/integration/main.rs index 6ec588cd5..48bdb1fa3 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -10,12 +10,12 @@ use miden_client::store::data_store::SqliteDataStore; use miden_client::store::sqlite_store::SqliteStore; use miden_client::store::{NoteFilter, TransactionFilter}; -use miden_tx::{DataStoreError, TransactionExecutorError}; -use objects::{ +use miden_objects::{ accounts::AccountData, assets::{Asset, FungibleAsset}, utils::serde::Deserializable, }; +use miden_tx::{DataStoreError, TransactionExecutorError}; use std::env::temp_dir; use std::fs;