From a174eb4b85c95a744c1bb72c30fa74174c0bb1b0 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 10 Jul 2025 14:53:25 -0300 Subject: [PATCH 01/18] add un/verified signed transaction structs --- common/src/transaction.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 3f2dcb2..9509a7c 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,5 +1,5 @@ use log::info; -use secp256k1_zkp::{PedersenCommitment, Tweak}; +use secp256k1_zkp::{PedersenCommitment, PublicKey, Scalar, Tweak}; use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; @@ -57,6 +57,25 @@ pub struct Transaction { pub state_changes: (serde_json::Value, usize), } +#[derive(Serialize, Deserialize)] +struct TransactionSignature {} + +/// A transaction with a signature. +/// Meant to be sent through the network to the sequencer +#[derive(Serialize, Deserialize)] +pub struct UnverifiedSignedTransaction { + transaction: Transaction, + signature: TransactionSignature +} + +/// A transaction with a valid signature over its hash. +/// Can only be constructed from an `UnverifiedSignedTransaction` +/// if the signature is valid +pub struct VerifiedSignedTransaction { + transaction: Transaction, + signature: TransactionSignature +} + #[derive(Debug, Serialize, Deserialize)] pub struct MintMoneyPublicTx { pub acc: [u8; 32], @@ -150,6 +169,9 @@ impl ActionData { } } +type SignaturePrivateKey = Scalar; +type SignaturePublicKey = PublicKey; + impl Transaction { /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. pub fn hash(&self) -> TreeHashType { @@ -162,6 +184,13 @@ impl Transaction { TreeHashType::from(hasher.finalize_fixed()) } + pub fn sign(self, _private_key: SignaturePrivateKey) -> UnverifiedSignedTransaction { + let _hash = self.hash(); + // Implement actual signature over `hash` + let signature = TransactionSignature {}; + UnverifiedSignedTransaction { transaction: self, signature } + } + pub fn log(&self) { info!("Transaction hash is {:?}", hex::encode(self.hash())); info!("Transaction tx_kind is {:?}", self.tx_kind); @@ -214,6 +243,14 @@ impl Transaction { } } +impl UnverifiedSignedTransaction { + pub fn into_verified(self) -> VerifiedSignedTransaction { + let hash = self.transaction.hash(); + // Check signature over hash + todo!() + } +} + #[cfg(test)] mod tests { use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak}; From f02c97e622289312d615a26c4adb3ef4a0f125c5 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 14 Jul 2025 08:45:38 -0300 Subject: [PATCH 02/18] rename structs. Implement serde for AuthenticatedTransaction --- common/src/block.rs | 6 +- common/src/merkle_tree_public/merkle_tree.rs | 4 +- .../src/merkle_tree_public/tree_leav_item.rs | 4 +- common/src/rpc_primitives/requests.rs | 4 +- common/src/transaction.rs | 67 ++++++++++++++----- node_core/src/chain_storage/block_store.rs | 4 +- node_core/src/chain_storage/mod.rs | 6 +- node_core/src/lib.rs | 32 ++++----- node_core/src/sequencer_client/json.rs | 4 +- node_core/src/sequencer_client/mod.rs | 4 +- sc_core/src/transaction_payloads_tools.rs | 6 +- sequencer_core/src/lib.rs | 14 ++-- sequencer_core/src/transaction_mempool.rs | 10 +-- 13 files changed, 99 insertions(+), 66 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index 7757667..ae8765e 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,7 +1,7 @@ use rs_merkle::Hasher; use serde::{Deserialize, Serialize}; -use crate::{merkle_tree_public::hasher::OwnHasher, transaction::Transaction}; +use crate::{merkle_tree_public::hasher::OwnHasher, transaction::TransactionBody}; pub type BlockHash = [u8; 32]; pub type Data = Vec; @@ -13,7 +13,7 @@ pub struct Block { pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } @@ -22,7 +22,7 @@ pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index 3540b06..d8229af 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -7,7 +7,7 @@ use serde::{ Deserialize, Deserializer, Serialize, }; -use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; +use crate::{transaction::TransactionBody, utxo_commitment::UTXOCommitment}; use super::{hasher::OwnHasher, tree_leav_item::TreeLeavItem, TreeHashType}; @@ -81,7 +81,7 @@ impl<'de, Leav: TreeLeavItem + Clone + Deserialize<'de>> serde::Deserialize<'de> } } -pub type PublicTransactionMerkleTree = HashStorageMerkleTree; +pub type PublicTransactionMerkleTree = HashStorageMerkleTree; pub type UTXOCommitmentsMerkleTree = HashStorageMerkleTree; diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs index 1fbbb1a..52bb8ef 100644 --- a/common/src/merkle_tree_public/tree_leav_item.rs +++ b/common/src/merkle_tree_public/tree_leav_item.rs @@ -1,4 +1,4 @@ -use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; +use crate::{transaction::TransactionBody, utxo_commitment::UTXOCommitment}; use super::TreeHashType; @@ -6,7 +6,7 @@ pub trait TreeLeavItem { fn hash(&self) -> TreeHashType; } -impl TreeLeavItem for Transaction { +impl TreeLeavItem for TransactionBody { fn hash(&self) -> TreeHashType { self.hash() } diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 6cad01d..7508ce9 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,6 +1,6 @@ use crate::block::Block; use crate::parse_request; -use crate::transaction::Transaction; +use crate::transaction::TransactionBody; use super::errors::RpcParseError; use super::parser::parse_params; @@ -20,7 +20,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: Transaction, + pub transaction: TransactionBody, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 9509a7c..fd0b4c3 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -25,7 +25,7 @@ pub enum TxKind { #[derive(Debug, Serialize, Deserialize, Clone)] ///General transaction object -pub struct Transaction { +pub struct TransactionBody { pub tx_kind: TxKind, ///Tx input data (public part) pub execution_input: Vec, @@ -58,22 +58,25 @@ pub struct Transaction { } #[derive(Serialize, Deserialize)] -struct TransactionSignature {} +struct TransactionSignature; + +#[derive(Serialize, Deserialize)] +struct TransactionHash; /// A transaction with a signature. /// Meant to be sent through the network to the sequencer #[derive(Serialize, Deserialize)] -pub struct UnverifiedSignedTransaction { - transaction: Transaction, - signature: TransactionSignature +pub struct SignedTransaction { + body: TransactionBody, + signature: TransactionSignature, } /// A transaction with a valid signature over its hash. /// Can only be constructed from an `UnverifiedSignedTransaction` /// if the signature is valid -pub struct VerifiedSignedTransaction { - transaction: Transaction, - signature: TransactionSignature +pub struct AuthenticatedTransaction { + hash: TransactionHash, + signed_tx: SignedTransaction } #[derive(Debug, Serialize, Deserialize)] @@ -172,7 +175,7 @@ impl ActionData { type SignaturePrivateKey = Scalar; type SignaturePublicKey = PublicKey; -impl Transaction { +impl TransactionBody { /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. pub fn hash(&self) -> TreeHashType { // TODO: Remove `unwrap` by implementing a `to_bytes` method @@ -184,11 +187,14 @@ impl Transaction { TreeHashType::from(hasher.finalize_fixed()) } - pub fn sign(self, _private_key: SignaturePrivateKey) -> UnverifiedSignedTransaction { + pub fn sign(self, _private_key: SignaturePrivateKey) -> SignedTransaction { let _hash = self.hash(); // Implement actual signature over `hash` let signature = TransactionSignature {}; - UnverifiedSignedTransaction { transaction: self, signature } + SignedTransaction { + body: self, + signature, + } } pub fn log(&self) { @@ -243,11 +249,38 @@ impl Transaction { } } -impl UnverifiedSignedTransaction { - pub fn into_verified(self) -> VerifiedSignedTransaction { - let hash = self.transaction.hash(); +impl SignedTransaction { + pub fn into_authenticated(self) -> AuthenticatedTransaction { + let hash = TransactionHash; // self.body.hash(); // Check signature over hash - todo!() + AuthenticatedTransaction { + hash, + signed_tx: self + } + } +} + +impl AuthenticatedTransaction { + pub fn as_signed(&self) -> &SignedTransaction { + &self.signed_tx + } +} + +impl Serialize for AuthenticatedTransaction { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.as_signed().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for AuthenticatedTransaction { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + SignedTransaction::deserialize(deserializer).map(|signed_tx| signed_tx.into_authenticated()) } } @@ -258,12 +291,12 @@ mod tests { use crate::{ merkle_tree_public::TreeHashType, - transaction::{Transaction, TxKind}, + transaction::{TransactionBody, TxKind}, }; #[test] fn test_transaction_hash_is_sha256_of_json_bytes() { - let tx = Transaction { + let tx = TransactionBody { tx_kind: TxKind::Public, execution_input: vec![1, 2, 3, 4], execution_output: vec![5, 6, 7, 8], diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index 677f426..ba5b24b 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Result}; use common::block::Block; use common::merkle_tree_public::merkle_tree::HashStorageMerkleTree; use common::nullifier::UTXONullifier; -use common::transaction::Transaction; +use common::transaction::TransactionBody; use common::utxo_commitment::UTXOCommitment; use log::error; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; @@ -87,7 +87,7 @@ impl NodeBlockStore { )?) } - pub fn get_snapshot_transaction(&self) -> Result> { + pub fn get_snapshot_transaction(&self) -> Result> { Ok(serde_json::from_slice( &self.dbio.get_snapshot_transaction()?, )?) diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 3f61393..c875bbd 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -278,7 +278,7 @@ mod tests { use accounts::account_core::Account; use common::block::{Block, Data}; use common::merkle_tree_public::TreeHashType; - use common::transaction::{Transaction, TxKind}; + use common::transaction::{TransactionBody, TxKind}; use secp256k1_zkp::Tweak; use std::path::PathBuf; use tempfile::tempdir; @@ -300,10 +300,10 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> Transaction { + ) -> TransactionBody { let mut rng = rand::thread_rng(); - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 73775eb..fa49001 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -8,7 +8,7 @@ use common::ExecutionFailureKind; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; use chain_storage::NodeChainStore; -use common::transaction::{Transaction, TxKind}; +use common::transaction::{TransactionBody, TxKind}; use config::NodeConfig; use log::info; use sc_core::proofs_circuits::{ @@ -189,7 +189,7 @@ impl NodeCore { &self, acc: AccountAddress, amount: u128, - ) -> Result<(Transaction, [u8; 32]), ExecutionFailureKind> { + ) -> Result<(TransactionBody, [u8; 32]), ExecutionFailureKind> { let (utxo, receipt) = prove_mint_utxo(amount, acc)?; let result_hash = utxo.hash; @@ -246,7 +246,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -278,7 +278,7 @@ impl NodeCore { acc: AccountAddress, amount: u128, number_of_assets: usize, - ) -> Result<(Transaction, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(TransactionBody, Vec<[u8; 32]>), ExecutionFailureKind> { let (utxos, receipt) = prove_mint_utxo_multiple_assets(amount, number_of_assets, acc)?; let result_hashes = utxos.iter().map(|utxo| utxo.hash).collect(); @@ -343,7 +343,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -375,7 +375,7 @@ impl NodeCore { utxo: UTXO, commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -459,7 +459,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -492,7 +492,7 @@ impl NodeCore { commitments_in: Vec<[u8; 32]>, number_to_send: usize, receiver: AccountAddress, - ) -> Result<(Transaction, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(TransactionBody, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxos[0].owner).unwrap(); @@ -604,7 +604,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -637,7 +637,7 @@ impl NodeCore { acc: AccountAddress, balance: u64, receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&acc).unwrap(); @@ -727,7 +727,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Shielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( SendMoneyShieldedTx { @@ -765,7 +765,7 @@ impl NodeCore { utxo: UTXO, comm_gen_hash: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result { + ) -> Result { let acc_map_read_guard = self.storage.read().await; let commitment_in = acc_map_read_guard @@ -820,7 +820,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(Transaction { + Ok(TransactionBody { tx_kind: TxKind::Deshielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( SendMoneyDeshieldedTx { @@ -924,7 +924,7 @@ impl NodeCore { let new_len = 0; let state_changes = (serde_json::to_value(state_changes).unwrap(), new_len); - let tx: Transaction = + let tx: TransactionBody = sc_core::transaction_payloads_tools::create_public_transaction_payload( serde_json::to_vec(&ActionData::MintMoneyPublicTx(MintMoneyPublicTx { acc, @@ -1361,7 +1361,7 @@ impl NodeCore { commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, visibility_list: [bool; 3], - ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -1459,7 +1459,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - Transaction { + TransactionBody { tx_kind: TxKind::Shielded, execution_input: vec![], execution_output: serde_json::to_vec(&publication).unwrap(), diff --git a/node_core/src/sequencer_client/json.rs b/node_core/src/sequencer_client/json.rs index df94ca2..b3ada03 100644 --- a/node_core/src/sequencer_client/json.rs +++ b/node_core/src/sequencer_client/json.rs @@ -1,11 +1,11 @@ -use common::transaction::Transaction; +use common::transaction::TransactionBody; use serde::{Deserialize, Serialize}; //Requests #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: Transaction, + pub transaction: TransactionBody, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/node_core/src/sequencer_client/mod.rs b/node_core/src/sequencer_client/mod.rs index 7accd5f..3c96a85 100644 --- a/node_core/src/sequencer_client/mod.rs +++ b/node_core/src/sequencer_client/mod.rs @@ -4,7 +4,7 @@ use common::rpc_primitives::requests::{ GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, RegisterAccountRequest, RegisterAccountResponse, }; -use common::transaction::Transaction; +use common::transaction::TransactionBody; use common::{SequencerClientError, SequencerRpcError}; use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcResponse}; use k256::elliptic_curve::group::GroupEncoding; @@ -72,7 +72,7 @@ impl SequencerClient { pub async fn send_tx( &self, - transaction: Transaction, + transaction: TransactionBody, tx_roots: [[u8; 32]; 2], ) -> Result { let tx_req = SendTxRequest { diff --git a/sc_core/src/transaction_payloads_tools.rs b/sc_core/src/transaction_payloads_tools.rs index 4846be1..f8d3e3e 100644 --- a/sc_core/src/transaction_payloads_tools.rs +++ b/sc_core/src/transaction_payloads_tools.rs @@ -1,6 +1,6 @@ use accounts::account_core::Account; use anyhow::Result; -use common::transaction::{Transaction, TxKind}; +use common::transaction::{TransactionBody, TxKind}; use rand::thread_rng; use risc0_zkvm::Receipt; use secp256k1_zkp::{CommitmentSecrets, PedersenCommitment, Tweak}; @@ -15,8 +15,8 @@ pub fn create_public_transaction_payload( secret_r: [u8; 32], sc_addr: String, state_changes: (serde_json::Value, usize), -) -> Transaction { - Transaction { +) -> TransactionBody { + TransactionBody { tx_kind: TxKind::Public, execution_input, execution_output: vec![], diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index f2890ed..bec16b0 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -5,7 +5,7 @@ use common::{ block::{Block, HashableBlockData}, merkle_tree_public::TreeHashType, nullifier::UTXONullifier, - transaction::{Transaction, TxKind}, + transaction::{TransactionBody, TxKind}, utxo_commitment::UTXOCommitment, }; use config::SequencerConfig; @@ -71,10 +71,10 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, - tx: &Transaction, + tx: &TransactionBody, tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { - let Transaction { + let TransactionBody { tx_kind, ref execution_input, ref execution_output, @@ -188,7 +188,7 @@ impl SequencerCore { &mut self, tx: TransactionMempool, ) -> Result<(), TransactionMalformationErrorKind> { - let Transaction { + let TransactionBody { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. @@ -259,7 +259,7 @@ mod tests { use super::*; use std::path::PathBuf; - use common::transaction::{Transaction, TxKind}; + use common::transaction::{TransactionBody, TxKind}; use rand::Rng; use secp256k1_zkp::Tweak; use transaction_mempool::TransactionMempool; @@ -285,10 +285,10 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> Transaction { + ) -> TransactionBody { let mut rng = rand::thread_rng(); - Transaction { + TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/transaction_mempool.rs index d932b24..d6d81f2 100644 --- a/sequencer_core/src/transaction_mempool.rs +++ b/sequencer_core/src/transaction_mempool.rs @@ -1,14 +1,14 @@ -use common::{merkle_tree_public::TreeHashType, transaction::Transaction}; +use common::{merkle_tree_public::TreeHashType, transaction::TransactionBody}; use mempool::mempoolitem::MemPoolItem; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone)] pub struct TransactionMempool { - pub tx: Transaction, + pub tx: TransactionBody, } -impl From for TransactionMempool { - fn from(value: Transaction) -> Self { +impl From for TransactionMempool { + fn from(value: TransactionBody) -> Self { Self { tx: value } } } @@ -27,7 +27,7 @@ impl<'de> Deserialize<'de> for TransactionMempool { where D: serde::Deserializer<'de>, { - match Transaction::deserialize(deserializer) { + match TransactionBody::deserialize(deserializer) { Ok(tx) => Ok(TransactionMempool { tx }), Err(err) => Err(err), } From 6e48bcfd9e58b1d4eb5bb99e2305b7ed84ef2f91 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 14 Jul 2025 09:37:00 -0300 Subject: [PATCH 03/18] sign transactions from node's end --- Cargo.lock | 1 + common/Cargo.toml | 1 + common/src/lib.rs | 6 + common/src/transaction.rs | 106 +++-- node_core/src/lib.rs | 582 +++++++++++++------------ node_core/src/sequencer_client/json.rs | 4 +- node_core/src/sequencer_client/mod.rs | 4 +- node_rpc/src/process.rs | 82 ++-- 8 files changed, 411 insertions(+), 375 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0200d46..c19ab6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -996,6 +996,7 @@ dependencies = [ "anyhow", "elliptic-curve", "hex", + "k256", "log", "reqwest 0.11.27", "risc0-zkvm", diff --git a/common/Cargo.toml b/common/Cargo.toml index b1988ad..e00bf4d 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -10,6 +10,7 @@ serde_json.workspace = true serde.workspace = true reqwest.workspace = true risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-2.2" } +k256.workspace = true rs_merkle.workspace = true sha2.workspace = true diff --git a/common/src/lib.rs b/common/src/lib.rs index 46868d9..96a7b55 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -88,3 +88,9 @@ impl ExecutionFailureKind { Self::DBError(err) } } + +#[derive(Debug, thiserror::Error)] +pub enum TransactionSignatureError { + #[error("invalid signature for transaction body")] + InvalidSignature, +} diff --git a/common/src/transaction.rs b/common/src/transaction.rs index fd0b4c3..6e6f651 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,5 +1,6 @@ +use k256::Scalar; use log::info; -use secp256k1_zkp::{PedersenCommitment, PublicKey, Scalar, Tweak}; +use secp256k1_zkp::{PedersenCommitment, Tweak}; use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; @@ -11,6 +12,8 @@ use elliptic_curve::{ }; use sha2::digest::typenum::{UInt, UTerm}; +use crate::TransactionSignatureError; + pub type CipherText = Vec; pub type Nonce = GenericArray, B1>, B0>, B0>>; pub type Tag = u8; @@ -57,28 +60,6 @@ pub struct TransactionBody { pub state_changes: (serde_json::Value, usize), } -#[derive(Serialize, Deserialize)] -struct TransactionSignature; - -#[derive(Serialize, Deserialize)] -struct TransactionHash; - -/// A transaction with a signature. -/// Meant to be sent through the network to the sequencer -#[derive(Serialize, Deserialize)] -pub struct SignedTransaction { - body: TransactionBody, - signature: TransactionSignature, -} - -/// A transaction with a valid signature over its hash. -/// Can only be constructed from an `UnverifiedSignedTransaction` -/// if the signature is valid -pub struct AuthenticatedTransaction { - hash: TransactionHash, - signed_tx: SignedTransaction -} - #[derive(Debug, Serialize, Deserialize)] pub struct MintMoneyPublicTx { pub acc: [u8; 32], @@ -172,9 +153,6 @@ impl ActionData { } } -type SignaturePrivateKey = Scalar; -type SignaturePublicKey = PublicKey; - impl TransactionBody { /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. pub fn hash(&self) -> TreeHashType { @@ -187,16 +165,6 @@ impl TransactionBody { TreeHashType::from(hasher.finalize_fixed()) } - pub fn sign(self, _private_key: SignaturePrivateKey) -> SignedTransaction { - let _hash = self.hash(); - // Implement actual signature over `hash` - let signature = TransactionSignature {}; - SignedTransaction { - body: self, - signature, - } - } - pub fn log(&self) { info!("Transaction hash is {:?}", hex::encode(self.hash())); info!("Transaction tx_kind is {:?}", self.tx_kind); @@ -249,38 +217,62 @@ impl TransactionBody { } } +type SignaturePrivateKey = Scalar; + +#[derive(Debug, Serialize, Deserialize)] +struct TransactionSignature; + +type TransactionHash = TreeHashType; + +/// A transaction with a signature. +/// Meant to be sent through the network to the sequencer +#[derive(Debug, Serialize, Deserialize)] +pub struct SignedTransaction { + pub body: TransactionBody, + signature: TransactionSignature, +} + impl SignedTransaction { - pub fn into_authenticated(self) -> AuthenticatedTransaction { - let hash = TransactionHash; // self.body.hash(); - // Check signature over hash - AuthenticatedTransaction { - hash, - signed_tx: self - } + pub fn from_transaction_body( + body: TransactionBody, + _private_key: SignaturePrivateKey, + ) -> SignedTransaction { + let _hash = body.hash(); + // TODO: Implement actual signature over `hash` + let signature = TransactionSignature {}; + Self { body, signature } } + + pub fn into_authenticated(self) -> Result { + let hash = self.body.hash(); + // TODO: Check signature over hash + Err(TransactionSignatureError::InvalidSignature) + } +} + +/// A transaction with a valid signature over the hash of its body. +/// Can only be constructed from an `SignedTransaction` +/// if the signature is valid +pub struct AuthenticatedTransaction { + hash: TransactionHash, + signed_tx: SignedTransaction, } impl AuthenticatedTransaction { pub fn as_signed(&self) -> &SignedTransaction { &self.signed_tx } -} -impl Serialize for AuthenticatedTransaction { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.as_signed().serialize(serializer) + pub fn body(&self) -> &TransactionBody { + &self.signed_tx.body } -} -impl<'de> Deserialize<'de> for AuthenticatedTransaction { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - SignedTransaction::deserialize(deserializer).map(|signed_tx| signed_tx.into_authenticated()) + pub fn signature(&self) -> &TransactionSignature { + &self.signed_tx.signature + } + + pub fn hash(&self) -> &TransactionHash { + &self.hash } } diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index fa49001..04aa467 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::{ Arc, }; -use common::ExecutionFailureKind; +use common::{transaction::SignedTransaction, ExecutionFailureKind}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; @@ -189,7 +189,7 @@ impl NodeCore { &self, acc: AccountAddress, amount: u128, - ) -> Result<(TransactionBody, [u8; 32]), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, [u8; 32]), ExecutionFailureKind> { let (utxo, receipt) = prove_mint_utxo(amount, acc)?; let result_hash = utxo.hash; @@ -244,31 +244,34 @@ impl NodeCore { let vec_public_info: Vec = vec_values_u64.into_iter().flatten().collect(); let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); + let transaction_body = TransactionBody { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: comm + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![], + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) + .unwrap(), + encoded_data: vec![(encoded_data.0, encoded_data.1.to_vec(), tag)], + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; Ok(( - TransactionBody { - tx_kind: TxKind::Private, - execution_input: vec![], - execution_output: vec![], - utxo_commitments_spent_hashes: vec![], - utxo_commitments_created_hashes: comm - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: vec![], - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) - .unwrap(), - encoded_data: vec![(encoded_data.0, encoded_data.1.to_vec(), tag)], - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), result_hash, )) } @@ -278,7 +281,7 @@ impl NodeCore { acc: AccountAddress, amount: u128, number_of_assets: usize, - ) -> Result<(TransactionBody, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, Vec<[u8; 32]>), ExecutionFailureKind> { let (utxos, receipt) = prove_mint_utxo_multiple_assets(amount, number_of_assets, acc)?; let result_hashes = utxos.iter().map(|utxo| utxo.hash).collect(); @@ -342,30 +345,34 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - TransactionBody { - tx_kind: TxKind::Private, - execution_input: vec![], - execution_output: vec![], - utxo_commitments_spent_hashes: vec![], - utxo_commitments_created_hashes: comm - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: vec![], - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) + let transaction_body = TransactionBody { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: comm + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![], + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) .unwrap(), - encoded_data, - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + + Ok(( + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), result_hashes, )) } @@ -375,7 +382,7 @@ impl NodeCore { utxo: UTXO, commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -458,30 +465,35 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - TransactionBody { - tx_kind: TxKind::Private, - execution_input: vec![], - execution_output: vec![], - utxo_commitments_spent_hashes: vec![commitment_in], - utxo_commitments_created_hashes: commitments - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) + let transaction_body = TransactionBody { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![commitment_in], + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) .unwrap(), - encoded_data, - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + + Ok(( + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -492,7 +504,7 @@ impl NodeCore { commitments_in: Vec<[u8; 32]>, number_to_send: usize, receiver: AccountAddress, - ) -> Result<(TransactionBody, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxos[0].owner).unwrap(); @@ -603,30 +615,35 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - TransactionBody { - tx_kind: TxKind::Private, - execution_input: vec![], - execution_output: vec![], - utxo_commitments_spent_hashes: commitments_in, - utxo_commitments_created_hashes: commitments - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: nullifiers, - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) + let transaction_body = TransactionBody { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: commitments_in, + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: nullifiers, + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) .unwrap(), - encoded_data, - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + + Ok(( + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), utxo_hashes_receiver, utxo_hashes_not_spent, )) @@ -637,7 +654,7 @@ impl NodeCore { acc: AccountAddress, balance: u64, receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&acc).unwrap(); @@ -726,36 +743,41 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); + let transaction_body = TransactionBody { + tx_kind: TxKind::Shielded, + execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( + SendMoneyShieldedTx { + acc_sender: acc, + amount: balance as u128, + }, + )) + .unwrap(), + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) + .unwrap(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + Ok(( - TransactionBody { - tx_kind: TxKind::Shielded, - execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( - SendMoneyShieldedTx { - acc_sender: acc, - amount: balance as u128, - }, - )) - .unwrap(), - execution_output: vec![], - utxo_commitments_spent_hashes: vec![], - utxo_commitments_created_hashes: commitments - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) - .unwrap(), - encoded_data, - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -765,7 +787,7 @@ impl NodeCore { utxo: UTXO, comm_gen_hash: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result { + ) -> Result { let acc_map_read_guard = self.storage.read().await; let commitment_in = acc_map_read_guard @@ -820,7 +842,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(TransactionBody { + let transaction_body = TransactionBody { tx_kind: TxKind::Deshielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( SendMoneyDeshieldedTx { @@ -841,8 +863,18 @@ impl NodeCore { secret_r, sc_addr, state_changes, - } - .into()) + }; + + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + + Ok(SignedTransaction::from_transaction_body( + transaction_body, + key_to_sign_transaction, + )) } pub async fn send_private_mint_tx( @@ -855,10 +887,10 @@ impl NodeCore { let point_before_prove = std::time::Instant::now(); let (tx, utxo_hash) = self.mint_utxo_private(acc, amount).await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); - let commitment_generated_hash = tx.utxo_commitments_created_hashes[0]; + let commitment_generated_hash = tx.body.utxo_commitments_created_hashes[0]; let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Mint utxo proof spent {timedelta:?} milliseconds"); @@ -883,10 +915,10 @@ impl NodeCore { let (tx, utxo_hashes) = self .mint_utxo_multiple_assets_private(acc, amount, number_of_assets) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); - let commitment_generated_hashes = tx.utxo_commitments_created_hashes.clone(); + let commitment_generated_hashes = tx.body.utxo_commitments_created_hashes.clone(); let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Mint utxo proof spent {timedelta:?} milliseconds"); @@ -898,50 +930,50 @@ impl NodeCore { )) } - pub async fn send_public_deposit( - &self, - acc: AccountAddress, - amount: u128, - ) -> Result { - //Considering proof time, needs to be done before proof - let tx_roots = self.get_roots().await; - - let public_context = { - let read_guard = self.storage.read().await; - - read_guard.produce_context(acc) - }; - - let (tweak, secret_r, commitment) = pedersen_commitment_vec( - //Will not panic, as public context is serializable - public_context.produce_u64_list_from_context().unwrap(), - ); - - let sc_addr = hex::encode([0; 32]); - - //Sc does not change its state - let state_changes: Vec = vec![]; - let new_len = 0; - let state_changes = (serde_json::to_value(state_changes).unwrap(), new_len); - - let tx: TransactionBody = - sc_core::transaction_payloads_tools::create_public_transaction_payload( - serde_json::to_vec(&ActionData::MintMoneyPublicTx(MintMoneyPublicTx { - acc, - amount, - })) - .unwrap(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - ) - .into(); - tx.log(); - - Ok(self.sequencer_client.send_tx(tx, tx_roots).await?) - } + // pub async fn send_public_deposit( + // &self, + // acc: AccountAddress, + // amount: u128, + // ) -> Result { + // //Considering proof time, needs to be done before proof + // let tx_roots = self.get_roots().await; + // + // let public_context = { + // let read_guard = self.storage.read().await; + // + // read_guard.produce_context(acc) + // }; + // + // let (tweak, secret_r, commitment) = pedersen_commitment_vec( + // //Will not panic, as public context is serializable + // public_context.produce_u64_list_from_context().unwrap(), + // ); + // + // let sc_addr = hex::encode([0; 32]); + // + // //Sc does not change its state + // let state_changes: Vec = vec![]; + // let new_len = 0; + // let state_changes = (serde_json::to_value(state_changes).unwrap(), new_len); + // + // let tx: TransactionBody = + // sc_core::transaction_payloads_tools::create_public_transaction_payload( + // serde_json::to_vec(&ActionData::MintMoneyPublicTx(MintMoneyPublicTx { + // acc, + // amount, + // })) + // .unwrap(), + // commitment, + // tweak, + // secret_r, + // sc_addr, + // state_changes, + // ) + // .into(); + // tx.log(); + // + // Ok(self.sequencer_client.send_tx(tx, tx_roots).await?) + // } pub async fn send_private_send_tx( &self, @@ -956,7 +988,7 @@ impl NodeCore { let (tx, utxo_hashes) = self .transfer_utxo_private(utxo, comm_hash, receivers) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -982,7 +1014,7 @@ impl NodeCore { let (tx, utxo_hashes_received, utxo_hashes_not_spent) = self .transfer_utxo_multiple_assets_private(utxos, comm_hashes, number_to_send, receiver) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1008,7 +1040,7 @@ impl NodeCore { let (tx, utxo_hashes) = self .transfer_balance_shielded(acc, amount, receivers) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1033,7 +1065,7 @@ impl NodeCore { let tx = self .transfer_utxo_deshielded(utxo, comm_gen_hash, receivers) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1167,46 +1199,46 @@ impl NodeCore { Ok(()) } - pub async fn operate_account_deposit_public( - &mut self, - acc_addr: AccountAddress, - amount: u128, - ) -> Result<(), ExecutionFailureKind> { - let old_balance = { - let acc_map_read_guard = self.storage.read().await; - - let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); - - acc.balance - }; - - info!( - "Balance of {:?} now is {old_balance:?}", - hex::encode(acc_addr) - ); - - let resp = self.send_public_deposit(acc_addr, amount).await?; - info!("Response for public deposit is {resp:?}"); - - info!("Awaiting new blocks"); - tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; - - let new_balance = { - let acc_map_read_guard = self.storage.read().await; - - let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); - - acc.balance - }; - - info!( - "Balance of {:?} now is {new_balance:?}, delta is {:?}", - hex::encode(acc_addr), - new_balance - old_balance - ); - - Ok(()) - } + // pub async fn operate_account_deposit_public( + // &mut self, + // acc_addr: AccountAddress, + // amount: u128, + // ) -> Result<(), ExecutionFailureKind> { + // let old_balance = { + // let acc_map_read_guard = self.storage.read().await; + // + // let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + // + // acc.balance + // }; + // + // info!( + // "Balance of {:?} now is {old_balance:?}", + // hex::encode(acc_addr) + // ); + // + // let resp = self.send_public_deposit(acc_addr, amount).await?; + // info!("Response for public deposit is {resp:?}"); + // + // info!("Awaiting new blocks"); + // tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + // + // let new_balance = { + // let acc_map_read_guard = self.storage.read().await; + // + // let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + // + // acc.balance + // }; + // + // info!( + // "Balance of {:?} now is {new_balance:?}, delta is {:?}", + // hex::encode(acc_addr), + // new_balance - old_balance + // ); + // + // Ok(()) + // } pub async fn operate_account_send_shielded_one_receiver( &mut self, @@ -1361,7 +1393,7 @@ impl NodeCore { commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, visibility_list: [bool; 3], - ) -> Result<(TransactionBody, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -1458,31 +1490,35 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - TransactionBody { - tx_kind: TxKind::Shielded, - execution_input: vec![], - execution_output: serde_json::to_vec(&publication).unwrap(), - utxo_commitments_spent_hashes: vec![commitment_in], - utxo_commitments_created_hashes: commitments - .clone() - .into_iter() - .map(|hash_data| hash_data.try_into().unwrap()) - .collect(), - nullifier_created_hashes: vec![nullifier.try_into().unwrap()], - execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt( - receipt, - ) + let transaction_body = TransactionBody { + tx_kind: TxKind::Shielded, + execution_input: vec![], + execution_output: serde_json::to_vec(&publication).unwrap(), + utxo_commitments_spent_hashes: vec![commitment_in], + utxo_commitments_created_hashes: commitments + .clone() + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: sc_core::transaction_payloads_tools::encode_receipt(receipt) .unwrap(), - encoded_data, - ephemeral_pub_key: eph_pub_key.to_vec(), - commitment, - tweak, - secret_r, - sc_addr, - state_changes, - } - .into(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + // TODO: Change to the correct key once established. + let key_to_sign_transaction = account + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key; + + Ok(( + SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -1502,13 +1538,13 @@ impl NodeCore { let (tx, utxo_hashes) = self .split_utxo(utxo, comm_hash, receivers, visibility_list) .await?; - tx.log(); + tx.body.log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Send private utxo proof spent {timedelta:?} milliseconds"); - let commitments = tx.utxo_commitments_created_hashes.clone(); + let commitments = tx.body.utxo_commitments_created_hashes.clone(); Ok(( self.sequencer_client.send_tx(tx, tx_roots).await?, @@ -1582,17 +1618,17 @@ impl NodeCore { Ok(()) } - ///Deposit balance, make it private - pub async fn subscenario_2(&mut self) -> Result<(), ExecutionFailureKind> { - let acc_addr = self.create_new_account().await; - - self.operate_account_deposit_public(acc_addr, 100).await?; - - self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr, 100) - .await?; - - Ok(()) - } + // ///Deposit balance, make it private + // pub async fn subscenario_2(&mut self) -> Result<(), ExecutionFailureKind> { + // let acc_addr = self.create_new_account().await; + // + // self.operate_account_deposit_public(acc_addr, 100).await?; + // + // self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr, 100) + // .await?; + // + // Ok(()) + // } ///Mint utxo, privately send it to another user pub async fn subscenario_3(&mut self) -> Result<(), ExecutionFailureKind> { @@ -1607,18 +1643,18 @@ impl NodeCore { Ok(()) } - ///Deposit balance, shielded send it to another user - pub async fn subscenario_4(&mut self) -> Result<(), ExecutionFailureKind> { - let acc_addr = self.create_new_account().await; - let acc_addr_rec = self.create_new_account().await; - - self.operate_account_deposit_public(acc_addr, 100).await?; - - self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr_rec, 100) - .await?; - - Ok(()) - } + // ///Deposit balance, shielded send it to another user + // pub async fn subscenario_4(&mut self) -> Result<(), ExecutionFailureKind> { + // let acc_addr = self.create_new_account().await; + // let acc_addr_rec = self.create_new_account().await; + // + // self.operate_account_deposit_public(acc_addr, 100).await?; + // + // self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr_rec, 100) + // .await?; + // + // Ok(()) + // } ///Mint utxo, deshielded send it to another user pub async fn subscenario_5(&mut self) -> Result<(), ExecutionFailureKind> { diff --git a/node_core/src/sequencer_client/json.rs b/node_core/src/sequencer_client/json.rs index b3ada03..9275ca5 100644 --- a/node_core/src/sequencer_client/json.rs +++ b/node_core/src/sequencer_client/json.rs @@ -1,11 +1,11 @@ -use common::transaction::TransactionBody; +use common::transaction::SignedTransaction; use serde::{Deserialize, Serialize}; //Requests #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: TransactionBody, + pub transaction: SignedTransaction, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/node_core/src/sequencer_client/mod.rs b/node_core/src/sequencer_client/mod.rs index 3c96a85..95a3378 100644 --- a/node_core/src/sequencer_client/mod.rs +++ b/node_core/src/sequencer_client/mod.rs @@ -4,7 +4,7 @@ use common::rpc_primitives::requests::{ GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, RegisterAccountRequest, RegisterAccountResponse, }; -use common::transaction::TransactionBody; +use common::transaction::SignedTransaction; use common::{SequencerClientError, SequencerRpcError}; use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcResponse}; use k256::elliptic_curve::group::GroupEncoding; @@ -72,7 +72,7 @@ impl SequencerClient { pub async fn send_tx( &self, - transaction: TransactionBody, + transaction: SignedTransaction, tx_roots: [[u8; 32]; 2], ) -> Result { let tx_req = SendTxRequest { diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index f4937e9..a188ec8 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -42,7 +42,7 @@ pub const EXECUTE_SCENARIO_MULTIPLE_SEND: &str = "execute_scenario_multiple_send pub const SHOW_ACCOUNT_PUBLIC_BALANCE: &str = "show_account_public_balance"; pub const SHOW_ACCOUNT_UTXO: &str = "show_account_utxo"; pub const SHOW_TRANSACTION: &str = "show_transaction"; -pub const WRITE_DEPOSIT_PUBLIC_BALANCE: &str = "write_deposit_public_balance"; +// pub const WRITE_DEPOSIT_PUBLIC_BALANCE: &str = "write_deposit_public_balance"; pub const WRITE_MINT_UTXO: &str = "write_mint_utxo"; pub const WRITE_MINT_UTXO_MULTIPLE_ASSETS: &str = "write_mint_utxo_multiple_assets"; pub const WRITE_SEND_UTXO_PRIVATE: &str = "write_send_utxo_private"; @@ -84,18 +84,18 @@ impl JsonHandler { .subscenario_1() .await .map_err(cast_common_execution_error_into_rpc_error)?, - 2 => store - .subscenario_2() - .await - .map_err(cast_common_execution_error_into_rpc_error)?, + // 2 => store + // .subscenario_2() + // .await + // .map_err(cast_common_execution_error_into_rpc_error)?, 3 => store .subscenario_3() .await .map_err(cast_common_execution_error_into_rpc_error)?, - 4 => store - .subscenario_4() - .await - .map_err(cast_common_execution_error_into_rpc_error)?, + // 4 => store + // .subscenario_4() + // .await + // .map_err(cast_common_execution_error_into_rpc_error)?, 5 => store .subscenario_5() .await @@ -355,35 +355,35 @@ impl JsonHandler { respond(helperstruct) } - pub async fn process_write_deposit_public_balance( - &self, - request: Request, - ) -> Result { - let req = WriteDepositPublicBalanceRequest::parse(Some(request.params))?; - - let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { - RpcError::parse_error("Failed to decode account address from hex string".to_string()) - })?; - - let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { - RpcError::parse_error("Failed to parse account address from bytes".to_string()) - })?; - - { - let mut cover_guard = self.node_chain_store.lock().await; - - cover_guard - .operate_account_deposit_public(acc_addr, req.amount as u128) - .await - .map_err(cast_common_execution_error_into_rpc_error)?; - }; - - let helperstruct = WriteDepositPublicBalanceResponse { - status: SUCCESS.to_string(), - }; - - respond(helperstruct) - } + // pub async fn process_write_deposit_public_balance( + // &self, + // request: Request, + // ) -> Result { + // let req = WriteDepositPublicBalanceRequest::parse(Some(request.params))?; + // + // let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { + // RpcError::parse_error("Failed to decode account address from hex string".to_string()) + // })?; + // + // let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { + // RpcError::parse_error("Failed to parse account address from bytes".to_string()) + // })?; + // + // { + // let mut cover_guard = self.node_chain_store.lock().await; + // + // cover_guard + // .operate_account_deposit_public(acc_addr, req.amount as u128) + // .await + // .map_err(cast_common_execution_error_into_rpc_error)?; + // }; + // + // let helperstruct = WriteDepositPublicBalanceResponse { + // status: SUCCESS.to_string(), + // }; + // + // respond(helperstruct) + // } pub async fn process_write_mint_utxo(&self, request: Request) -> Result { let req = WriteMintPrivateUTXORequest::parse(Some(request.params))?; @@ -777,9 +777,9 @@ impl JsonHandler { SHOW_ACCOUNT_PUBLIC_BALANCE => self.process_show_account_public_balance(request).await, SHOW_ACCOUNT_UTXO => self.process_show_account_utxo_request(request).await, SHOW_TRANSACTION => self.process_show_transaction(request).await, - WRITE_DEPOSIT_PUBLIC_BALANCE => { - self.process_write_deposit_public_balance(request).await - } + // WRITE_DEPOSIT_PUBLIC_BALANCE => { + // self.process_write_deposit_public_balance(request).await + // } WRITE_MINT_UTXO => self.process_write_mint_utxo(request).await, WRITE_MINT_UTXO_MULTIPLE_ASSETS => { self.process_write_mint_utxo_multiple_assets(request).await From e7b727c0bac035fc9b93ff283a3a4253d4939766 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 14 Jul 2025 15:45:20 -0300 Subject: [PATCH 04/18] make sequencer validate transaction signatures before adding transactions to blocks --- common/src/block.rs | 6 +- common/src/merkle_tree_public/merkle_tree.rs | 11 ++- .../src/merkle_tree_public/tree_leav_item.rs | 6 +- common/src/rpc_primitives/requests.rs | 3 +- common/src/transaction.rs | 7 +- node_core/src/chain_storage/block_store.rs | 4 +- node_core/src/chain_storage/mod.rs | 28 ++++--- node_rpc/src/process.rs | 12 ++- sequencer_core/src/lib.rs | 79 +++++++++++-------- sequencer_core/src/transaction_mempool.rs | 55 +++++++------ sequencer_rpc/src/process.rs | 5 +- 11 files changed, 118 insertions(+), 98 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index ae8765e..4553245 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,7 +1,7 @@ use rs_merkle::Hasher; use serde::{Deserialize, Serialize}; -use crate::{merkle_tree_public::hasher::OwnHasher, transaction::TransactionBody}; +use crate::{merkle_tree_public::hasher::OwnHasher, transaction::SignedTransaction}; pub type BlockHash = [u8; 32]; pub type Data = Vec; @@ -13,7 +13,7 @@ pub struct Block { pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } @@ -22,7 +22,7 @@ pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index d8229af..8886745 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -7,7 +7,10 @@ use serde::{ Deserialize, Deserializer, Serialize, }; -use crate::{transaction::TransactionBody, utxo_commitment::UTXOCommitment}; +use crate::{ + transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody}, + utxo_commitment::UTXOCommitment, +}; use super::{hasher::OwnHasher, tree_leav_item::TreeLeavItem, TreeHashType}; @@ -81,7 +84,7 @@ impl<'de, Leav: TreeLeavItem + Clone + Deserialize<'de>> serde::Deserialize<'de> } } -pub type PublicTransactionMerkleTree = HashStorageMerkleTree; +pub type PublicTransactionMerkleTree = HashStorageMerkleTree; pub type UTXOCommitmentsMerkleTree = HashStorageMerkleTree; @@ -139,7 +142,7 @@ impl HashStorageMerkleTree { } } - pub fn add_tx(&mut self, tx: Leav) { + pub fn add_tx(&mut self, tx: &Leav) { let last = self.leaves.len(); self.leaves.insert(last, tx.clone()); @@ -266,7 +269,7 @@ mod tests { let mut tree = HashStorageMerkleTree::new(vec![tx1.clone()]); - tree.add_tx(tx2.clone()); + tree.add_tx(&tx2); assert_eq!(tree.leaves.len(), 2); assert_eq!(tree.get_tx(tx2.hash()), Some(&tx2)); } diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs index 52bb8ef..5599172 100644 --- a/common/src/merkle_tree_public/tree_leav_item.rs +++ b/common/src/merkle_tree_public/tree_leav_item.rs @@ -1,4 +1,4 @@ -use crate::{transaction::TransactionBody, utxo_commitment::UTXOCommitment}; +use crate::{transaction::SignedTransaction, utxo_commitment::UTXOCommitment}; use super::TreeHashType; @@ -6,9 +6,9 @@ pub trait TreeLeavItem { fn hash(&self) -> TreeHashType; } -impl TreeLeavItem for TransactionBody { +impl TreeLeavItem for SignedTransaction { fn hash(&self) -> TreeHashType { - self.hash() + self.body.hash() } } diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 7508ce9..95b004f 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,5 +1,6 @@ use crate::block::Block; use crate::parse_request; +use crate::transaction::SignedTransaction; use crate::transaction::TransactionBody; use super::errors::RpcParseError; @@ -20,7 +21,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: TransactionBody, + pub transaction: SignedTransaction, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 6e6f651..1544d67 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -217,16 +217,16 @@ impl TransactionBody { } } -type SignaturePrivateKey = Scalar; +pub type SignaturePrivateKey = Scalar; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] struct TransactionSignature; type TransactionHash = TreeHashType; /// A transaction with a signature. /// Meant to be sent through the network to the sequencer -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct SignedTransaction { pub body: TransactionBody, signature: TransactionSignature, @@ -253,6 +253,7 @@ impl SignedTransaction { /// A transaction with a valid signature over the hash of its body. /// Can only be constructed from an `SignedTransaction` /// if the signature is valid +#[derive(Clone)] pub struct AuthenticatedTransaction { hash: TransactionHash, signed_tx: SignedTransaction, diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index ba5b24b..c6bc386 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Result}; use common::block::Block; use common::merkle_tree_public::merkle_tree::HashStorageMerkleTree; use common::nullifier::UTXONullifier; -use common::transaction::TransactionBody; +use common::transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody}; use common::utxo_commitment::UTXOCommitment; use log::error; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; @@ -87,7 +87,7 @@ impl NodeBlockStore { )?) } - pub fn get_snapshot_transaction(&self) -> Result> { + pub fn get_snapshot_transaction(&self) -> Result> { Ok(serde_json::from_slice( &self.dbio.get_snapshot_transaction()?, )?) diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index c875bbd..04ba03a 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -127,8 +127,8 @@ impl NodeChainStore { let block_id = block.block_id; for tx in &block.transactions { - if !tx.execution_input.is_empty() { - let public_action = serde_json::from_slice::(&tx.execution_input); + if !tx.body.execution_input.is_empty() { + let public_action = serde_json::from_slice::(&tx.body.execution_input); if let Ok(public_action) = public_action { match public_action { @@ -162,24 +162,25 @@ impl NodeChainStore { } self.utxo_commitments_store.add_tx_multiple( - tx.utxo_commitments_created_hashes + tx.body + .utxo_commitments_created_hashes .clone() .into_iter() .map(|hash| UTXOCommitment { hash }) .collect(), ); - for nullifier in tx.nullifier_created_hashes.iter() { + for nullifier in tx.body.nullifier_created_hashes.iter() { self.nullifier_store.insert(UTXONullifier { utxo_hash: *nullifier, }); } - if !tx.encoded_data.is_empty() { + if !tx.body.encoded_data.is_empty() { let ephemeral_public_key_sender = - serde_json::from_slice::(&tx.ephemeral_pub_key)?; + serde_json::from_slice::(&tx.body.ephemeral_pub_key)?; - for (ciphertext, nonce, tag) in tx.encoded_data.clone() { + for (ciphertext, nonce, tag) in tx.body.encoded_data.clone() { let slice = nonce.as_slice(); let nonce = accounts::key_management::constants_types::Nonce::clone_from_slice(slice); @@ -204,7 +205,7 @@ impl NodeChainStore { } } - self.pub_tx_store.add_tx(tx.clone()); + self.pub_tx_store.add_tx(tx); } self.block_store.put_block_at_id(block)?; @@ -278,7 +279,7 @@ mod tests { use accounts::account_core::Account; use common::block::{Block, Data}; use common::merkle_tree_public::TreeHashType; - use common::transaction::{TransactionBody, TxKind}; + use common::transaction::{SignaturePrivateKey, SignedTransaction, TransactionBody, TxKind}; use secp256k1_zkp::Tweak; use std::path::PathBuf; use tempfile::tempdir; @@ -300,10 +301,10 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> TransactionBody { + ) -> SignedTransaction { let mut rng = rand::thread_rng(); - TransactionBody { + let body = TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -318,7 +319,8 @@ mod tests { secret_r: [0; 32], sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), - } + }; + SignedTransaction::from_transaction_body(body, SignaturePrivateKey::ONE) } fn create_sample_block(block_id: u64, prev_block_id: u64) -> Block { @@ -414,7 +416,7 @@ mod tests { store .utxo_commitments_store .add_tx_multiple(vec![UTXOCommitment { hash: [3u8; 32] }]); - store.pub_tx_store.add_tx(create_dummy_transaction( + store.pub_tx_store.add_tx(&create_dummy_transaction( [12; 32], vec![[9; 32]], vec![[7; 32]], diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index a188ec8..149121a 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -312,42 +312,46 @@ impl JsonHandler { ShowTransactionResponse { hash: req.tx_hash, - tx_kind: tx.tx_kind, + tx_kind: tx.body.tx_kind, public_input: if let Ok(action) = - serde_json::from_slice::(&tx.execution_input) + serde_json::from_slice::(&tx.body.execution_input) { action.into_hexed_print() } else { "".to_string() }, public_output: if let Ok(action) = - serde_json::from_slice::(&tx.execution_output) + serde_json::from_slice::(&tx.body.execution_output) { action.into_hexed_print() } else { "".to_string() }, utxo_commitments_created_hashes: tx + .body .utxo_commitments_created_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), utxo_commitments_spent_hashes: tx + .body .utxo_commitments_spent_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), utxo_nullifiers_created_hashes: tx + .body .nullifier_created_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), encoded_data: tx + .body .encoded_data .iter() .map(|val| (hex::encode(val.0.clone()), hex::encode(val.1.clone()))) .collect::>(), - ephemeral_pub_key: hex::encode(tx.ephemeral_pub_key.clone()), + ephemeral_pub_key: hex::encode(tx.body.ephemeral_pub_key.clone()), } } }; diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index bec16b0..d480670 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -5,14 +5,14 @@ use common::{ block::{Block, HashableBlockData}, merkle_tree_public::TreeHashType, nullifier::UTXONullifier, - transaction::{TransactionBody, TxKind}, + transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody, TxKind}, utxo_commitment::UTXOCommitment, }; use config::SequencerConfig; use mempool::MemPool; use sequencer_store::{accounts_store::AccountPublicData, SequecerChainStore}; use serde::{Deserialize, Serialize}; -use transaction_mempool::TransactionMempool; +use transaction_mempool::MempoolTransaction; pub mod config; pub mod sequencer_store; @@ -20,7 +20,7 @@ pub mod transaction_mempool; pub struct SequencerCore { pub store: SequecerChainStore, - pub mempool: MemPool, + pub mempool: MemPool, pub sequencer_config: SequencerConfig, pub chain_height: u64, } @@ -35,6 +35,7 @@ pub enum TransactionMalformationErrorKind { MempoolFullForRound { tx: TreeHashType }, ChainStateFurtherThanTransactionState { tx: TreeHashType }, FailedToInsert { tx: TreeHashType, details: String }, + InvalidSignature, } impl Display for TransactionMalformationErrorKind { @@ -53,7 +54,7 @@ impl SequencerCore { config.genesis_id, config.is_genesis_random, ), - mempool: MemPool::::default(), + mempool: MemPool::::default(), chain_height: config.genesis_id, sequencer_config: config, } @@ -71,9 +72,12 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, - tx: &TransactionBody, + tx: SignedTransaction, tx_roots: [[u8; 32]; 2], - ) -> Result<(), TransactionMalformationErrorKind> { + ) -> Result { + let tx = tx + .into_authenticated() + .map_err(|_| TransactionMalformationErrorKind::InvalidSignature)?; let TransactionBody { tx_kind, ref execution_input, @@ -81,10 +85,10 @@ impl SequencerCore { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx; - let tx_hash = tx.hash(); + } = tx.body(); let mempool_size = self.mempool.len(); + let tx_hash = *tx.hash(); if mempool_size >= self.sequencer_config.max_num_tx_in_block { return Err(TransactionMalformationErrorKind::MempoolFullForRound { tx: tx_hash }); @@ -151,53 +155,53 @@ impl SequencerCore { if tx_tree_check { return Err( - TransactionMalformationErrorKind::TxHashAlreadyPresentInTree { tx: tx.hash() }, + TransactionMalformationErrorKind::TxHashAlreadyPresentInTree { tx: *tx.hash() }, ); } if nullifier_tree_check { return Err( - TransactionMalformationErrorKind::NullifierAlreadyPresentInTree { tx: tx.hash() }, + TransactionMalformationErrorKind::NullifierAlreadyPresentInTree { tx: *tx.hash() }, ); } if utxo_commitments_check { return Err( TransactionMalformationErrorKind::UTXOCommitmentAlreadyPresentInTree { - tx: tx.hash(), + tx: *tx.hash(), }, ); } - Ok(()) + Ok(tx) } pub fn push_tx_into_mempool_pre_check( &mut self, - item: TransactionMempool, + item: SignedTransaction, tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { - self.transaction_pre_check(&item.tx, tx_roots)?; + let item = self.transaction_pre_check(item, tx_roots)?; - self.mempool.push_item(item); + self.mempool.push_item(item.into()); Ok(()) } fn execute_check_transaction_on_state( &mut self, - tx: TransactionMempool, + tx: &MempoolTransaction, ) -> Result<(), TransactionMalformationErrorKind> { let TransactionBody { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx.tx; + } = tx.tx.body(); for utxo_comm in utxo_commitments_created_hashes { self.store .utxo_commitments_store - .add_tx(UTXOCommitment { hash: *utxo_comm }); + .add_tx(&UTXOCommitment { hash: *utxo_comm }); } for nullifier in nullifier_created_hashes.iter() { @@ -206,7 +210,7 @@ impl SequencerCore { }); } - self.store.pub_tx_store.add_tx(tx.tx); + self.store.pub_tx_store.add_tx(tx.tx.as_signed()); Ok(()) } @@ -227,7 +231,7 @@ impl SequencerCore { .pop_size(self.sequencer_config.max_num_tx_in_block); for tx in &transactions { - self.execute_check_transaction_on_state(tx.clone())?; + self.execute_check_transaction_on_state(&tx)?; } let prev_block_hash = self @@ -239,7 +243,10 @@ impl SequencerCore { let hashable_data = HashableBlockData { block_id: new_block_height, prev_block_id: self.chain_height, - transactions: transactions.into_iter().map(|tx_mem| tx_mem.tx).collect(), + transactions: transactions + .into_iter() + .map(|tx_mem| tx_mem.tx.as_signed().clone()) + .collect(), data: vec![], prev_block_hash, }; @@ -259,10 +266,10 @@ mod tests { use super::*; use std::path::PathBuf; - use common::transaction::{TransactionBody, TxKind}; + use common::transaction::{SignaturePrivateKey, SignedTransaction, TransactionBody, TxKind}; use rand::Rng; use secp256k1_zkp::Tweak; - use transaction_mempool::TransactionMempool; + use transaction_mempool::MempoolTransaction; fn setup_sequencer_config() -> SequencerConfig { let mut rng = rand::thread_rng(); @@ -285,10 +292,10 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> TransactionBody { + ) -> SignedTransaction { let mut rng = rand::thread_rng(); - TransactionBody { + let body = TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -303,12 +310,15 @@ mod tests { secret_r: [0; 32], sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), - } + }; + SignedTransaction::from_transaction_body(body, SignaturePrivateKey::ONE) } fn common_setup(sequencer: &mut SequencerCore) { let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]); - let tx_mempool = TransactionMempool { tx }; + let tx_mempool = MempoolTransaction { + tx: tx.into_authenticated().unwrap(), + }; sequencer.mempool.push_item(tx_mempool); sequencer.produce_new_block_with_mempool_transactions(); @@ -344,7 +354,7 @@ mod tests { let tx = create_dummy_transaction(vec![[91; 32]], vec![[71; 32]], vec![[81; 32]]); let tx_roots = sequencer.get_tree_roots(); - let result = sequencer.transaction_pre_check(&tx, tx_roots); + let result = sequencer.transaction_pre_check(tx, tx_roots); assert!(result.is_ok()); } @@ -363,10 +373,12 @@ mod tests { let tx_roots = sequencer.get_tree_roots(); // Fill the mempool - let dummy_tx = TransactionMempool { tx: tx.clone() }; + let dummy_tx = MempoolTransaction { + tx: tx.clone().into_authenticated().unwrap(), + }; sequencer.mempool.push_item(dummy_tx); - let result = sequencer.transaction_pre_check(&tx, tx_roots); + let result = sequencer.transaction_pre_check(tx, tx_roots); assert!(matches!( result, @@ -383,9 +395,8 @@ mod tests { let tx = create_dummy_transaction(vec![[93; 32]], vec![[73; 32]], vec![[83; 32]]); let tx_roots = sequencer.get_tree_roots(); - let tx_mempool = TransactionMempool { tx }; - let result = sequencer.push_tx_into_mempool_pre_check(tx_mempool.clone(), tx_roots); + let result = sequencer.push_tx_into_mempool_pre_check(tx, tx_roots); assert!(result.is_ok()); assert_eq!(sequencer.mempool.len(), 1); } @@ -396,7 +407,9 @@ mod tests { let mut sequencer = SequencerCore::start_from_config(config); let tx = create_dummy_transaction(vec![[94; 32]], vec![[7; 32]], vec![[8; 32]]); - let tx_mempool = TransactionMempool { tx }; + let tx_mempool = MempoolTransaction { + tx: tx.into_authenticated().unwrap(), + }; sequencer.mempool.push_item(tx_mempool); let block_id = sequencer.produce_new_block_with_mempool_transactions(); diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/transaction_mempool.rs index d6d81f2..cb40ad5 100644 --- a/sequencer_core/src/transaction_mempool.rs +++ b/sequencer_core/src/transaction_mempool.rs @@ -1,43 +1,42 @@ -use common::{merkle_tree_public::TreeHashType, transaction::TransactionBody}; +use common::{merkle_tree_public::TreeHashType, transaction::AuthenticatedTransaction}; use mempool::mempoolitem::MemPoolItem; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone)] -pub struct TransactionMempool { - pub tx: TransactionBody, +pub struct MempoolTransaction { + pub tx: AuthenticatedTransaction, } -impl From for TransactionMempool { - fn from(value: TransactionBody) -> Self { +impl From for MempoolTransaction { + fn from(value: AuthenticatedTransaction) -> Self { Self { tx: value } } } -impl Serialize for TransactionMempool { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.tx.serialize(serializer) - } -} +// impl Serialize for TransactionMempool { +// fn serialize(&self, serializer: S) -> Result +// where +// S: serde::Serializer, +// { +// self.tx.serialize(serializer) +// } +// } +// +// impl<'de> Deserialize<'de> for TransactionMempool { +// fn deserialize(deserializer: D) -> Result +// where +// D: serde::Deserializer<'de>, +// { +// match TransactionBody::deserialize(deserializer) { +// Ok(tx) => Ok(TransactionMempool { tx }), +// Err(err) => Err(err), +// } +// } +// } -impl<'de> Deserialize<'de> for TransactionMempool { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - match TransactionBody::deserialize(deserializer) { - Ok(tx) => Ok(TransactionMempool { tx }), - Err(err) => Err(err), - } - } -} - -impl MemPoolItem for TransactionMempool { +impl MemPoolItem for MempoolTransaction { type Identifier = TreeHashType; fn identifier(&self) -> Self::Identifier { - self.tx.hash() + *self.tx.hash() } } diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 5bac835..102da72 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -81,10 +81,7 @@ impl JsonHandler { { let mut state = self.sequencer_state.lock().await; - state.push_tx_into_mempool_pre_check( - send_tx_req.transaction.into(), - send_tx_req.tx_roots, - )?; + state.push_tx_into_mempool_pre_check(send_tx_req.transaction, send_tx_req.tx_roots)?; } let helperstruct = SendTxResponse { From 1ef7be0af6ee410d7456a036fd9df396cf681b48 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 15 Jul 2025 15:48:59 -0300 Subject: [PATCH 05/18] add pem feature to k256 and remove manual impls of Serialize and Deserialize --- Cargo.lock | 11 +++++++ Cargo.toml | 2 +- accounts/src/key_management/mod.rs | 18 ++++++++++- common/src/transaction.rs | 48 +++++++++++++++++++++--------- node_core/src/chain_storage/mod.rs | 2 +- node_core/src/lib.rs | 35 +++++----------------- sequencer_core/src/lib.rs | 2 +- 7 files changed, 72 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c19ab6f..554d101 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1247,6 +1247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -1486,6 +1487,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -3196,6 +3198,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" diff --git a/Cargo.toml b/Cargo.toml index 7af2e24..0419f1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ features = ["std", "std_rng", "getrandom"] version = "0.8.5" [workspace.dependencies.k256] -features = ["ecdsa-core", "arithmetic", "expose-field", "serde"] +features = ["ecdsa-core", "arithmetic", "expose-field", "serde", "pem"] version = "0.13.4" [workspace.dependencies.elliptic-curve] diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 69b8ce0..2598294 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -3,12 +3,14 @@ use common::merkle_tree_public::TreeHashType; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; use ephemeral_key_holder::EphemeralKeyHolder; -use k256::AffinePoint; +use k256::{ecdsa::SigningKey, AffinePoint, FieldBytes}; use log::info; +use rand::{rngs::OsRng, RngCore}; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use serde::{Deserialize, Serialize}; use crate::account_core::PublicKey; +pub type PublicAccountSecretKey = [u8; 32]; pub mod constants_types; pub mod ephemeral_key_holder; @@ -21,6 +23,7 @@ pub struct AddressKeyHolder { #[allow(dead_code)] top_secret_key_holder: TopSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder, + pub pub_account_secret_key: PublicAccountSecretKey, pub address: TreeHashType, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, @@ -39,15 +42,28 @@ impl AddressKeyHolder { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + let pub_account_secret_key = { + let mut bytes = [0; 32]; + OsRng.fill_bytes(&mut bytes); + bytes + }; + Self { top_secret_key_holder, utxo_secret_key_holder, address, nullifer_public_key, viewing_public_key, + pub_account_secret_key, } } + pub fn pub_account_secret_key(&self) -> SigningKey { + let field_bytes = FieldBytes::from_slice(&self.pub_account_secret_key); + // TODO: remove unwrap + SigningKey::from_bytes(&field_bytes).unwrap() + } + pub fn calculate_shared_secret_receiver( &self, ephemeral_public_key_sender: AffinePoint, diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 1544d67..ee832c7 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,8 +1,17 @@ -use k256::Scalar; +use k256::{ + ecdsa::{ + signature::{Signer, Verifier}, + Signature, SigningKey, VerifyingKey, + }, + EncodedPoint, Scalar, +}; use log::info; use secp256k1_zkp::{PedersenCommitment, Tweak}; -use serde::{Deserialize, Serialize}; +use serde::de::{Error as DeError, Visitor}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use sha2::{digest::FixedOutput, Digest}; +use std::fmt; use crate::merkle_tree_public::TreeHashType; @@ -217,10 +226,9 @@ impl TransactionBody { } } -pub type SignaturePrivateKey = Scalar; - -#[derive(Debug, Serialize, Deserialize, Clone)] -struct TransactionSignature; +pub type SignaturePrivateKey = SigningKey; +pub type SignaturePublicKey = VerifyingKey; +pub type TransactionSignature = Signature; type TransactionHash = TreeHashType; @@ -229,24 +237,36 @@ type TransactionHash = TreeHashType; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SignedTransaction { pub body: TransactionBody, - signature: TransactionSignature, + signature: (TransactionSignature, SignaturePublicKey), } impl SignedTransaction { pub fn from_transaction_body( body: TransactionBody, - _private_key: SignaturePrivateKey, + private_key: SignaturePrivateKey, ) -> SignedTransaction { - let _hash = body.hash(); + let hash = body.hash(); + let signature: Signature = private_key.sign(&hash); // TODO: Implement actual signature over `hash` - let signature = TransactionSignature {}; - Self { body, signature } + let public_key = VerifyingKey::from(&private_key); + Self { + body, + signature: (signature, public_key), + } } pub fn into_authenticated(self) -> Result { let hash = self.body.hash(); - // TODO: Check signature over hash - Err(TransactionSignatureError::InvalidSignature) + let (signature, public_key) = &self.signature; + + public_key + .verify(&hash, signature) + .map_err(|_| TransactionSignatureError::InvalidSignature)?; + + Ok(AuthenticatedTransaction { + hash, + signed_tx: self, + }) } } @@ -269,7 +289,7 @@ impl AuthenticatedTransaction { } pub fn signature(&self) -> &TransactionSignature { - &self.signed_tx.signature + &self.signed_tx.signature.0 } pub fn hash(&self) -> &TransactionHash { diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 04ba03a..bb8641a 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -320,7 +320,7 @@ mod tests { sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), }; - SignedTransaction::from_transaction_body(body, SignaturePrivateKey::ONE) + SignedTransaction::from_transaction_body(body, SignaturePrivateKey::random(&mut rng)) } fn create_sample_block(block_id: u64, prev_block_id: u64) -> Block { diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 04aa467..7812614 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -265,10 +265,7 @@ impl NodeCore { state_changes, }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -366,10 +363,7 @@ impl NodeCore { state_changes, }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -487,10 +481,7 @@ impl NodeCore { }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -637,10 +628,7 @@ impl NodeCore { }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -771,10 +759,7 @@ impl NodeCore { }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -866,10 +851,7 @@ impl NodeCore { }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(SignedTransaction::from_transaction_body( transaction_body, @@ -1512,10 +1494,7 @@ impl NodeCore { state_changes, }; // TODO: Change to the correct key once established. - let key_to_sign_transaction = account - .key_holder - .utxo_secret_key_holder - .nullifier_secret_key; + let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index d480670..f812ea0 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -311,7 +311,7 @@ mod tests { sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), }; - SignedTransaction::from_transaction_body(body, SignaturePrivateKey::ONE) + SignedTransaction::from_transaction_body(body, SignaturePrivateKey::random(&mut rng)) } fn common_setup(sequencer: &mut SequencerCore) { From b1724ec235465e9f685b8efe7baae8b6f53ebd22 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 08:43:05 -0300 Subject: [PATCH 06/18] move mempool checks outside transaction check function --- common/src/transaction.rs | 1 - sequencer_core/src/lib.rs | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/common/src/transaction.rs b/common/src/transaction.rs index ee832c7..d02ed41 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -247,7 +247,6 @@ impl SignedTransaction { ) -> SignedTransaction { let hash = body.hash(); let signature: Signature = private_key.sign(&hash); - // TODO: Implement actual signature over `hash` let public_key = VerifyingKey::from(&private_key); Self { body, diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index f812ea0..188394d 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -78,6 +78,7 @@ impl SequencerCore { let tx = tx .into_authenticated() .map_err(|_| TransactionMalformationErrorKind::InvalidSignature)?; + let TransactionBody { tx_kind, ref execution_input, @@ -87,13 +88,8 @@ impl SequencerCore { .. } = tx.body(); - let mempool_size = self.mempool.len(); let tx_hash = *tx.hash(); - if mempool_size >= self.sequencer_config.max_num_tx_in_block { - return Err(TransactionMalformationErrorKind::MempoolFullForRound { tx: tx_hash }); - } - let curr_sequencer_roots = self.get_tree_roots(); if tx_roots != curr_sequencer_roots { @@ -178,12 +174,19 @@ impl SequencerCore { pub fn push_tx_into_mempool_pre_check( &mut self, - item: SignedTransaction, + signed_tx: SignedTransaction, tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { - let item = self.transaction_pre_check(item, tx_roots)?; + let mempool_size = self.mempool.len(); + if mempool_size >= self.sequencer_config.max_num_tx_in_block { + return Err(TransactionMalformationErrorKind::MempoolFullForRound { + tx: signed_tx.body.hash(), + }); + } - self.mempool.push_item(item.into()); + let authenticated_tx = self.transaction_pre_check(signed_tx, tx_roots)?; + + self.mempool.push_item(authenticated_tx.into()); Ok(()) } From 8b1d3b478c8a1875927610f8b22df307ef73b1a9 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 10:04:23 -0300 Subject: [PATCH 07/18] minor refactor --- accounts/src/account_core/mod.rs | 2 +- accounts/src/key_management/mod.rs | 10 +++--- common/src/transaction.rs | 26 ++++++-------- node_core/src/lib.rs | 21 ++++-------- node_rpc/src/process.rs | 42 ----------------------- sequencer_core/src/lib.rs | 4 +-- sequencer_core/src/transaction_mempool.rs | 21 ------------ 7 files changed, 26 insertions(+), 100 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 192c158..66d143e 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use anyhow::Result; use common::{merkle_tree_public::TreeHashType, transaction::Tag}; -use k256::AffinePoint; +use k256::{ecdsa::VerifyingKey, AffinePoint}; use log::info; use serde::{Deserialize, Serialize}; use utxo::utxo_core::UTXO; diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 2598294..e871a0f 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -10,7 +10,7 @@ use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use serde::{Deserialize, Serialize}; use crate::account_core::PublicKey; -pub type PublicAccountSecretKey = [u8; 32]; +pub type PublicAccountSigningKey = [u8; 32]; pub mod constants_types; pub mod ephemeral_key_holder; @@ -23,7 +23,7 @@ pub struct AddressKeyHolder { #[allow(dead_code)] top_secret_key_holder: TopSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder, - pub pub_account_secret_key: PublicAccountSecretKey, + pub_account_signing_key: PublicAccountSigningKey, pub address: TreeHashType, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, @@ -54,12 +54,12 @@ impl AddressKeyHolder { address, nullifer_public_key, viewing_public_key, - pub_account_secret_key, + pub_account_signing_key: pub_account_secret_key, } } - pub fn pub_account_secret_key(&self) -> SigningKey { - let field_bytes = FieldBytes::from_slice(&self.pub_account_secret_key); + pub fn get_pub_account_signing_key(&self) -> SigningKey { + let field_bytes = FieldBytes::from_slice(&self.pub_account_signing_key); // TODO: remove unwrap SigningKey::from_bytes(&field_bytes).unwrap() } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index d02ed41..68bf640 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -226,40 +226,40 @@ impl TransactionBody { } } -pub type SignaturePrivateKey = SigningKey; -pub type SignaturePublicKey = VerifyingKey; +type TransactionHash = [u8; 32]; pub type TransactionSignature = Signature; - -type TransactionHash = TreeHashType; +pub type SignaturePublicKey = VerifyingKey; +pub type SignaturePrivateKey = SigningKey; /// A transaction with a signature. /// Meant to be sent through the network to the sequencer #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SignedTransaction { pub body: TransactionBody, - signature: (TransactionSignature, SignaturePublicKey), + signature: TransactionSignature, + public_key: VerifyingKey, } impl SignedTransaction { pub fn from_transaction_body( body: TransactionBody, - private_key: SignaturePrivateKey, + private_key: SigningKey, ) -> SignedTransaction { let hash = body.hash(); - let signature: Signature = private_key.sign(&hash); + let signature: TransactionSignature = private_key.sign(&hash); let public_key = VerifyingKey::from(&private_key); Self { body, - signature: (signature, public_key), + signature, + public_key, } } pub fn into_authenticated(self) -> Result { let hash = self.body.hash(); - let (signature, public_key) = &self.signature; - public_key - .verify(&hash, signature) + self.public_key + .verify(&hash, &self.signature) .map_err(|_| TransactionSignatureError::InvalidSignature)?; Ok(AuthenticatedTransaction { @@ -287,10 +287,6 @@ impl AuthenticatedTransaction { &self.signed_tx.body } - pub fn signature(&self) -> &TransactionSignature { - &self.signed_tx.signature.0 - } - pub fn hash(&self) -> &TransactionHash { &self.hash } diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 7812614..5bed54a 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -264,8 +264,7 @@ impl NodeCore { sc_addr, state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -362,8 +361,7 @@ impl NodeCore { sc_addr, state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -480,8 +478,7 @@ impl NodeCore { state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -627,8 +624,7 @@ impl NodeCore { state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -758,8 +754,7 @@ impl NodeCore { state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), @@ -850,8 +845,7 @@ impl NodeCore { state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(SignedTransaction::from_transaction_body( transaction_body, @@ -1493,8 +1487,7 @@ impl NodeCore { sc_addr, state_changes, }; - // TODO: Change to the correct key once established. - let key_to_sign_transaction = account.key_holder.pub_account_secret_key(); + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index 149121a..9f45b98 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -42,7 +42,6 @@ pub const EXECUTE_SCENARIO_MULTIPLE_SEND: &str = "execute_scenario_multiple_send pub const SHOW_ACCOUNT_PUBLIC_BALANCE: &str = "show_account_public_balance"; pub const SHOW_ACCOUNT_UTXO: &str = "show_account_utxo"; pub const SHOW_TRANSACTION: &str = "show_transaction"; -// pub const WRITE_DEPOSIT_PUBLIC_BALANCE: &str = "write_deposit_public_balance"; pub const WRITE_MINT_UTXO: &str = "write_mint_utxo"; pub const WRITE_MINT_UTXO_MULTIPLE_ASSETS: &str = "write_mint_utxo_multiple_assets"; pub const WRITE_SEND_UTXO_PRIVATE: &str = "write_send_utxo_private"; @@ -84,18 +83,10 @@ impl JsonHandler { .subscenario_1() .await .map_err(cast_common_execution_error_into_rpc_error)?, - // 2 => store - // .subscenario_2() - // .await - // .map_err(cast_common_execution_error_into_rpc_error)?, 3 => store .subscenario_3() .await .map_err(cast_common_execution_error_into_rpc_error)?, - // 4 => store - // .subscenario_4() - // .await - // .map_err(cast_common_execution_error_into_rpc_error)?, 5 => store .subscenario_5() .await @@ -359,36 +350,6 @@ impl JsonHandler { respond(helperstruct) } - // pub async fn process_write_deposit_public_balance( - // &self, - // request: Request, - // ) -> Result { - // let req = WriteDepositPublicBalanceRequest::parse(Some(request.params))?; - // - // let acc_addr_hex_dec = hex::decode(req.account_addr.clone()).map_err(|_| { - // RpcError::parse_error("Failed to decode account address from hex string".to_string()) - // })?; - // - // let acc_addr: [u8; 32] = acc_addr_hex_dec.try_into().map_err(|_| { - // RpcError::parse_error("Failed to parse account address from bytes".to_string()) - // })?; - // - // { - // let mut cover_guard = self.node_chain_store.lock().await; - // - // cover_guard - // .operate_account_deposit_public(acc_addr, req.amount as u128) - // .await - // .map_err(cast_common_execution_error_into_rpc_error)?; - // }; - // - // let helperstruct = WriteDepositPublicBalanceResponse { - // status: SUCCESS.to_string(), - // }; - // - // respond(helperstruct) - // } - pub async fn process_write_mint_utxo(&self, request: Request) -> Result { let req = WriteMintPrivateUTXORequest::parse(Some(request.params))?; @@ -781,9 +742,6 @@ impl JsonHandler { SHOW_ACCOUNT_PUBLIC_BALANCE => self.process_show_account_public_balance(request).await, SHOW_ACCOUNT_UTXO => self.process_show_account_utxo_request(request).await, SHOW_TRANSACTION => self.process_show_transaction(request).await, - // WRITE_DEPOSIT_PUBLIC_BALANCE => { - // self.process_write_deposit_public_balance(request).await - // } WRITE_MINT_UTXO => self.process_write_mint_utxo(request).await, WRITE_MINT_UTXO_MULTIPLE_ASSETS => { self.process_write_mint_utxo_multiple_assets(request).await diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 188394d..174763e 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -363,7 +363,7 @@ mod tests { } #[test] - fn test_transaction_pre_check_fail_mempool_full() { + fn test_push_tx_into_mempool_fails_mempool_full() { let config = SequencerConfig { max_num_tx_in_block: 1, ..setup_sequencer_config() @@ -381,7 +381,7 @@ mod tests { }; sequencer.mempool.push_item(dummy_tx); - let result = sequencer.transaction_pre_check(tx, tx_roots); + let result = sequencer.push_tx_into_mempool_pre_check(tx, tx_roots); assert!(matches!( result, diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/transaction_mempool.rs index cb40ad5..fc73fc9 100644 --- a/sequencer_core/src/transaction_mempool.rs +++ b/sequencer_core/src/transaction_mempool.rs @@ -12,27 +12,6 @@ impl From for MempoolTransaction { } } -// impl Serialize for TransactionMempool { -// fn serialize(&self, serializer: S) -> Result -// where -// S: serde::Serializer, -// { -// self.tx.serialize(serializer) -// } -// } -// -// impl<'de> Deserialize<'de> for TransactionMempool { -// fn deserialize(deserializer: D) -> Result -// where -// D: serde::Deserializer<'de>, -// { -// match TransactionBody::deserialize(deserializer) { -// Ok(tx) => Ok(TransactionMempool { tx }), -// Err(err) => Err(err), -// } -// } -// } - impl MemPoolItem for MempoolTransaction { type Identifier = TreeHashType; From ab06501e040d9c08a7157ce0fb86365a6fe8c629 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 10:06:06 -0300 Subject: [PATCH 08/18] rename scenarios --- node_core/src/lib.rs | 29 ++--------------------------- node_rpc/src/process.rs | 8 ++++---- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 5bed54a..0c10578 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -1590,20 +1590,8 @@ impl NodeCore { Ok(()) } - // ///Deposit balance, make it private - // pub async fn subscenario_2(&mut self) -> Result<(), ExecutionFailureKind> { - // let acc_addr = self.create_new_account().await; - // - // self.operate_account_deposit_public(acc_addr, 100).await?; - // - // self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr, 100) - // .await?; - // - // Ok(()) - // } - ///Mint utxo, privately send it to another user - pub async fn subscenario_3(&mut self) -> Result<(), ExecutionFailureKind> { + pub async fn subscenario_2(&mut self) -> Result<(), ExecutionFailureKind> { let acc_addr = self.create_new_account().await; let acc_addr_rec = self.create_new_account().await; @@ -1615,21 +1603,8 @@ impl NodeCore { Ok(()) } - // ///Deposit balance, shielded send it to another user - // pub async fn subscenario_4(&mut self) -> Result<(), ExecutionFailureKind> { - // let acc_addr = self.create_new_account().await; - // let acc_addr_rec = self.create_new_account().await; - // - // self.operate_account_deposit_public(acc_addr, 100).await?; - // - // self.operate_account_send_shielded_one_receiver(acc_addr, acc_addr_rec, 100) - // .await?; - // - // Ok(()) - // } - ///Mint utxo, deshielded send it to another user - pub async fn subscenario_5(&mut self) -> Result<(), ExecutionFailureKind> { + pub async fn subscenario_3(&mut self) -> Result<(), ExecutionFailureKind> { let acc_addr = self.create_new_account().await; let acc_addr_rec = self.create_new_account().await; diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index 9f45b98..a3acd3e 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -83,12 +83,12 @@ impl JsonHandler { .subscenario_1() .await .map_err(cast_common_execution_error_into_rpc_error)?, - 3 => store - .subscenario_3() + 2 => store + .subscenario_2() .await .map_err(cast_common_execution_error_into_rpc_error)?, - 5 => store - .subscenario_5() + 3 => store + .subscenario_3() .await .map_err(cast_common_execution_error_into_rpc_error)?, _ => return Err(RpcErr(RpcError::invalid_params("Scenario id not found"))), From b6ea00daa43be0508b41a98e8d5270088ad76447 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 11:36:20 -0300 Subject: [PATCH 09/18] add transactions tests --- common/src/block.rs | 6 +- common/src/merkle_tree_public/merkle_tree.rs | 4 +- .../src/merkle_tree_public/tree_leav_item.rs | 4 +- common/src/rpc_primitives/requests.rs | 4 +- common/src/transaction.rs | 100 ++++++++++++++---- node_core/src/chain_storage/block_store.rs | 4 +- node_core/src/chain_storage/mod.rs | 6 +- node_core/src/lib.rs | 33 +++--- node_core/src/sequencer_client/json.rs | 4 +- node_core/src/sequencer_client/mod.rs | 4 +- sequencer_core/src/lib.rs | 20 ++-- 11 files changed, 120 insertions(+), 69 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index 4553245..7757667 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,7 +1,7 @@ use rs_merkle::Hasher; use serde::{Deserialize, Serialize}; -use crate::{merkle_tree_public::hasher::OwnHasher, transaction::SignedTransaction}; +use crate::{merkle_tree_public::hasher::OwnHasher, transaction::Transaction}; pub type BlockHash = [u8; 32]; pub type Data = Vec; @@ -13,7 +13,7 @@ pub struct Block { pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } @@ -22,7 +22,7 @@ pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, - pub transactions: Vec, + pub transactions: Vec, pub data: Data, } diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index 8886745..bf98eb6 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -8,7 +8,7 @@ use serde::{ }; use crate::{ - transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody}, + transaction::{AuthenticatedTransaction, Transaction, TransactionBody}, utxo_commitment::UTXOCommitment, }; @@ -84,7 +84,7 @@ impl<'de, Leav: TreeLeavItem + Clone + Deserialize<'de>> serde::Deserialize<'de> } } -pub type PublicTransactionMerkleTree = HashStorageMerkleTree; +pub type PublicTransactionMerkleTree = HashStorageMerkleTree; pub type UTXOCommitmentsMerkleTree = HashStorageMerkleTree; diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs index 5599172..fadc4c8 100644 --- a/common/src/merkle_tree_public/tree_leav_item.rs +++ b/common/src/merkle_tree_public/tree_leav_item.rs @@ -1,4 +1,4 @@ -use crate::{transaction::SignedTransaction, utxo_commitment::UTXOCommitment}; +use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; use super::TreeHashType; @@ -6,7 +6,7 @@ pub trait TreeLeavItem { fn hash(&self) -> TreeHashType; } -impl TreeLeavItem for SignedTransaction { +impl TreeLeavItem for Transaction { fn hash(&self) -> TreeHashType { self.body.hash() } diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 95b004f..532cfb7 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,6 +1,6 @@ use crate::block::Block; use crate::parse_request; -use crate::transaction::SignedTransaction; +use crate::transaction::Transaction; use crate::transaction::TransactionBody; use super::errors::RpcParseError; @@ -21,7 +21,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: SignedTransaction, + pub transaction: Transaction, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 68bf640..d79c832 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -27,7 +27,7 @@ pub type CipherText = Vec; pub type Nonce = GenericArray, B1>, B0>, B0>>; pub type Tag = u8; -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum TxKind { Public, Private, @@ -35,7 +35,7 @@ pub enum TxKind { Deshielded, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] ///General transaction object pub struct TransactionBody { pub tx_kind: TxKind, @@ -233,18 +233,15 @@ pub type SignaturePrivateKey = SigningKey; /// A transaction with a signature. /// Meant to be sent through the network to the sequencer -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SignedTransaction { +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Transaction { pub body: TransactionBody, signature: TransactionSignature, public_key: VerifyingKey, } -impl SignedTransaction { - pub fn from_transaction_body( - body: TransactionBody, - private_key: SigningKey, - ) -> SignedTransaction { +impl Transaction { + pub fn new(body: TransactionBody, private_key: SigningKey) -> Transaction { let hash = body.hash(); let signature: TransactionSignature = private_key.sign(&hash); let public_key = VerifyingKey::from(&private_key); @@ -264,27 +261,27 @@ impl SignedTransaction { Ok(AuthenticatedTransaction { hash, - signed_tx: self, + transaction: self, }) } } /// A transaction with a valid signature over the hash of its body. -/// Can only be constructed from an `SignedTransaction` +/// Can only be constructed from an `Transaction` /// if the signature is valid -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct AuthenticatedTransaction { hash: TransactionHash, - signed_tx: SignedTransaction, + transaction: Transaction, } impl AuthenticatedTransaction { - pub fn as_signed(&self) -> &SignedTransaction { - &self.signed_tx + pub fn as_transaction(&self) -> &Transaction { + &self.transaction } pub fn body(&self) -> &TransactionBody { - &self.signed_tx.body + &self.transaction.body } pub fn hash(&self) -> &TransactionHash { @@ -294,17 +291,18 @@ impl AuthenticatedTransaction { #[cfg(test)] mod tests { + use super::*; + use k256::FieldBytes; use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak}; use sha2::{digest::FixedOutput, Digest}; use crate::{ merkle_tree_public::TreeHashType, - transaction::{TransactionBody, TxKind}, + transaction::{Transaction, TransactionBody, TxKind}, }; - #[test] - fn test_transaction_hash_is_sha256_of_json_bytes() { - let tx = TransactionBody { + fn test_transaction_body() -> TransactionBody { + TransactionBody { tx_kind: TxKind::Public, execution_input: vec![1, 2, 3, 4], execution_output: vec![5, 6, 7, 8], @@ -319,16 +317,72 @@ mod tests { secret_r: [8; 32], sc_addr: "someAddress".to_string(), state_changes: (serde_json::Value::Null, 10), - }; + } + } + + #[test] + fn test_transaction_hash_is_sha256_of_json_bytes() { + let body = test_transaction_body(); let expected_hash = { - let data = serde_json::to_vec(&tx).unwrap(); + let data = serde_json::to_vec(&body).unwrap(); let mut hasher = sha2::Sha256::new(); hasher.update(&data); TreeHashType::from(hasher.finalize_fixed()) }; - let hash = tx.hash(); + let hash = body.hash(); assert_eq!(expected_hash, hash); } + + #[test] + fn test_transaction_constructor() { + let body = test_transaction_body(); + let key_bytes = FieldBytes::from_slice(&[37; 32]); + let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); + let transaction = Transaction::new(body.clone(), private_key.clone()); + assert_eq!( + transaction.public_key, + SignaturePublicKey::from(&private_key) + ); + assert_eq!(transaction.body, body); + } + + #[test] + fn test_into_authenticated_succeeds_for_valid_signature() { + let body = test_transaction_body(); + let key_bytes = FieldBytes::from_slice(&[37; 32]); + let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); + let transaction = Transaction::new(body, private_key.clone()); + let authenticated_tx = transaction.clone().into_authenticated().unwrap(); + + let signature = authenticated_tx.as_transaction().signature; + let hash = authenticated_tx.hash(); + assert_eq!(authenticated_tx.as_transaction(), &transaction); + assert_eq!(hash, &transaction.body.hash()); + + assert!(authenticated_tx + .as_transaction() + .public_key + .verify(hash, &signature) + .is_ok()); + } + + #[test] + fn test_into_authenticated_fails_for_invalid_signature() { + let body = test_transaction_body(); + let key_bytes = FieldBytes::from_slice(&[37; 32]); + let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); + let transaction = { + let mut this = Transaction::new(body, private_key.clone()); + // Modify the signature to make it invalid + // We do this by changing it to the signature of something else + this.signature = private_key.sign(b"deadbeef"); + this + }; + matches!( + transaction.into_authenticated(), + Err(TransactionSignatureError::InvalidSignature) + ); + } } diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index c6bc386..bcb988b 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Result}; use common::block::Block; use common::merkle_tree_public::merkle_tree::HashStorageMerkleTree; use common::nullifier::UTXONullifier; -use common::transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody}; +use common::transaction::{AuthenticatedTransaction, Transaction, TransactionBody}; use common::utxo_commitment::UTXOCommitment; use log::error; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; @@ -87,7 +87,7 @@ impl NodeBlockStore { )?) } - pub fn get_snapshot_transaction(&self) -> Result> { + pub fn get_snapshot_transaction(&self) -> Result> { Ok(serde_json::from_slice( &self.dbio.get_snapshot_transaction()?, )?) diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index bb8641a..32a2eea 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -279,7 +279,7 @@ mod tests { use accounts::account_core::Account; use common::block::{Block, Data}; use common::merkle_tree_public::TreeHashType; - use common::transaction::{SignaturePrivateKey, SignedTransaction, TransactionBody, TxKind}; + use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use secp256k1_zkp::Tweak; use std::path::PathBuf; use tempfile::tempdir; @@ -301,7 +301,7 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> SignedTransaction { + ) -> Transaction { let mut rng = rand::thread_rng(); let body = TransactionBody { @@ -320,7 +320,7 @@ mod tests { sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), }; - SignedTransaction::from_transaction_body(body, SignaturePrivateKey::random(&mut rng)) + Transaction::new(body, SignaturePrivateKey::random(&mut rng)) } fn create_sample_block(block_id: u64, prev_block_id: u64) -> Block { diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 0c10578..45b0988 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::{ Arc, }; -use common::{transaction::SignedTransaction, ExecutionFailureKind}; +use common::{transaction::Transaction, ExecutionFailureKind}; use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; @@ -189,7 +189,7 @@ impl NodeCore { &self, acc: AccountAddress, amount: u128, - ) -> Result<(SignedTransaction, [u8; 32]), ExecutionFailureKind> { + ) -> Result<(Transaction, [u8; 32]), ExecutionFailureKind> { let (utxo, receipt) = prove_mint_utxo(amount, acc)?; let result_hash = utxo.hash; @@ -267,7 +267,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), result_hash, )) } @@ -277,7 +277,7 @@ impl NodeCore { acc: AccountAddress, amount: u128, number_of_assets: usize, - ) -> Result<(SignedTransaction, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(Transaction, Vec<[u8; 32]>), ExecutionFailureKind> { let (utxos, receipt) = prove_mint_utxo_multiple_assets(amount, number_of_assets, acc)?; let result_hashes = utxos.iter().map(|utxo| utxo.hash).collect(); @@ -364,7 +364,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), result_hashes, )) } @@ -374,7 +374,7 @@ impl NodeCore { utxo: UTXO, commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -481,7 +481,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -492,7 +492,7 @@ impl NodeCore { commitments_in: Vec<[u8; 32]>, number_to_send: usize, receiver: AccountAddress, - ) -> Result<(SignedTransaction, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { + ) -> Result<(Transaction, Vec<[u8; 32]>, Vec<[u8; 32]>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxos[0].owner).unwrap(); @@ -627,7 +627,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes_receiver, utxo_hashes_not_spent, )) @@ -638,7 +638,7 @@ impl NodeCore { acc: AccountAddress, balance: u64, receivers: Vec<(u128, AccountAddress)>, - ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&acc).unwrap(); @@ -757,7 +757,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -767,7 +767,7 @@ impl NodeCore { utxo: UTXO, comm_gen_hash: [u8; 32], receivers: Vec<(u128, AccountAddress)>, - ) -> Result { + ) -> Result { let acc_map_read_guard = self.storage.read().await; let commitment_in = acc_map_read_guard @@ -847,10 +847,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); - Ok(SignedTransaction::from_transaction_body( - transaction_body, - key_to_sign_transaction, - )) + Ok(Transaction::new(transaction_body, key_to_sign_transaction)) } pub async fn send_private_mint_tx( @@ -1369,7 +1366,7 @@ impl NodeCore { commitment_in: [u8; 32], receivers: Vec<(u128, AccountAddress)>, visibility_list: [bool; 3], - ) -> Result<(SignedTransaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { + ) -> Result<(Transaction, Vec<(AccountAddress, [u8; 32])>), ExecutionFailureKind> { let acc_map_read_guard = self.storage.read().await; let account = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); @@ -1490,7 +1487,7 @@ impl NodeCore { let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - SignedTransaction::from_transaction_body(transaction_body, key_to_sign_transaction), + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } diff --git a/node_core/src/sequencer_client/json.rs b/node_core/src/sequencer_client/json.rs index 9275ca5..df94ca2 100644 --- a/node_core/src/sequencer_client/json.rs +++ b/node_core/src/sequencer_client/json.rs @@ -1,11 +1,11 @@ -use common::transaction::SignedTransaction; +use common::transaction::Transaction; use serde::{Deserialize, Serialize}; //Requests #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: SignedTransaction, + pub transaction: Transaction, ///UTXO Commitment Root, Pub Tx Root pub tx_roots: [[u8; 32]; 2], } diff --git a/node_core/src/sequencer_client/mod.rs b/node_core/src/sequencer_client/mod.rs index 95a3378..7accd5f 100644 --- a/node_core/src/sequencer_client/mod.rs +++ b/node_core/src/sequencer_client/mod.rs @@ -4,7 +4,7 @@ use common::rpc_primitives::requests::{ GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, RegisterAccountRequest, RegisterAccountResponse, }; -use common::transaction::SignedTransaction; +use common::transaction::Transaction; use common::{SequencerClientError, SequencerRpcError}; use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcResponse}; use k256::elliptic_curve::group::GroupEncoding; @@ -72,7 +72,7 @@ impl SequencerClient { pub async fn send_tx( &self, - transaction: SignedTransaction, + transaction: Transaction, tx_roots: [[u8; 32]; 2], ) -> Result { let tx_req = SendTxRequest { diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 174763e..3a74119 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -5,7 +5,7 @@ use common::{ block::{Block, HashableBlockData}, merkle_tree_public::TreeHashType, nullifier::UTXONullifier, - transaction::{AuthenticatedTransaction, SignedTransaction, TransactionBody, TxKind}, + transaction::{AuthenticatedTransaction, Transaction, TransactionBody, TxKind}, utxo_commitment::UTXOCommitment, }; use config::SequencerConfig; @@ -72,7 +72,7 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, - tx: SignedTransaction, + tx: Transaction, tx_roots: [[u8; 32]; 2], ) -> Result { let tx = tx @@ -174,17 +174,17 @@ impl SequencerCore { pub fn push_tx_into_mempool_pre_check( &mut self, - signed_tx: SignedTransaction, + transaction: Transaction, tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { let mempool_size = self.mempool.len(); if mempool_size >= self.sequencer_config.max_num_tx_in_block { return Err(TransactionMalformationErrorKind::MempoolFullForRound { - tx: signed_tx.body.hash(), + tx: transaction.body.hash(), }); } - let authenticated_tx = self.transaction_pre_check(signed_tx, tx_roots)?; + let authenticated_tx = self.transaction_pre_check(transaction, tx_roots)?; self.mempool.push_item(authenticated_tx.into()); @@ -213,7 +213,7 @@ impl SequencerCore { }); } - self.store.pub_tx_store.add_tx(tx.tx.as_signed()); + self.store.pub_tx_store.add_tx(tx.tx.as_transaction()); Ok(()) } @@ -248,7 +248,7 @@ impl SequencerCore { prev_block_id: self.chain_height, transactions: transactions .into_iter() - .map(|tx_mem| tx_mem.tx.as_signed().clone()) + .map(|tx_mem| tx_mem.tx.as_transaction().clone()) .collect(), data: vec![], prev_block_hash, @@ -269,7 +269,7 @@ mod tests { use super::*; use std::path::PathBuf; - use common::transaction::{SignaturePrivateKey, SignedTransaction, TransactionBody, TxKind}; + use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use rand::Rng; use secp256k1_zkp::Tweak; use transaction_mempool::MempoolTransaction; @@ -295,7 +295,7 @@ mod tests { nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, - ) -> SignedTransaction { + ) -> Transaction { let mut rng = rand::thread_rng(); let body = TransactionBody { @@ -314,7 +314,7 @@ mod tests { sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), }; - SignedTransaction::from_transaction_body(body, SignaturePrivateKey::random(&mut rng)) + Transaction::new(body, SignaturePrivateKey::random(&mut rng)) } fn common_setup(sequencer: &mut SequencerCore) { From e79c30f765d45eb65eb761bb429dca89ca345d75 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 11:43:40 -0300 Subject: [PATCH 10/18] add test for signing key getter --- accounts/src/key_management/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index e871a0f..e3ba4b3 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -325,6 +325,16 @@ mod tests { assert_eq!(decrypted_data, plaintext); } + #[test] + fn test_get_public_account_signing_key() { + let address_key_holder = AddressKeyHolder::new_os_random(); + let signing_key = address_key_holder.get_pub_account_signing_key(); + assert_eq!( + signing_key.to_bytes().as_slice(), + address_key_holder.pub_account_signing_key + ); + } + #[test] fn key_generation_test() { let seed_holder = SeedHolder::new_os_random(); From c60c7235c13ab8d5e38ae03a8877a71ee4b0304c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 11:54:11 -0300 Subject: [PATCH 11/18] add getter for transaction body and test it --- accounts/src/key_management/mod.rs | 1 + .../src/merkle_tree_public/tree_leav_item.rs | 2 +- common/src/transaction.rs | 61 +++++++++++++++---- node_core/src/chain_storage/mod.rs | 15 ++--- node_core/src/lib.rs | 20 +++--- node_rpc/src/process.rs | 16 ++--- sequencer_core/src/lib.rs | 26 ++++---- ...tion_mempool.rs => mempool_transaction.rs} | 8 +-- 8 files changed, 94 insertions(+), 55 deletions(-) rename sequencer_core/src/{transaction_mempool.rs => mempool_transaction.rs} (72%) diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index e3ba4b3..ea4eff2 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -58,6 +58,7 @@ impl AddressKeyHolder { } } + /// Returns the signing key for public transaction signatures pub fn get_pub_account_signing_key(&self) -> SigningKey { let field_bytes = FieldBytes::from_slice(&self.pub_account_signing_key); // TODO: remove unwrap diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs index fadc4c8..3b919a8 100644 --- a/common/src/merkle_tree_public/tree_leav_item.rs +++ b/common/src/merkle_tree_public/tree_leav_item.rs @@ -8,7 +8,7 @@ pub trait TreeLeavItem { impl TreeLeavItem for Transaction { fn hash(&self) -> TreeHashType { - self.body.hash() + self.body().hash() } } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index d79c832..b48afce 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -231,16 +231,18 @@ pub type TransactionSignature = Signature; pub type SignaturePublicKey = VerifyingKey; pub type SignaturePrivateKey = SigningKey; -/// A transaction with a signature. +/// A container for a transaction body with a signature. /// Meant to be sent through the network to the sequencer #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct Transaction { - pub body: TransactionBody, + body: TransactionBody, signature: TransactionSignature, public_key: VerifyingKey, } impl Transaction { + /// Returns a new transaction signed with the provided `private_key`. + /// The signature is generated over the hash of the body as computed by `body.hash()` pub fn new(body: TransactionBody, private_key: SigningKey) -> Transaction { let hash = body.hash(); let signature: TransactionSignature = private_key.sign(&hash); @@ -252,6 +254,8 @@ impl Transaction { } } + /// Converts the transaction into an `AuthenticatedTransaction` by verifying its signature. + /// Returns an error if the signature verification fails. pub fn into_authenticated(self) -> Result { let hash = self.body.hash(); @@ -264,6 +268,11 @@ impl Transaction { transaction: self, }) } + + /// Returns the body of the transaction + pub fn body(&self) -> &TransactionBody { + &self.body + } } /// A transaction with a valid signature over the hash of its body. @@ -276,14 +285,12 @@ pub struct AuthenticatedTransaction { } impl AuthenticatedTransaction { - pub fn as_transaction(&self) -> &Transaction { + /// Returns the underlying transaction + pub fn transaction(&self) -> &Transaction { &self.transaction } - pub fn body(&self) -> &TransactionBody { - &self.transaction.body - } - + /// Returns the precomputed hash over the body of the transaction pub fn hash(&self) -> &TransactionHash { &self.hash } @@ -320,6 +327,13 @@ mod tests { } } + fn test_transaction() -> Transaction { + let body = test_transaction_body(); + let key_bytes = FieldBytes::from_slice(&[37; 32]); + let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); + Transaction::new(body, private_key) + } + #[test] fn test_transaction_hash_is_sha256_of_json_bytes() { let body = test_transaction_body(); @@ -349,20 +363,26 @@ mod tests { } #[test] - fn test_into_authenticated_succeeds_for_valid_signature() { + fn test_transaction_body_getter() { let body = test_transaction_body(); let key_bytes = FieldBytes::from_slice(&[37; 32]); let private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); - let transaction = Transaction::new(body, private_key.clone()); + let transaction = Transaction::new(body.clone(), private_key.clone()); + assert_eq!(transaction.body(), &body); + } + + #[test] + fn test_into_authenticated_succeeds_for_valid_signature() { + let transaction = test_transaction(); let authenticated_tx = transaction.clone().into_authenticated().unwrap(); - let signature = authenticated_tx.as_transaction().signature; + let signature = authenticated_tx.transaction().signature; let hash = authenticated_tx.hash(); - assert_eq!(authenticated_tx.as_transaction(), &transaction); - assert_eq!(hash, &transaction.body.hash()); + assert_eq!(authenticated_tx.transaction(), &transaction); + assert_eq!(hash, &transaction.body.hash()); assert!(authenticated_tx - .as_transaction() + .transaction() .public_key .verify(hash, &signature) .is_ok()); @@ -380,9 +400,24 @@ mod tests { this.signature = private_key.sign(b"deadbeef"); this }; + matches!( transaction.into_authenticated(), Err(TransactionSignatureError::InvalidSignature) ); } + + #[test] + fn test_authenticated_transaction_getter() { + let transaction = test_transaction(); + let authenticated_tx = transaction.clone().into_authenticated().unwrap(); + assert_eq!(authenticated_tx.transaction(), &transaction); + } + + #[test] + fn test_authenticated_transaction_hash_getter() { + let transaction = test_transaction(); + let authenticated_tx = transaction.clone().into_authenticated().unwrap(); + assert_eq!(authenticated_tx.hash(), &transaction.body.hash()); + } } diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 32a2eea..f190ff3 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -127,8 +127,9 @@ impl NodeChainStore { let block_id = block.block_id; for tx in &block.transactions { - if !tx.body.execution_input.is_empty() { - let public_action = serde_json::from_slice::(&tx.body.execution_input); + if !tx.body().execution_input.is_empty() { + let public_action = + serde_json::from_slice::(&tx.body().execution_input); if let Ok(public_action) = public_action { match public_action { @@ -162,7 +163,7 @@ impl NodeChainStore { } self.utxo_commitments_store.add_tx_multiple( - tx.body + tx.body() .utxo_commitments_created_hashes .clone() .into_iter() @@ -170,17 +171,17 @@ impl NodeChainStore { .collect(), ); - for nullifier in tx.body.nullifier_created_hashes.iter() { + for nullifier in tx.body().nullifier_created_hashes.iter() { self.nullifier_store.insert(UTXONullifier { utxo_hash: *nullifier, }); } - if !tx.body.encoded_data.is_empty() { + if !tx.body().encoded_data.is_empty() { let ephemeral_public_key_sender = - serde_json::from_slice::(&tx.body.ephemeral_pub_key)?; + serde_json::from_slice::(&tx.body().ephemeral_pub_key)?; - for (ciphertext, nonce, tag) in tx.body.encoded_data.clone() { + for (ciphertext, nonce, tag) in tx.body().encoded_data.clone() { let slice = nonce.as_slice(); let nonce = accounts::key_management::constants_types::Nonce::clone_from_slice(slice); diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 45b0988..7debc2f 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -860,10 +860,10 @@ impl NodeCore { let point_before_prove = std::time::Instant::now(); let (tx, utxo_hash) = self.mint_utxo_private(acc, amount).await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); - let commitment_generated_hash = tx.body.utxo_commitments_created_hashes[0]; + let commitment_generated_hash = tx.body().utxo_commitments_created_hashes[0]; let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Mint utxo proof spent {timedelta:?} milliseconds"); @@ -888,10 +888,10 @@ impl NodeCore { let (tx, utxo_hashes) = self .mint_utxo_multiple_assets_private(acc, amount, number_of_assets) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); - let commitment_generated_hashes = tx.body.utxo_commitments_created_hashes.clone(); + let commitment_generated_hashes = tx.body().utxo_commitments_created_hashes.clone(); let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Mint utxo proof spent {timedelta:?} milliseconds"); @@ -961,7 +961,7 @@ impl NodeCore { let (tx, utxo_hashes) = self .transfer_utxo_private(utxo, comm_hash, receivers) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -987,7 +987,7 @@ impl NodeCore { let (tx, utxo_hashes_received, utxo_hashes_not_spent) = self .transfer_utxo_multiple_assets_private(utxos, comm_hashes, number_to_send, receiver) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1013,7 +1013,7 @@ impl NodeCore { let (tx, utxo_hashes) = self .transfer_balance_shielded(acc, amount, receivers) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1038,7 +1038,7 @@ impl NodeCore { let tx = self .transfer_utxo_deshielded(utxo, comm_gen_hash, receivers) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); @@ -1507,13 +1507,13 @@ impl NodeCore { let (tx, utxo_hashes) = self .split_utxo(utxo, comm_hash, receivers, visibility_list) .await?; - tx.body.log(); + tx.body().log(); let point_after_prove = std::time::Instant::now(); let timedelta = (point_after_prove - point_before_prove).as_millis(); info!("Send private utxo proof spent {timedelta:?} milliseconds"); - let commitments = tx.body.utxo_commitments_created_hashes.clone(); + let commitments = tx.body().utxo_commitments_created_hashes.clone(); Ok(( self.sequencer_client.send_tx(tx, tx_roots).await?, diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index a3acd3e..be44794 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -303,46 +303,46 @@ impl JsonHandler { ShowTransactionResponse { hash: req.tx_hash, - tx_kind: tx.body.tx_kind, + tx_kind: tx.body().tx_kind, public_input: if let Ok(action) = - serde_json::from_slice::(&tx.body.execution_input) + serde_json::from_slice::(&tx.body().execution_input) { action.into_hexed_print() } else { "".to_string() }, public_output: if let Ok(action) = - serde_json::from_slice::(&tx.body.execution_output) + serde_json::from_slice::(&tx.body().execution_output) { action.into_hexed_print() } else { "".to_string() }, utxo_commitments_created_hashes: tx - .body + .body() .utxo_commitments_created_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), utxo_commitments_spent_hashes: tx - .body + .body() .utxo_commitments_spent_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), utxo_nullifiers_created_hashes: tx - .body + .body() .nullifier_created_hashes .iter() .map(|val| hex::encode(val.clone())) .collect::>(), encoded_data: tx - .body + .body() .encoded_data .iter() .map(|val| (hex::encode(val.0.clone()), hex::encode(val.1.clone()))) .collect::>(), - ephemeral_pub_key: hex::encode(tx.body.ephemeral_pub_key.clone()), + ephemeral_pub_key: hex::encode(tx.body().ephemeral_pub_key.clone()), } } }; diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 3a74119..7ee365c 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -10,13 +10,13 @@ use common::{ }; use config::SequencerConfig; use mempool::MemPool; +use mempool_transaction::MempoolTransaction; use sequencer_store::{accounts_store::AccountPublicData, SequecerChainStore}; use serde::{Deserialize, Serialize}; -use transaction_mempool::MempoolTransaction; pub mod config; +pub mod mempool_transaction; pub mod sequencer_store; -pub mod transaction_mempool; pub struct SequencerCore { pub store: SequecerChainStore, @@ -86,7 +86,7 @@ impl SequencerCore { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx.body(); + } = tx.transaction().body(); let tx_hash = *tx.hash(); @@ -180,7 +180,7 @@ impl SequencerCore { let mempool_size = self.mempool.len(); if mempool_size >= self.sequencer_config.max_num_tx_in_block { return Err(TransactionMalformationErrorKind::MempoolFullForRound { - tx: transaction.body.hash(), + tx: transaction.body().hash(), }); } @@ -193,13 +193,13 @@ impl SequencerCore { fn execute_check_transaction_on_state( &mut self, - tx: &MempoolTransaction, + mempool_tx: &MempoolTransaction, ) -> Result<(), TransactionMalformationErrorKind> { let TransactionBody { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx.tx.body(); + } = mempool_tx.auth_tx.transaction().body(); for utxo_comm in utxo_commitments_created_hashes { self.store @@ -213,7 +213,9 @@ impl SequencerCore { }); } - self.store.pub_tx_store.add_tx(tx.tx.as_transaction()); + self.store + .pub_tx_store + .add_tx(mempool_tx.auth_tx.transaction()); Ok(()) } @@ -248,7 +250,7 @@ impl SequencerCore { prev_block_id: self.chain_height, transactions: transactions .into_iter() - .map(|tx_mem| tx_mem.tx.as_transaction().clone()) + .map(|tx_mem| tx_mem.auth_tx.transaction().clone()) .collect(), data: vec![], prev_block_hash, @@ -270,9 +272,9 @@ mod tests { use std::path::PathBuf; use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; + use mempool_transaction::MempoolTransaction; use rand::Rng; use secp256k1_zkp::Tweak; - use transaction_mempool::MempoolTransaction; fn setup_sequencer_config() -> SequencerConfig { let mut rng = rand::thread_rng(); @@ -320,7 +322,7 @@ mod tests { fn common_setup(sequencer: &mut SequencerCore) { let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]); let tx_mempool = MempoolTransaction { - tx: tx.into_authenticated().unwrap(), + auth_tx: tx.into_authenticated().unwrap(), }; sequencer.mempool.push_item(tx_mempool); @@ -377,7 +379,7 @@ mod tests { // Fill the mempool let dummy_tx = MempoolTransaction { - tx: tx.clone().into_authenticated().unwrap(), + auth_tx: tx.clone().into_authenticated().unwrap(), }; sequencer.mempool.push_item(dummy_tx); @@ -411,7 +413,7 @@ mod tests { let tx = create_dummy_transaction(vec![[94; 32]], vec![[7; 32]], vec![[8; 32]]); let tx_mempool = MempoolTransaction { - tx: tx.into_authenticated().unwrap(), + auth_tx: tx.into_authenticated().unwrap(), }; sequencer.mempool.push_item(tx_mempool); diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/mempool_transaction.rs similarity index 72% rename from sequencer_core/src/transaction_mempool.rs rename to sequencer_core/src/mempool_transaction.rs index fc73fc9..104e063 100644 --- a/sequencer_core/src/transaction_mempool.rs +++ b/sequencer_core/src/mempool_transaction.rs @@ -3,12 +3,12 @@ use mempool::mempoolitem::MemPoolItem; use serde::{Deserialize, Serialize}; pub struct MempoolTransaction { - pub tx: AuthenticatedTransaction, + pub auth_tx: AuthenticatedTransaction, } impl From for MempoolTransaction { - fn from(value: AuthenticatedTransaction) -> Self { - Self { tx: value } + fn from(auth_tx: AuthenticatedTransaction) -> Self { + Self { auth_tx } } } @@ -16,6 +16,6 @@ impl MemPoolItem for MempoolTransaction { type Identifier = TreeHashType; fn identifier(&self) -> Self::Identifier { - *self.tx.hash() + *self.auth_tx.hash() } } From 079b3439819a44f2e2975a28926110e6958fcfaf Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 16 Jul 2025 13:51:13 -0300 Subject: [PATCH 12/18] remove unused hash parameter in dummy function --- node_core/src/chain_storage/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index f190ff3..09240fd 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -297,8 +297,6 @@ mod tests { } fn create_dummy_transaction( - hash: TreeHashType, - // execution_input: Vec, nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, @@ -418,7 +416,6 @@ mod tests { .utxo_commitments_store .add_tx_multiple(vec![UTXOCommitment { hash: [3u8; 32] }]); store.pub_tx_store.add_tx(&create_dummy_transaction( - [12; 32], vec![[9; 32]], vec![[7; 32]], vec![[8; 32]], From 6d5e2a1a9ffbd8832d7c231bafcbd4674e0b6c96 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 17 Jul 2025 08:36:46 -0300 Subject: [PATCH 13/18] change variable name --- accounts/src/key_management/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 0ad0b4c..66c02c6 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -42,7 +42,7 @@ impl AddressKeyHolder { let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); - let pub_account_secret_key = { + let pub_account_signing_key = { let mut bytes = [0; 32]; OsRng.fill_bytes(&mut bytes); bytes @@ -54,7 +54,7 @@ impl AddressKeyHolder { address, nullifer_public_key, viewing_public_key, - pub_account_signing_key: pub_account_secret_key, + pub_account_signing_key, } } From 39b4c866a128feed88097deea32adc9fe0eef850 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 21 Jul 2025 17:50:08 -0300 Subject: [PATCH 14/18] fix double-hashing bug --- common/src/transaction.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/src/transaction.rs b/common/src/transaction.rs index b48afce..779a19e 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,6 +1,6 @@ use k256::{ ecdsa::{ - signature::{Signer, Verifier}, + signature::hazmat::{PrehashSigner, PrehashVerifier}, Signature, SigningKey, VerifyingKey, }, EncodedPoint, Scalar, @@ -245,7 +245,7 @@ impl Transaction { /// The signature is generated over the hash of the body as computed by `body.hash()` pub fn new(body: TransactionBody, private_key: SigningKey) -> Transaction { let hash = body.hash(); - let signature: TransactionSignature = private_key.sign(&hash); + let signature: TransactionSignature = private_key.sign_prehash(&hash).unwrap(); let public_key = VerifyingKey::from(&private_key); Self { body, @@ -260,7 +260,7 @@ impl Transaction { let hash = self.body.hash(); self.public_key - .verify(&hash, &self.signature) + .verify_prehash(&hash, &self.signature) .map_err(|_| TransactionSignatureError::InvalidSignature)?; Ok(AuthenticatedTransaction { @@ -299,7 +299,7 @@ impl AuthenticatedTransaction { #[cfg(test)] mod tests { use super::*; - use k256::FieldBytes; + use k256::{ecdsa::signature::Signer, FieldBytes}; use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak}; use sha2::{digest::FixedOutput, Digest}; @@ -384,7 +384,7 @@ mod tests { assert!(authenticated_tx .transaction() .public_key - .verify(hash, &signature) + .verify_prehash(hash, &signature) .is_ok()); } From b3323c78cf96a42169bc6e9cf6b6453c8c34c7e6 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 21 Jul 2025 18:46:50 -0300 Subject: [PATCH 15/18] clippy --- common/src/merkle_tree_public/merkle_tree.rs | 2 +- common/src/rpc_primitives/requests.rs | 1 - common/src/transaction.rs | 19 +++++++------------ storage/src/lib.rs | 10 +++++----- utxo/src/utxo_core.rs | 2 +- 5 files changed, 14 insertions(+), 20 deletions(-) diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index 9b98a87..5e104f8 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -8,7 +8,7 @@ use serde::{ }; use crate::{ - transaction::{AuthenticatedTransaction, Transaction, TransactionBody}, + transaction::Transaction, utxo_commitment::UTXOCommitment, }; diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index 5bebfad..395badf 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -1,7 +1,6 @@ use crate::block::Block; use crate::parse_request; use crate::transaction::Transaction; -use crate::transaction::TransactionBody; use super::errors::RpcParseError; use super::parser::parse_params; diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 779a19e..1704b4f 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,17 +1,12 @@ -use k256::{ - ecdsa::{ +use k256::ecdsa::{ signature::hazmat::{PrehashSigner, PrehashVerifier}, Signature, SigningKey, VerifyingKey, - }, - EncodedPoint, Scalar, -}; + }; use log::info; use secp256k1_zkp::{PedersenCommitment, Tweak}; -use serde::de::{Error as DeError, Visitor}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; -use std::fmt; use crate::merkle_tree_public::TreeHashType; @@ -156,7 +151,7 @@ impl ActionData { .into_iter() .map(|owned_utxo| owned_utxo.into()) .collect(); - format!("Published utxos {:?}", pub_own_utxo) + format!("Published utxos {pub_own_utxo:?}") } } } @@ -195,21 +190,21 @@ impl TransactionBody { "Transaction utxo_commitments_spent_hashes is {:?}", self.utxo_commitments_spent_hashes .iter() - .map(|val| hex::encode(val.clone())) + .map(|val| hex::encode(*val)) .collect::>() ); info!( "Transaction utxo_commitments_created_hashes is {:?}", self.utxo_commitments_created_hashes .iter() - .map(|val| hex::encode(val.clone())) + .map(|val| hex::encode(*val)) .collect::>() ); info!( "Transaction nullifier_created_hashes is {:?}", self.nullifier_created_hashes .iter() - .map(|val| hex::encode(val.clone())) + .map(|val| hex::encode(*val)) .collect::>() ); info!( diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 7616842..ac54406 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -316,7 +316,7 @@ impl RocksDBIO { let cf_sc = self.sc_column(); let sc_addr_loc = format!("{sc_addr:?}{SC_LEN_SUFFIX}"); - let sc_len_addr = sc_addr_loc.as_str().as_bytes(); + let sc_len_addr = sc_addr_loc.as_bytes(); self.db .put_cf(&cf_sc, sc_len_addr, length.to_be_bytes()) @@ -360,7 +360,7 @@ impl RocksDBIO { let cf_sc = self.sc_column(); let sc_addr_loc = format!("{sc_addr:?}{SC_LEN_SUFFIX}"); - let sc_len_addr = sc_addr_loc.as_str().as_bytes(); + let sc_len_addr = sc_addr_loc.as_bytes(); let sc_len = self .db @@ -379,11 +379,11 @@ impl RocksDBIO { ///Get full sc state from DB pub fn get_sc_sc_state(&self, sc_addr: &str) -> DbResult> { let cf_sc = self.sc_column(); - let sc_len = self.get_sc_sc_state_len(&sc_addr)?; + let sc_len = self.get_sc_sc_state_len(sc_addr)?; let mut data_blob_list = vec![]; for id in 0..sc_len { - let blob_addr = produce_address_for_data_blob_at_id(&sc_addr, id); + let blob_addr = produce_address_for_data_blob_at_id(sc_addr, id); let blob = self .db @@ -541,7 +541,7 @@ impl RocksDBIO { ///Creates address for sc data blob at corresponding id fn produce_address_for_data_blob_at_id(sc_addr: &str, id: usize) -> Vec { - let mut prefix_bytes: Vec = sc_addr.as_bytes().iter().cloned().collect(); + let mut prefix_bytes: Vec = sc_addr.as_bytes().to_vec(); let id_bytes = id.to_be_bytes(); diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs index 4e40ce3..07a8830 100644 --- a/utxo/src/utxo_core.rs +++ b/utxo/src/utxo_core.rs @@ -58,7 +58,7 @@ impl UTXO { } pub fn create_utxo_from_payload(payload_with_asset: UTXOPayload) -> Self { let mut hasher = sha2::Sha256::new(); - hasher.update(&payload_with_asset.to_bytes()); + hasher.update(payload_with_asset.to_bytes()); let hash = ::from(hasher.finalize_fixed()); Self { From 361e3b6f76def95627eba7274c2907cface24c35 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 21 Jul 2025 19:04:49 -0300 Subject: [PATCH 16/18] fmt --- accounts/src/account_core/mod.rs | 2 +- accounts/src/key_management/mod.rs | 1 - common/src/merkle_tree_public/merkle_tree.rs | 5 +---- common/src/transaction.rs | 6 +++--- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index bb0fd41..b1b4a91 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use anyhow::Result; use common::{merkle_tree_public::TreeHashType, transaction::Tag}; -use k256::{ecdsa::VerifyingKey, AffinePoint}; +use k256::AffinePoint; use log::info; use serde::{Deserialize, Serialize}; use utxo::utxo_core::UTXO; diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 66c02c6..98a8b39 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -2,7 +2,6 @@ use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use common::merkle_tree_public::TreeHashType; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; -use ephemeral_key_holder::EphemeralKeyHolder; use k256::{ecdsa::SigningKey, AffinePoint, FieldBytes}; use log::info; use rand::{rngs::OsRng, RngCore}; diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index 5e104f8..e2eb715 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -7,10 +7,7 @@ use serde::{ Deserialize, Deserializer, Serialize, }; -use crate::{ - transaction::Transaction, - utxo_commitment::UTXOCommitment, -}; +use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; use super::{hasher::OwnHasher, tree_leav_item::TreeLeavItem, TreeHashType}; diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 1704b4f..e5d0721 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,7 +1,7 @@ use k256::ecdsa::{ - signature::hazmat::{PrehashSigner, PrehashVerifier}, - Signature, SigningKey, VerifyingKey, - }; + signature::hazmat::{PrehashSigner, PrehashVerifier}, + Signature, SigningKey, VerifyingKey, +}; use log::info; use secp256k1_zkp::{PedersenCommitment, Tweak}; use serde::{Deserialize, Serialize}; From 71fdce8050763c50894e4079e3dea82a597a1033 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 21 Jul 2025 19:13:43 -0300 Subject: [PATCH 17/18] remove unused dependencies --- node_core/src/chain_storage/block_store.rs | 2 +- node_rpc/src/process.rs | 3 +-- sequencer_core/src/lib.rs | 2 +- sequencer_core/src/mempool_transaction.rs | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/node_core/src/chain_storage/block_store.rs b/node_core/src/chain_storage/block_store.rs index bcb988b..677f426 100644 --- a/node_core/src/chain_storage/block_store.rs +++ b/node_core/src/chain_storage/block_store.rs @@ -6,7 +6,7 @@ use anyhow::{anyhow, Result}; use common::block::Block; use common::merkle_tree_public::merkle_tree::HashStorageMerkleTree; use common::nullifier::UTXONullifier; -use common::transaction::{AuthenticatedTransaction, Transaction, TransactionBody}; +use common::transaction::Transaction; use common::utxo_commitment::UTXOCommitment; use log::error; use storage::sc_db_utils::{DataBlob, DataBlobChangeVariant}; diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index be44794..5fa52ad 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -23,8 +23,7 @@ use crate::types::{ ExecuteScenarioSplitResponse, ExecuteSubscenarioRequest, ExecuteSubscenarioResponse, ShowAccountPublicBalanceRequest, ShowAccountPublicBalanceResponse, ShowAccountUTXORequest, ShowAccountUTXOResponse, ShowTransactionRequest, ShowTransactionResponse, - UTXOShortEssentialStruct, WriteDepositPublicBalanceRequest, - WriteDepositPublicBalanceResponse, WriteMintPrivateUTXOMultipleAssetsRequest, + UTXOShortEssentialStruct, WriteMintPrivateUTXOMultipleAssetsRequest, WriteMintPrivateUTXOMultipleAssetsResponse, WriteMintPrivateUTXORequest, WriteMintPrivateUTXOResponse, WriteSendDeshieldedBalanceRequest, WriteSendDeshieldedUTXOResponse, WriteSendPrivateUTXORequest, WriteSendPrivateUTXOResponse, diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 1e6d199..40ff1a2 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -12,7 +12,7 @@ use common::{ use config::SequencerConfig; use mempool::MemPool; use mempool_transaction::MempoolTransaction; -use sequencer_store::{accounts_store::AccountPublicData, SequecerChainStore}; +use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; pub mod config; diff --git a/sequencer_core/src/mempool_transaction.rs b/sequencer_core/src/mempool_transaction.rs index 104e063..551aaf3 100644 --- a/sequencer_core/src/mempool_transaction.rs +++ b/sequencer_core/src/mempool_transaction.rs @@ -1,6 +1,5 @@ use common::{merkle_tree_public::TreeHashType, transaction::AuthenticatedTransaction}; use mempool::mempoolitem::MemPoolItem; -use serde::{Deserialize, Serialize}; pub struct MempoolTransaction { pub auth_tx: AuthenticatedTransaction, From 5de28c062e94dc3a8bcef616d0a48459e685496f Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 21 Jul 2025 21:07:16 -0300 Subject: [PATCH 18/18] remove usage of prehash sinc it is hazard material. Prefer use of secure api --- common/src/transaction.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/common/src/transaction.rs b/common/src/transaction.rs index e5d0721..26dc670 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,5 +1,5 @@ use k256::ecdsa::{ - signature::hazmat::{PrehashSigner, PrehashVerifier}, + signature::{Signer, Verifier}, Signature, SigningKey, VerifyingKey, }; use log::info; @@ -160,13 +160,17 @@ impl ActionData { impl TransactionBody { /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. pub fn hash(&self) -> TreeHashType { + let bytes_to_hash = self.to_bytes(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&bytes_to_hash); + TreeHashType::from(hasher.finalize_fixed()) + } + + fn to_bytes(&self) -> Vec { // TODO: Remove `unwrap` by implementing a `to_bytes` method // that deterministically encodes all transaction fields to bytes // and guarantees serialization will succeed. - let raw_data = serde_json::to_vec(&self).unwrap(); - let mut hasher = sha2::Sha256::new(); - hasher.update(&raw_data); - TreeHashType::from(hasher.finalize_fixed()) + serde_json::to_vec(&self).unwrap() } pub fn log(&self) { @@ -239,8 +243,7 @@ impl Transaction { /// Returns a new transaction signed with the provided `private_key`. /// The signature is generated over the hash of the body as computed by `body.hash()` pub fn new(body: TransactionBody, private_key: SigningKey) -> Transaction { - let hash = body.hash(); - let signature: TransactionSignature = private_key.sign_prehash(&hash).unwrap(); + let signature: TransactionSignature = private_key.sign(&body.to_bytes()); let public_key = VerifyingKey::from(&private_key); Self { body, @@ -255,7 +258,7 @@ impl Transaction { let hash = self.body.hash(); self.public_key - .verify_prehash(&hash, &self.signature) + .verify(&self.body.to_bytes(), &self.signature) .map_err(|_| TransactionSignatureError::InvalidSignature)?; Ok(AuthenticatedTransaction { @@ -379,7 +382,7 @@ mod tests { assert!(authenticated_tx .transaction() .public_key - .verify_prehash(hash, &signature) + .verify(&transaction.body.to_bytes(), &signature) .is_ok()); }