From c38f083fcf8f5bc7d6babac305da1748f611e381 Mon Sep 17 00:00:00 2001 From: "Augusto F. Hack" Date: Mon, 4 Dec 2023 14:59:03 +0100 Subject: [PATCH] serde: implementation for proven transaction --- Cargo.toml | 20 +++++---- objects/src/accounts/account_id.rs | 18 ++++++++ objects/src/assets/mod.rs | 43 +++++++++++++++++++ objects/src/notes/envelope.rs | 24 +++++++++++ objects/src/notes/inputs.rs | 22 ++++++++++ objects/src/notes/metadata.rs | 28 +++++++++++++ objects/src/notes/mod.rs | 49 ++++++++++++++++++++++ objects/src/notes/origin.rs | 50 +++++++++++++++++++++++ objects/src/notes/script.rs | 22 ++++++++++ objects/src/notes/vault.rs | 23 +++++++++++ objects/src/transaction/consumed_notes.rs | 44 ++++++++++++++++++++ objects/src/transaction/proven_tx.rs | 50 +++++++++++++++++++++++ 12 files changed, 386 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2612243b..5a85fd986 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,16 @@ codegen-units = 1 lto = true [workspace.dependencies] -assembly = { package = "miden-assembly", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -miden-prover = { package = "miden-prover", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -miden-stdlib = { package = "miden-stdlib", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -miden-test-utils = { package = "miden-test-utils", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -miden-verifier = { package = "miden-verifier", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -vm-core = { package = "miden-core", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } -vm-processor = { package = "miden-processor", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "next", default-features = false } +assembly = { package = "miden-assembly", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +miden-prover = { package = "miden-prover", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +miden-stdlib = { package = "miden-stdlib", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +miden-test-utils = { package = "miden-test-utils", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +miden-verifier = { package = "miden-verifier", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +vm-core = { package = "miden-core", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } +vm-processor = { package = "miden-processor", git = "https://github.com/0xPolygonMiden/miden-vm.git", branch = "hacka-codeblock-programast-serialize-deserialize", default-features = false } + +[patch.crates-io] +winter-utils = { package = "winter-utils", git = "https://github.com/hackaugusto/winterfell.git", branch = "hacka-default-deserializers" } + +[patch."https://github.com/0xPolygonMiden/crypto"] +miden-crypto = { package = "miden-crypto", git = "https://github.com/hackaugusto/crypto.git", branch = "hacka-serde-merklepath" } 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..0c65eb31e 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 // ================================================================================================ @@ -230,3 +232,50 @@ 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 }) + } +} 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..d1d2daf13 100644 --- a/objects/src/notes/script.rs +++ b/objects/src/notes/script.rs @@ -1,3 +1,6 @@ +use miden_crypto::utils::{ByteReader, ByteWriter, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{Assembler, AssemblyContext, CodeBlock, Digest, NoteError, ProgramAst}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -55,3 +58,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); + } +} + +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, + }) + } +}