From ef73336aa573cacd98dc04058e0a504da724d9ec Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 18 Nov 2025 17:52:46 +0200 Subject: [PATCH] fix: borsh derivation on publci transactions --- nssa/core/Cargo.toml | 1 + nssa/core/src/address.rs | 3 +- nssa/program_methods/guest/Cargo.lock | 1 + nssa/src/encoding/public_transaction.rs | 144 +-------------------- nssa/src/public_transaction/message.rs | 3 +- nssa/src/public_transaction/transaction.rs | 3 +- nssa/src/public_transaction/witness_set.rs | 4 +- nssa/src/signature/mod.rs | 3 +- nssa/src/signature/public_key.rs | 3 +- nssa/test_program_methods/guest/Cargo.lock | 1 + 10 files changed, 20 insertions(+), 146 deletions(-) diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 5712eaf..0e16a3f 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -12,6 +12,7 @@ chacha20 = { version = "0.9", default-features = false } k256 = { version = "0.13.3", optional = true } base58 = { version = "0.2.0", optional = true } anyhow = { version = "1.0.98", optional = true } +borsh = "1.5.7" [features] default = [] diff --git a/nssa/core/src/address.rs b/nssa/core/src/address.rs index 6355351..1d61e9e 100644 --- a/nssa/core/src/address.rs +++ b/nssa/core/src/address.rs @@ -1,3 +1,4 @@ +use borsh::{BorshDeserialize, BorshSerialize}; use serde::{Deserialize, Serialize}; #[cfg(feature = "host")] @@ -6,7 +7,7 @@ use std::{fmt::Display, str::FromStr}; #[cfg(feature = "host")] use base58::{FromBase58, ToBase58}; -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)] #[cfg_attr( any(feature = "host", test), derive(Debug, Copy, PartialOrd, Ord, Default) diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index 47585ba..563e8b9 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -1574,6 +1574,7 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" name = "nssa-core" version = "0.1.0" dependencies = [ + "borsh", "chacha20", "risc0-zkvm", "serde", diff --git a/nssa/src/encoding/public_transaction.rs b/nssa/src/encoding/public_transaction.rs index 03c34ee..5e6838c 100644 --- a/nssa/src/encoding/public_transaction.rs +++ b/nssa/src/encoding/public_transaction.rs @@ -1,153 +1,17 @@ -// TODO: Consider switching to deriving Borsh - -use std::io::{Cursor, Read}; - -use nssa_core::program::ProgramId; - -use crate::{ - Address, PublicKey, PublicTransaction, Signature, - error::NssaError, - public_transaction::{Message, WitnessSet}, -}; - -const MESSAGE_ENCODING_PREFIX_LEN: usize = 32; -const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = - b"/NSSA/v0.2/TxMessage/Public/\x00\x00\x00\x00"; +use crate::{PublicTransaction, error::NssaError, public_transaction::Message}; impl Message { - /// Serializes a `Message` into bytes in the following layout: - /// PREFIX || (4 bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes LE * M) || instruction_data_len || instruction_data (4 bytes LE * K) - /// Integers and words 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(); - // program_id: [u32; 8] - for word in &self.program_id { - bytes.extend_from_slice(&word.to_le_bytes()); - } - // addresses: Vec<[u8;32]> - // serialize length as u32 little endian, then all addresses concatenated - let addresses_len = self.addresses.len() as u32; - bytes.extend(&addresses_len.to_le_bytes()); - for addr in &self.addresses { - bytes.extend_from_slice(addr.value()); - } - // nonces: Vec - // serialize length as u32 little endian, then all nonces concatenated in LE - let nonces_len = self.nonces.len() as u32; - bytes.extend(&nonces_len.to_le_bytes()); - for nonce in &self.nonces { - bytes.extend(&nonce.to_le_bytes()); - } - // instruction_data: Vec - // serialize length as u32 little endian, then all addresses concatenated - let instr_len = self.instruction_data.len() as u32; - bytes.extend(&instr_len.to_le_bytes()); - for word in &self.instruction_data { - bytes.extend(&word.to_le_bytes()); - } - - 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 program_id: ProgramId = { - let mut this = [0u32; 8]; - for item in &mut this { - *item = u32_from_cursor(cursor)?; - } - this - }; - let addresses_len = u32_from_cursor(cursor)?; - let mut addresses = Vec::with_capacity(addresses_len as usize); - for _ in 0..addresses_len { - let mut value = [0u8; 32]; - cursor.read_exact(&mut value)?; - addresses.push(Address::new(value)) - } - let nonces_len = u32_from_cursor(cursor)?; - let mut nonces = Vec::with_capacity(nonces_len as usize); - for _ in 0..nonces_len { - let mut buf = [0u8; 16]; - cursor.read_exact(&mut buf)?; - nonces.push(u128::from_le_bytes(buf)) - } - let instruction_data_len = u32_from_cursor(cursor)?; - let mut instruction_data = Vec::with_capacity(instruction_data_len as usize); - for _ in 0..instruction_data_len { - let word = u32_from_cursor(cursor)?; - instruction_data.push(word) - } - Ok(Self { - program_id, - addresses, - nonces, - instruction_data, - }) - } -} - -impl WitnessSet { - pub(crate) fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let size = self.signatures_and_public_keys().len() as u32; - bytes.extend_from_slice(&size.to_le_bytes()); - for (signature, public_key) in self.signatures_and_public_keys() { - bytes.extend_from_slice(signature.to_bytes()); - bytes.extend_from_slice(public_key.to_bytes()); - } - bytes - } - - pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { - let num_signatures: u32 = { - let mut buf = [0u8; 4]; - cursor.read_exact(&mut buf)?; - u32::from_le_bytes(buf) - }; - let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize); - for _i in 0..num_signatures { - let signature = Signature::from_cursor(cursor)?; - let public_key = PublicKey::from_cursor(cursor)?; - signatures_and_public_keys.push((signature, public_key)) - } - Ok(Self { - signatures_and_public_keys, - }) + borsh::to_vec(&self).unwrap() } } impl PublicTransaction { pub fn to_bytes(&self) -> Vec { - let mut bytes = self.message().to_bytes(); - bytes.extend_from_slice(&self.witness_set().to_bytes()); - bytes + borsh::to_vec(&self).unwrap() } 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)?; - let witness_set = WitnessSet::from_cursor(cursor)?; - Ok(PublicTransaction::new(message, witness_set)) + Ok(borsh::from_slice(bytes)?) } } - -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/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index 68cb5fb..86db4c7 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -1,3 +1,4 @@ +use borsh::{BorshDeserialize, BorshSerialize}; use nssa_core::{ account::Nonce, program::{InstructionData, ProgramId}, @@ -6,7 +7,7 @@ use serde::Serialize; use crate::{Address, error::NssaError, program::Program}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Message { pub(crate) program_id: ProgramId, pub(crate) addresses: Vec
, diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index d118d0c..00dd2d3 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -1,5 +1,6 @@ use std::collections::{HashMap, HashSet}; +use borsh::{BorshDeserialize, BorshSerialize}; use nssa_core::{ account::{Account, AccountWithMetadata}, address::Address, @@ -13,7 +14,7 @@ use crate::{ public_transaction::{Message, WitnessSet}, }; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct PublicTransaction { message: Message, witness_set: WitnessSet, diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index e5095ba..359ed72 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -1,6 +1,8 @@ +use borsh::{BorshDeserialize, BorshSerialize}; + use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct WitnessSet { pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>, } diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 2f18360..97c8117 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -2,12 +2,13 @@ mod encoding; mod private_key; mod public_key; +use borsh::{BorshDeserialize, BorshSerialize}; pub use private_key::PrivateKey; pub use public_key::PublicKey; use rand::{RngCore, rngs::OsRng}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Signature { value: [u8; 64], } diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index dbd7d64..181ea97 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -1,10 +1,11 @@ +use borsh::{BorshDeserialize, BorshSerialize}; use nssa_core::address::Address; use crate::{PrivateKey, error::NssaError}; use sha2::{Digest, Sha256}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct PublicKey([u8; 32]); impl PublicKey { diff --git a/nssa/test_program_methods/guest/Cargo.lock b/nssa/test_program_methods/guest/Cargo.lock index d7e5b67..85f566c 100644 --- a/nssa/test_program_methods/guest/Cargo.lock +++ b/nssa/test_program_methods/guest/Cargo.lock @@ -1579,6 +1579,7 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" name = "nssa-core" version = "0.1.0" dependencies = [ + "borsh", "chacha20", "risc0-zkvm", "serde",