From f05a3f8314f903ef1a184996dc2e4f2b5b937983 Mon Sep 17 00:00:00 2001 From: Martin Fraga Date: Fri, 15 Mar 2024 17:27:44 -0300 Subject: [PATCH] refactor: split input output note register structs (#199) * split input notes * make enum for notes tables * rename recipients variable and add more NoteTable uses * run fmt * change to_string for to_hex * correct nullable fields and reorder fields * remove unnecesarry note filter * remove unnecessary commit_height field * move related rows into metadata row for notes * remove dbg statements * switch to_string for to_hex * Revert "move related rows into metadata row for notes" This reverts commit bf46c0afbb74a0db44efdc69bccb0e8ee1418ea4. * restore filtering by pending notes * store grouped json fields * add json fields with serialization/deserialization I still have pending polishing the changes (abstracting some deserialization and removing debugs, etc.) and we should also add some kind of validation as well as having the proper NULL / NOT NULL columns (now everything is nullable) * remove dbg calls and set nullable/non-nullable cols accordingly * apply clippy suggestion * add helper functions to work with json values returned as strings * add documentation for handling json with sqlite * fix incorrect function use to fetch output notes * ignore doc tests * add json validity constraints * use structs to represent json columns * remove dbg statements * use propper serialization * use existing NoteMetadata instead of NoteRecordMetadata * also remove NoteRecordInclusionProof and use NoteInclusionProof instead * add implementations for output notes * change input note record struct TODO: fix tests * Fix test failures There were two different kind of errors: * When we queried for less fields than what parse_input_notes_columns expected (this happened on the function to get peding input note nullifiers) * When we serialized the note status. Apparently serde serializes the note as `\"Variant\"` instead of `variant`. * update doc comments * cleanup struct * remove dbg statements * fix serialization for output notes * change from to tryfrom for notestatus * move note record structs to its own module * remove todo comment * remove quotes from note status --- src/cli/input_notes.rs | 96 ++++---- src/cli/mod.rs | 4 +- src/cli/transactions.rs | 2 +- src/client/sync.rs | 4 +- src/store/data_store.rs | 2 +- src/store/mod.rs | 151 ++---------- src/store/note_record/input_note_record.rs | 194 ++++++++++++++++ src/store/note_record/mod.rs | 132 +++++++++++ src/store/note_record/output_note_record.rs | 96 ++++++++ src/store/sqlite_store/mod.rs | 7 +- src/store/sqlite_store/notes.rs | 242 ++++++++++++++++---- src/store/sqlite_store/store.sql | 4 +- src/store/sqlite_store/sync.rs | 12 +- src/store/sqlite_store/transactions.rs | 16 +- src/tests.rs | 4 +- tests/integration/main.rs | 8 +- 16 files changed, 720 insertions(+), 254 deletions(-) create mode 100644 src/store/note_record/input_note_record.rs create mode 100644 src/store/note_record/mod.rs create mode 100644 src/store/note_record/output_note_record.rs diff --git a/src/cli/input_notes.rs b/src/cli/input_notes.rs index c3cddc3e8..40cde5b53 100644 --- a/src/cli/input_notes.rs +++ b/src/cli/input_notes.rs @@ -10,7 +10,10 @@ use miden_client::{ client::rpc::NodeRpcClient, store::{InputNoteRecord, NoteFilter as ClientNoteFilter, Store}, }; -use miden_objects::{notes::NoteId, Digest}; +use miden_objects::{ + notes::{NoteId, NoteInputs, NoteScript}, + Digest, +}; use miden_tx::utils::{Deserializable, Serializable}; use super::{Client, Parser}; @@ -119,7 +122,7 @@ fn list_input_notes( filter: ClientNoteFilter, ) -> Result<(), String> { let notes = client.get_input_notes(filter)?; - print_notes_summary(¬es); + print_notes_summary(¬es)?; Ok(()) } @@ -164,7 +167,7 @@ pub fn import_note( let input_note_record = InputNoteRecord::read_from_bytes(&contents).map_err(|err| err.to_string())?; - let note_id = input_note_record.note().id(); + let note_id = input_note_record.id(); client.import_input_note(input_note_record)?; Ok(note_id) @@ -183,7 +186,7 @@ fn show_input_note( 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)); + print_notes_summary(core::iter::once(&input_note_record))?; let mut table = Table::new(); table @@ -192,14 +195,17 @@ fn show_input_note( // print note script if show_script { + let script = NoteScript::read_from_bytes(input_note_record.details().script()) + .map_err(|err| format!("Failed to parse the note record's program AST: {}", err))?; + table .add_row(vec![ Cell::new("Note Script hash").add_attribute(Attribute::Bold), - Cell::new(input_note_record.note().script().hash()), + Cell::new(script.hash()), ]) .add_row(vec![ Cell::new("Note Script code").add_attribute(Attribute::Bold), - Cell::new(input_note_record.note().script().code()), + Cell::new(script.code()), ]); }; @@ -208,32 +214,29 @@ fn show_input_note( table .add_row(vec![ Cell::new("Note Vault hash").add_attribute(Attribute::Bold), - Cell::new(input_note_record.note().assets().commitment()), + Cell::new(input_note_record.assets().commitment()), ]) .add_row(vec![Cell::new("Note Vault").add_attribute(Attribute::Bold)]); - input_note_record.note().assets().iter().for_each(|asset| { + input_note_record.assets().iter().for_each(|asset| { table.add_row(vec![Cell::new(format!("{:?}", asset))]); }) }; if show_inputs { + let inputs = NoteInputs::read_from_bytes(input_note_record.details().inputs()) + .map_err(|err| format!("Failed to parse the note record's inputs: {}", err))?; + table .add_row(vec![ Cell::new("Note Inputs hash").add_attribute(Attribute::Bold), - Cell::new(input_note_record.note().inputs().commitment()), + Cell::new(inputs.commitment()), ]) .add_row(vec![Cell::new("Note Inputs").add_attribute(Attribute::Bold)]); - input_note_record - .note() - .inputs() - .values() - .iter() - .enumerate() - .for_each(|(idx, input)| { - table - .add_row(vec![Cell::new(idx).add_attribute(Attribute::Bold), Cell::new(input)]); - }); + + inputs.values().iter().enumerate().for_each(|(idx, input)| { + table.add_row(vec![Cell::new(idx).add_attribute(Attribute::Bold), Cell::new(input)]); + }); }; println!("{table}"); @@ -242,7 +245,7 @@ fn show_input_note( // HELPERS // ================================================================================================ -fn print_notes_summary<'a, I>(notes: I) +fn print_notes_summary<'a, I>(notes: I) -> Result<(), String> where I: IntoIterator, { @@ -255,22 +258,31 @@ where "Commit Height", ]); - notes.into_iter().for_each(|input_note_record| { + for input_note_record in notes { let commit_height = input_note_record .inclusion_proof() .map(|proof| proof.origin().block_num.to_string()) .unwrap_or("-".to_string()); + + let script = NoteScript::read_from_bytes(input_note_record.details().script()) + .map_err(|err| format!("Failed to parse the note record's program AST: {}", err))?; + + let inputs = NoteInputs::read_from_bytes(input_note_record.details().inputs()) + .map_err(|err| format!("Failed to parse the note record's inputs: {}", err))?; + table.add_row(vec![ - input_note_record.note().id().inner().to_string(), - input_note_record.note().script().hash().to_string(), - input_note_record.note().assets().commitment().to_string(), - input_note_record.note().inputs().commitment().to_string(), - Digest::new(input_note_record.note().serial_num()).to_string(), + input_note_record.id().inner().to_string(), + script.hash().to_string(), + input_note_record.assets().commitment().to_string(), + inputs.commitment().to_string(), + Digest::new(input_note_record.details().serial_num()).to_string(), commit_height, ]); - }); + } println!("{table}"); + + Ok(()) } // TESTS @@ -319,7 +331,7 @@ mod tests { let (_, commited_notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); let committed_note: InputNoteRecord = commited_notes.first().unwrap().clone().into(); - let pending_note = InputNoteRecord::new(created_notes.first().unwrap().clone(), None); + 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(); @@ -332,18 +344,14 @@ mod tests { let mut filename_path_pending = temp_dir(); filename_path_pending.push("test_import_pending"); - export_note( - &client, - &committed_note.note_id().inner().to_string(), - Some(filename_path.clone()), - ) - .unwrap(); + export_note(&client, &committed_note.id().inner().to_string(), Some(filename_path.clone())) + .unwrap(); assert!(filename_path.exists()); export_note( &client, - &pending_note.note_id().inner().to_string(), + &pending_note.id().inner().to_string(), Some(filename_path_pending.clone()), ) .unwrap(); @@ -368,14 +376,14 @@ mod tests { import_note(&mut client, filename_path).unwrap(); let imported_note_record: InputNoteRecord = - client.get_input_note(committed_note.note().id()).unwrap(); + client.get_input_note(committed_note.id()).unwrap(); - assert_eq!(committed_note.note().id(), imported_note_record.note().id()); + assert_eq!(committed_note.id(), imported_note_record.id()); import_note(&mut client, filename_path_pending).unwrap(); - let imported_pending_note_record = client.get_input_note(pending_note.note().id()).unwrap(); + let imported_pending_note_record = client.get_input_note(pending_note.id()).unwrap(); - assert_eq!(imported_pending_note_record.note().id(), pending_note.note().id()); + assert_eq!(imported_pending_note_record.id(), pending_note.id()); } #[tokio::test] @@ -410,7 +418,7 @@ mod tests { let (_, notes, _, _) = mock_full_chain_mmr_and_notes(consumed_notes); let committed_note: InputNoteRecord = notes.first().unwrap().clone().into(); - let pending_note = InputNoteRecord::new(created_notes.first().unwrap().clone(), None); + 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(); @@ -418,11 +426,11 @@ mod tests { assert!(committed_note.inclusion_proof().is_some()); // Check that we can fetch Both notes - let note = get_note_with_id_prefix(&client, &committed_note.note_id().to_hex()).unwrap(); - assert_eq!(note.note_id(), committed_note.note_id()); + 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.note_id().to_hex()).unwrap(); - assert_eq!(note.note_id(), pending_note.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"; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index c044e6b93..609b1426f 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -151,7 +151,7 @@ pub(crate) fn get_note_with_id_prefix( NoteIdPrefixFetchError::NoMatch(note_id_prefix.to_string()) })? .into_iter() - .filter(|note_record| note_record.note_id().to_hex().starts_with(note_id_prefix)) + .filter(|note_record| note_record.id().to_hex().starts_with(note_id_prefix)) .collect::>(); if input_note_records.is_empty() { @@ -160,7 +160,7 @@ pub(crate) fn get_note_with_id_prefix( if input_note_records.len() > 1 { let input_note_record_ids = input_note_records .iter() - .map(|input_note_record| input_note_record.note_id()) + .map(|input_note_record| input_note_record.id()) .collect::>(); tracing::error!( "Multiple notes found for the prefix {}: {:?}", diff --git a/src/cli/transactions.rs b/src/cli/transactions.rs index e7e59f7ac..5ee21a8bf 100644 --- a/src/cli/transactions.rs +++ b/src/cli/transactions.rs @@ -142,7 +142,7 @@ fn build_transaction_template( .iter() .map(|note_id| { get_note_with_id_prefix(client, note_id) - .map(|note_record| note_record.note_id()) + .map(|note_record| note_record.id()) .map_err(|err| err.to_string()) }) .collect::, _>>()?; diff --git a/src/client/sync.rs b/src/client/sync.rs index 1733db784..eb224c08f 100644 --- a/src/client/sync.rs +++ b/src/client/sync.rs @@ -206,14 +206,14 @@ impl Client { .store .get_input_notes(NoteFilter::Pending)? .iter() - .map(|n| n.note().id()) + .map(|n| n.id()) .collect(); let pending_output_notes: Vec = self .store .get_output_notes(NoteFilter::Pending)? .iter() - .map(|n| n.note().id()) + .map(|n| n.id()) .collect(); let mut pending_notes = [pending_input_notes, pending_output_notes].concat(); diff --git a/src/store/data_store.rs b/src/store/data_store.rs index 92af96ad9..a6bc05ec2 100644 --- a/src/store/data_store.rs +++ b/src/store/data_store.rs @@ -38,7 +38,7 @@ impl DataStore for ClientDataStore { .store .get_input_notes(NoteFilter::Committed)? .iter() - .map(|note_record| note_record.note_id()) + .map(|note_record| note_record.id()) .collect::>(); for note_id in notes { diff --git a/src/store/mod.rs b/src/store/mod.rs index 0e1c94693..50062bec3 100644 --- a/src/store/mod.rs +++ b/src/store/mod.rs @@ -5,22 +5,24 @@ use miden_objects::{ dsa::rpo_falcon512::KeyPair, merkle::{InOrderIndex, MmrPeaks}, }, - notes::{Note, NoteId, NoteInclusionProof, Nullifier}, - transaction::{InputNote, TransactionId}, + notes::{NoteId, NoteInclusionProof, Nullifier}, + transaction::TransactionId, utils::collections::BTreeMap, BlockHeader, Digest, Word, }; use miden_tx::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}; -use serde::{Deserialize, Serialize}; use crate::{ client::transactions::{TransactionRecord, TransactionResult}, - errors::{ClientError, StoreError}, + errors::StoreError, }; pub mod data_store; pub mod sqlite_store; +mod note_record; +pub use note_record::{InputNoteRecord, NoteRecordDetails, NoteStatus, OutputNoteRecord}; + #[cfg(any(test, feature = "mock"))] pub mod mock_executor_data_store; @@ -68,7 +70,7 @@ pub trait Store { fn get_output_notes( &self, filter: NoteFilter, - ) -> Result, StoreError>; + ) -> Result, StoreError>; /// Retrieves an [InputNoteRecord] for the input note corresponding to the specified ID from /// the store. @@ -88,10 +90,10 @@ pub trait Store { let nullifiers = self .get_input_notes(NoteFilter::Committed)? .iter() - .map(|input_note| input_note.note().nullifier()) - .collect(); + .map(|input_note| Ok(Nullifier::from(Digest::try_from(input_note.nullifier())?))) + .collect::, _>>(); - Ok(nullifiers) + nullifiers } /// Inserts the provided input note into the database @@ -301,131 +303,6 @@ impl Deserializable for AuthInfo { } } -// INPUT NOTE RECORD -// ================================================================================================ - -/// Represents a Note of which the [Store] can keep track and retrieve. -/// -/// An [InputNoteRecord] contains all the information of a [Note], in addition of (optionally) -/// the [NoteInclusionProof] that identifies when the note was included in the chain. Once the -/// proof is set, the [InputNoteRecord] can be transformed into an [InputNote] and used as input -/// for transactions. -#[derive(Clone, Debug, PartialEq)] -pub struct InputNoteRecord { - note: Note, - inclusion_proof: Option, -} - -impl InputNoteRecord { - pub fn new( - note: Note, - inclusion_proof: Option, - ) -> InputNoteRecord { - InputNoteRecord { - note, - inclusion_proof, - } - } - pub fn note(&self) -> &Note { - &self.note - } - - pub fn note_id(&self) -> NoteId { - self.note.id() - } - - pub fn inclusion_proof(&self) -> Option<&NoteInclusionProof> { - self.inclusion_proof.as_ref() - } -} - -impl Serializable for InputNoteRecord { - fn write_into( - &self, - target: &mut W, - ) { - self.note().write_into(target); - self.inclusion_proof.write_into(target); - } -} - -impl Deserializable for InputNoteRecord { - fn read_from( - source: &mut R - ) -> std::prelude::v1::Result { - let note = Note::read_from(source)?; - let proof = Option::::read_from(source)?; - Ok(InputNoteRecord::new(note, proof)) - } -} - -impl From for InputNoteRecord { - fn from(note: Note) -> Self { - InputNoteRecord { - note, - inclusion_proof: None, - } - } -} - -impl From for InputNoteRecord { - fn from(recorded_note: InputNote) -> Self { - InputNoteRecord { - note: recorded_note.note().clone(), - inclusion_proof: Some(recorded_note.proof().clone()), - } - } -} - -impl TryInto for InputNoteRecord { - type Error = ClientError; - - fn try_into(self) -> Result { - match self.inclusion_proof() { - Some(proof) => Ok(InputNote::new(self.note().clone(), proof.clone())), - None => Err(ClientError::NoteError(miden_objects::NoteError::invalid_origin_index( - "Input Note Record contains no inclusion proof".to_string(), - ))), - } - } -} - -#[derive(Serialize, Deserialize)] -struct NoteRecordDetails { - nullifier: String, - script: Vec, - inputs: Vec, - serial_num: Word, -} - -impl NoteRecordDetails { - fn new( - nullifier: String, - script: Vec, - inputs: Vec, - serial_num: Word, - ) -> Self { - Self { - nullifier, - script, - inputs, - serial_num, - } - } - - fn script(&self) -> &Vec { - &self.script - } - - fn inputs(&self) -> &Vec { - &self.inputs - } - - fn serial_num(&self) -> &Word { - &self.serial_num - } -} - // CHAIN MMR NODE FILTER // ================================================================================================ @@ -451,14 +328,14 @@ pub enum TransactionFilter { // ================================================================================================ pub enum NoteFilter { - /// Return a list of all [InputNoteRecord]. + /// Return a list of all notes ([InputNoteRecord] or [OutputNoteRecord]). All, - /// Filter by consumed [InputNoteRecord]. notes that have been used as inputs in transactions. + /// Filter by consumed notes ([InputNoteRecord] or [OutputNoteRecord]). notes that have been used as inputs in transactions. Consumed, - /// Return a list of committed [InputNoteRecord]. These represent notes that the blockchain + /// Return a list of committed notes ([InputNoteRecord] or [OutputNoteRecord]). These represent notes that the blockchain /// has included in a block, and for which we are storing anchor data. Committed, - /// Return a list of pending [InputNoteRecord]. These represent notes for which the store + /// Return a list of pending notes ([InputNoteRecord] or [OutputNoteRecord]). These represent notes for which the store /// does not have anchor data. Pending, } diff --git a/src/store/note_record/input_note_record.rs b/src/store/note_record/input_note_record.rs new file mode 100644 index 000000000..029545a4c --- /dev/null +++ b/src/store/note_record/input_note_record.rs @@ -0,0 +1,194 @@ +use miden_objects::{ + notes::{Note, NoteAssets, NoteId, NoteInclusionProof, NoteInputs, NoteMetadata, NoteScript}, + transaction::InputNote, + utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, + Digest, NoteError, +}; + +use super::{NoteRecordDetails, NoteStatus}; +use crate::errors::ClientError; + +// INPUT NOTE RECORD +// ================================================================================================ + +/// Represents a Note of which the [Store] can keep track and retrieve. +/// +/// An [InputNoteRecord] contains all the information of a [Note], in addition of (optionally) +/// the [NoteInclusionProof] that identifies when the note was included in the chain. Once the +/// proof is set, the [InputNoteRecord] can be transformed into an [InputNote] and used as input +/// for transactions. +#[derive(Clone, Debug, PartialEq)] +pub struct InputNoteRecord { + assets: NoteAssets, + details: NoteRecordDetails, + id: NoteId, + inclusion_proof: Option, + metadata: Option, + recipient: Digest, + status: NoteStatus, +} + +impl InputNoteRecord { + pub fn new( + id: NoteId, + recipient: Digest, + assets: NoteAssets, + status: NoteStatus, + metadata: Option, + inclusion_proof: Option, + details: NoteRecordDetails, + ) -> InputNoteRecord { + InputNoteRecord { + id, + recipient, + assets, + status, + metadata, + inclusion_proof, + details, + } + } + + pub fn id(&self) -> NoteId { + self.id + } + + pub fn recipient(&self) -> Digest { + self.recipient + } + + pub fn assets(&self) -> &NoteAssets { + &self.assets + } + + pub fn status(&self) -> NoteStatus { + self.status + } + + pub fn metadata(&self) -> Option<&NoteMetadata> { + self.metadata.as_ref() + } + + pub fn nullifier(&self) -> &str { + &self.details.nullifier + } + + pub fn inclusion_proof(&self) -> Option<&NoteInclusionProof> { + self.inclusion_proof.as_ref() + } + + pub fn details(&self) -> &NoteRecordDetails { + &self.details + } +} + +impl Serializable for InputNoteRecord { + fn write_into( + &self, + target: &mut W, + ) { + self.id().write_into(target); + self.recipient().write_into(target); + self.assets().write_into(target); + self.status().write_into(target); + self.metadata().write_into(target); + self.details().write_into(target); + self.inclusion_proof().write_into(target); + } +} + +impl Deserializable for InputNoteRecord { + fn read_from(source: &mut R) -> Result { + let id = NoteId::read_from(source)?; + let recipient = Digest::read_from(source)?; + let assets = NoteAssets::read_from(source)?; + let status = NoteStatus::read_from(source)?; + let metadata = Option::::read_from(source)?; + let details = NoteRecordDetails::read_from(source)?; + let inclusion_proof = Option::::read_from(source)?; + + Ok(InputNoteRecord { + id, + recipient, + assets, + status, + metadata, + inclusion_proof, + details, + }) + } +} + +impl From for InputNoteRecord { + fn from(note: Note) -> Self { + InputNoteRecord { + id: note.id(), + recipient: note.recipient(), + assets: note.assets().clone(), + status: NoteStatus::Pending, + metadata: Some(*note.metadata()), + inclusion_proof: None, + details: NoteRecordDetails { + nullifier: note.nullifier().to_string(), + script: note.script().to_bytes(), + inputs: note.inputs().to_bytes(), + serial_num: note.serial_num(), + }, + } + } +} + +impl From for InputNoteRecord { + fn from(recorded_note: InputNote) -> Self { + InputNoteRecord { + id: recorded_note.note().id(), + recipient: recorded_note.note().recipient(), + assets: recorded_note.note().assets().clone(), + status: NoteStatus::Pending, + metadata: Some(*recorded_note.note().metadata()), + details: NoteRecordDetails { + nullifier: recorded_note.note().nullifier().to_string(), + script: recorded_note.note().script().to_bytes(), + inputs: recorded_note.note().inputs().to_bytes(), + serial_num: recorded_note.note().serial_num(), + }, + inclusion_proof: Some(recorded_note.proof().clone()), + } + } +} + +impl TryInto for InputNoteRecord { + type Error = ClientError; + + fn try_into(self) -> Result { + match (self.inclusion_proof, self.metadata) { + (Some(proof), Some(metadata)) => { + let script = NoteScript::read_from_bytes(&self.details.script).map_err(|err| { + ClientError::NoteError(NoteError::NoteDeserializationError(err)) + })?; + let inputs = NoteInputs::read_from_bytes(&self.details.inputs).map_err(|err| { + ClientError::NoteError(NoteError::NoteDeserializationError(err)) + })?; + let note = Note::from_parts( + script, + inputs, + self.assets, + self.details.serial_num, + metadata, + ); + Ok(InputNote::new(note, proof.clone())) + }, + + (None, _) => { + Err(ClientError::NoteError(miden_objects::NoteError::invalid_origin_index( + "Input Note Record contains no inclusion proof".to_string(), + ))) + }, + (_, None) => { + Err(ClientError::NoteError(miden_objects::NoteError::invalid_origin_index( + "Input Note Record contains no metadata".to_string(), + ))) + }, + } + } +} diff --git a/src/store/note_record/mod.rs b/src/store/note_record/mod.rs new file mode 100644 index 000000000..b0b9c3932 --- /dev/null +++ b/src/store/note_record/mod.rs @@ -0,0 +1,132 @@ +use miden_objects::{ + utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable}, + Word, +}; +use serde::{Deserialize, Serialize}; + +mod input_note_record; +mod output_note_record; + +pub use input_note_record::InputNoteRecord; +pub use output_note_record::OutputNoteRecord; + +// NOTE STATUS +// ================================================================================================ +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub enum NoteStatus { + Pending, + Committed, + Consumed, +} + +impl From for u8 { + fn from(value: NoteStatus) -> Self { + match value { + NoteStatus::Pending => 0, + NoteStatus::Committed => 1, + NoteStatus::Consumed => 2, + } + } +} + +impl TryFrom for NoteStatus { + type Error = DeserializationError; + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(NoteStatus::Pending), + 1 => Ok(NoteStatus::Committed), + 2 => Ok(NoteStatus::Consumed), + _ => Err(DeserializationError::InvalidValue(value.to_string())), + } + } +} + +impl Serializable for NoteStatus { + fn write_into( + &self, + target: &mut W, + ) { + target.write_bytes(&[(*self).into()]); + } +} + +impl Deserializable for NoteStatus { + fn read_from(source: &mut R) -> Result { + let enum_byte = u8::read_from(source)?; + enum_byte.try_into() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct NoteRecordDetails { + nullifier: String, + script: Vec, + inputs: Vec, + serial_num: Word, +} + +impl NoteRecordDetails { + pub fn new( + nullifier: String, + script: Vec, + inputs: Vec, + serial_num: Word, + ) -> Self { + Self { + nullifier, + script, + inputs, + serial_num, + } + } + + pub fn script(&self) -> &Vec { + &self.script + } + + pub fn inputs(&self) -> &Vec { + &self.inputs + } + + pub fn serial_num(&self) -> Word { + self.serial_num + } +} + +impl Serializable for NoteRecordDetails { + fn write_into( + &self, + target: &mut W, + ) { + let nullifier_bytes = self.nullifier.as_bytes(); + target.write_usize(nullifier_bytes.len()); + target.write_bytes(nullifier_bytes); + + target.write_usize(self.script().len()); + target.write_bytes(self.script()); + + target.write_usize(self.inputs().len()); + target.write_bytes(self.inputs()); + + self.serial_num().write_into(target); + } +} + +impl Deserializable for NoteRecordDetails { + fn read_from(source: &mut R) -> Result { + let nullifier_len = usize::read_from(source)?; + let nullifier_bytes = source.read_vec(nullifier_len)?; + let nullifier = + String::from_utf8(nullifier_bytes).expect("Nullifier String bytes should be readable."); + + let script_len = usize::read_from(source)?; + let script = source.read_vec(script_len)?; + + let inputs_len = usize::read_from(source)?; + let inputs = source.read_vec(inputs_len)?; + + let serial_num = Word::read_from(source)?; + + Ok(NoteRecordDetails::new(nullifier, script, inputs, serial_num)) + } +} diff --git a/src/store/note_record/output_note_record.rs b/src/store/note_record/output_note_record.rs new file mode 100644 index 000000000..5b4bb422b --- /dev/null +++ b/src/store/note_record/output_note_record.rs @@ -0,0 +1,96 @@ +use miden_objects::{ + notes::{Note, NoteAssets, NoteId, NoteInclusionProof, NoteMetadata}, + utils::Serializable, + Digest, +}; + +use super::{NoteRecordDetails, NoteStatus}; + +// OUTPUT NOTE RECORD +// ================================================================================================ + +/// Represents a Note which was the result of executing some transaction of which the [Store] can +/// keep track and retrieve. +/// +/// An [OutputNoteRecord] contains all the information of a [Note] while it allows for not +/// knowing the details (nullifier, script, inputs and serial number), in addition of (optionally) +/// the [NoteInclusionProof] that identifies when the note was included in the chain. +#[derive(Clone, Debug, PartialEq)] +pub struct OutputNoteRecord { + assets: NoteAssets, + details: Option, + id: NoteId, + inclusion_proof: Option, + metadata: NoteMetadata, + recipient: Digest, + status: NoteStatus, +} + +impl OutputNoteRecord { + pub fn new( + id: NoteId, + recipient: Digest, + assets: NoteAssets, + status: NoteStatus, + metadata: NoteMetadata, + inclusion_proof: Option, + details: Option, + ) -> OutputNoteRecord { + OutputNoteRecord { + id, + recipient, + assets, + status, + metadata, + inclusion_proof, + details, + } + } + + pub fn id(&self) -> NoteId { + self.id + } + + pub fn recipient(&self) -> Digest { + self.recipient + } + + pub fn assets(&self) -> &NoteAssets { + &self.assets + } + + pub fn status(&self) -> NoteStatus { + self.status + } + + pub fn metadata(&self) -> &NoteMetadata { + &self.metadata + } + + pub fn inclusion_proof(&self) -> Option<&NoteInclusionProof> { + self.inclusion_proof.as_ref() + } + + pub fn details(&self) -> Option<&NoteRecordDetails> { + self.details.as_ref() + } +} + +impl From for OutputNoteRecord { + fn from(note: Note) -> Self { + OutputNoteRecord { + id: note.id(), + recipient: note.recipient(), + assets: note.assets().clone(), + status: NoteStatus::Pending, + metadata: *note.metadata(), + inclusion_proof: None, + details: Some(NoteRecordDetails { + nullifier: note.nullifier().to_string(), + script: note.script().to_bytes(), + inputs: note.inputs().to_bytes(), + serial_num: note.serial_num(), + }), + } + } +} diff --git a/src/store/sqlite_store/mod.rs b/src/store/sqlite_store/mod.rs index b2a36c8a5..cf8e9583c 100644 --- a/src/store/sqlite_store/mod.rs +++ b/src/store/sqlite_store/mod.rs @@ -8,7 +8,10 @@ use miden_objects::{ }; use rusqlite::Connection; -use super::{AuthInfo, ChainMmrNodeFilter, InputNoteRecord, NoteFilter, Store, TransactionFilter}; +use super::{ + AuthInfo, ChainMmrNodeFilter, InputNoteRecord, NoteFilter, OutputNoteRecord, Store, + TransactionFilter, +}; use crate::{ client::transactions::{TransactionRecord, TransactionResult}, config::StoreConfig, @@ -163,7 +166,7 @@ impl Store for SqliteStore { fn get_output_notes( &self, note_filter: NoteFilter, - ) -> Result, StoreError> { + ) -> Result, StoreError> { self.get_output_notes(note_filter) } diff --git a/src/store/sqlite_store/notes.rs b/src/store/sqlite_store/notes.rs index 613bca6a4..52ebfbe0b 100644 --- a/src/store/sqlite_store/notes.rs +++ b/src/store/sqlite_store/notes.rs @@ -2,19 +2,16 @@ use std::fmt; use clap::error::Result; use miden_objects::{ - notes::{ - Note, NoteAssets, NoteId, NoteInclusionProof, NoteInputs, NoteMetadata, NoteScript, - Nullifier, - }, + crypto::utils::{Deserializable, Serializable}, + notes::{NoteAssets, NoteId, NoteInclusionProof, NoteMetadata, Nullifier}, Digest, }; -use miden_tx::utils::{Deserializable, Serializable}; use rusqlite::{named_params, params, Transaction}; use super::SqliteStore; use crate::{ errors::StoreError, - store::{InputNoteRecord, NoteFilter, NoteRecordDetails}, + store::{InputNoteRecord, NoteFilter, NoteRecordDetails, NoteStatus, OutputNoteRecord}, }; fn insert_note_query(table_name: NoteTable) -> String { @@ -27,9 +24,13 @@ fn insert_note_query(table_name: NoteTable) -> String { // TYPES // ================================================================================================ -type SerializedInputNoteData = (String, Vec, String, String, String, String, Option); +type SerializedInputNoteData = + (String, Vec, String, String, Option, String, Option); +type SerializedOutputNoteData = + (String, Vec, String, String, String, Option, Option); -type SerializedInputNoteParts = (Vec, String, String, Option); +type SerializedInputNoteParts = (Vec, String, String, String, Option, Option); +type SerializedOutputNoteParts = (Vec, Option, String, String, String, Option); // NOTE TABLE // ================================================================================================ @@ -65,6 +66,8 @@ impl NoteFilter { "SELECT assets, details, + recipient, + status, metadata, inclusion_proof from {notes_table}" @@ -72,9 +75,9 @@ impl NoteFilter { match self { NoteFilter::All => base, - NoteFilter::Committed => format!("{base} WHERE status = 'committed'"), - NoteFilter::Consumed => format!("{base} WHERE status = 'consumed'"), - NoteFilter::Pending => format!("{base} WHERE status = 'pending'"), + NoteFilter::Committed => format!("{base} WHERE status = 'Committed'"), + NoteFilter::Consumed => format!("{base} WHERE status = 'Consumed'"), + NoteFilter::Pending => format!("{base} WHERE status = 'Pending'"), } } } @@ -99,13 +102,13 @@ impl SqliteStore { pub(crate) fn get_output_notes( &self, filter: NoteFilter, - ) -> Result, StoreError> { + ) -> Result, StoreError> { self.db .prepare(&filter.to_query(NoteTable::OutputNotes))? - .query_map([], parse_input_note_columns) + .query_map([], parse_output_note_columns) .expect("no binding parameters used in query") - .map(|result| Ok(result?).and_then(parse_input_note)) - .collect::, _>>() + .map(|result| Ok(result?).and_then(parse_output_note)) + .collect::, _>>() } pub(crate) fn get_input_note( @@ -117,6 +120,8 @@ impl SqliteStore { const QUERY: &str = "SELECT assets, details, + recipient, + status, metadata, inclusion_proof from input_notes WHERE note_id = ?"; @@ -142,7 +147,7 @@ impl SqliteStore { /// Returns the nullifiers of all unspent input notes pub fn get_unspent_input_note_nullifiers(&self) -> Result, StoreError> { - const QUERY: &str = "SELECT json_extract(details, '$.nullifier') FROM input_notes WHERE status = 'committed'"; + const QUERY: &str = "SELECT json_extract(details, '$.nullifier') FROM input_notes WHERE status = 'Committed'"; self.db .prepare(QUERY)? @@ -168,7 +173,7 @@ pub(super) fn insert_input_note_tx( note: &InputNoteRecord, ) -> Result<(), StoreError> { let (note_id, assets, recipient, status, metadata, details, inclusion_proof) = - serialize_note(note)?; + serialize_input_note(note)?; tx.execute( &insert_note_query(NoteTable::InputNotes), @@ -189,10 +194,10 @@ pub(super) fn insert_input_note_tx( /// Inserts the provided input note into the database pub fn insert_output_note_tx( tx: &Transaction<'_>, - note: &InputNoteRecord, + note: &OutputNoteRecord, ) -> Result<(), StoreError> { let (note_id, assets, recipient, status, metadata, details, inclusion_proof) = - serialize_note(note)?; + serialize_output_note(note)?; tx.execute( &insert_note_query(NoteTable::OutputNotes), @@ -216,31 +221,34 @@ fn parse_input_note_columns( ) -> Result { let assets: Vec = row.get(0)?; let details: String = row.get(1)?; - let metadata: String = row.get(2)?; - let inclusion_proof: Option = row.get(3)?; + let recipient: String = row.get(2)?; + let status: String = row.get(3)?; + let metadata: Option = row.get(4)?; + let inclusion_proof: Option = row.get(5)?; - Ok((assets, details, metadata, inclusion_proof)) + Ok((assets, details, recipient, status, metadata, inclusion_proof)) } /// Parse a note from the provided parts. fn parse_input_note( serialized_input_note_parts: SerializedInputNoteParts ) -> Result { - let (note_assets, note_details, note_metadata, note_inclusion_proof) = + let (note_assets, note_details, recipient, status, note_metadata, note_inclusion_proof) = serialized_input_note_parts; let note_details: NoteRecordDetails = serde_json::from_str(¬e_details).map_err(StoreError::JsonDataDeserializationError)?; - let note_metadata: NoteMetadata = - serde_json::from_str(¬e_metadata).map_err(StoreError::JsonDataDeserializationError)?; - let script = NoteScript::read_from_bytes(note_details.script())?; - let inputs = NoteInputs::read_from_bytes(note_details.inputs())?; + let note_metadata: Option = if let Some(metadata_as_json_str) = note_metadata { + Some( + serde_json::from_str(&metadata_as_json_str) + .map_err(StoreError::JsonDataDeserializationError)?, + ) + } else { + None + }; - let serial_num = note_details.serial_num(); - let note_metadata = NoteMetadata::new(note_metadata.sender(), note_metadata.tag()); let note_assets = NoteAssets::read_from_bytes(¬e_assets)?; - let note = Note::from_parts(script, inputs, note_assets, *serial_num, note_metadata); let inclusion_proof = match note_inclusion_proof { Some(note_inclusion_proof) => { @@ -253,15 +261,29 @@ fn parse_input_note( _ => None, }; - Ok(InputNoteRecord::new(note, inclusion_proof)) + let recipient = Digest::try_from(recipient)?; + let id = NoteId::new(recipient, note_assets.commitment()); + let status: NoteStatus = serde_json::from_str(&format!("\"{status}\"")) + .map_err(StoreError::JsonDataDeserializationError)?; + + Ok(InputNoteRecord::new( + id, + recipient, + note_assets, + status, + note_metadata, + inclusion_proof, + note_details, + )) } /// Serialize the provided input note into database compatible types. -pub(crate) fn serialize_note( +pub(crate) fn serialize_input_note( note: &InputNoteRecord ) -> Result { - let note_id = note.note_id().inner().to_string(); - let note_assets = note.note().assets().to_bytes(); + 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 @@ -286,24 +308,152 @@ pub(crate) fn serialize_note( )?) .map_err(StoreError::InputSerializationError)?; - (Some(inclusion_proof), String::from("committed")) + let status = serde_json::to_string(&NoteStatus::Committed) + .map_err(StoreError::InputSerializationError)? + .replace('\"', ""); + + (Some(inclusion_proof), status) + }, + None => { + let status = serde_json::to_string(&NoteStatus::Pending) + .map_err(StoreError::InputSerializationError)? + .replace('\"', ""); + + (None, status) }, - None => (None, String::from("pending")), }; - let recipient = note.note().recipient().to_hex(); + let recipient = note.recipient().to_hex(); - let sender_id = note.note().metadata().sender(); - let tag = note.note().metadata().tag(); - let metadata = serde_json::to_string(&NoteMetadata::new(sender_id, tag)) - .map_err(StoreError::InputSerializationError)?; + let metadata = if let Some(metadata) = note.metadata() { + Some(serde_json::to_string(metadata).map_err(StoreError::InputSerializationError)?) + } else { + None + }; - let nullifier = note.note().nullifier().inner().to_string(); - let script = note.note().script().to_bytes(); - let inputs = note.note().inputs().to_bytes(); - let serial_num = note.note().serial_num(); let details = - serde_json::to_string(&NoteRecordDetails::new(nullifier, script, inputs, serial_num)) + serde_json::to_string(¬e.details()).map_err(StoreError::InputSerializationError)?; + + Ok((note_id, note_assets, recipient, status, metadata, details, inclusion_proof)) +} + +/// Parse input note columns from the provided row into native types. +fn parse_output_note_columns( + row: &rusqlite::Row<'_> +) -> Result { + let assets: Vec = row.get(0)?; + let details: Option = row.get(1)?; + let recipient: String = row.get(2)?; + let status: String = row.get(3)?; + let metadata: String = row.get(4)?; + let inclusion_proof: Option = row.get(5)?; + + Ok((assets, details, recipient, status, metadata, inclusion_proof)) +} + +/// Parse a note from the provided parts. +fn parse_output_note( + serialized_output_note_parts: SerializedOutputNoteParts +) -> Result { + let (note_assets, note_details, recipient, status, note_metadata, note_inclusion_proof) = + serialized_output_note_parts; + + let note_details: Option = if let Some(details_as_json_str) = note_details { + Some( + serde_json::from_str(&details_as_json_str) + .map_err(StoreError::JsonDataDeserializationError)?, + ) + } else { + None + }; + + let note_metadata: NoteMetadata = + serde_json::from_str(¬e_metadata).map_err(StoreError::JsonDataDeserializationError)?; + + let note_assets = NoteAssets::read_from_bytes(¬e_assets)?; + + let inclusion_proof = match note_inclusion_proof { + Some(note_inclusion_proof) => { + let note_inclusion_proof: NoteInclusionProof = + serde_json::from_str(¬e_inclusion_proof) + .map_err(StoreError::JsonDataDeserializationError)?; + + Some(note_inclusion_proof) + }, + _ => None, + }; + + let recipient = Digest::try_from(recipient)?; + let id = NoteId::new(recipient, note_assets.commitment()); + let status: NoteStatus = serde_json::from_str(&format!("\"{status}\"")) + .map_err(StoreError::JsonDataDeserializationError)?; + + Ok(OutputNoteRecord::new( + id, + recipient, + note_assets, + status, + note_metadata, + inclusion_proof, + note_details, + )) +} + +/// Serialize the provided output note into database compatible types. +pub(crate) fn serialize_output_note( + note: &OutputNoteRecord +) -> 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 + // they are constructed using note ID instead of authentication hash, so for now we remove the first + // node here. + // + // Note: once removed we can also stop creating a new `NoteInclusionProof` + // + // See: https://github.com/0xPolygonMiden/miden-node/blob/main/store/src/state.rs#L274 + let mut path = proof.note_path().clone(); + if path.len() > 0 { + let _removed = path.remove(0); + } + + let block_num = proof.origin().block_num; + let node_index = proof.origin().node_index.value(); + let sub_hash = proof.sub_hash(); + let note_root = proof.note_root(); + + let inclusion_proof = serde_json::to_string(&NoteInclusionProof::new( + block_num, sub_hash, note_root, node_index, path, + )?) .map_err(StoreError::InputSerializationError)?; + let status = serde_json::to_string(&NoteStatus::Committed) + .map_err(StoreError::InputSerializationError)? + .replace('\"', ""); + + (Some(inclusion_proof), status) + }, + None => { + let status = serde_json::to_string(&NoteStatus::Pending) + .map_err(StoreError::InputSerializationError)? + .replace('\"', ""); + + (None, status) + }, + }; + let recipient = note.recipient().to_hex(); + + let sender_id = note.metadata().sender(); + let tag = note.metadata().tag(); + let metadata = serde_json::to_string(&NoteMetadata::new(sender_id, tag)) + .map_err(StoreError::InputSerializationError)?; + + let details = if let Some(details) = note.details() { + Some(serde_json::to_string(&details).map_err(StoreError::InputSerializationError)?) + } else { + None + }; + Ok((note_id, note_assets, recipient, status, metadata, details, inclusion_proof)) } diff --git a/src/store/sqlite_store/store.sql b/src/store/sqlite_store/store.sql index f9f881331..fd2725e3e 100644 --- a/src/store/sqlite_store/store.sql +++ b/src/store/sqlite_store/store.sql @@ -73,7 +73,7 @@ CREATE TABLE input_notes ( recipient BLOB NOT NULL, -- the note recipient assets BLOB NOT NULL, -- the serialized NoteAssets, including vault hash and list of assets status TEXT CHECK( status IN ( -- the status of the note - either pending, committed or consumed - 'pending', 'committed', 'consumed' + 'Pending', 'Committed', 'Consumed' )), inclusion_proof JSON NULL, -- JSON consisting of the following fields: @@ -112,7 +112,7 @@ CREATE TABLE output_notes ( recipient BLOB NOT NULL, -- the note recipient assets BLOB NOT NULL, -- the serialized NoteAssets, including vault hash and list of assets status TEXT CHECK( status IN ( -- the status of the note - either pending, committed or consumed - 'pending', 'committed', 'consumed' + 'Pending', 'Committed', 'Consumed' )), inclusion_proof JSON NULL, -- JSON consisting of the following fields: diff --git a/src/store/sqlite_store/sync.rs b/src/store/sqlite_store/sync.rs index f874602dd..766b6b762 100644 --- a/src/store/sqlite_store/sync.rs +++ b/src/store/sqlite_store/sync.rs @@ -75,12 +75,12 @@ impl SqliteStore { // Update spent notes for nullifier in nullifiers.iter() { const SPENT_INPUT_NOTE_QUERY: &str = - "UPDATE input_notes SET status = 'consumed' WHERE json_extract(details, '$.nullifier') = ?"; + "UPDATE input_notes SET status = 'Consumed' WHERE json_extract(details, '$.nullifier') = ?"; let nullifier = nullifier.to_hex(); tx.execute(SPENT_INPUT_NOTE_QUERY, params![nullifier])?; const SPENT_OUTPUT_NOTE_QUERY: &str = - "UPDATE output_notes SET status = 'consumed' WHERE json_extract(details, '$.nullifier') = ?"; + "UPDATE output_notes SET status = 'Consumed' WHERE json_extract(details, '$.nullifier') = ?"; tx.execute(SPENT_OUTPUT_NOTE_QUERY, params![nullifier])?; } @@ -109,25 +109,25 @@ impl SqliteStore { .map_err(StoreError::InputSerializationError)?; const COMMITTED_INPUT_NOTES_QUERY: &str = - "UPDATE input_notes SET status = 'committed', inclusion_proof = json(:inclusion_proof) WHERE note_id = :note_id"; + "UPDATE input_notes SET status = 'Committed', inclusion_proof = json(:inclusion_proof) WHERE note_id = :note_id"; tx.execute( COMMITTED_INPUT_NOTES_QUERY, named_params! { ":inclusion_proof": inclusion_proof, - ":note_id": note_id.inner().to_hex() + ":note_id": note_id.inner().to_hex(), }, )?; // Update output notes const COMMITTED_OUTPUT_NOTES_QUERY: &str = - "UPDATE output_notes SET status = 'committed', inclusion_proof = json(:inclusion_proof) WHERE note_id = :note_id"; + "UPDATE output_notes SET status = 'Committed', inclusion_proof = json(:inclusion_proof) WHERE note_id = :note_id"; tx.execute( COMMITTED_OUTPUT_NOTES_QUERY, named_params! { ":inclusion_proof": inclusion_proof, - ":note_id": note_id.inner().to_hex() + ":note_id": note_id.inner().to_hex(), }, )?; } diff --git a/src/store/sqlite_store/transactions.rs b/src/store/sqlite_store/transactions.rs index 8e2581591..6d81f3240 100644 --- a/src/store/sqlite_store/transactions.rs +++ b/src/store/sqlite_store/transactions.rs @@ -1,11 +1,11 @@ use miden_objects::{ accounts::{Account, AccountId}, assembly::{AstSerdeOptions, ProgramAst}, + crypto::utils::{Deserializable, Serializable}, transaction::{OutputNote, OutputNotes, TransactionId, TransactionScript}, utils::collections::BTreeMap, Digest, Felt, }; -use miden_tx::utils::{Deserializable, Serializable}; use rusqlite::{params, Transaction}; use tracing::info; @@ -17,7 +17,7 @@ use super::{ use crate::{ client::transactions::{TransactionRecord, TransactionResult, TransactionStatus}, errors::StoreError, - store::{InputNoteRecord, TransactionFilter}, + store::{InputNoteRecord, OutputNoteRecord, TransactionFilter}, }; pub(crate) const INSERT_TRANSACTION_QUERY: &str = @@ -88,12 +88,18 @@ impl SqliteStore { account.apply_delta(account_delta).map_err(StoreError::AccountError)?; - let created_notes = tx_result + let created_input_notes = tx_result .created_notes() .iter() .map(|note| InputNoteRecord::from(note.clone())) .collect::>(); + let created_output_notes = tx_result + .created_notes() + .iter() + .map(|note| OutputNoteRecord::from(note.clone())) + .collect::>(); + let tx = self.db.transaction()?; // Transaction Data @@ -106,11 +112,11 @@ impl SqliteStore { // TODO: see if we should filter the input notes we store to keep notes we can consume with // existing accounts - for note in &created_notes { + for note in &created_input_notes { insert_input_note_tx(&tx, note)?; } - for note in &created_notes { + for note in &created_output_notes { insert_output_note_tx(&tx, note)?; } diff --git a/src/tests.rs b/src/tests.rs index 2e00ea174..c1cbb50a6 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -45,7 +45,7 @@ async fn test_input_notes_round_trip() { 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.note_id(), retrieved_note.note_id()); + assert_eq!(recorded_note.id(), retrieved_note.id()); } } @@ -65,7 +65,7 @@ async fn test_get_input_note() { client.get_input_note(created_notes.first().unwrap().clone().id()).unwrap(); let recorded_note: InputNoteRecord = created_notes.first().unwrap().clone().into(); - assert_eq!(recorded_note.note_id(), retrieved_note.note_id()); + assert_eq!(recorded_note.id(), retrieved_note.id()); } #[tokio::test] diff --git a/tests/integration/main.rs b/tests/integration/main.rs index ff10612c6..ee0fa6878 100644 --- a/tests/integration/main.rs +++ b/tests/integration/main.rs @@ -163,7 +163,7 @@ async fn main() { assert!(!notes.is_empty()); let tx_template = - TransactionTemplate::ConsumeNotes(first_regular_account_id, vec![notes[0].note_id()]); + TransactionTemplate::ConsumeNotes(first_regular_account_id, vec![notes[0].id()]); println!("Consuming Note..."); execute_tx_and_sync(&mut client, tx_template).await; @@ -195,7 +195,7 @@ async fn main() { // Consume P2ID note let tx_template = - TransactionTemplate::ConsumeNotes(second_regular_account_id, vec![notes[0].note_id()]); + TransactionTemplate::ConsumeNotes(second_regular_account_id, vec![notes[0].id()]); println!("Consuming Note..."); execute_tx_and_sync(&mut client, tx_template).await; @@ -224,7 +224,7 @@ async fn main() { // Check that we can't consume the P2ID note again let tx_template = - TransactionTemplate::ConsumeNotes(second_regular_account_id, vec![notes[0].note_id()]); + TransactionTemplate::ConsumeNotes(second_regular_account_id, vec![notes[0].id()]); println!("Consuming Note..."); // Double-spend error expected to be received since we are consuming the same note @@ -235,7 +235,7 @@ async fn main() { ), )) => {}, Ok(_) => panic!("Double-spend error: Note should not be consumable!"), - _ => panic!("Unexpected error: {}", notes[0].note_id().to_hex()), + _ => panic!("Unexpected error: {}", notes[0].id().to_hex()), } println!("Test ran successfully!");