diff --git a/Cargo.toml b/Cargo.toml index 2b98867..93a5ac7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ ark-bn254 = "0.5.0" ark-ff = "0.5.0" tiny-keccak = { version = "2.0.2", features = ["keccak"] } base64 = "0.22.1" +chrono = "0.4.41" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/common/src/block.rs b/common/src/block.rs index 3760d65..24da8c7 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,9 +1,7 @@ +use rs_merkle::Hasher; use std::io::{Cursor, Read}; -use k256::ecdsa::Signature; -use rs_merkle::Hasher; - -use crate::{merkle_tree_public::hasher::OwnHasher, transaction::AuthenticatedTransaction}; +use crate::{transaction::TransactionBody, OwnHasher}; pub type BlockHash = [u8; 32]; pub type BlockId = u64; @@ -16,12 +14,12 @@ pub struct BlockHeader { pub prev_block_hash: BlockHash, pub hash: BlockHash, pub timestamp: TimeStamp, - pub signature: Signature, + pub signature: nssa::Signature, } #[derive(Debug, Clone)] pub struct BlockBody { - pub transactions: Vec, + pub transactions: Vec, } #[derive(Debug, Clone)] @@ -36,26 +34,25 @@ pub struct HashableBlockData { pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub timestamp: TimeStamp, - pub signature: Signature, - pub transactions: Vec, + pub transactions: Vec, } -impl From for Block { - fn from(value: HashableBlockData) -> Self { - let data = value.to_bytes(); - let hash = OwnHasher::hash(&data); - - Self { +impl HashableBlockData { + pub fn into_block(self, signing_key: &nssa::PrivateKey) -> Block { + let data_bytes = self.to_bytes(); + let signature = nssa::Signature::new(signing_key, &data_bytes); + let hash = OwnHasher::hash(&data_bytes); + Block { header: BlockHeader { - block_id: value.block_id, - prev_block_id: value.prev_block_id, - prev_block_hash: value.prev_block_hash, + block_id: self.block_id, + prev_block_id: self.prev_block_id, + prev_block_hash: self.prev_block_hash, hash, - timestamp: value.timestamp, - signature: value.signature, + timestamp: self.timestamp, + signature, }, body: BlockBody { - transactions: value.transactions, + transactions: self.transactions, }, } } @@ -68,7 +65,6 @@ impl From for HashableBlockData { prev_block_id: value.header.prev_block_id, prev_block_hash: value.header.prev_block_hash, timestamp: value.header.timestamp, - signature: value.header.signature, transactions: value.body.transactions, } } @@ -81,10 +77,13 @@ impl HashableBlockData { bytes.extend_from_slice(&self.prev_block_id.to_le_bytes()); bytes.extend_from_slice(&self.prev_block_hash); bytes.extend_from_slice(&self.timestamp.to_le_bytes()); - bytes.extend_from_slice(&self.signature.to_bytes()); let num_transactions: u32 = self.transactions.len() as u32; bytes.extend_from_slice(&num_transactions.to_le_bytes()); for tx in &self.transactions { + let transaction_bytes = tx.to_bytes(); + let num_transaction_bytes: u32 = transaction_bytes.len() as u32; + + bytes.extend_from_slice(&num_transaction_bytes.to_le_bytes()); bytes.extend_from_slice(&tx.to_bytes()); } bytes @@ -101,17 +100,21 @@ impl HashableBlockData { cursor.read_exact(&mut prev_block_hash).unwrap(); let timestamp = u64_from_cursor(&mut cursor); - - let signature_bytes_len = u32_from_cursor(&mut cursor) as usize; - let mut signature_bytes = Vec::with_capacity(signature_bytes_len); - cursor.read_exact(&mut signature_bytes).unwrap(); - let signature = Signature::from_bytes(signature_bytes.as_slice().try_into().unwrap()).unwrap(); let num_transactions = u32_from_cursor(&mut cursor) as usize; let mut transactions = Vec::with_capacity(num_transactions); for _ in 0..num_transactions { - let tx = AuthenticatedTransaction::from_cursor(&mut cursor); + let tx_len = u32_from_cursor(&mut cursor) as usize; + let mut tx_bytes = Vec::with_capacity(tx_len); + + for _ in 0..tx_len { + let mut buff = [0; 1]; + cursor.read_exact(&mut buff).unwrap(); + tx_bytes.push(buff[0]); + } + + let tx = TransactionBody::from_bytes(tx_bytes); transactions.push(tx); } @@ -120,7 +123,6 @@ impl HashableBlockData { prev_block_id, prev_block_hash, timestamp, - signature, transactions, } } diff --git a/common/src/commitment.rs b/common/src/commitment.rs index 0874bc7..a905ad1 100644 --- a/common/src/commitment.rs +++ b/common/src/commitment.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::merkle_tree_public::CommitmentHashType; +use crate::CommitmentHashType; #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)] pub struct Commitment { diff --git a/common/src/execution_input.rs b/common/src/execution_input.rs index c4f2141..08ff598 100644 --- a/common/src/execution_input.rs +++ b/common/src/execution_input.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::merkle_tree_public::TreeHashType; +use crate::TreeHashType; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PublicNativeTokenSend { diff --git a/common/src/lib.rs b/common/src/lib.rs index 3b2ddff..a0e6df8 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,10 +1,10 @@ -use merkle_tree_public::TreeHashType; +use rs_merkle::Hasher; use serde::Deserialize; +use sha2::{digest::FixedOutput, Digest, Sha256}; pub mod block; pub mod commitment; pub mod execution_input; -pub mod merkle_tree_public; pub mod nullifier; pub mod rpc_primitives; pub mod sequencer_client; @@ -16,6 +16,25 @@ pub mod test_utils; use rpc_primitives::errors::RpcError; +pub type TreeHashType = [u8; 32]; +pub type CommitmentHashType = Vec; + +#[derive(Debug, Clone)] +///Our own hasher. +/// Currently it is SHA256 hasher wrapper. May change in a future. +pub struct OwnHasher {} + +impl Hasher for OwnHasher { + type Hash = TreeHashType; + + fn hash(data: &[u8]) -> TreeHashType { + let mut hasher = Sha256::new(); + + hasher.update(data); + ::from(hasher.finalize_fixed()) + } +} + ///Account id on blockchain pub type AccountId = TreeHashType; diff --git a/common/src/merkle_tree_public/hasher.rs b/common/src/merkle_tree_public/hasher.rs deleted file mode 100644 index b23388f..0000000 --- a/common/src/merkle_tree_public/hasher.rs +++ /dev/null @@ -1,20 +0,0 @@ -use rs_merkle::Hasher; -use sha2::{digest::FixedOutput, Digest, Sha256}; - -use super::TreeHashType; - -#[derive(Debug, Clone)] -///Our own hasher. -/// Currently it is SHA256 hasher wrapper. May change in a future. -pub struct OwnHasher {} - -impl Hasher for OwnHasher { - type Hash = TreeHashType; - - fn hash(data: &[u8]) -> TreeHashType { - let mut hasher = Sha256::new(); - - hasher.update(data); - ::from(hasher.finalize_fixed()) - } -} diff --git a/common/src/merkle_tree_public/merkle_tree.rs b/common/src/merkle_tree_public/merkle_tree.rs deleted file mode 100644 index e2eb715..0000000 --- a/common/src/merkle_tree_public/merkle_tree.rs +++ /dev/null @@ -1,312 +0,0 @@ -use std::{collections::HashMap, fmt, marker::PhantomData}; - -use rs_merkle::{MerkleProof, MerkleTree}; -use serde::{ - de::{SeqAccess, Visitor}, - ser::SerializeSeq, - Deserialize, Deserializer, Serialize, -}; - -use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; - -use super::{hasher::OwnHasher, tree_leav_item::TreeLeavItem, TreeHashType}; - -#[derive(Clone)] -pub struct HashStorageMerkleTree { - leaves: HashMap, - hash_to_id_map: HashMap, - tree: MerkleTree, -} - -impl Serialize for HashStorageMerkleTree { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut vector = self.leaves.iter().collect::>(); - vector.sort_by(|a, b| a.0.cmp(b.0)); - - let mut seq = serializer.serialize_seq(Some(self.leaves.len()))?; - for element in vector.iter() { - seq.serialize_element(element.1)?; - } - seq.end() - } -} - -struct HashStorageMerkleTreeDeserializer { - marker: PhantomData HashStorageMerkleTree>, -} - -impl HashStorageMerkleTreeDeserializer { - fn new() -> Self { - HashStorageMerkleTreeDeserializer { - marker: PhantomData, - } - } -} - -impl<'de, Leav: TreeLeavItem + Clone + Deserialize<'de>> Visitor<'de> - for HashStorageMerkleTreeDeserializer -{ - type Value = HashStorageMerkleTree; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("HashStorageMerkleTree key value sequence.") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut vector = vec![]; - - loop { - let opt_key = seq.next_element::()?; - if let Some(value) = opt_key { - vector.push(value); - } else { - break; - } - } - - Ok(HashStorageMerkleTree::new(vector)) - } -} - -impl<'de, Leav: TreeLeavItem + Clone + Deserialize<'de>> serde::Deserialize<'de> - for HashStorageMerkleTree -{ - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_seq(HashStorageMerkleTreeDeserializer::new()) - } -} - -pub type PublicTransactionMerkleTree = HashStorageMerkleTree; - -pub type UTXOCommitmentsMerkleTree = HashStorageMerkleTree; - -impl HashStorageMerkleTree { - pub fn new(leaves_vec: Vec) -> Self { - let mut leaves_map = HashMap::new(); - let mut hash_to_id_map = HashMap::new(); - - let leaves_hashed: Vec = leaves_vec - .iter() - .enumerate() - .map(|(id, tx)| { - leaves_map.insert(id, tx.clone()); - hash_to_id_map.insert(tx.hash(), id); - tx.hash() - }) - .collect(); - Self { - leaves: leaves_map, - hash_to_id_map, - tree: MerkleTree::from_leaves(&leaves_hashed), - } - } - - pub fn get_tx(&self, hash: TreeHashType) -> Option<&Leav> { - self.hash_to_id_map - .get(&hash) - .and_then(|id| self.leaves.get(id)) - } - - pub fn get_root(&self) -> Option { - self.tree.root() - } - - pub fn get_proof(&self, hash: TreeHashType) -> Option> { - self.hash_to_id_map - .get(&hash) - .map(|id| self.tree.proof(&[*id])) - } - - pub fn get_proof_multiple(&self, hashes: &[TreeHashType]) -> Option> { - let ids_opt: Vec> = hashes - .iter() - .map(|hash| self.hash_to_id_map.get(hash)) - .collect(); - - let is_valid = ids_opt.iter().all(|el| el.is_some()); - - if is_valid { - let ids: Vec = ids_opt.into_iter().map(|el| *el.unwrap()).collect(); - - Some(self.tree.proof(&ids)) - } else { - None - } - } - - pub fn add_tx(&mut self, tx: &Leav) { - let last = self.leaves.len(); - - self.leaves.insert(last, tx.clone()); - self.hash_to_id_map.insert(tx.hash(), last); - - self.tree.insert(tx.hash()); - - self.tree.commit(); - } - - pub fn add_tx_multiple(&mut self, txs: Vec) { - for tx in txs.iter() { - let last = self.leaves.len(); - - self.leaves.insert(last, tx.clone()); - self.hash_to_id_map.insert(tx.hash(), last); - } - - self.tree - .append(&mut txs.iter().map(|tx| tx.hash()).collect()); - - self.tree.commit(); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // Mock implementation of TreeLeavItem trait for testing - #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] - struct MockTransaction { - pub hash: TreeHashType, - } - - impl TreeLeavItem for MockTransaction { - fn hash(&self) -> TreeHashType { - self.hash - } - } - - fn get_first_32_bytes(s: &str) -> [u8; 32] { - let mut buffer = [0u8; 32]; - let bytes = s.as_bytes(); - let len = std::cmp::min(32, bytes.len()); - - buffer[..len].copy_from_slice(&bytes[..len]); - buffer - } - - #[test] - fn test_new_merkle_tree() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - - let tree = HashStorageMerkleTree::new(vec![tx1.clone(), tx2.clone()]); - - assert_eq!(tree.leaves.len(), 2); - assert!(tree.get_root().is_some()); - } - - #[test] - fn test_new_merkle_tree_serialize() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - - let tree = HashStorageMerkleTree::new(vec![tx1.clone(), tx2.clone()]); - - let binding = serde_json::to_vec(&tree).unwrap(); - - let obj: HashStorageMerkleTree = serde_json::from_slice(&binding).unwrap(); - - assert_eq!(tree.leaves, obj.leaves); - assert_eq!(tree.hash_to_id_map, obj.hash_to_id_map); - assert_eq!(tree.tree.root(), obj.tree.root()); - } - - #[test] - fn test_get_tx() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - - let tree = HashStorageMerkleTree::new(vec![tx1.clone(), tx2.clone()]); - - assert_eq!(tree.get_tx(tx1.hash()), Some(&tx1)); - assert_eq!(tree.get_tx(tx2.hash()), Some(&tx2)); - } - - #[test] - fn test_get_proof() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - - let tree = HashStorageMerkleTree::new(vec![tx1.clone(), tx2.clone()]); - - let proof = tree.get_proof(tx1.hash()); - assert!(proof.is_some()); - } - - #[test] - fn test_add_tx() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - - let mut tree = HashStorageMerkleTree::new(vec![tx1.clone()]); - - tree.add_tx(&tx2); - assert_eq!(tree.leaves.len(), 2); - assert_eq!(tree.get_tx(tx2.hash()), Some(&tx2)); - } - - #[test] - fn test_add_tx_multiple() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - let tx3 = MockTransaction { - hash: get_first_32_bytes("tx3"), - }; - - let mut tree = HashStorageMerkleTree::new(vec![tx1.clone()]); - tree.add_tx_multiple(vec![tx2.clone(), tx3.clone()]); - - assert_eq!(tree.leaves.len(), 3); - assert_eq!(tree.get_tx(tx2.hash()), Some(&tx2)); - assert_eq!(tree.get_tx(tx3.hash()), Some(&tx3)); - } - - #[test] - fn test_get_proof_multiple() { - let tx1 = MockTransaction { - hash: get_first_32_bytes("tx1"), - }; - let tx2 = MockTransaction { - hash: get_first_32_bytes("tx2"), - }; - let tx3 = MockTransaction { - hash: get_first_32_bytes("tx3"), - }; - - let tree = HashStorageMerkleTree::new(vec![tx1.clone(), tx2.clone(), tx3.clone()]); - let proof = tree.get_proof_multiple(&[tx1.hash(), tx2.hash()]); - - assert!(proof.is_some()); - } -} diff --git a/common/src/merkle_tree_public/mod.rs b/common/src/merkle_tree_public/mod.rs deleted file mode 100644 index f1fc89e..0000000 --- a/common/src/merkle_tree_public/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod hasher; -pub mod merkle_tree; -pub mod tree_leav_item; - -pub type TreeHashType = [u8; 32]; -pub type CommitmentHashType = Vec; diff --git a/common/src/merkle_tree_public/tree_leav_item.rs b/common/src/merkle_tree_public/tree_leav_item.rs deleted file mode 100644 index 3b919a8..0000000 --- a/common/src/merkle_tree_public/tree_leav_item.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::{transaction::Transaction, utxo_commitment::UTXOCommitment}; - -use super::TreeHashType; - -pub trait TreeLeavItem { - fn hash(&self) -> TreeHashType; -} - -impl TreeLeavItem for Transaction { - fn hash(&self) -> TreeHashType { - self.body().hash() - } -} - -impl TreeLeavItem for UTXOCommitment { - fn hash(&self) -> TreeHashType { - self.hash - } -} diff --git a/common/src/nullifier.rs b/common/src/nullifier.rs index 2c95741..f277efd 100644 --- a/common/src/nullifier.rs +++ b/common/src/nullifier.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::merkle_tree_public::TreeHashType; +use crate::TreeHashType; //ToDo: Update Nullifier model, when it is clear #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index ad66d82..c6da098 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -12,6 +12,7 @@ use crate::rpc_primitives::requests::{ GetTransactionByHashResponse, }; use crate::sequencer_client::json::AccountInitialData; +use crate::transaction::TransactionBody; use crate::{SequencerClientError, SequencerRpcError}; pub mod json; @@ -127,10 +128,12 @@ impl SequencerClient { } ///Send transaction to sequencer - pub async fn send_tx( + pub async fn send_tx_public( &self, transaction: nssa::PublicTransaction, ) -> Result { + let transaction = TransactionBody::from(nssa::NSSATransaction::Public(transaction)); + let tx_req = SendTxRequest { transaction: transaction.to_bytes(), }; diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 7f8d8c1..7fb58d3 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -1,8 +1,15 @@ -use k256::{ecdsa::{signature::SignerMut, SigningKey}, FieldBytes}; -use nssa::{self, NSSATransaction}; -use rand::rngs::OsRng; +use nssa::NSSATransaction; -use crate::{block::{Block, HashableBlockData}, transaction::{Transaction, TransactionBody}}; +use crate::{ + block::{Block, HashableBlockData}, + transaction::TransactionBody, +}; + +//Helpers + +pub fn sequencer_sign_key_for_testing() -> nssa::PrivateKey { + nssa::PrivateKey::try_new([37; 32]).unwrap() +} //Dummy producers @@ -12,38 +19,24 @@ use crate::{block::{Block, HashableBlockData}, transaction::{Transaction, Transa /// /// `prev_hash` - hash of previous block, provide None for genesis /// -/// `transactions` - vector of `Transaction` objects +/// `transactions` - vector of `AuthenticatedTransaction` objects pub fn produce_dummy_block( id: u64, prev_hash: Option<[u8; 32]>, - transactions: Vec, + transactions: Vec, ) -> Block { - let transactions = transactions.into_iter().map( - |tx| { - let tx_body = TransactionBody::from(NSSATransaction::Public(tx)); - //ToDo: Fix signing key - let transaction = Transaction::new(tx_body, SigningKey::random(&mut OsRng)); - transaction.into_authenticated().unwrap() - }).collect(); - - //ToDo: Fix signature - let key_bytes = FieldBytes::from_slice(&[37; 32]); - let mut private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); - let signature = private_key.sign(&[1; 32]); - let block_data = HashableBlockData { block_id: id, prev_block_id: id.saturating_sub(1), prev_block_hash: prev_hash.unwrap_or_default(), - timestamp: 0, - signature, + timestamp: id * 100, transactions, }; - block_data.into() + block_data.into_block(&sequencer_sign_key_for_testing()) } -pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { +pub fn produce_dummy_empty_transaction() -> TransactionBody { let program_id = nssa::program::Program::authenticated_transfer_program().id(); let addresses = vec![]; let nonces = vec![]; @@ -53,7 +46,10 @@ pub fn produce_dummy_empty_transaction() -> nssa::PublicTransaction { .unwrap(); let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]); - nssa::PublicTransaction::new(message, witness_set) + + let nssa_tx = nssa::PublicTransaction::new(message, witness_set); + + TransactionBody::from(NSSATransaction::Public(nssa_tx)) } pub fn create_transaction_native_token_transfer( @@ -62,7 +58,7 @@ pub fn create_transaction_native_token_transfer( to: [u8; 32], balance_to_move: u128, signing_key: nssa::PrivateKey, -) -> nssa::PublicTransaction { +) -> TransactionBody { let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)]; let nonces = vec![nonce]; let program_id = nssa::program::Program::authenticated_transfer_program().id(); @@ -70,5 +66,8 @@ pub fn create_transaction_native_token_transfer( nssa::public_transaction::Message::try_new(program_id, addresses, nonces, balance_to_move) .unwrap(); let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]); - nssa::PublicTransaction::new(message, witness_set) + + let nssa_tx = nssa::PublicTransaction::new(message, witness_set); + + TransactionBody::from(NSSATransaction::Public(nssa_tx)) } diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 297841c..83cfbc9 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -1,23 +1,16 @@ -use std::io::{Cursor, Read}; - -use k256::ecdsa::{ - signature::{Signer, Verifier}, - Signature, SigningKey, VerifyingKey, -}; +use k256::ecdsa::{Signature, SigningKey, VerifyingKey}; use log::info; use serde::{Deserialize, Serialize}; use sha2::{digest::FixedOutput, Digest}; -use crate::{block::u32_from_cursor, merkle_tree_public::TreeHashType}; - use elliptic_curve::{ consts::{B0, B1}, generic_array::GenericArray, }; use sha2::digest::typenum::{UInt, UTerm}; -use crate::TransactionSignatureError; +use crate::TreeHashType; pub type CipherText = Vec; pub type Nonce = GenericArray, B1>, B0>, B0>>; @@ -40,33 +33,29 @@ pub struct TransactionBody { impl From for TransactionBody { fn from(value: nssa::NSSATransaction) -> Self { match value { - nssa::NSSATransaction::Public(tx) => { - Self { - tx_kind: TxKind::Public, - encoded_transaction_data: tx.to_bytes(), - } + nssa::NSSATransaction::Public(tx) => Self { + tx_kind: TxKind::Public, + encoded_transaction_data: tx.to_bytes(), + }, + nssa::NSSATransaction::PrivacyPreserving(tx) => Self { + tx_kind: TxKind::PrivacyPreserving, + encoded_transaction_data: tx.to_bytes(), }, - nssa::NSSATransaction::PrivacyPreserving(tx) => { - Self { - tx_kind: TxKind::PrivacyPreserving, - encoded_transaction_data: tx.to_bytes(), - } - } } } } -impl TryFrom for nssa::NSSATransaction { +impl TryFrom<&TransactionBody> for nssa::NSSATransaction { type Error = nssa::error::NssaError; - fn try_from(value: TransactionBody) -> Result { + fn try_from(value: &TransactionBody) -> Result { match value.tx_kind { - TxKind::Public => { - nssa::PublicTransaction::from_bytes(&value.encoded_transaction_data).map(|tx| tx.into()) - }, + TxKind::Public => nssa::PublicTransaction::from_bytes(&value.encoded_transaction_data) + .map(|tx| tx.into()), TxKind::PrivacyPreserving => { - nssa::PrivacyPreservingTransaction::from_bytes(&value.encoded_transaction_data).map(|tx| tx.into()) - }, + nssa::PrivacyPreservingTransaction::from_bytes(&value.encoded_transaction_data) + .map(|tx| tx.into()) + } } } } @@ -173,14 +162,14 @@ impl TransactionBody { TreeHashType::from(hasher.finalize_fixed()) } - fn to_bytes(&self) -> Vec { + pub 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. serde_json::to_vec(&self).unwrap() } - fn from_bytes(bytes: Vec) -> Self { + pub fn from_bytes(bytes: Vec) -> Self { serde_json::from_slice(&bytes).unwrap() } @@ -190,171 +179,31 @@ impl TransactionBody { } } -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, - pub signature: TransactionSignature, - pub 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 - } -} - -impl Transaction { - pub fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - let body_bytes = self.body.to_bytes(); - let signature_bytes = self.signature.to_bytes(); - let public_key_bytes = self.public_key.to_sec1_bytes(); - - let body_bytes_len = body_bytes.len() as u32; - let signature_bytes_len = signature_bytes.len() as u32; - let public_key_bytes_len = public_key_bytes.len() as u32; - - bytes.extend_from_slice(&body_bytes_len.to_le_bytes()); - bytes.extend_from_slice(&signature_bytes_len.to_le_bytes()); - bytes.extend_from_slice(&public_key_bytes_len.to_le_bytes()); - - bytes.extend_from_slice(&body_bytes); - bytes.extend_from_slice(&signature_bytes); - bytes.extend_from_slice(&public_key_bytes); - - bytes - } - - // TODO: Improve error handling. Remove unwraps. - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let body_bytes_len = u32_from_cursor(cursor) as usize; - let signature_bytes_len = u32_from_cursor(cursor) as usize; - let public_key_bytes_len = u32_from_cursor(cursor) as usize; - - let mut body_bytes = Vec::with_capacity(body_bytes_len); - let mut signature_bytes = Vec::with_capacity(signature_bytes_len); - let mut public_key_bytes = Vec::with_capacity(public_key_bytes_len); - - cursor.read_exact(&mut body_bytes).unwrap(); - let body = TransactionBody::from_bytes(body_bytes); - - cursor.read_exact(&mut signature_bytes).unwrap(); - let signature = Signature::from_bytes(signature_bytes.as_slice().try_into().unwrap()).unwrap(); - - cursor.read_exact(&mut public_key_bytes).unwrap(); - let public_key = VerifyingKey::from_sec1_bytes(&public_key_bytes).unwrap(); - - Self { body, signature, public_key } - } -} - -/// 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, PartialEq, Eq)] -pub struct AuthenticatedTransaction { - hash: TransactionHash, - transaction: Transaction, -} - -impl AuthenticatedTransaction { - /// Returns the underlying transaction - pub fn transaction(&self) -> &Transaction { - &self.transaction - } - - pub fn into_transaction(self) -> Transaction { - self.transaction - } - - /// Returns the precomputed hash over the body of the transaction - pub fn hash(&self) -> &TransactionHash { - &self.hash - } -} - -impl AuthenticatedTransaction { - pub fn to_bytes(&self) -> Vec { - let mut bytes = Vec::new(); - bytes.extend_from_slice(&self.hash); - bytes.extend_from_slice(&self.transaction.to_bytes()); - bytes - } - - // TODO: Improve error handling. Remove unwraps. - pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { - let mut hash: [u8; 32] = [0; 32]; - cursor.read_exact(&mut hash).unwrap(); - let transaction = Transaction::from_cursor(cursor); - Self { hash, transaction } - } -} - #[cfg(test)] mod tests { - use super::*; - use k256::{ecdsa::signature::Signer, FieldBytes}; use sha2::{digest::FixedOutput, Digest}; use crate::{ - merkle_tree_public::TreeHashType, - transaction::{Transaction, TransactionBody, TxKind}, + transaction::{TransactionBody, TxKind}, + TreeHashType, }; fn test_transaction_body() -> TransactionBody { TransactionBody { tx_kind: TxKind::Public, - encoded_transaction_data: vec![1,2,3,4], + encoded_transaction_data: vec![1, 2, 3, 4], } } - 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(&body).unwrap(); + let data = body.to_bytes(); let mut hasher = sha2::Sha256::new(); hasher.update(&data); TreeHashType::from(hasher.finalize_fixed()) @@ -366,81 +215,12 @@ mod tests { } #[test] - fn test_transaction_constructor() { + fn test_to_bytes_from_bytes() { 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); - } + let body_bytes = body.to_bytes(); + let body_new = TransactionBody::from_bytes(body_bytes); - #[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()); - } - - #[test] - fn test_authenticated_transaction_into_transaction() { - let transaction = test_transaction(); - let authenticated_tx = transaction.clone().into_authenticated().unwrap(); - assert_eq!(authenticated_tx.into_transaction(), transaction); + assert_eq!(body, body_new); } } diff --git a/common/src/utxo_commitment.rs b/common/src/utxo_commitment.rs index 75c0543..1b42421 100644 --- a/common/src/utxo_commitment.rs +++ b/common/src/utxo_commitment.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use crate::merkle_tree_public::TreeHashType; +use crate::TreeHashType; //ToDo: Update UTXO Commitment model, when it is clear #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/debug/sequencer/sequencer_config.json index 37bfd0a..3317a11 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/debug/sequencer/sequencer_config.json @@ -15,5 +15,7 @@ "addr": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "balance": 20000 } - ] + ], + "signing_key": [37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, +37, 37, 37, 37, 37, 37] } diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index 47342ec..739e160 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -1,4 +1,4 @@ -use common::merkle_tree_public::TreeHashType; +use common::TreeHashType; use elliptic_curve::PrimeField; use k256::{AffinePoint, FieldBytes, Scalar}; use rand::{rngs::OsRng, RngCore}; diff --git a/nssa/src/privacy_preserving_transaction/encoding.rs b/nssa/src/privacy_preserving_transaction/encoding.rs index fe70e65..ff5a269 100644 --- a/nssa/src/privacy_preserving_transaction/encoding.rs +++ b/nssa/src/privacy_preserving_transaction/encoding.rs @@ -7,7 +7,11 @@ use nssa_core::{ }; use crate::{ - error::NssaError, privacy_preserving_transaction::{circuit::Proof, message::EncryptedAccountData, witness_set::WitnessSet}, Address, PrivacyPreservingTransaction, PublicKey, Signature + Address, PrivacyPreservingTransaction, PublicKey, Signature, + error::NssaError, + privacy_preserving_transaction::{ + circuit::Proof, message::EncryptedAccountData, witness_set::WitnessSet, + }, }; use super::message::Message; diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 2e3570b..40b3642 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -15,6 +15,7 @@ elliptic-curve.workspace = true k256.workspace = true tiny-keccak.workspace = true tempfile.workspace = true +chrono.workspace = true [dependencies.storage] path = "../storage" diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 4a99f0b..cdf79a3 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -27,4 +27,6 @@ pub struct SequencerConfig { pub port: u16, ///List of initial accounts data pub initial_accounts: Vec, + ///Sequencer own signing key + pub signing_key: [u8; 32], } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 2137c8b..0ce778a 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -1,10 +1,11 @@ use std::fmt::Display; use anyhow::Result; -use common::{block::HashableBlockData, merkle_tree_public::TreeHashType}; +use common::{block::HashableBlockData, transaction::TransactionBody, TreeHashType}; use config::SequencerConfig; -use log::warn; +use log::{info, warn}; use mempool::MemPool; +use nssa::NSSATransaction; use sequencer_store::SequecerChainStore; use serde::{Deserialize, Serialize}; @@ -13,7 +14,7 @@ pub mod sequencer_store; pub struct SequencerCore { pub store: SequecerChainStore, - pub mempool: MemPool, + pub mempool: MemPool, pub sequencer_config: SequencerConfig, pub chain_height: u64, } @@ -51,6 +52,7 @@ impl SequencerCore { config.genesis_id, config.is_genesis_random, &config.initial_accounts, + nssa::PrivateKey::try_new(config.signing_key).unwrap(), ), mempool: MemPool::default(), chain_height: config.genesis_id, @@ -60,20 +62,39 @@ impl SequencerCore { pub fn transaction_pre_check( &mut self, - tx: nssa::PublicTransaction, - ) -> Result { + tx: NSSATransaction, + ) -> Result { // Stateless checks here - if tx.witness_set().is_valid_for(tx.message()) { - Ok(tx) - } else { - Err(TransactionMalformationErrorKind::InvalidSignature) + match tx { + NSSATransaction::Public(tx) => { + if tx.witness_set().is_valid_for(tx.message()) { + Ok(NSSATransaction::Public(tx)) + } else { + Err(TransactionMalformationErrorKind::InvalidSignature) + } + } + NSSATransaction::PrivacyPreserving(tx) => { + if tx.witness_set().signatures_are_valid_for(tx.message()) { + Ok(NSSATransaction::PrivacyPreserving(tx)) + } else { + Err(TransactionMalformationErrorKind::InvalidSignature) + } + } } } pub fn push_tx_into_mempool_pre_check( &mut self, - transaction: nssa::PublicTransaction, + transaction: TransactionBody, ) -> Result<(), TransactionMalformationErrorKind> { + let transaction = NSSATransaction::try_from(&transaction).map_err(|_| { + TransactionMalformationErrorKind::FailedToDecode { + tx: transaction.hash(), + } + })?; + + info!("Transaction got {transaction:#?}"); + let mempool_size = self.mempool.len(); if mempool_size >= self.sequencer_config.max_num_tx_in_block { return Err(TransactionMalformationErrorKind::MempoolFullForRound); @@ -83,19 +104,29 @@ impl SequencerCore { .transaction_pre_check(transaction) .inspect_err(|err| warn!("Error at pre_check {err:#?}"))?; - self.mempool.push_item(authenticated_tx); + self.mempool.push_item(authenticated_tx.into()); Ok(()) } fn execute_check_transaction_on_state( &mut self, - tx: nssa::PublicTransaction, - ) -> Result { - self.store - .state - .transition_from_public_transaction(&tx) - .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + tx: NSSATransaction, + ) -> Result { + match &tx { + NSSATransaction::Public(tx) => { + self.store + .state + .transition_from_public_transaction(tx) + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + } + NSSATransaction::PrivacyPreserving(tx) => { + self.store + .state + .transition_from_privacy_preserving_transaction(tx) + .inspect_err(|err| warn!("Error at transition {err:#?}"))?; + } + } Ok(tx) } @@ -108,25 +139,39 @@ impl SequencerCore { .mempool .pop_size(self.sequencer_config.max_num_tx_in_block); - let valid_transactions: Vec<_> = transactions + let mut nssa_transactions = vec![]; + + for tx in transactions { + let nssa_transaction = NSSATransaction::try_from(&tx) + .map_err(|_| TransactionMalformationErrorKind::FailedToDecode { tx: tx.hash() })?; + + nssa_transactions.push(nssa_transaction); + } + + let valid_transactions: Vec<_> = nssa_transactions .into_iter() .filter_map(|tx| self.execute_check_transaction_on_state(tx).ok()) + .map(Into::into) .collect(); let prev_block_hash = self .store .block_store .get_block_at_id(self.chain_height)? + .header .hash; + let curr_time = chrono::Utc::now().timestamp_millis() as u64; + let hashable_data = HashableBlockData { block_id: new_block_height, prev_block_id: self.chain_height, transactions: valid_transactions, prev_block_hash, + timestamp: curr_time, }; - let block = hashable_data.into(); + let block = hashable_data.into_block(&self.store.block_store.signing_key); self.store.block_store.put_block_at_id(block)?; @@ -138,10 +183,18 @@ impl SequencerCore { #[cfg(test)] mod tests { + use common::test_utils::sequencer_sign_key_for_testing; + use crate::config::AccountInitialData; use super::*; + fn parse_unwrap_tx_body_into_nssa_tx(tx_body: TransactionBody) -> NSSATransaction { + NSSATransaction::try_from(&tx_body) + .map_err(|_| TransactionMalformationErrorKind::FailedToDecode { tx: tx_body.hash() }) + .unwrap() + } + fn setup_sequencer_config_variable_initial_accounts( initial_accounts: Vec, ) -> SequencerConfig { @@ -157,6 +210,7 @@ mod tests { block_create_timeout_millis: 1000, port: 8080, initial_accounts, + signing_key: *sequencer_sign_key_for_testing().value(), } } @@ -298,7 +352,7 @@ mod tests { common_setup(&mut sequencer); let tx = common::test_utils::produce_dummy_empty_transaction(); - let result = sequencer.transaction_pre_check(tx); + let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx)); assert!(result.is_ok()); } @@ -324,7 +378,7 @@ mod tests { let tx = common::test_utils::create_transaction_native_token_transfer( acc1, 0, acc2, 10, sign_key1, ); - let result = sequencer.transaction_pre_check(tx); + let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx)); assert!(result.is_ok()); } @@ -352,7 +406,9 @@ mod tests { ); // Signature is valid, stateless check pass - let tx = sequencer.transaction_pre_check(tx).unwrap(); + let tx = sequencer + .transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx)) + .unwrap(); // Signature is not from sender. Execution fails let result = sequencer.execute_check_transaction_on_state(tx); @@ -385,7 +441,7 @@ mod tests { acc1, 0, acc2, 10000000, sign_key1, ); - let result = sequencer.transaction_pre_check(tx); + let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx)); //Passed pre-check assert!(result.is_ok()); @@ -421,7 +477,9 @@ mod tests { acc1, 0, acc2, 100, sign_key1, ); - sequencer.execute_check_transaction_on_state(tx).unwrap(); + sequencer + .execute_check_transaction_on_state(parse_unwrap_tx_body_into_nssa_tx(tx)) + .unwrap(); let bal_from = sequencer .store @@ -528,7 +586,7 @@ mod tests { .unwrap(); // Only one should be included in the block - assert_eq!(block.transactions, vec![tx.clone()]); + assert_eq!(block.body.transactions, vec![tx.clone()]); } #[test] @@ -563,7 +621,7 @@ mod tests { .block_store .get_block_at_id(current_height) .unwrap(); - assert_eq!(block.transactions, vec![tx.clone()]); + assert_eq!(block.body.transactions, vec![tx.clone()]); // Add same transaction should fail sequencer.mempool.push_item(tx); @@ -575,6 +633,6 @@ mod tests { .block_store .get_block_at_id(current_height) .unwrap(); - assert!(block.transactions.is_empty()); + assert!(block.body.transactions.is_empty()); } } diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 3c05792..b3b74ee 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, path::Path}; use anyhow::Result; -use common::{block::Block, merkle_tree_public::TreeHashType}; +use common::{block::Block, transaction::TransactionBody, TreeHashType}; use storage::RocksDBIO; pub struct SequecerBlockStore { @@ -9,6 +9,7 @@ pub struct SequecerBlockStore { // TODO: Consider adding the hashmap to the database for faster recovery. tx_hash_to_block_map: HashMap, pub genesis_id: u64, + pub signing_key: nssa::PrivateKey, } impl SequecerBlockStore { @@ -16,7 +17,11 @@ impl SequecerBlockStore { /// Creates files if necessary. /// /// ATTENTION: Will overwrite genesis block. - pub fn open_db_with_genesis(location: &Path, genesis_block: Option) -> Result { + pub fn open_db_with_genesis( + location: &Path, + genesis_block: Option, + signing_key: nssa::PrivateKey, + ) -> Result { let tx_hash_to_block_map = if let Some(block) = &genesis_block { block_to_transactions_map(block) } else { @@ -31,16 +36,17 @@ impl SequecerBlockStore { dbio, genesis_id, tx_hash_to_block_map, + signing_key, }) } ///Reopening existing database - pub fn open_db_restart(location: &Path) -> Result { - SequecerBlockStore::open_db_with_genesis(location, None) + pub fn open_db_restart(location: &Path, signing_key: nssa::PrivateKey) -> Result { + SequecerBlockStore::open_db_with_genesis(location, None, signing_key) } pub fn get_block_at_id(&self, id: u64) -> Result { - Ok(self.dbio.get_block(id)?) + Ok(self.dbio.get_block(id)?.into_block(&self.signing_key)) } pub fn put_block_at_id(&mut self, block: Block) -> Result<()> { @@ -51,11 +57,11 @@ impl SequecerBlockStore { } /// Returns the transaction corresponding to the given hash, if it exists in the blockchain. - pub fn get_transaction_by_hash(&self, hash: TreeHashType) -> Option { + pub fn get_transaction_by_hash(&self, hash: TreeHashType) -> Option { let block_id = self.tx_hash_to_block_map.get(&hash); let block = block_id.map(|&id| self.get_block_at_id(id)); if let Some(Ok(block)) = block { - for transaction in block.transactions.into_iter() { + for transaction in block.body.transactions.into_iter() { if transaction.hash() == hash { return Some(transaction); } @@ -67,9 +73,10 @@ impl SequecerBlockStore { fn block_to_transactions_map(block: &Block) -> HashMap { block + .body .transactions .iter() - .map(|transaction| (transaction.hash(), block.block_id)) + .map(|transaction| (transaction.hash(), block.header.block_id)) .collect() } @@ -77,11 +84,7 @@ fn block_to_transactions_map(block: &Block) -> HashMap { mod tests { use super::*; - use common::block::{BlockBody, BlockHeader}; - use k256::{ - ecdsa::{signature::SignerMut, Signature, SigningKey}, - FieldBytes, - }; + use common::{block::HashableBlockData, test_utils::sequencer_sign_key_for_testing}; use tempfile::tempdir; #[test] @@ -89,25 +92,21 @@ mod tests { let temp_dir = tempdir().unwrap(); let path = temp_dir.path(); - let key_bytes = FieldBytes::from_slice(&[37; 32]); - let mut private_key: SigningKey = SigningKey::from_bytes(key_bytes).unwrap(); + let signing_key = sequencer_sign_key_for_testing(); - let genesis_block = Block { - header: BlockHeader { - block_id: 0, - prev_block_id: 0, - prev_block_hash: [0; 32], - hash: [1; 32], - timestamp: 0, - signature: private_key.sign(&[42; 32]), - }, - body: BlockBody { - transactions: vec![], - }, + let genesis_block_hashable_data = HashableBlockData { + block_id: 0, + prev_block_id: 0, + prev_block_hash: [0; 32], + timestamp: 0, + transactions: vec![], }; + + let genesis_block = genesis_block_hashable_data.into_block(&signing_key); // Start an empty node store let mut node_store = - SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); + SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block), signing_key) + .unwrap(); let tx = common::test_utils::produce_dummy_empty_transaction(); let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]); diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 4254ed7..520ec4f 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -20,6 +20,7 @@ impl SequecerChainStore { genesis_id: u64, is_genesis_random: bool, initial_accounts: &[AccountInitialData], + signing_key: nssa::PrivateKey, ) -> Self { let init_accs: Vec<(Address, u128)> = initial_accounts .iter() @@ -36,20 +37,24 @@ impl SequecerChainStore { OsRng.fill_bytes(&mut prev_block_hash); } + let curr_time = chrono::Utc::now().timestamp_millis() as u64; + let hashable_data = HashableBlockData { block_id: genesis_id, prev_block_id: genesis_id.saturating_sub(1), transactions: vec![], prev_block_hash, + timestamp: curr_time, }; - let genesis_block = hashable_data.into(); + let genesis_block = hashable_data.into_block(&signing_key); //Sequencer should panic if unable to open db, //as fixing this issue may require actions non-native to program scope let block_store = SequecerBlockStore::open_db_with_genesis( &home_dir.join("rocksdb"), Some(genesis_block), + signing_key, ) .unwrap(); diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index d994210..648c19c 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -6,7 +6,6 @@ use serde_json::Value; use common::{ block::HashableBlockData, - merkle_tree_public::TreeHashType, rpc_primitives::{ errors::RpcError, message::{Message, Request}, @@ -17,6 +16,8 @@ use common::{ GetTransactionByHashRequest, GetTransactionByHashResponse, }, }, + transaction::TransactionBody, + TreeHashType, }; use common::rpc_primitives::requests::{ @@ -72,8 +73,7 @@ impl JsonHandler { async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; - let tx = nssa::PublicTransaction::from_bytes(&send_tx_req.transaction) - .map_err(|e| RpcError::serialization_error(&e.to_string()))?; + let tx = TransactionBody::from_bytes(send_tx_req.transaction); let tx_hash = hex::encode(tx.hash()); { @@ -254,7 +254,10 @@ mod tests { use crate::{rpc_handler, JsonHandler}; use base64::{engine::general_purpose, Engine}; - use common::rpc_primitives::RpcPollingConfig; + use common::{ + rpc_primitives::RpcPollingConfig, test_utils::sequencer_sign_key_for_testing, + transaction::TransactionBody, + }; use sequencer_core::{ config::{AccountInitialData, SequencerConfig}, @@ -298,14 +301,11 @@ mod tests { block_create_timeout_millis: 1000, port: 8080, initial_accounts, + signing_key: *sequencer_sign_key_for_testing().value(), } } - fn components_for_tests() -> ( - JsonHandler, - Vec, - nssa::PublicTransaction, - ) { + fn components_for_tests() -> (JsonHandler, Vec, TransactionBody) { let config = sequencer_config_for_tests(); let mut sequencer_core = SequencerCore::start_from_config(config); let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 3e612b9..35aed64 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -248,7 +248,7 @@ impl RocksDBIO { Ok(()) } - pub fn get_block(&self, block_id: u64) -> DbResult { + pub fn get_block(&self, block_id: u64) -> DbResult { let cf_block = self.block_column(); let res = self .db @@ -256,7 +256,7 @@ impl RocksDBIO { .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; if let Some(data) = res { - Ok(HashableBlockData::from_bytes(&data).into()) + Ok(HashableBlockData::from_bytes(&data)) } else { Err(DbError::db_interaction_error( "Block on this id not found".to_string(), diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index 6f4201c..a803cf8 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -1,14 +1,12 @@ use std::collections::HashMap; use anyhow::Result; -use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree; use key_protocol::key_protocol_core::NSSAUserData; use crate::config::{PersistentAccountData, WalletConfig}; pub struct WalletChainStore { pub user_data: NSSAUserData, - pub utxo_commitments_store: UTXOCommitmentsMerkleTree, pub wallet_config: WalletConfig, } @@ -21,11 +19,8 @@ impl WalletChainStore { .map(|init_acc_data| (init_acc_data.address, init_acc_data.pub_sign_key)) .collect(); - let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); - Ok(Self { user_data: NSSAUserData::new_with_accounts(accounts_keys)?, - utxo_commitments_store, wallet_config: config, }) } @@ -94,11 +89,6 @@ mod tests { let config = create_sample_wallet_config(path.to_path_buf()); - let store = WalletChainStore::new(config.clone()).unwrap(); - - assert_eq!( - store.utxo_commitments_store.get_root().unwrap_or([0; 32]), - [0; 32] - ); + let _ = WalletChainStore::new(config.clone()).unwrap(); } } diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index 7ce7955..085c6be 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -3,6 +3,7 @@ use std::{fs::File, io::Write, path::PathBuf, str::FromStr, sync::Arc}; use base64::Engine; use common::{ sequencer_client::{json::SendTxResponse, SequencerClient}, + transaction::TransactionBody, ExecutionFailureKind, }; @@ -108,7 +109,6 @@ impl WalletCore { balance_to_move, ) .unwrap(); - let signing_key = self.storage.user_data.get_account_signing_key(&from); if let Some(signing_key) = signing_key { @@ -119,7 +119,7 @@ impl WalletCore { let tx = nssa::PublicTransaction::new(message, witness_set); - Ok(self.sequencer_client.send_tx(tx).await?) + Ok(self.sequencer_client.send_tx_public(tx).await?) } else { Err(ExecutionFailureKind::KeyNotFoundError) } @@ -156,13 +156,13 @@ impl WalletCore { pub async fn poll_public_native_token_transfer( &self, hash: String, - ) -> Result { + ) -> Result { let transaction_encoded = self.poller.poll_tx(hash).await?; let tx_base64_decode = base64::engine::general_purpose::STANDARD.decode(transaction_encoded)?; - let pub_tx = nssa::PublicTransaction::from_bytes(&tx_base64_decode)?; + let pub_tx = TransactionBody::from_bytes(tx_base64_decode); - Ok(pub_tx) + Ok(nssa::NSSATransaction::try_from(&pub_tx)?) } }