diff --git a/objects/src/accounts/account_id.rs b/objects/src/accounts/account_id.rs index d8ae4a60f..88b0954a8 100644 --- a/objects/src/accounts/account_id.rs +++ b/objects/src/accounts/account_id.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{ get_account_seed, Account, AccountError, Digest, Felt, FieldElement, Hasher, StarkField, ToString, Vec, Word, @@ -278,6 +281,21 @@ impl Ord for AccountId { } } +// SERIALIZATION +// ================================================================================================ + +impl Serializable for AccountId { + fn write_into(&self, target: &mut W) { + self.0.write_into(target); + } +} + +impl Deserializable for AccountId { + fn read_from(source: &mut R) -> Result { + Ok(AccountId(Felt::read_from(source)?)) + } +} + // HELPER FUNCTIONS // ================================================================================================ fn parse_felt(bytes: &[u8]) -> Result { diff --git a/objects/src/assets/mod.rs b/objects/src/assets/mod.rs index 1f27473a8..ad99b8194 100644 --- a/objects/src/assets/mod.rs +++ b/objects/src/assets/mod.rs @@ -8,10 +8,12 @@ mod fungible; pub use fungible::FungibleAsset; mod nonfungible; +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; pub use nonfungible::{NonFungibleAsset, NonFungibleAssetDetails}; mod token_symbol; pub use token_symbol::TokenSymbol; +use vm_processor::DeserializationError; // ASSET // ================================================================================================ @@ -112,6 +114,12 @@ impl From for Word { } } +impl From<&Asset> for Word { + fn from(value: &Asset) -> Self { + (*value).into() + } +} + impl From for [u8; 32] { fn from(asset: Asset) -> Self { use Asset::*; @@ -122,6 +130,12 @@ impl From for [u8; 32] { } } +impl From<&Asset> for [u8; 32] { + fn from(value: &Asset) -> Self { + (*value).into() + } +} + impl TryFrom for Asset { type Error = AssetError; @@ -148,6 +162,35 @@ impl TryFrom<[u8; 32]> for Asset { } } +impl TryFrom<&[u8; 32]> for Asset { + type Error = AssetError; + + fn try_from(value: &[u8; 32]) -> Result { + (*value).try_into() + } +} + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for Asset { + fn write_into(&self, target: &mut W) { + let data: [u8; 32] = self.into(); + target.write_bytes(&data); + } +} + +impl Deserializable for Asset { + fn read_from(source: &mut R) -> Result { + let data_vec = source.read_vec(32)?; + let data_array: [u8; 32] = data_vec.try_into().expect("Vec must be of size 32"); + + let asset = Asset::try_from(&data_array) + .map_err(|v| DeserializationError::InvalidValue(format!("{v:?}")))?; + Ok(asset) + } +} + // HELPER FUNCTIONS // ================================================================================================ diff --git a/objects/src/notes/envelope.rs b/objects/src/notes/envelope.rs index a17e1a5a1..a17e7a928 100644 --- a/objects/src/notes/envelope.rs +++ b/objects/src/notes/envelope.rs @@ -1,5 +1,7 @@ use super::{Digest, Felt, Note, NoteMetadata, Vec, Word}; +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use vm_core::StarkField; +use vm_processor::DeserializationError; // NOTE ENVELOPE // ================================================================================================ @@ -103,3 +105,25 @@ impl From<&Note> for NoteEnvelope { } } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteEnvelope { + fn write_into(&self, target: &mut W) { + self.note_hash.write_into(target); + self.note_metadata.write_into(target); + } +} + +impl Deserializable for NoteEnvelope { + fn read_from(source: &mut R) -> Result { + let note_hash = Digest::read_from(source)?; + let note_metadata = NoteMetadata::read_from(source)?; + + Ok(Self { + note_hash, + note_metadata, + }) + } +} diff --git a/objects/src/notes/inputs.rs b/objects/src/notes/inputs.rs index e66c2a807..90cf0ee4f 100644 --- a/objects/src/notes/inputs.rs +++ b/objects/src/notes/inputs.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{Digest, Felt, Hasher, NoteError, Vec, ZERO}; /// Holds the inputs which are placed onto the stack before a note's script is executed. @@ -61,6 +64,25 @@ impl NoteInputs { } } +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteInputs { + fn write_into(&self, target: &mut W) { + self.inputs.write_into(target); + } +} + +impl Deserializable for NoteInputs { + fn read_from(source: &mut R) -> Result { + let inputs = <[Felt; 16]>::read_from(source)?; + Self::new(&inputs).map_err(|v| DeserializationError::InvalidValue(format!("{v}"))) + } +} + +// TESTS +// ================================================================================================ + #[test] fn test_input_ordering() { use super::Vec; diff --git a/objects/src/notes/metadata.rs b/objects/src/notes/metadata.rs index 89566891b..7a2e2be74 100644 --- a/objects/src/notes/metadata.rs +++ b/objects/src/notes/metadata.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{AccountId, Felt, NoteError, Word}; /// Represents metadata associated with a note. This includes the sender, tag, and number of assets. @@ -67,3 +70,28 @@ impl TryFrom for NoteMetadata { }) } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteMetadata { + fn write_into(&self, target: &mut W) { + self.sender.write_into(target); + self.tag.write_into(target); + self.num_assets.write_into(target); + } +} + +impl Deserializable for NoteMetadata { + fn read_from(source: &mut R) -> Result { + let sender = AccountId::read_from(source)?; + let tag = Felt::read_from(source)?; + let num_assets = Felt::read_from(source)?; + + Ok(Self { + sender, + tag, + num_assets, + }) + } +} diff --git a/objects/src/notes/mod.rs b/objects/src/notes/mod.rs index 0f033064f..31e927949 100644 --- a/objects/src/notes/mod.rs +++ b/objects/src/notes/mod.rs @@ -16,6 +16,7 @@ mod metadata; pub use metadata::NoteMetadata; mod origin; +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; pub use origin::{NoteInclusionProof, NoteOrigin}; mod script; @@ -26,6 +27,7 @@ pub use stub::NoteStub; mod vault; pub use vault::NoteVault; +use vm_processor::DeserializationError; // CONSTANTS // ================================================================================================ @@ -59,6 +61,7 @@ pub const NOTE_LEAF_DEPTH: u8 = NOTE_TREE_DEPTH + 1; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Note { + #[cfg_attr(feature = "serde", serde(with = "serialization"))] script: NoteScript, inputs: NoteInputs, vault: NoteVault, @@ -230,3 +233,73 @@ impl RecordedNote { self.proof.origin() } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for Note { + fn write_into(&self, target: &mut W) { + self.script.write_into(target); + self.inputs.write_into(target); + self.vault.write_into(target); + self.serial_num.write_into(target); + self.metadata.write_into(target); + } +} + +impl Deserializable for Note { + fn read_from(source: &mut R) -> Result { + let script = NoteScript::read_from(source)?; + let inputs = NoteInputs::read_from(source)?; + let vault = NoteVault::read_from(source)?; + let serial_num = Word::read_from(source)?; + let metadata = NoteMetadata::read_from(source)?; + + Ok(Self { + script, + inputs, + vault, + serial_num, + metadata, + }) + } +} + +impl Serializable for RecordedNote { + fn write_into(&self, target: &mut W) { + self.note.write_into(target); + self.proof.write_into(target); + } +} + +impl Deserializable for RecordedNote { + fn read_from(source: &mut R) -> Result { + let note = Note::read_from(source)?; + let proof = NoteInclusionProof::read_from(source)?; + + Ok(Self { note, proof }) + } +} + +#[cfg(feature = "serde")] +mod serialization { + use super::NoteScript; + use crate::utils::serde::{Deserializable, Serializable}; + + pub fn serialize(code: &NoteScript, serializer: S) -> Result + where + S: serde::Serializer, + { + let bytes = code.to_bytes(); + serializer.serialize_bytes(&bytes) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes: Vec = as serde::Deserialize>::deserialize(deserializer)?; + + NoteScript::read_from_bytes(&bytes).map_err(serde::de::Error::custom) + } +} diff --git a/objects/src/notes/origin.rs b/objects/src/notes/origin.rs index 19a8bb2f3..0615efaeb 100644 --- a/objects/src/notes/origin.rs +++ b/objects/src/notes/origin.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{Digest, Felt, NoteError, ToString, NOTE_TREE_DEPTH}; use crate::crypto::merkle::{MerklePath, NodeIndex}; @@ -73,3 +76,50 @@ impl NoteInclusionProof { &self.note_path } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteOrigin { + fn write_into(&self, target: &mut W) { + self.block_num.write_into(target); + self.node_index.write_into(target); + } +} + +impl Deserializable for NoteOrigin { + fn read_from(source: &mut R) -> Result { + let block_num = Felt::read_from(source)?; + let node_index = NodeIndex::read_from(source)?; + + Ok(Self { + block_num, + node_index, + }) + } +} + +impl Serializable for NoteInclusionProof { + fn write_into(&self, target: &mut W) { + self.origin.write_into(target); + self.sub_hash.write_into(target); + self.note_root.write_into(target); + self.note_path.write_into(target); + } +} + +impl Deserializable for NoteInclusionProof { + fn read_from(source: &mut R) -> Result { + let origin = NoteOrigin::read_from(source)?; + let sub_hash = Digest::read_from(source)?; + let note_root = Digest::read_from(source)?; + let note_path = MerklePath::read_from(source)?; + + Ok(Self { + origin, + sub_hash, + note_root, + note_path, + }) + } +} diff --git a/objects/src/notes/script.rs b/objects/src/notes/script.rs index ace746968..76e56e75b 100644 --- a/objects/src/notes/script.rs +++ b/objects/src/notes/script.rs @@ -1,38 +1,23 @@ use super::{Assembler, AssemblyContext, CodeBlock, Digest, NoteError, ProgramAst}; +use crate::utils::serde::{ByteReader, ByteWriter, Deserializable, Serializable}; +use assembly::ast::AstSerdeOptions; +use vm_processor::DeserializationError; + +// CONSTANTS +// ================================================================================================ + +/// Default serialization options for script code AST. +const CODE_SERDE_OPTIONS: AstSerdeOptions = AstSerdeOptions::new(true); + +// NOTE SCRIPT +// ================================================================================================ #[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct NoteScript { hash: Digest, - #[cfg_attr(feature = "serde", serde(with = "serialization"))] code: ProgramAst, } -#[cfg(feature = "serde")] -mod serialization { - use assembly::ast::AstSerdeOptions; - - pub fn serialize(module: &super::ProgramAst, serializer: S) -> Result - where - S: serde::Serializer, - { - let bytes = module.to_bytes(AstSerdeOptions { - serialize_imports: true, - }); - - serializer.serialize_bytes(&bytes) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let bytes: Vec = as serde::Deserialize>::deserialize(deserializer)?; - - super::ProgramAst::from_bytes(&bytes).map_err(serde::de::Error::custom) - } -} - impl NoteScript { pub fn new(code: ProgramAst, assembler: &Assembler) -> Result<(Self, CodeBlock), NoteError> { let code_block = assembler @@ -55,3 +40,22 @@ impl NoteScript { &self.code } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteScript { + fn write_into(&self, target: &mut W) { + self.hash.write_into(target); + self.code.write_into(target, CODE_SERDE_OPTIONS); + } +} + +impl Deserializable for NoteScript { + fn read_from(source: &mut R) -> Result { + let hash = Digest::read_from(source)?; + let code = ProgramAst::read_from(source)?; + + Ok(Self { hash, code }) + } +} diff --git a/objects/src/notes/vault.rs b/objects/src/notes/vault.rs index d92c1f953..a92d5651b 100644 --- a/objects/src/notes/vault.rs +++ b/objects/src/notes/vault.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{Asset, Digest, Felt, Hasher, NoteError, Vec, Word, WORD_SIZE, ZERO}; // NOTE VAULT @@ -129,3 +132,23 @@ impl TryFrom<&[Word]> for NoteVault { Self::new(&assets) } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for NoteVault { + fn write_into(&self, target: &mut W) { + debug_assert!(self.assets.len() <= NoteVault::MAX_NUM_ASSETS); + target.write_u8((self.assets.len() - 1) as u8); + self.assets.write_into(target); + } +} + +impl Deserializable for NoteVault { + fn read_from(source: &mut R) -> Result { + let count = source.read_u8()? + 1; + let assets = Asset::read_batch_from(source, count.into())?; + + Self::new(&assets).map_err(|e| DeserializationError::InvalidValue(format!("{e:?}"))) + } +} diff --git a/objects/src/transaction/consumed_notes.rs b/objects/src/transaction/consumed_notes.rs index 7d6ce0975..55fb035cf 100644 --- a/objects/src/transaction/consumed_notes.rs +++ b/objects/src/transaction/consumed_notes.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{ utils::generate_consumed_notes_commitment, AdviceInputsBuilder, Digest, Felt, RecordedNote, ToAdviceInputs, Vec, Word, @@ -19,6 +22,7 @@ pub struct ConsumedNotes { impl ConsumedNotes { /// Creates a new [ConsumedNotes] object. pub fn new(notes: Vec) -> Self { + assert!(notes.len() <= u16::MAX.into()); let commitment = generate_consumed_notes_commitment(¬es); Self { notes, commitment } } @@ -199,3 +203,43 @@ impl From<&RecordedNote> for ConsumedNoteInfo { Self::new(recorded_note.note().nullifier(), recorded_note.note().script().hash()) } } + +// SERIALIZATION +// ================================================================================================ +// + +impl Serializable for ConsumedNotes { + fn write_into(&self, target: &mut W) { + assert!(self.notes.len() <= u16::MAX.into()); + target.write_u16(self.notes.len() as u16); + self.notes.write_into(target); + } +} + +impl Deserializable for ConsumedNotes { + fn read_from(source: &mut R) -> Result { + let count = source.read_u16()?; + let notes = RecordedNote::read_batch_from(source, count.into())?; + + Ok(Self::new(notes)) + } +} + +impl Serializable for ConsumedNoteInfo { + fn write_into(&self, target: &mut W) { + target.write_bytes(&self.nullifier.to_bytes()); + target.write_bytes(&self.script_root.to_bytes()); + } +} + +impl Deserializable for ConsumedNoteInfo { + fn read_from(source: &mut R) -> Result { + let nullifier = Digest::read_from(source)?; + let script_root = Digest::read_from(source)?; + + Ok(Self { + nullifier, + script_root, + }) + } +} diff --git a/objects/src/transaction/proven_tx.rs b/objects/src/transaction/proven_tx.rs index 91e5e26c3..690dfd136 100644 --- a/objects/src/transaction/proven_tx.rs +++ b/objects/src/transaction/proven_tx.rs @@ -1,6 +1,8 @@ use super::{AccountId, ConsumedNoteInfo, Digest, NoteEnvelope, Vec}; +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; use miden_verifier::ExecutionProof; +use vm_processor::DeserializationError; /// Resultant object of executing and proving a transaction. It contains the minimal /// amount of data needed to verify that the transaction was executed correctly. @@ -91,3 +93,51 @@ impl ProvenTransaction { self.block_ref } } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for ProvenTransaction { + fn write_into(&self, target: &mut W) { + self.account_id.write_into(target); + self.initial_account_hash.write_into(target); + self.final_account_hash.write_into(target); + target.write_u64(self.consumed_notes.len() as u64); + self.consumed_notes.write_into(target); + target.write_u64(self.created_notes.len() as u64); + self.created_notes.write_into(target); + self.tx_script_root.write_into(target); + self.block_ref.write_into(target); + self.proof.write_into(target); + } +} + +impl Deserializable for ProvenTransaction { + fn read_from(source: &mut R) -> Result { + let account_id = AccountId::read_from(source)?; + let initial_account_hash = Digest::read_from(source)?; + let final_account_hash = Digest::read_from(source)?; + + let count = source.read_u64()?; + let consumed_notes = ConsumedNoteInfo::read_batch_from(source, count as usize)?; + + let count = source.read_u64()?; + let created_notes = NoteEnvelope::read_batch_from(source, count as usize)?; + + let tx_script_root = Deserializable::read_from(source)?; + + let block_ref = Digest::read_from(source)?; + let proof = ExecutionProof::read_from(source)?; + + Ok(Self { + account_id, + initial_account_hash, + final_account_hash, + consumed_notes, + created_notes, + tx_script_root, + block_ref, + proof, + }) + } +}