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() } }