From 61d1eb0ee10b4f573b95413ee3cdc9ecbe4cfd22 Mon Sep 17 00:00:00 2001 From: Santiago Pittella <87827390+SantiagoPittella@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:33:32 -0300 Subject: [PATCH] feat: implement serialization for `TransactionWitness` (#888) --- CHANGELOG.md | 1 + objects/src/transaction/chain_mmr.rs | 45 +++++++++++++++++++++++++++ objects/src/transaction/inputs.rs | 22 +++++++++++++ objects/src/transaction/tx_args.rs | 37 +++++++++++++++++++++- objects/src/transaction/tx_witness.rs | 24 ++++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd593c6d3..268d8c22d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.6.0 (TBD) +- Implemented serialization for `TransactionWitness`, `ChainMmr`, `TransactionInputs` and `TransactionArgs` (#888). - [BREAKING] Renamed the `TransactionProver` struct to `LocalTransactionProver` and added the `TransactionProver` trait (#865). - Implemented `Display`, `TryFrom<&str>` and `FromStr` for `AccountStorageMode` (#861). - Implemented offset based storage access (#843). diff --git a/objects/src/transaction/chain_mmr.rs b/objects/src/transaction/chain_mmr.rs index 048e53072..d024ef4b5 100644 --- a/objects/src/transaction/chain_mmr.rs +++ b/objects/src/transaction/chain_mmr.rs @@ -1,5 +1,7 @@ use alloc::{collections::BTreeMap, vec::Vec}; +use vm_core::utils::{Deserializable, Serializable}; + use crate::{ crypto::merkle::{InnerNodeInfo, MmrPeaks, PartialMmr}, BlockHeader, ChainMmrError, @@ -113,11 +115,37 @@ impl ChainMmr { } } +impl Serializable for ChainMmr { + fn write_into(&self, target: &mut W) { + self.mmr.write_into(target); + self.blocks.len().write_into(target); + for block in self.blocks.values() { + block.write_into(target); + } + } +} + +impl Deserializable for ChainMmr { + fn read_from( + source: &mut R, + ) -> Result { + let mmr = PartialMmr::read_from(source)?; + let block_count = usize::read_from(source)?; + let mut blocks = BTreeMap::new(); + for _ in 0..block_count { + let block = BlockHeader::read_from(source)?; + blocks.insert(block.block_num(), block); + } + Ok(Self { mmr, blocks }) + } +} // TESTS // ================================================================================================ #[cfg(test)] mod tests { + use vm_core::utils::{Deserializable, Serializable}; + use super::ChainMmr; use crate::{ alloc::vec::Vec, @@ -170,6 +198,23 @@ mod tests { ); } + #[test] + fn tst_chain_mmr_serialization() { + // create chain MMR with 3 blocks - i.e., 2 peaks + let mut mmr = Mmr::default(); + for i in 0..3 { + let block_header = int_to_block_header(i); + mmr.add(block_header.hash()); + } + let partial_mmr: PartialMmr = mmr.peaks(mmr.forest()).unwrap().into(); + let chain_mmr = ChainMmr::new(partial_mmr, Vec::new()).unwrap(); + + let bytes = chain_mmr.to_bytes(); + let deserialized = ChainMmr::read_from_bytes(&bytes).unwrap(); + + assert_eq!(chain_mmr, deserialized); + } + fn int_to_block_header(block_num: u32) -> BlockHeader { BlockHeader::new( 0, diff --git a/objects/src/transaction/inputs.rs b/objects/src/transaction/inputs.rs index e94002cb5..c62108f18 100644 --- a/objects/src/transaction/inputs.rs +++ b/objects/src/transaction/inputs.rs @@ -134,6 +134,28 @@ impl TransactionInputs { } } +impl Serializable for TransactionInputs { + fn write_into(&self, target: &mut W) { + self.account.write_into(target); + self.account_seed.write_into(target); + self.block_header.write_into(target); + self.block_chain.write_into(target); + self.input_notes.write_into(target); + } +} + +impl Deserializable for TransactionInputs { + fn read_from(source: &mut R) -> Result { + let account = Account::read_from(source)?; + let account_seed = source.read()?; + let block_header = BlockHeader::read_from(source)?; + let block_chain = ChainMmr::read_from(source)?; + let input_notes = InputNotes::read_from(source)?; + Self::new(account, account_seed, block_header, block_chain, input_notes) + .map_err(|err| DeserializationError::InvalidValue(format!("{}", err))) + } +} + // TO INPUT NOTE COMMITMENT // ================================================================================================ diff --git a/objects/src/transaction/tx_args.rs b/objects/src/transaction/tx_args.rs index 9470cde9f..3e5182a5a 100644 --- a/objects/src/transaction/tx_args.rs +++ b/objects/src/transaction/tx_args.rs @@ -27,7 +27,7 @@ use crate::{ /// different from note inputs, as the user executing the transaction can specify arbitrary note /// args. /// - Advice inputs: Provides data needed by the runtime, like the details of public output notes. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct TransactionArgs { tx_script: Option, note_args: BTreeMap, @@ -145,6 +145,24 @@ impl TransactionArgs { } } +impl Serializable for TransactionArgs { + fn write_into(&self, target: &mut W) { + self.tx_script.write_into(target); + self.note_args.write_into(target); + self.advice_inputs.write_into(target); + } +} + +impl Deserializable for TransactionArgs { + fn read_from(source: &mut R) -> Result { + let tx_script = Option::::read_from(source)?; + let note_args = BTreeMap::::read_from(source)?; + let advice_inputs = AdviceInputs::read_from(source)?; + + Ok(Self { tx_script, note_args, advice_inputs }) + } +} + // TRANSACTION SCRIPT // ================================================================================================ @@ -245,3 +263,20 @@ impl Deserializable for TransactionScript { Ok(Self::from_parts(Arc::new(mast), entrypoint, inputs)) } } + +#[cfg(test)] +mod tests { + use vm_core::utils::{Deserializable, Serializable}; + use vm_processor::AdviceMap; + + use crate::transaction::TransactionArgs; + + #[test] + fn test_tx_args_serialization() { + let args = TransactionArgs::new(None, None, AdviceMap::default()); + let bytes: std::vec::Vec = args.to_bytes(); + let decoded = TransactionArgs::read_from_bytes(&bytes).unwrap(); + + assert_eq!(args, decoded); + } +} diff --git a/objects/src/transaction/tx_witness.rs b/objects/src/transaction/tx_witness.rs index 50ba29d75..a38a921d3 100644 --- a/objects/src/transaction/tx_witness.rs +++ b/objects/src/transaction/tx_witness.rs @@ -1,3 +1,6 @@ +use vm_core::utils::{ByteReader, Deserializable, Serializable}; +use vm_processor::DeserializationError; + use super::{AdviceInputs, TransactionArgs, TransactionInputs}; // TRANSACTION WITNESS @@ -20,8 +23,29 @@ use super::{AdviceInputs, TransactionArgs, TransactionInputs}; /// TODO: currently, the advice witness contains redundant and irrelevant data (e.g., tx inputs /// and tx outputs). we should optimize it to contain only the minimum data required for /// executing/proving the transaction. +#[derive(Clone, Debug, PartialEq, Eq)] pub struct TransactionWitness { pub tx_inputs: TransactionInputs, pub tx_args: TransactionArgs, pub advice_witness: AdviceInputs, } + +// SERIALIZATION +// ================================================================================================ + +impl Serializable for TransactionWitness { + fn write_into(&self, target: &mut W) { + self.tx_inputs.write_into(target); + self.tx_args.write_into(target); + self.advice_witness.write_into(target); + } +} + +impl Deserializable for TransactionWitness { + fn read_from(source: &mut R) -> Result { + let tx_inputs = TransactionInputs::read_from(source)?; + let tx_args = TransactionArgs::read_from(source)?; + let advice_witness = AdviceInputs::read_from(source)?; + Ok(Self { tx_inputs, tx_args, advice_witness }) + } +}