diff --git a/Cargo.toml b/Cargo.toml index ac01a24..4764b7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ light-poseidon = "0.3.0" ark-bn254 = "0.5.0" ark-ff = "0.5.0" tiny-keccak = { version = "2.0.2", features = ["keccak"] } +base64 = "0.22.1" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/common/src/block.rs b/common/src/block.rs index 65514e2..974c230 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -1,36 +1,33 @@ +use std::io::{Cursor, Read}; + use rs_merkle::Hasher; -use serde::{Deserialize, Serialize}; use crate::merkle_tree_public::hasher::OwnHasher; use nssa; pub type BlockHash = [u8; 32]; -pub type Data = Vec; pub type BlockId = u64; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Clone)] pub struct Block { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub hash: BlockHash, pub transactions: Vec, - pub data: Data, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq)] pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_id: BlockId, pub prev_block_hash: BlockHash, pub transactions: Vec, - pub data: Data, } impl From for Block { fn from(value: HashableBlockData) -> Self { - let data = serde_json::to_vec(&value).unwrap(); - + let data = value.to_bytes(); let hash = OwnHasher::hash(&data); Self { @@ -38,8 +35,87 @@ impl From for Block { prev_block_id: value.prev_block_id, hash, transactions: value.transactions, - data: value.data, prev_block_hash: value.prev_block_hash, } } } + +impl From for HashableBlockData { + fn from(value: Block) -> Self { + Self { + block_id: value.block_id, + prev_block_id: value.prev_block_id, + prev_block_hash: value.prev_block_hash, + transactions: value.transactions, + } + } +} + +impl HashableBlockData { + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&self.block_id.to_le_bytes()); + bytes.extend_from_slice(&self.prev_block_id.to_le_bytes()); + bytes.extend_from_slice(&self.prev_block_hash); + let num_transactions: u32 = self.transactions.len() as u32; + bytes.extend_from_slice(&num_transactions.to_le_bytes()); + for tx in &self.transactions { + bytes.extend_from_slice(&tx.to_bytes()); + } + bytes + } + + pub fn from_bytes(data: &[u8]) -> Self { + let mut cursor = Cursor::new(data); + + let block_id = u64_from_cursor(&mut cursor); + let prev_block_id = u64_from_cursor(&mut cursor); + + let mut prev_block_hash = [0u8; 32]; + cursor.read_exact(&mut prev_block_hash).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 = nssa::PublicTransaction::from_cursor(&mut cursor); + transactions.push(tx); + } + + Self { + block_id, + prev_block_id, + prev_block_hash, + transactions, + } + } +} + +fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 { + let mut word_buf = [0u8; 4]; + cursor.read_exact(&mut word_buf).unwrap(); + u32::from_le_bytes(word_buf) +} + +fn u64_from_cursor(cursor: &mut Cursor<&[u8]>) -> u64 { + let mut word_buf = [0u8; 8]; + cursor.read_exact(&mut word_buf).unwrap(); + u64::from_le_bytes(word_buf) +} + + + +#[cfg(test)] +mod tests { + use crate::{block::HashableBlockData, test_utils}; + + #[test] + fn test() { + let transactions = vec![test_utils::produce_dummy_empty_transaction()]; + let block = test_utils::produce_dummy_block(1, Some([1; 32]), transactions); + let hashable = HashableBlockData::from(block); + let bytes = hashable.to_bytes(); + let recov = HashableBlockData::from_bytes(&bytes); + assert_eq!(hashable, recov); + } +} diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index ebfad60..1b2bbbe 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -17,7 +17,7 @@ pub struct RegisterAccountRequest { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: nssa::PublicTransaction, + pub transaction: Vec } #[derive(Serialize, Deserialize, Debug)] @@ -71,7 +71,7 @@ pub struct SendTxResponse { #[derive(Serialize, Deserialize, Debug)] pub struct GetBlockDataResponse { - pub block: Block, + pub block: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -91,5 +91,5 @@ pub struct GetAccountBalanceResponse { #[derive(Serialize, Deserialize, Debug)] pub struct GetTransactionByHashResponse { - pub transaction: Option, + pub transaction: Option, } diff --git a/common/src/sequencer_client/json.rs b/common/src/sequencer_client/json.rs index 119442b..71b1719 100644 --- a/common/src/sequencer_client/json.rs +++ b/common/src/sequencer_client/json.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] pub struct SendTxRequest { - pub transaction: nssa::PublicTransaction, + pub transaction: Vec, } //Responses diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index 82f30a9..44f0e23 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -91,7 +91,7 @@ impl SequencerClient { &self, transaction: nssa::PublicTransaction, ) -> Result { - let tx_req = SendTxRequest { transaction }; + let tx_req = SendTxRequest { transaction: transaction.to_bytes() }; let req = serde_json::to_value(tx_req)?; diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index bd3fadc..d982a39 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -11,20 +11,16 @@ use crate::block::{Block, HashableBlockData}; /// `prev_hash` - hash of previous block, provide None for genesis /// /// `transactions` - vector of `Transaction` objects -/// -/// `additional_data` - vector with additional data pub fn produce_dummy_block( id: u64, prev_hash: Option<[u8; 32]>, transactions: Vec, - additional_data: Vec, ) -> Block { let block_data = HashableBlockData { block_id: id, prev_block_id: id.saturating_sub(1), prev_block_hash: prev_hash.unwrap_or_default(), transactions, - data: additional_data, }; block_data.into() diff --git a/nssa/src/public_transaction/mod.rs b/nssa/src/public_transaction/mod.rs index 269f7d5..e91baba 100644 --- a/nssa/src/public_transaction/mod.rs +++ b/nssa/src/public_transaction/mod.rs @@ -47,16 +47,15 @@ impl PublicTransaction { } } - fn to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { let mut bytes = self.message.message_to_bytes(); bytes.extend_from_slice(&self.witness_set.to_bytes()); bytes } - fn from_bytes(bytes: &[u8]) -> Self { - let mut cursor = Cursor::new(bytes); - let message = Message::from_cursor(&mut cursor); - let witness_set = WitnessSet::from_cursor(&mut cursor); + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Self { + let message = Message::from_cursor(cursor); + let witness_set = WitnessSet::from_cursor(cursor); Self { message, witness_set, @@ -137,6 +136,8 @@ impl PublicTransaction { #[cfg(test)] mod tests { + use std::io::Cursor; + use crate::{ Address, PrivateKey, PublicTransaction, PublicKey, program::Program, @@ -163,7 +164,8 @@ mod tests { let tx = PublicTransaction::new(message, witness_set); let bytes = tx.to_bytes(); - let recov_tx = PublicTransaction::from_bytes(&bytes); + let mut cursor: Cursor<&[u8]> = Cursor::new(&bytes); + let recov_tx = PublicTransaction::from_cursor(&mut cursor); assert_eq!(tx, recov_tx); } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index ac59b63..8430b83 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -120,7 +120,6 @@ impl SequencerCore { block_id: new_block_height, prev_block_id: self.chain_height, transactions: valid_transactions, - data: vec![], prev_block_hash, }; diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index da92b82..3bd68cb 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -89,14 +89,13 @@ mod tests { prev_block_hash: [0; 32], hash: [1; 32], transactions: vec![], - data: vec![], }; // Start an empty node store let mut node_store = SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); let tx = common::test_utils::produce_dummy_empty_transaction(); - let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()], vec![]); + let block = common::test_utils::produce_dummy_block(1, None, vec![tx.clone()]); // Try retrieve a tx that's not in the chain yet. let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index e0a15fc..fd39473 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -49,7 +49,6 @@ impl SequecerChainStore { block_id: genesis_id, prev_block_id: genesis_id.saturating_sub(1), transactions: vec![], - data: data.to_vec(), prev_block_hash, }; diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index ceb43f7..ce61f7e 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -14,6 +14,7 @@ actix-cors.workspace = true futures.workspace = true hex.workspace = true tempfile.workspace = true +base64.workspace = true actix-web.workspace = true tokio.workspace = true diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index ca284ed..69c9c8d 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,9 +1,13 @@ +use std::io::Cursor; + use actix_web::Error as HttpError; +use base64::{engine::general_purpose, Engine}; use nssa; use sequencer_core::config::AccountInitialData; use serde_json::Value; use common::{ + block::HashableBlockData, merkle_tree_public::TreeHashType, rpc_primitives::{ errors::RpcError, @@ -68,11 +72,15 @@ impl JsonHandler { async fn process_send_tx(&self, request: Request) -> Result { let send_tx_req = SendTxRequest::parse(Some(request.params))?; + let tx = { + let mut cursor: Cursor<&[u8]> = Cursor::new(&send_tx_req.transaction); + nssa::PublicTransaction::from_cursor(&mut cursor) + }; { let mut state = self.sequencer_state.lock().await; - state.push_tx_into_mempool_pre_check(send_tx_req.transaction)?; + state.push_tx_into_mempool_pre_check(tx)?; } let helperstruct = SendTxResponse { @@ -94,7 +102,9 @@ impl JsonHandler { .get_block_at_id(get_block_req.block_id)? }; - let helperstruct = GetBlockDataResponse { block }; + let helperstruct = GetBlockDataResponse { + block: HashableBlockData::from(block).to_bytes(), + }; respond(helperstruct) } @@ -177,9 +187,16 @@ impl JsonHandler { let transaction = { let state = self.sequencer_state.lock().await; - state.store.block_store.get_transaction_by_hash(hash) + state + .store + .block_store + .get_transaction_by_hash(hash) + .map(|tx| tx.to_bytes()) + }; + let base64_encoded = transaction.map(|tx| general_purpose::STANDARD.encode(tx)); + let helperstruct = GetTransactionByHashResponse { + transaction: base64_encoded, }; - let helperstruct = GetTransactionByHashResponse { transaction }; respond(helperstruct) } @@ -217,15 +234,13 @@ mod tests { let tempdir = tempdir().unwrap(); let home = tempdir.path().to_path_buf(); let acc1_addr = vec![ - // 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, - // 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, - 1; 32 + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, + 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, ]; let acc2_addr = vec![ - // 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, - // 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, - 2; 32 + 77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, + 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, ]; let initial_acc1 = AccountInitialData { @@ -264,7 +279,10 @@ mod tests { let signing_key = nssa::PrivateKey::try_new([1; 32]).unwrap(); let balance_to_move = 10; let tx = common::test_utils::create_transaction_native_token_transfer( - [1; 32], + [ + 27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, + 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, + ], 0, [2; 32], balance_to_move, @@ -490,25 +508,9 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "transaction": { - "message": { - "addresses": [ - { "value": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] }, - { "value": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] } - ], - "instruction_data": [10, 0, 0, 0], - "nonces": [0], - "program_id": nssa::program::Program::authenticated_transfer_program().id(), - }, - "witness_set": { - "signatures_and_public_keys": [ - [1, 1] - ] - } - } + "transaction": "TlNTQS92MC4xL1R4TWVzc2FnZTYHPd+eRGuYF2kuC9CQp8t7bp1UuMIyqCp4yOzP4zCBAgAAABuExVZ7EmRAmV0+1aq6BWXXHhg0YEgZ/5wX9enV3QePAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAKAAAAAAAAAAAAAAAAAAAAAQAAAKvsz0Lg2apthMPOsOhYarLVuZmMeUYDneKtr95L7Ia6C+Z3/dw3CAtb2wIa4/Ow5JwatpstOGwC9uS2mySzf9UbhMVWexJkQJldPtWqugVl1x4YNGBIGf+cF/Xp1d0Hjw==", } }); - let response = call_rpc_handler_with_json(json_handler, request).await; assert_eq!(response, expected_response); diff --git a/storage/src/lib.rs b/storage/src/lib.rs index ac54406..0dd12b7 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,6 +1,6 @@ use std::{path::Path, sync::Arc}; -use common::block::Block; +use common::block::{Block, HashableBlockData}; use error::DbError; use rocksdb::{ BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, @@ -242,12 +242,7 @@ impl RocksDBIO { .put_cf( &cf_block, block.block_id.to_be_bytes(), - serde_json::to_vec(&block).map_err(|serr| { - DbError::serde_cast_message( - serr, - Some("Block Serialization failed".to_string()), - ) - })?, + HashableBlockData::from(block).to_bytes(), ) .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; Ok(()) @@ -261,9 +256,7 @@ impl RocksDBIO { .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; if let Some(data) = res { - Ok(serde_json::from_slice::(&data).map_err(|serr| { - DbError::serde_cast_message(serr, Some("Block Deserialization failed".to_string())) - })?) + Ok(HashableBlockData::from_bytes(&data).into()) } 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 c117157..4467162 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -80,11 +80,11 @@ mod tests { fn create_initial_accounts() -> Vec { let initial_acc1 = serde_json::from_str(r#"{ - "address": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143], "balance": 100, "key_holder": { "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": 1, + "pub_account_signing_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "top_secret_key_holder": { "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" }, @@ -98,11 +98,11 @@ mod tests { }"#).unwrap(); let initial_acc2 = serde_json::from_str(r#"{ - "address": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + "address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102], "balance": 200, "key_holder": { "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": 2, + "pub_account_signing_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], "top_secret_key_holder": { "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" },