From bfde33d78d4ef89b840e48e6d76c5dd4db345650 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 15 Oct 2025 18:00:35 -0300 Subject: [PATCH] add add canonical serialization for program deployment txs --- .../src/{test_utils.rs => test_utils/mod.rs} | 0 common/src/transaction.rs | 7 ++ nssa/src/encoding/mod.rs | 1 + .../program_deployment_transaction.rs | 70 +++++++++++++++++++ nssa/src/lib.rs | 1 + .../src/program_deployment_transaction/mod.rs | 1 + .../transaction.rs | 2 +- sequencer_core/src/lib.rs | 7 ++ 8 files changed, 88 insertions(+), 1 deletion(-) rename common/src/{test_utils.rs => test_utils/mod.rs} (100%) create mode 100644 nssa/src/encoding/program_deployment_transaction.rs diff --git a/common/src/test_utils.rs b/common/src/test_utils/mod.rs similarity index 100% rename from common/src/test_utils.rs rename to common/src/test_utils/mod.rs diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 3a2bda1..add67b0 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -15,6 +15,7 @@ use sha2::digest::typenum::{UInt, UTerm}; pub enum NSSATransaction { Public(nssa::PublicTransaction), PrivacyPreserving(nssa::PrivacyPreservingTransaction), + ProgramDeployment(nssa::ProgramDeploymentTransaction), } impl From for NSSATransaction { @@ -41,6 +42,7 @@ pub type Tag = u8; pub enum TxKind { Public, PrivacyPreserving, + ProgramDeployment, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] @@ -62,6 +64,10 @@ impl From for EncodedTransaction { tx_kind: TxKind::PrivacyPreserving, encoded_transaction_data: tx.to_bytes(), }, + NSSATransaction::ProgramDeployment(tx) => Self { + tx_kind: TxKind::ProgramDeployment, + encoded_transaction_data: todo!(), + }, } } } @@ -77,6 +83,7 @@ impl TryFrom<&EncodedTransaction> for NSSATransaction { nssa::PrivacyPreservingTransaction::from_bytes(&value.encoded_transaction_data) .map(|tx| tx.into()) } + TxKind::ProgramDeployment => todo!(), } } } diff --git a/nssa/src/encoding/mod.rs b/nssa/src/encoding/mod.rs index 5ff45e2..383fb9a 100644 --- a/nssa/src/encoding/mod.rs +++ b/nssa/src/encoding/mod.rs @@ -1,2 +1,3 @@ pub mod privacy_preserving_transaction; pub mod public_transaction; +pub mod program_deployment_transaction; diff --git a/nssa/src/encoding/program_deployment_transaction.rs b/nssa/src/encoding/program_deployment_transaction.rs new file mode 100644 index 0000000..1a31e59 --- /dev/null +++ b/nssa/src/encoding/program_deployment_transaction.rs @@ -0,0 +1,70 @@ +// TODO: Consider switching to deriving Borsh + +use std::io::{Cursor, Read}; + +use nssa_core::program::ProgramId; + +use crate::{ + Address, ProgramDeploymentTransaction, PublicKey, PublicTransaction, Signature, + error::NssaError, program_deployment_transaction::Message, +}; + +const MESSAGE_ENCODING_PREFIX_LEN: usize = 22; +const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"\x02/NSSA/v0.1/TxMessage/"; + +impl Message { + /// Serializes a `Message` into bytes in the following layout: + /// PREFIX || bytecode_len (4 bytes LE) || + /// Integers are encoded in little-endian byte order, and fields appear in the above order. + pub(crate) fn to_bytes(&self) -> Vec { + let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec(); + let bytecode_len = self.bytecode.len() as u32; + bytes.extend(&bytecode_len.to_le_bytes()); + bytes.extend(&self.bytecode); + bytes + } + + pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let prefix = { + let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN]; + cursor.read_exact(&mut this)?; + this + }; + if &prefix != MESSAGE_ENCODING_PREFIX { + return Err(NssaError::TransactionDeserializationError( + "Invalid public message prefix".to_string(), + )); + } + let bytecode_len = u32_from_cursor(cursor)?; + let mut bytecode = Vec::with_capacity(bytecode_len as usize); + let num_bytes = cursor.read(&mut bytecode)?; + if num_bytes != bytecode_len as usize { + return Err(NssaError::TransactionDeserializationError( + "Invalid number of bytes".to_string(), + )); + } + Ok(Self { bytecode }) + } +} + +impl ProgramDeploymentTransaction { + pub fn to_bytes(&self) -> Vec { + self.message.to_bytes() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut cursor = Cursor::new(bytes); + Self::from_cursor(&mut cursor) + } + + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let message = Message::from_cursor(cursor)?; + Ok(Self::new(message)) + } +} + +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf)?; + Ok(u32::from_le_bytes(word_buf)) +} diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index b394cad..d357daf 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -14,6 +14,7 @@ pub use privacy_preserving_transaction::{ PrivacyPreservingTransaction, circuit::execute_and_prove, }; pub use public_transaction::PublicTransaction; +pub use program_deployment_transaction::ProgramDeploymentTransaction; pub use signature::PrivateKey; pub use signature::PublicKey; pub use signature::Signature; diff --git a/nssa/src/program_deployment_transaction/mod.rs b/nssa/src/program_deployment_transaction/mod.rs index 0bf8c7f..42c6cd8 100644 --- a/nssa/src/program_deployment_transaction/mod.rs +++ b/nssa/src/program_deployment_transaction/mod.rs @@ -2,3 +2,4 @@ mod message; mod transaction; pub use transaction::ProgramDeploymentTransaction; +pub use message::Message; diff --git a/nssa/src/program_deployment_transaction/transaction.rs b/nssa/src/program_deployment_transaction/transaction.rs index dfd348b..87724c5 100644 --- a/nssa/src/program_deployment_transaction/transaction.rs +++ b/nssa/src/program_deployment_transaction/transaction.rs @@ -4,7 +4,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProgramDeploymentTransaction { - message: Message, + pub(crate) message: Message, } impl ProgramDeploymentTransaction { diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 7da9fab..172d41e 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -98,6 +98,7 @@ impl SequencerCore { Err(TransactionMalformationErrorKind::InvalidSignature) } } + NSSATransaction::ProgramDeployment(program_deployment_transaction) => todo!(), } } @@ -142,6 +143,12 @@ impl SequencerCore { .transition_from_privacy_preserving_transaction(tx) .inspect_err(|err| warn!("Error at transition {err:#?}"))?; } + NSSATransaction::ProgramDeployment(tx) => { + self.store + .state + .transition_from_program_deployment_transaction(tx) + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + } } Ok(tx)