From 01aa3e2429f3de13d264f92e5fdf08c1b31d26fb Mon Sep 17 00:00:00 2001 From: tomyrd Date: Thu, 25 Apr 2024 10:28:44 -0300 Subject: [PATCH 01/17] Add verify flag to import note command --- src/cli/input_notes.rs | 30 +++++++++++++++++++----------- src/cli/mod.rs | 2 +- src/client/notes.rs | 25 ++++++++++++++++++++++++- src/errors.rs | 4 ++++ src/mock.rs | 4 ++-- src/tests.rs | 7 +++++-- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index d879b2003..ea1ea7e43 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -77,11 +77,15 @@ pub enum InputNotes { /// Path to the file that contains the input note data #[clap()] filename: PathBuf, + + /// Verify note's existance in the chain + #[clap(short, long, default_value = "true")] + verify: bool, }, } impl InputNotes { - pub fn execute( + pub async fn execute( &self, mut client: Client, ) -> Result<(), String> { @@ -103,8 +107,8 @@ impl InputNotes { export_note(&client, id, filename.clone())?; println!("Succesfully exported note {}", id); }, - InputNotes::Import { filename } => { - let note_id = import_note(&mut client, filename.clone())?; + InputNotes::Import { filename, verify } => { + let note_id = import_note(&mut client, filename.clone(), *verify).await?; println!("Succesfully imported note {}", note_id.inner()); }, } @@ -150,9 +154,10 @@ pub fn export_note( // IMPORT INPUT NOTE // ================================================================================================ -pub fn import_note( +pub async fn import_note( client: &mut Client, filename: PathBuf, + verify: bool, ) -> Result { let mut contents = vec![]; let mut _file = File::open(filename) @@ -165,7 +170,10 @@ pub fn import_note( InputNoteRecord::read_from_bytes(&contents).map_err(|err| err.to_string())?; let note_id = input_note_record.id(); - client.import_input_note(input_note_record)?; + client + .import_input_note(input_note_record, verify) + .await + .map_err(|err| err.to_string())?; Ok(note_id) } @@ -346,8 +354,8 @@ mod tests { let committed_note: InputNoteRecord = committed_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(); + client.import_input_note(committed_note.clone(), false).await.unwrap(); + client.import_input_note(pending_note.clone(), false).await.unwrap(); assert!(pending_note.inclusion_proof().is_none()); assert!(committed_note.inclusion_proof().is_some()); @@ -389,13 +397,13 @@ mod tests { true, ); - import_note(&mut client, filename_path).unwrap(); + import_note(&mut client, filename_path, false).await.unwrap(); let imported_note_record: InputNoteRecord = client.get_input_note(committed_note.id()).unwrap(); assert_eq!(committed_note.id(), imported_note_record.id()); - import_note(&mut client, filename_path_pending).unwrap(); + import_note(&mut client, filename_path_pending, false).await.unwrap(); let imported_pending_note_record = client.get_input_note(pending_note.id()).unwrap(); assert_eq!(imported_pending_note_record.id(), pending_note.id()); @@ -438,8 +446,8 @@ mod tests { 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(); + client.import_input_note(committed_note.clone(), false).await.unwrap(); + client.import_input_note(pending_note.clone(), false).await.unwrap(); assert!(pending_note.inclusion_proof().is_none()); assert!(committed_note.inclusion_proof().is_some()); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index d305a6467..887281aa2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -107,7 +107,7 @@ impl Cli { Command::Account(account) => account.execute(client), Command::Init => Ok(()), Command::Info => info::print_client_info(&client), - Command::InputNotes(notes) => notes.execute(client), + Command::InputNotes(notes) => notes.execute(client).await, Command::Sync => sync::sync_state(client).await, Command::Tags(tags) => tags.execute(client).await, Command::Transaction(transaction) => transaction.execute(client).await, diff --git a/src/client/notes.rs b/src/client/notes.rs index fef07427a..09a287543 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -29,7 +29,30 @@ impl Client { // -------------------------------------------------------------------------------------------- /// Imports a new input note into the client's store. - pub fn import_input_note(&mut self, note: InputNoteRecord) -> Result<(), ClientError> { + pub async fn import_input_note( + &mut self, + note: InputNoteRecord, + verify: bool, + ) -> Result<(), ClientError> { + if verify { + let mut chain_notes = self.rpc_api.get_notes_by_id(&[note.id()]).await?; + + if chain_notes.is_empty() { + return Err(ClientError::ExistanceVerificationError(note.id())); + } + + let note_details = + chain_notes.pop().expect("chain_notes should have at least one element"); + + let inclusion_details = match note_details { + super::rpc::NoteDetails::OffChain(_, _, inclusion) => inclusion, + super::rpc::NoteDetails::Public(_, inclusion) => inclusion, + }; + + if self.get_sync_height()? > inclusion_details.block_num { + //Set inclusion proof + } + } self.store.insert_input_note(¬e).map_err(|err| err.into()) } diff --git a/src/errors.rs b/src/errors.rs index 7d8ffc050..bf9fcc166 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -28,6 +28,7 @@ pub enum ClientError { StoreError(StoreError), TransactionExecutorError(TransactionExecutorError), TransactionProvingError(TransactionProverError), + ExistanceVerificationError(NoteId), } impl fmt::Display for ClientError { @@ -63,6 +64,9 @@ impl fmt::Display for ClientError { ClientError::TransactionProvingError(err) => { write!(f, "transaction prover error: {err}") }, + ClientError::ExistanceVerificationError(note_id) => { + write!(f, "The note with ID {note_id} doesn't exist in the chain") + }, } } } diff --git a/src/mock.rs b/src/mock.rs index d4af4fa3a..9b70d2c37 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -411,12 +411,12 @@ pub async fn insert_mock_data(client: &mut MockClient) -> Vec { // insert notes into database for note in consumed_notes.clone() { - client.import_input_note(note.into()).unwrap(); + client.import_input_note(note.into(), false).await.unwrap(); } // insert notes into database for note in created_notes.clone() { - client.import_input_note(note.into()).unwrap(); + client.import_input_note(note.into(), false).await.unwrap(); } // insert account diff --git a/src/tests.rs b/src/tests.rs index a56f19473..7d6c8f034 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -35,7 +35,7 @@ async fn test_input_notes_round_trip() { // insert notes into database for note in consumed_notes.iter().cloned() { - client.import_input_note(note.into()).unwrap(); + client.import_input_note(note.into(), false).await.unwrap(); } // retrieve notes from database @@ -59,7 +59,10 @@ async fn test_get_input_note() { let (_consumed_notes, created_notes) = mock_notes(&assembler); // insert Note into database - client.import_input_note(created_notes.first().unwrap().clone().into()).unwrap(); + client + .import_input_note(created_notes.first().unwrap().clone().into(), false) + .await + .unwrap(); // retrieve note from database let retrieved_note = From d8261e1cd9ecc545a053ad2f0a16836303a47f3c Mon Sep 17 00:00:00 2001 From: tomyrd Date: Thu, 25 Apr 2024 12:06:43 -0300 Subject: [PATCH 02/17] Add inclusion proof to note if possible --- src/client/notes.rs | 59 +++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/client/notes.rs b/src/client/notes.rs index 09a287543..eb8446e46 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -1,7 +1,7 @@ use miden_objects::{ assembly::ProgramAst, crypto::rand::FeltRng, - notes::{NoteId, NoteScript}, + notes::{NoteId, NoteInclusionProof, NoteScript}, }; use miden_tx::ScriptTarget; @@ -31,28 +31,55 @@ impl Client { /// Imports a new input note into the client's store. pub async fn import_input_note( &mut self, - note: InputNoteRecord, + mut note: InputNoteRecord, verify: bool, ) -> Result<(), ClientError> { - if verify { - let mut chain_notes = self.rpc_api.get_notes_by_id(&[note.id()]).await?; + if !verify { + return self.store.insert_input_note(¬e).map_err(|err| err.into()); + } + + // Verify that note exists in chain + let mut chain_notes = self.rpc_api.get_notes_by_id(&[note.id()]).await?; + + if chain_notes.is_empty() { + return Err(ClientError::ExistanceVerificationError(note.id())); + } + + let note_details = chain_notes.pop().expect("chain_notes should have at least one element"); - if chain_notes.is_empty() { - return Err(ClientError::ExistanceVerificationError(note.id())); - } + let inclusion_details = match note_details { + super::rpc::NoteDetails::OffChain(_, _, inclusion) => inclusion, + super::rpc::NoteDetails::Public(_, inclusion) => inclusion, + }; - let note_details = - chain_notes.pop().expect("chain_notes should have at least one element"); + if note.inclusion_proof().is_none() + && self.get_sync_height()? >= inclusion_details.block_num + { + // Add the inclusion proof to the imported note + let block_header = self + .rpc_api + .get_block_header_by_number(Some(inclusion_details.block_num)) + .await?; - let inclusion_details = match note_details { - super::rpc::NoteDetails::OffChain(_, _, inclusion) => inclusion, - super::rpc::NoteDetails::Public(_, inclusion) => inclusion, - }; + let inclusion_proof = NoteInclusionProof::new( + inclusion_details.block_num, + block_header.sub_hash(), + block_header.note_root(), + inclusion_details.note_index.into(), + inclusion_details.merkle_path, + )?; - if self.get_sync_height()? > inclusion_details.block_num { - //Set inclusion proof - } + note = InputNoteRecord::new( + note.id(), + note.recipient(), + note.assets().clone(), + note.status(), + note.metadata().copied(), + Some(inclusion_proof), + note.details().clone(), + ); } + self.store.insert_input_note(¬e).map_err(|err| err.into()) } From b89a8b704a1a1461ff21f1c3ca83ca8388dc05b5 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Thu, 25 Apr 2024 13:12:38 -0300 Subject: [PATCH 03/17] Include uncommited notes tags to sync --- src/client/sync.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/client/sync.rs b/src/client/sync.rs index 7b4169832..98457552d 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -159,7 +159,15 @@ impl Client { let stored_note_tags: Vec = self.store.get_note_tags()?; - let note_tags = [account_note_tags, stored_note_tags].concat(); + let uncommited_note_tags: Vec = self + .store + .get_input_notes(NoteFilter::Pending)? + .iter() + .filter(|note| note.metadata().is_some()) + .map(|note| note.metadata().expect("Notes should have metadata after filter").tag()) + .collect(); + + let note_tags = [account_note_tags, stored_note_tags, uncommited_note_tags].concat(); // To receive information about added nullifiers, we reduce them to the higher 16 bits // Note that besides filtering by nullifier prefixes, the node also filters by block number From 7514da0488733df5eb69bae9e8ebdfff8e5ce27f Mon Sep 17 00:00:00 2001 From: tomyrd Date: Thu, 25 Apr 2024 13:59:34 -0300 Subject: [PATCH 04/17] Add tests for --- src/cli/input_notes.rs | 1 + tests/integration/main.rs | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index ea1ea7e43..d9652e1e2 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -355,6 +355,7 @@ mod tests { let pending_note = InputNoteRecord::from(created_notes.first().unwrap().clone()); client.import_input_note(committed_note.clone(), false).await.unwrap(); + assert!(client.import_input_note(pending_note.clone(), true).await.is_err()); client.import_input_note(pending_note.clone(), false).await.unwrap(); assert!(pending_note.inclusion_proof().is_none()); assert!(committed_note.inclusion_proof().is_some()); diff --git a/tests/integration/main.rs b/tests/integration/main.rs index 134f6761a..da2eea1f6 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -1020,3 +1020,57 @@ async fn test_onchain_notes_sync_with_tag() { assert_eq!(received_note.note(), ¬e); assert!(client_3.get_input_notes(NoteFilter::All).unwrap().is_empty()); } + +#[tokio::test] +async fn test_import_pending_notes() { + let mut client_1 = create_test_client(); + let (first_basic_account, second_basic_account, faucet_account) = + setup(&mut client_1, AccountStorageMode::Local).await; + let mut client_2 = create_test_client(); + wait_for_node(&mut client_2).await; + client_1.sync_state().await.unwrap(); + client_2.sync_state().await.unwrap(); + + let tx_template = TransactionTemplate::MintFungibleAsset( + FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap().into(), + first_basic_account.id(), + NoteType::OffChain, + ); + + let tx_request = client_1.build_transaction_request(tx_template).unwrap(); + let note = tx_request.expected_output_notes()[0].clone(); + + // If the verification is requested before execution then the import should fail + assert!(client_2.import_input_note(note.clone().into(), true).await.is_err()); + execute_tx_and_sync(&mut client_1, tx_request).await; + client_2.sync_state().await.unwrap(); + + client_2.import_input_note(note.clone().into(), true).await.unwrap(); + let input_note = client_2.get_input_note(note.id()).unwrap(); + + // If imported after execution then the inclusion proof should be Some + assert!(input_note.inclusion_proof().is_some()); + + let tx_template = TransactionTemplate::MintFungibleAsset( + FungibleAsset::new(faucet_account.id(), MINT_AMOUNT).unwrap().into(), + first_basic_account.id(), + NoteType::OffChain, + ); + + let tx_request = client_1.build_transaction_request(tx_template).unwrap(); + let note = tx_request.expected_output_notes()[0].clone(); + + // Import an uncommited note without verification + client_2.import_input_note(note.clone().into(), false).await.unwrap(); + let input_note = client_2.get_input_note(note.id()).unwrap(); + + // If imported before execution then the inclusion proof should be None + assert!(input_note.inclusion_proof().is_none()); + + execute_tx_and_sync(&mut client_1, tx_request).await; + client_2.sync_state().await.unwrap(); + + // After sync, the imported note should have inclusion proof even if it's not relevant for its accounts. + let input_note = client_2.get_input_note(note.id()).unwrap(); + assert!(input_note.inclusion_proof().is_some()); +} From a40e7e1b32f0db682507a40e5aeca33a04d48447 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Thu, 25 Apr 2024 14:16:30 -0300 Subject: [PATCH 05/17] Add change to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 690308ca9..e41d8a49d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* Added an option to verify note existance in the chain before importing. * Added nextest to be used as test runner * Added config file to run integration tests against a remote node * Added `CONTRIBUTING.MD` file. From 0b13bda2da1be825fb1a076370aa4afd20f6be06 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 14:21:03 -0300 Subject: [PATCH 06/17] Fix typo in error name --- CHANGELOG.md | 2 +- src/cli/input_notes.rs | 2 +- src/client/notes.rs | 2 +- src/errors.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e41d8a49d..3acc700c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* Added an option to verify note existance in the chain before importing. +* Added an option to verify note existence in the chain before importing. * Added nextest to be used as test runner * Added config file to run integration tests against a remote node * Added `CONTRIBUTING.MD` file. diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index d9652e1e2..7429e0e94 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -78,7 +78,7 @@ pub enum InputNotes { #[clap()] filename: PathBuf, - /// Verify note's existance in the chain + /// Verify note's existence in the chain #[clap(short, long, default_value = "true")] verify: bool, }, diff --git a/src/client/notes.rs b/src/client/notes.rs index eb8446e46..d20c85f0a 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -42,7 +42,7 @@ impl Client { let mut chain_notes = self.rpc_api.get_notes_by_id(&[note.id()]).await?; if chain_notes.is_empty() { - return Err(ClientError::ExistanceVerificationError(note.id())); + return Err(ClientError::ExistenceVerificationError(note.id())); } let note_details = chain_notes.pop().expect("chain_notes should have at least one element"); diff --git a/src/errors.rs b/src/errors.rs index bf9fcc166..c0d4f16e3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -28,7 +28,7 @@ pub enum ClientError { StoreError(StoreError), TransactionExecutorError(TransactionExecutorError), TransactionProvingError(TransactionProverError), - ExistanceVerificationError(NoteId), + ExistenceVerificationError(NoteId), } impl fmt::Display for ClientError { @@ -64,7 +64,7 @@ impl fmt::Display for ClientError { ClientError::TransactionProvingError(err) => { write!(f, "transaction prover error: {err}") }, - ClientError::ExistanceVerificationError(note_id) => { + ClientError::ExistenceVerificationError(note_id) => { write!(f, "The note with ID {note_id} doesn't exist in the chain") }, } From 7c3c239827e280d99b2981bbfff88914e1dee634 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 14:46:50 -0300 Subject: [PATCH 07/17] Change flag to be `no_verify` instead of `verify` --- docs/cli-reference.md | 2 ++ src/cli/input_notes.rs | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 50de955b6..686ef8738 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -69,6 +69,8 @@ You can call: miden-client input-notes show 0x70b7ec ``` +The `import` subcommand verifies that the note that is about to be imported exists in the chain. The user can add an optional flag `--no-verify` that skips this verification. + ### `sync` Sync the client with the latest state of the Miden network. diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index 7429e0e94..0fe73faf7 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -78,9 +78,9 @@ pub enum InputNotes { #[clap()] filename: PathBuf, - /// Verify note's existence in the chain - #[clap(short, long, default_value = "true")] - verify: bool, + /// Skip verification of note's existence in the chain + #[clap(short, long, default_value = "false")] + no_verify: bool, }, } @@ -107,8 +107,8 @@ impl InputNotes { export_note(&client, id, filename.clone())?; println!("Succesfully exported note {}", id); }, - InputNotes::Import { filename, verify } => { - let note_id = import_note(&mut client, filename.clone(), *verify).await?; + InputNotes::Import { filename, no_verify } => { + let note_id = import_note(&mut client, filename.clone(), !(*no_verify)).await?; println!("Succesfully imported note {}", note_id.inner()); }, } From e5d81935fc24d2f7c92dad8f98b9df49108fd64b Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 15:05:07 -0300 Subject: [PATCH 08/17] Improve doc comment for `import_input_note` --- src/client/notes.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client/notes.rs b/src/client/notes.rs index d20c85f0a..d4580ed05 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -28,7 +28,12 @@ impl Client { // INPUT NOTE CREATION // -------------------------------------------------------------------------------------------- - /// Imports a new input note into the client's store. + /// Imports a new input note into the client's store. The `verify` parameter dictates weather or + /// not the method verifies the existence of the note in the chain. + /// + /// If the imported note is verified to be on chain and it doesn't contain an inclusion proof + /// the method tries to build one if possible. + /// If the verification fails then a [ClientError::ExistenceVerificationError] is raised. pub async fn import_input_note( &mut self, mut note: InputNoteRecord, From 1aca550e9d362c6607075444f1ddfadcd1aeb4dc Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 17:12:15 -0300 Subject: [PATCH 09/17] Remove note tag duplicates before request --- src/client/sync.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/client/sync.rs b/src/client/sync.rs index 98457552d..cdbed4863 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeSet, HashMap}; use crypto::merkle::{InOrderIndex, MmrDelta, MmrPeaks, PartialMmr}; use miden_objects::{ @@ -167,7 +167,15 @@ impl Client { .map(|note| note.metadata().expect("Notes should have metadata after filter").tag()) .collect(); - let note_tags = [account_note_tags, stored_note_tags, uncommited_note_tags].concat(); + //TODO: Use BTreeSet to remove duplicates more efficiently once `Ord` is implemented for `NoteTag` + let note_tags: Vec = [account_note_tags, stored_note_tags, uncommited_note_tags] + .concat() + .into_iter() + .map(|tag| (tag.to_string(), tag)) + .collect::>() + .values() + .cloned() + .collect(); // To receive information about added nullifiers, we reduce them to the higher 16 bits // Note that besides filtering by nullifier prefixes, the node also filters by block number From 210b8610b76cb8749edf7db68febe8ae7403324d Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 17:14:43 -0300 Subject: [PATCH 10/17] Combine `filter` and `map` for uncommited note tags --- src/client/sync.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/sync.rs b/src/client/sync.rs index cdbed4863..0a64143fc 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -163,8 +163,7 @@ impl Client { .store .get_input_notes(NoteFilter::Pending)? .iter() - .filter(|note| note.metadata().is_some()) - .map(|note| note.metadata().expect("Notes should have metadata after filter").tag()) + .filter_map(|note| note.metadata().map(|metadata| metadata.tag())) .collect(); //TODO: Use BTreeSet to remove duplicates more efficiently once `Ord` is implemented for `NoteTag` From 220c80d6788303374e2f76b968f11dc88feaf5d9 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 17:18:15 -0300 Subject: [PATCH 11/17] Add explanation for inclusion proof check --- src/client/notes.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client/notes.rs b/src/client/notes.rs index d4580ed05..ee411b7b9 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -57,6 +57,9 @@ impl Client { super::rpc::NoteDetails::Public(_, inclusion) => inclusion, }; + // Check to see if it's possible to create an inclusion proof if the note doesn't have one. + // Only do this if the note exists in the chain and the client is synced to a height equal or + // greater than the note's creation block. if note.inclusion_proof().is_none() && self.get_sync_height()? >= inclusion_details.block_num { From 8a24a67a3369576aaa42d8bd4f93fd9e4cbd5d6c Mon Sep 17 00:00:00 2001 From: tomyrd Date: Mon, 29 Apr 2024 18:00:20 -0300 Subject: [PATCH 12/17] Consume note in integration test to check proof validity --- tests/integration/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/main.rs b/tests/integration/main.rs index da2eea1f6..c1baf1add 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -1024,7 +1024,7 @@ async fn test_onchain_notes_sync_with_tag() { #[tokio::test] async fn test_import_pending_notes() { let mut client_1 = create_test_client(); - let (first_basic_account, second_basic_account, faucet_account) = + let (first_basic_account, _second_basic_account, faucet_account) = setup(&mut client_1, AccountStorageMode::Local).await; let mut client_2 = create_test_client(); wait_for_node(&mut client_2).await; @@ -1073,4 +1073,7 @@ async fn test_import_pending_notes() { // After sync, the imported note should have inclusion proof even if it's not relevant for its accounts. let input_note = client_2.get_input_note(note.id()).unwrap(); assert!(input_note.inclusion_proof().is_some()); + + // If inclusion proof is invalid this should panic + consume_notes(&mut client_1, first_basic_account.id(), &[input_note.try_into().unwrap()]).await; } From 590ad6698083afc5117813564f972ae6bf999b36 Mon Sep 17 00:00:00 2001 From: Tomas Rodriguez Dala <43424983+tomyrd@users.noreply.github.com> Date: Fri, 3 May 2024 11:14:37 -0300 Subject: [PATCH 13/17] Fix typo in documentation Co-authored-by: igamigo --- src/client/notes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/notes.rs b/src/client/notes.rs index 3fb43e2b7..737da8b44 100644 --- a/src/client/notes.rs +++ b/src/client/notes.rs @@ -97,7 +97,7 @@ impl Client { // INPUT NOTE CREATION // -------------------------------------------------------------------------------------------- - /// Imports a new input note into the client's store. The `verify` parameter dictates weather or + /// Imports a new input note into the client's store. The `verify` parameter dictates whether or /// not the method verifies the existence of the note in the chain. /// /// If the imported note is verified to be on chain and it doesn't contain an inclusion proof From eed7383ae934eca8afbd04b683c86bfd97c48387 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Fri, 3 May 2024 11:23:29 -0300 Subject: [PATCH 14/17] Use BTreeSet from alloc --- src/client/sync.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/sync.rs b/src/client/sync.rs index 000b85f88..4ec564aee 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -1,4 +1,5 @@ -use std::collections::{BTreeSet, HashMap}; +use alloc::collections::BTreeSet; +use std::collections::HashMap; use crypto::merkle::{InOrderIndex, MmrDelta, MmrPeaks, PartialMmr}; use miden_objects::{ From 9c5b4f86aa9b3764d45449adef7a4185d52bc185 Mon Sep 17 00:00:00 2001 From: tomyrd Date: Fri, 3 May 2024 14:52:51 -0300 Subject: [PATCH 15/17] Remove git artifacts --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index febf58b9e..64510b5cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,7 @@ # Changelog -<<<<<<< HEAD * Added an option to verify note existence in the chain before importing. -======= * Add new store note filter to fetch multiple notes by their id in a single query. ->>>>>>> next * [BREAKING] `Client::new()` now does not need a `data_store_store` parameter, and `SqliteStore`'s implements interior mutability. * [BREAKING] The store's `get_input_note` was replaced by `get_input_notes` and a `NoteFilter::Unique` was added. * Refactored `get_account` to create the account from a single query. From f306b8b24d14f23c2ac615f3d2133a8d82a2ae33 Mon Sep 17 00:00:00 2001 From: Tomas Rodriguez Dala <43424983+tomyrd@users.noreply.github.com> Date: Fri, 3 May 2024 16:05:40 -0300 Subject: [PATCH 16/17] Fix cli-reference doc Co-authored-by: Martin Fraga --- docs/cli-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 50a3ec7d7..b62f6ab7f 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -83,7 +83,7 @@ You can call: miden-client input-notes show 0x70b7ec ``` -The `import` subcommand verifies that the note that is about to be imported exists in the chain. The user can add an optional flag `--no-verify` that skips this verification. +The `import` subcommand verifies that the note that is about to be imported exists on chain. The user can add an optional flag `--no-verify` that skips this verification. ### `sync` From ac1008d371d414f444a72d412a465e6c388fc166 Mon Sep 17 00:00:00 2001 From: Tomas Rodriguez Dala <43424983+tomyrd@users.noreply.github.com> Date: Fri, 3 May 2024 16:06:28 -0300 Subject: [PATCH 17/17] Fix comment in testing Co-authored-by: Martin Fraga --- tests/integration/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/main.rs b/tests/integration/main.rs index d0ec24195..e24080f13 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -424,7 +424,7 @@ async fn test_import_pending_notes() { client_2.import_input_note(note.clone().into(), true).await.unwrap(); let input_note = client_2.get_input_note(note.id()).unwrap(); - // If imported after execution then the inclusion proof should be Some + // If imported after execution and syncing then the inclusion proof should be Some assert!(input_note.inclusion_proof().is_some()); let tx_template = TransactionTemplate::MintFungibleAsset(