refactor encoding/decoding for public transactions without serde

This commit is contained in:
Sergio Chouhy 2025-08-12 10:16:04 -03:00
parent a5d0c27d6b
commit bab1112251
8 changed files with 172 additions and 49 deletions

View File

@ -34,7 +34,7 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction {
let program_id = nssa::program::Program::authenticated_transfer_program().id();
let addresses = vec![];
let nonces = vec![];
let instruction_data = 0;
let instruction_data: u128 = 0;
let message =
nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data).unwrap();
let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap();

View File

@ -9,7 +9,6 @@ risc0-zkvm = "2.3.1"
nssa-core = { path = "core" }
program-methods = { path = "program_methods" }
serde = "1.0.219"
serde_cbor = "0.11.2"
sha2 = "0.10.9"
secp256k1 = "0.31.1"

View File

@ -1,3 +1,5 @@
use std::io::{Cursor, Read};
use nssa_core::{
account::Nonce,
program::{InstructionData, ProgramId},
@ -5,6 +7,8 @@ use nssa_core::{
use serde::{Deserialize, Serialize};
use crate::{Address, error::NssaError, program::Program};
const MESSAGE_ENCODING_PREFIX_LEN: usize = 19;
const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] = b"NSSA/v0.1/TxMessage";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Message {
@ -34,8 +38,6 @@ impl Message {
/// TAG || <program_id> (bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes * M) || instruction_data_len || instruction_data (4 bytes * K)
/// Integers and words are encoded in little-endian byte order, and fields appear in the above order.
pub(crate) fn message_to_bytes(&self) -> Vec<u8> {
const MESSAGE_ENCODING_PREFIX: &[u8; 19] = b"NSSA/v0.1/TxMessage";
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
// program_id: [u32; 8]
for word in &self.program_id {
@ -64,4 +66,52 @@ impl Message {
bytes
}
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self {
let prefix = {
let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN];
cursor.read_exact(&mut this).unwrap();
this
};
assert_eq!(&prefix, MESSAGE_ENCODING_PREFIX);
let program_id: ProgramId = {
let mut this = [0u32; 8];
for i in 0..8 {
this[i] = 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).unwrap();
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).unwrap();
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)
}
Self {
program_id,
addresses,
nonces,
instruction_data,
}
}
}
fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 {
let mut word_buf = [0u8; 4];
cursor.read_exact(&mut word_buf).unwrap();
u32::from_le_bytes(word_buf)
}

View File

@ -1,10 +1,12 @@
use std::collections::{HashMap, HashSet};
use std::{
collections::{HashMap, HashSet},
io::Cursor,
};
use nssa_core::{
account::{Account, AccountWithMetadata},
program::validate_execution,
};
use serde::{Deserialize, Serialize};
use sha2::{Digest, digest::FixedOutput};
use crate::{V01State, address::Address, error::NssaError};
@ -15,7 +17,7 @@ mod witness_set;
pub use message::Message;
pub use witness_set::WitnessSet;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicTransaction {
message: Message,
witness_set: WitnessSet,
@ -45,8 +47,24 @@ impl PublicTransaction {
}
}
fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.message.message_to_bytes();
bytes.extend_from_slice(&self.witness_set.to_bytes());
bytes
}
fn from_bytes(bytes: &[u8]) -> Self {
let mut cursor = Cursor::new(bytes);
let message = Message::from_cursor(&mut cursor);
let witness_set = WitnessSet::from_cursor(&mut cursor);
Self {
message,
witness_set,
}
}
pub fn hash(&self) -> [u8; 32] {
let bytes = serde_cbor::to_vec(&self).unwrap();
let bytes = self.to_bytes();
let mut hasher = sha2::Sha256::new();
hasher.update(&bytes);
hasher.finalize_fixed().into()
@ -116,3 +134,36 @@ impl PublicTransaction {
Ok(message.addresses.iter().cloned().zip(post_states).collect())
}
}
#[cfg(test)]
mod tests {
use crate::{
Address, PrivateKey, PublicTransaction, PublicKey,
program::Program,
public_transaction::{Message, WitnessSet},
};
#[test]
fn test() {
let key1 = PrivateKey::try_new([1; 32]).unwrap();
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let addr1 = Address::from_public_key(&PublicKey::new(&key1));
let addr2 = Address::from_public_key(&PublicKey::new(&key2));
let nonces = vec![5, 99];
let instruction = 1337;
let message = Message::try_new(
Program::authenticated_transfer_program().id(),
vec![addr1, addr2],
nonces,
instruction,
)
.unwrap();
let witness_set = WitnessSet::for_message(&message, &[&key1, &key2]);
let tx = PublicTransaction::new(message, witness_set);
let bytes = tx.to_bytes();
let recov_tx = PublicTransaction::from_bytes(&bytes);
assert_eq!(tx, recov_tx);
}
}

View File

@ -1,8 +1,10 @@
use std::io::{Cursor, Read};
use serde::{Deserialize, Serialize};
use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message};
use crate::{PrivateKey, PublicKey, Signature, error::NssaError, public_transaction::Message};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WitnessSet {
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
}
@ -32,4 +34,33 @@ impl WitnessSet {
pub fn iter_signatures(&self) -> impl Iterator<Item = &(Signature, PublicKey)> {
self.signatures_and_public_keys.iter()
}
pub(crate) fn to_bytes(&self) -> Vec<u8> {
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.value);
bytes.extend_from_slice(&public_key.0);
}
bytes
}
// TODO: remove unwraps and return Result
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self {
let num_signatures: u32 = {
let mut buf = [0u8; 4];
cursor.read_exact(&mut buf).unwrap();
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))
}
Self {
signatures_and_public_keys,
}
}
}

View File

@ -1,10 +1,11 @@
use std::io::{Cursor, Read};
use serde::{Deserialize, Serialize};
use crate::PrivateKey;
// TODO: Dummy impl. Replace by actual public key.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey(pub(crate) [u8; 32]);
impl PublicKey {
@ -19,3 +20,12 @@ impl PublicKey {
Self(value)
}
}
impl PublicKey {
// TODO: remove unwraps and return Result
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self {
let mut value = [0u8; 32];
cursor.read_exact(&mut value).unwrap();
Self(value)
}
}

View File

@ -1,10 +1,10 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::io::{Cursor, Read};
use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Signature {
value: [u8; 64],
pub(crate) value: [u8; 64],
}
impl Signature {
@ -27,30 +27,11 @@ impl Signature {
}
}
impl Serialize for Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Serialize as a slice
serializer.serialize_bytes(&self.value)
}
}
impl<'de> Deserialize<'de> for Signature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes: &[u8] = Deserialize::deserialize(deserializer)?;
if bytes.len() != 64 {
return Err(serde::de::Error::invalid_length(
bytes.len(),
&"expected 64 bytes",
));
}
impl Signature {
// TODO: remove unwraps and return Result
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self {
let mut value = [0u8; 64];
value.copy_from_slice(bytes);
Ok(Signature { value })
cursor.read_exact(&mut value).unwrap();
Self { value }
}
}

View File

@ -102,10 +102,13 @@ impl SequencerCore {
.mempool
.pop_size(self.sequencer_config.max_num_tx_in_block);
let valid_transactions = transactions
println!("##");
println!("{:?}", transactions.len());
let valid_transactions: Vec<_> = transactions
.into_iter()
.filter_map(|tx| self.execute_check_transaction_on_state(tx).ok())
.collect();
println!("{:?}", valid_transactions.len());
let prev_block_hash = self
.store
@ -157,15 +160,13 @@ mod tests {
fn setup_sequencer_config() -> SequencerConfig {
let acc1_addr = vec![
// 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181,
// 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215,
1; 32
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
];
let acc2_addr = vec![
// 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29,
// 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100,
2; 32
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
];
let initial_acc1 = AccountInitialData {
@ -236,13 +237,13 @@ mod tests {
#[test]
fn test_start_different_intial_accounts_balances() {
let acc1_addr = vec![
13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181,
68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 42, 42, 42,
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
];
let acc2_addr = vec![
151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29,
135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 42, 42, 42,
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
];
let initial_acc1 = AccountInitialData {