diff --git a/Cargo.lock b/Cargo.lock index 10ab1e9..a3d8593 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -996,6 +996,7 @@ dependencies = [ "anyhow", "elliptic-curve", "hex", + "k256", "log", "reqwest 0.11.27", "risc0-zkvm", @@ -1246,6 +1247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -1485,6 +1487,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -3195,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 5665775..9b63a36 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -2,12 +2,14 @@ use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit}; use common::merkle_tree_public::TreeHashType; use constants_types::{CipherText, Nonce}; use elliptic_curve::point::AffineCoordinates; -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 PublicAccountSigningKey = [u8; 32]; pub mod constants_types; pub mod ephemeral_key_holder; @@ -20,6 +22,7 @@ pub struct AddressKeyHolder { #[allow(dead_code)] top_secret_key_holder: TopSecretKeyHolder, pub utxo_secret_key_holder: UTXOSecretKeyHolder, + pub_account_signing_key: PublicAccountSigningKey, pub address: TreeHashType, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, @@ -38,15 +41,29 @@ 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_signing_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_signing_key, } } + /// 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 + SigningKey::from_bytes(&field_bytes).unwrap() + } + pub fn calculate_shared_secret_receiver( &self, ephemeral_public_key_sender: AffinePoint, @@ -305,6 +322,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(); diff --git a/common/Cargo.toml b/common/Cargo.toml index bc929e3..7e4e0ab 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.3" } +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/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs index d8e99ec..e2eb715 100644 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ b/common/src/merkle_tree_public/merkle_tree.rs @@ -140,7 +140,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()); @@ -267,7 +267,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 1fbbb1a..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.hash() + self.body().hash() } } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index abc4c3f..26dc670 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,6 +1,11 @@ +use k256::ecdsa::{ + signature::{Signer, Verifier}, + Signature, SigningKey, VerifyingKey, +}; use log::info; use secp256k1_zkp::{PedersenCommitment, Tweak}; use serde::{Deserialize, Serialize}; + use sha2::{digest::FixedOutput, Digest}; use crate::merkle_tree_public::TreeHashType; @@ -11,11 +16,13 @@ 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; -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum TxKind { Public, Private, @@ -23,9 +30,9 @@ pub enum TxKind { Deshielded, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] ///General transaction object -pub struct Transaction { +pub struct TransactionBody { pub tx_kind: TxKind, ///Tx input data (public part) pub execution_input: Vec, @@ -150,16 +157,20 @@ impl ActionData { } } -impl Transaction { +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) { @@ -214,19 +225,89 @@ impl Transaction { } } +type TransactionHash = [u8; 32]; +pub type TransactionSignature = Signature; +pub type SignaturePublicKey = VerifyingKey; +pub type SignaturePrivateKey = SigningKey; + +/// 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 { + 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 signature: TransactionSignature = private_key.sign(&body.to_bytes()); + let public_key = VerifyingKey::from(&private_key); + Self { + body, + signature, + public_key, + } + } + + /// 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(); + + self.public_key + .verify(&self.body.to_bytes(), &self.signature) + .map_err(|_| TransactionSignatureError::InvalidSignature)?; + + Ok(AuthenticatedTransaction { + hash, + 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. +/// Can only be constructed from an `Transaction` +/// if the signature is valid +#[derive(Debug, Clone)] +pub struct AuthenticatedTransaction { + hash: TransactionHash, + transaction: Transaction, +} + +impl AuthenticatedTransaction { + /// Returns the underlying transaction + pub fn transaction(&self) -> &Transaction { + &self.transaction + } + + /// Returns the precomputed hash over the body of the transaction + pub fn hash(&self) -> &TransactionHash { + &self.hash + } +} + #[cfg(test)] mod tests { + use super::*; + use k256::{ecdsa::signature::Signer, FieldBytes}; use secp256k1_zkp::{constants::SECRET_KEY_SIZE, Tweak}; use sha2::{digest::FixedOutput, Digest}; use crate::{ merkle_tree_public::TreeHashType, - transaction::{Transaction, TxKind}, + transaction::{Transaction, TransactionBody, TxKind}, }; - #[test] - fn test_transaction_hash_is_sha256_of_json_bytes() { - let tx = Transaction { + fn test_transaction_body() -> TransactionBody { + TransactionBody { tx_kind: TxKind::Public, execution_input: vec![1, 2, 3, 4], execution_output: vec![5, 6, 7, 8], @@ -241,16 +322,100 @@ mod tests { secret_r: [8; 32], sc_addr: "someAddress".to_string(), state_changes: (serde_json::Value::Null, 10), - }; + } + } + + 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(); 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_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.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.transaction().signature; + let hash = authenticated_tx.hash(); + + assert_eq!(authenticated_tx.transaction(), &transaction); + assert_eq!(hash, &transaction.body.hash()); + assert!(authenticated_tx + .transaction() + .public_key + .verify(&transaction.body.to_bytes(), &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) + ); + } + + #[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 21d9da0..c470a0a 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -126,8 +126,9 @@ 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 { @@ -161,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); @@ -203,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)?; @@ -282,7 +284,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::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use secp256k1_zkp::Tweak; use std::path::PathBuf; use tempfile::tempdir; @@ -299,14 +301,13 @@ mod tests { } fn create_dummy_transaction( - // execution_input: Vec, nullifier_created_hashes: Vec<[u8; 32]>, utxo_commitments_spent_hashes: Vec<[u8; 32]>, utxo_commitments_created_hashes: Vec<[u8; 32]>, ) -> Transaction { let mut rng = rand::thread_rng(); - Transaction { + let body = TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -321,7 +322,8 @@ mod tests { secret_r: [0; 32], sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), - } + }; + Transaction::new(body, SignaturePrivateKey::random(&mut rng)) } fn create_sample_block(block_id: u64, prev_block_id: u64) -> Block { @@ -417,7 +419,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( vec![[9; 32]], vec![[7; 32]], vec![[8; 32]], diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 995b723..e2c31c5 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::Transaction, ExecutionFailureKind}; use accounts::{ account_core::{Account, AccountAddress}, @@ -11,7 +11,7 @@ use accounts::{ }; 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::{ @@ -246,30 +246,30 @@ 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, + }; + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); Ok(( - Transaction { - 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, - }, + Transaction::new(transaction_body, key_to_sign_transaction), result_hash, )) } @@ -342,29 +342,30 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - Transaction { - 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, - }, + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + + Ok(( + Transaction::new(transaction_body, key_to_sign_transaction), result_hashes, )) } @@ -455,29 +456,31 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - Transaction { - 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, - }, + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + + Ok(( + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -598,29 +601,31 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - Transaction { - 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, - }, + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + + Ok(( + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes_receiver, utxo_hashes_not_spent, )) @@ -718,35 +723,37 @@ 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, + }; + + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + Ok(( - Transaction { - 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, - }, + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -809,7 +816,7 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(Transaction { + let transaction_body = TransactionBody { tx_kind: TxKind::Deshielded, execution_input: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( SendMoneyDeshieldedTx { @@ -830,7 +837,11 @@ impl NodeCore { secret_r, sc_addr, state_changes, - }) + }; + + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + + Ok(Transaction::new(transaction_body, key_to_sign_transaction)) } pub async fn send_private_mint_tx( @@ -843,10 +854,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"); @@ -871,10 +882,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"); @@ -886,49 +897,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: Transaction = - 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, - ); - 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, @@ -943,7 +955,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(); @@ -969,7 +981,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(); @@ -995,7 +1007,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(); @@ -1020,7 +1032,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(); @@ -1154,46 +1166,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, @@ -1443,30 +1455,31 @@ impl NodeCore { let (tweak, secret_r, commitment) = pedersen_commitment_vec(vec_public_info); - Ok(( - Transaction { - 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, - }, + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + commitment, + tweak, + secret_r, + sc_addr, + state_changes, + }; + let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key(); + + Ok(( + Transaction::new(transaction_body, key_to_sign_transaction), utxo_hashes, )) } @@ -1486,13 +1499,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?, @@ -1563,20 +1576,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; @@ -1588,21 +1589,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 bbfb281..29ac787 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, @@ -42,7 +41,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"; @@ -92,14 +90,6 @@ impl JsonHandler { .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 - .map_err(cast_common_execution_error_into_rpc_error)?, _ => return Err(RpcErr(RpcError::invalid_params("Scenario id not found"))), } } @@ -312,42 +302,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(hex::encode) .collect::>(), utxo_commitments_spent_hashes: tx + .body() .utxo_commitments_spent_hashes .iter() .map(hex::encode) .collect::>(), utxo_nullifiers_created_hashes: tx + .body() .nullifier_created_hashes .iter() .map(hex::encode) .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()), } } }; @@ -355,36 +349,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))?; @@ -777,9 +741,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/sc_core/src/transaction_payloads_tools.rs b/sc_core/src/transaction_payloads_tools.rs index d7fe961..fa0e28e 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::{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 e5ddef8..5ac482e 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -6,22 +6,22 @@ use common::{ block::{Block, HashableBlockData}, merkle_tree_public::TreeHashType, nullifier::UTXONullifier, - transaction::{Transaction, TxKind}, + transaction::{AuthenticatedTransaction, Transaction, TransactionBody, TxKind}, utxo_commitment::UTXOCommitment, }; use config::SequencerConfig; use mempool::MemPool; +use mempool_transaction::MempoolTransaction; use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; -use transaction_mempool::TransactionMempool; pub mod config; +pub mod mempool_transaction; pub mod sequencer_store; -pub mod transaction_mempool; pub struct SequencerCore { pub store: SequecerChainStore, - pub mempool: MemPool, + pub mempool: MemPool, pub sequencer_config: SequencerConfig, pub chain_height: u64, } @@ -36,6 +36,7 @@ pub enum TransactionMalformationErrorKind { MempoolFullForRound { tx: TreeHashType }, ChainStateFurtherThanTransactionState { tx: TreeHashType }, FailedToInsert { tx: TreeHashType, details: String }, + InvalidSignature, } impl Display for TransactionMalformationErrorKind { @@ -55,7 +56,7 @@ impl SequencerCore { config.is_genesis_random, &config.initial_accounts, ), - mempool: MemPool::::default(), + mempool: MemPool::::default(), chain_height: config.genesis_id, sequencer_config: config, } @@ -73,24 +74,23 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, - tx: &Transaction, + tx: Transaction, tx_roots: [[u8; 32]; 2], - ) -> Result<(), TransactionMalformationErrorKind> { - let Transaction { + ) -> Result { + let tx = tx + .into_authenticated() + .map_err(|_| TransactionMalformationErrorKind::InvalidSignature)?; + + let TransactionBody { tx_kind, ref execution_input, ref execution_output, ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx; - let hash = tx.hash(); + } = tx.transaction().body(); - let mempool_size = self.mempool.len(); - - if mempool_size >= self.sequencer_config.max_num_tx_in_block { - return Err(TransactionMalformationErrorKind::MempoolFullForRound { tx: hash }); - } + let tx_hash = *tx.hash(); let curr_sequencer_roots = self.get_tree_roots(); @@ -149,50 +149,61 @@ impl SequencerCore { }); if tx_tree_check { - return Err(TransactionMalformationErrorKind::TxHashAlreadyPresentInTree { tx: hash }); + return Err( + TransactionMalformationErrorKind::TxHashAlreadyPresentInTree { tx: *tx.hash() }, + ); } if nullifier_tree_check { return Err( - TransactionMalformationErrorKind::NullifierAlreadyPresentInTree { tx: hash }, + TransactionMalformationErrorKind::NullifierAlreadyPresentInTree { tx: *tx.hash() }, ); } if utxo_commitments_check { return Err( - TransactionMalformationErrorKind::UTXOCommitmentAlreadyPresentInTree { tx: hash }, + TransactionMalformationErrorKind::UTXOCommitmentAlreadyPresentInTree { + tx: *tx.hash(), + }, ); } - Ok(()) + Ok(tx) } pub fn push_tx_into_mempool_pre_check( &mut self, - item: TransactionMempool, + transaction: Transaction, tx_roots: [[u8; 32]; 2], ) -> Result<(), TransactionMalformationErrorKind> { - self.transaction_pre_check(&item.tx, tx_roots)?; + 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(), + }); + } - self.mempool.push_item(item); + let authenticated_tx = self.transaction_pre_check(transaction, tx_roots)?; + + self.mempool.push_item(authenticated_tx.into()); Ok(()) } fn execute_check_transaction_on_state( &mut self, - tx: TransactionMempool, + mempool_tx: &MempoolTransaction, ) -> Result<(), TransactionMalformationErrorKind> { - let Transaction { + let TransactionBody { ref utxo_commitments_created_hashes, ref nullifier_created_hashes, .. - } = tx.tx; + } = mempool_tx.auth_tx.transaction().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() { @@ -201,7 +212,9 @@ impl SequencerCore { }); } - self.store.pub_tx_store.add_tx(tx.tx); + self.store + .pub_tx_store + .add_tx(mempool_tx.auth_tx.transaction()); Ok(()) } @@ -219,7 +232,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 @@ -231,7 +244,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.auth_tx.transaction().clone()) + .collect(), data: vec![], prev_block_hash, }; @@ -253,10 +269,10 @@ mod tests { use super::*; use std::path::PathBuf; - use common::transaction::{Transaction, TxKind}; + use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; + use mempool_transaction::MempoolTransaction; use rand::Rng; use secp256k1_zkp::Tweak; - use transaction_mempool::TransactionMempool; fn setup_sequencer_config_variable_initial_accounts( initial_accounts: Vec, @@ -302,7 +318,7 @@ mod tests { ) -> Transaction { let mut rng = rand::thread_rng(); - Transaction { + let body = TransactionBody { tx_kind: TxKind::Private, execution_input: vec![], execution_output: vec![], @@ -317,13 +333,16 @@ mod tests { secret_r: [0; 32], sc_addr: "sc_addr".to_string(), state_changes: (serde_json::Value::Null, 0), - } + }; + Transaction::new(body, SignaturePrivateKey::random(&mut rng)) } 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 }; - sequencer.mempool.push_item(tx_mempool); + let mempool_tx = MempoolTransaction { + auth_tx: tx.into_authenticated().unwrap(), + }; + sequencer.mempool.push_item(mempool_tx); sequencer .produce_new_block_with_mempool_transactions() @@ -445,13 +464,13 @@ 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()); } #[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() @@ -464,10 +483,12 @@ mod tests { let tx_roots = sequencer.get_tree_roots(); // Fill the mempool - let dummy_tx = TransactionMempool { tx: tx.clone() }; + let dummy_tx = MempoolTransaction { + auth_tx: tx.clone().into_authenticated().unwrap(), + }; 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, @@ -484,9 +505,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); } @@ -497,7 +517,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 { + auth_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/mempool_transaction.rs b/sequencer_core/src/mempool_transaction.rs new file mode 100644 index 0000000..551aaf3 --- /dev/null +++ b/sequencer_core/src/mempool_transaction.rs @@ -0,0 +1,20 @@ +use common::{merkle_tree_public::TreeHashType, transaction::AuthenticatedTransaction}; +use mempool::mempoolitem::MemPoolItem; + +pub struct MempoolTransaction { + pub auth_tx: AuthenticatedTransaction, +} + +impl From for MempoolTransaction { + fn from(auth_tx: AuthenticatedTransaction) -> Self { + Self { auth_tx } + } +} + +impl MemPoolItem for MempoolTransaction { + type Identifier = TreeHashType; + + fn identifier(&self) -> Self::Identifier { + *self.auth_tx.hash() + } +} diff --git a/sequencer_core/src/transaction_mempool.rs b/sequencer_core/src/transaction_mempool.rs deleted file mode 100644 index d932b24..0000000 --- a/sequencer_core/src/transaction_mempool.rs +++ /dev/null @@ -1,43 +0,0 @@ -use common::{merkle_tree_public::TreeHashType, transaction::Transaction}; -use mempool::mempoolitem::MemPoolItem; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone)] -pub struct TransactionMempool { - pub tx: Transaction, -} - -impl From for TransactionMempool { - fn from(value: Transaction) -> Self { - Self { tx: value } - } -} - -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 Transaction::deserialize(deserializer) { - Ok(tx) => Ok(TransactionMempool { tx }), - Err(err) => Err(err), - } - } -} - -impl MemPoolItem for TransactionMempool { - type Identifier = TreeHashType; - - fn identifier(&self) -> Self::Identifier { - self.tx.hash() - } -} diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index cab07bf..f9acbfb 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -76,10 +76,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 {