diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs index deecaf7..1fbbb1a 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.hash + self.hash() } } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index bc25abf..abc4c3f 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -26,7 +26,6 @@ pub enum TxKind { #[derive(Debug, Serialize, Deserialize, Clone)] ///General transaction object pub struct Transaction { - pub hash: TreeHashType, pub tx_kind: TxKind, ///Tx input data (public part) pub execution_input: Vec, @@ -58,70 +57,6 @@ pub struct Transaction { pub state_changes: (serde_json::Value, usize), } -#[derive(Debug, Serialize, Deserialize, Clone)] -///General transaction object -pub struct TransactionPayload { - pub tx_kind: TxKind, - ///Tx input data (public part) - pub execution_input: Vec, - ///Tx output data (public_part) - pub execution_output: Vec, - ///Tx input utxo commitments - pub utxo_commitments_spent_hashes: Vec, - ///Tx output utxo commitments - pub utxo_commitments_created_hashes: Vec, - ///Tx output nullifiers - pub nullifier_created_hashes: Vec, - ///Execution proof (private part) - pub execution_proof_private: String, - ///Encoded blobs of data - pub encoded_data: Vec<(CipherText, Vec, Tag)>, - ///Transaction senders ephemeral pub key - pub ephemeral_pub_key: Vec, - ///Public (Pedersen) commitment - pub commitment: Vec, - ///tweak - pub tweak: Tweak, - ///secret_r - pub secret_r: [u8; 32], - ///Hex-encoded address of a smart contract account called - pub sc_addr: String, - ///Recorded changes in state of smart contract - /// - /// First value represents vector of changes, second is new length of a state - pub state_changes: (serde_json::Value, usize), -} - -impl From for Transaction { - fn from(value: TransactionPayload) -> Self { - let raw_data = serde_json::to_vec(&value).unwrap(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(&raw_data); - - let hash = ::from(hasher.finalize_fixed()); - - Self { - hash, - tx_kind: value.tx_kind, - execution_input: value.execution_input, - execution_output: value.execution_output, - utxo_commitments_spent_hashes: value.utxo_commitments_spent_hashes, - utxo_commitments_created_hashes: value.utxo_commitments_created_hashes, - nullifier_created_hashes: value.nullifier_created_hashes, - execution_proof_private: value.execution_proof_private, - encoded_data: value.encoded_data, - ephemeral_pub_key: value.ephemeral_pub_key, - commitment: value.commitment, - tweak: value.tweak, - secret_r: value.secret_r, - sc_addr: value.sc_addr, - state_changes: value.state_changes, - } - } -} - #[derive(Debug, Serialize, Deserialize)] pub struct MintMoneyPublicTx { pub acc: [u8; 32], @@ -216,8 +151,19 @@ impl ActionData { } impl Transaction { + /// 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 + // 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()) + } + pub fn log(&self) { - info!("Transaction hash is {:?}", hex::encode(self.hash)); + info!("Transaction hash is {:?}", hex::encode(self.hash())); info!("Transaction tx_kind is {:?}", self.tx_kind); info!("Transaction execution_input is {:?}", { if let Ok(action) = serde_json::from_slice::(&self.execution_input) { @@ -267,3 +213,44 @@ impl Transaction { ); } } + +#[cfg(test)] +mod tests { + use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak}; + use sha2::{digest::FixedOutput, Digest}; + + use crate::{ + merkle_tree_public::TreeHashType, + transaction::{Transaction, TxKind}, + }; + + #[test] + fn test_transaction_hash_is_sha256_of_json_bytes() { + let tx = Transaction { + tx_kind: TxKind::Public, + execution_input: vec![1, 2, 3, 4], + execution_output: vec![5, 6, 7, 8], + utxo_commitments_spent_hashes: vec![[9; 32], [10; 32], [11; 32], [12; 32]], + utxo_commitments_created_hashes: vec![[13; 32]], + nullifier_created_hashes: vec![[0; 32], [1; 32], [2; 32], [3; 32]], + execution_proof_private: "loremipsum".to_string(), + encoded_data: vec![(vec![255, 255, 255], vec![254, 254, 254], 1)], + ephemeral_pub_key: vec![5; 32], + commitment: vec![], + tweak: Tweak::from_slice(&[7; SECRET_KEY_SIZE]).unwrap(), + secret_r: [8; 32], + sc_addr: "someAddress".to_string(), + state_changes: (serde_json::Value::Null, 10), + }; + let expected_hash = { + let data = serde_json::to_vec(&tx).unwrap(); + let mut hasher = sha2::Sha256::new(); + hasher.update(&data); + TreeHashType::from(hasher.finalize_fixed()) + }; + + let hash = tx.hash(); + + assert_eq!(expected_hash, hash); + } +} diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 6412ca0..3ee0e0b 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -308,7 +308,6 @@ mod tests { let mut rng = rand::thread_rng(); Transaction { - hash, 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 bab62c1..b8ea8fe 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -11,7 +11,7 @@ use accounts::{ }; use anyhow::Result; use chain_storage::NodeChainStore; -use common::transaction::{Transaction, TransactionPayload, TxKind}; +use common::transaction::{Transaction, TxKind}; use config::NodeConfig; use log::info; use sc_core::proofs_circuits::{ @@ -250,7 +250,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -346,7 +346,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -460,7 +460,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -604,7 +604,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -725,7 +725,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Shielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( SendMoneyShieldedTx { @@ -816,7 +816,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(TransactionPayload { + Ok(Transaction { tx_kind: TxKind::Deshielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( SendMoneyDeshieldedTx { @@ -1453,7 +1453,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); Ok(( - TransactionPayload { + Transaction { tx_kind: TxKind::Shielded, execution_input: vec![], execution_output: serde_json::to_vec(&publication).unwrap(), diff --git a/sc_core/src/transaction_payloads_tools.rs b/sc_core/src/transaction_payloads_tools.rs index bf138d4..d7fe961 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, key_management::ephemeral_key_holder::EphemeralKeyHolder}; use anyhow::Result; -use common::transaction::{TransactionPayload, TxKind}; +use common::transaction::{Transaction, 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), -) -> TransactionPayload { - TransactionPayload { +) -> Transaction { + Transaction { tx_kind: TxKind::Public, execution_input, execution_output: vec![], diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/transaction_mempool.rs index 8c0d720..d932b24 100644 --- a/sequencer_core/src/transaction_mempool.rs +++ b/sequencer_core/src/transaction_mempool.rs @@ -38,6 +38,6 @@ impl MemPoolItem for TransactionMempool { type Identifier = TreeHashType; fn identifier(&self) -> Self::Identifier { - self.tx.hash + self.tx.hash() } }