From 9d85ea234e7f4c93ab54bd1b3427e22c98ac5a34 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 10:23:52 -0300 Subject: [PATCH 1/9] add get transaction by hash rpc method --- common/src/rpc_primitives/requests.rs | 11 ++++ .../src/sequencer_store/block_store.rs | 45 +++++++++++++-- sequencer_rpc/src/process.rs | 55 +++++++++++++++++-- 3 files changed, 101 insertions(+), 10 deletions(-) diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index e17c786..48e41ea 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -39,6 +39,11 @@ pub struct GetAccountBalanceRequest { pub address: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct GetTransactionByHashRequest { + pub hash: String, +} + parse_request!(HelloRequest); parse_request!(RegisterAccountRequest); parse_request!(SendTxRequest); @@ -46,6 +51,7 @@ parse_request!(GetBlockDataRequest); parse_request!(GetGenesisIdRequest); parse_request!(GetLastBlockRequest); parse_request!(GetAccountBalanceRequest); +parse_request!(GetTransactionByHashRequest); #[derive(Serialize, Deserialize, Debug)] pub struct HelloResponse { @@ -81,3 +87,8 @@ pub struct GetLastBlockResponse { pub struct GetAccountBalanceResponse { pub balance: u64, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetTransactionByHashResponse { + pub transaction: Option, +} diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 8c556a3..e4611c5 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -1,11 +1,12 @@ -use std::path::Path; +use std::{collections::HashMap, path::Path}; use anyhow::Result; -use common::block::Block; +use common::{block::Block, merkle_tree_public::TreeHashType, transaction::Transaction}; use storage::RocksDBIO; pub struct SequecerBlockStore { dbio: RocksDBIO, + tx_hash_to_block_map: HashMap, pub genesis_id: u64, } @@ -15,11 +16,21 @@ impl SequecerBlockStore { /// /// ATTENTION: Will overwrite genesis block. pub fn open_db_with_genesis(location: &Path, genesis_block: Option) -> Result { + let tx_hash_to_block_map = if let Some(block) = &genesis_block { + block_to_transactions_map(block) + } else { + HashMap::new() + }; + let dbio = RocksDBIO::new(location, genesis_block)?; let genesis_id = dbio.get_meta_first_block_in_db()?; - Ok(Self { dbio, genesis_id }) + Ok(Self { + dbio, + genesis_id, + tx_hash_to_block_map, + }) } ///Reopening existing database @@ -31,7 +42,31 @@ impl SequecerBlockStore { Ok(self.dbio.get_block(id)?) } - pub fn put_block_at_id(&self, block: Block) -> Result<()> { - Ok(self.dbio.put_block(block, false)?) + pub fn put_block_at_id(&mut self, block: Block) -> Result<()> { + let new_transactions_map = block_to_transactions_map(&block); + self.dbio.put_block(block, false)?; + self.tx_hash_to_block_map.extend(new_transactions_map); + Ok(()) + } + + 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() { + if transaction.hash() == hash { + return Some(transaction); + } + } + } + None } } + +fn block_to_transactions_map(block: &Block) -> HashMap { + block + .transactions + .iter() + .map(|transaction| (transaction.hash(), block.block_id)) + .collect() +} diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index f8bc880..3e68598 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,11 +1,17 @@ use actix_web::Error as HttpError; use serde_json::Value; -use common::rpc_primitives::{ - errors::{RpcError, RpcParseError}, - message::{Message, Request}, - parser::RpcRequest, - requests::{GetAccountBalanceRequest, GetAccountBalanceResponse}, +use common::{ + merkle_tree_public::TreeHashType, + rpc_primitives::{ + errors::{RpcError, RpcParseError}, + message::{Message, Request}, + parser::RpcRequest, + requests::{ + GetAccountBalanceRequest, GetAccountBalanceResponse, GetTransactionByHashRequest, + GetTransactionByHashResponse, + }, + }, }; use common::rpc_primitives::requests::{ @@ -23,6 +29,7 @@ pub const GET_BLOCK: &str = "get_block"; pub const GET_GENESIS: &str = "get_genesis"; pub const GET_LAST_BLOCK: &str = "get_last_block"; pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance"; +pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash"; pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER"; @@ -156,6 +163,21 @@ impl JsonHandler { respond(helperstruct) } + async fn process_get_transaction_by_hash(&self, request: Request) -> Result { + let get_transaction_req = GetTransactionByHashRequest::parse(Some(request.params))?; + let bytes: Vec = hex::decode(get_transaction_req.hash) + .map_err(|_| RpcParseError("invalid hash".to_string()))?; + let hash: TreeHashType = bytes + .try_into() + .map_err(|_| RpcParseError("invalid hash".to_string()))?; + + let transaction = { + let state = self.sequencer_state.lock().await; + state.store.block_store.get_transaction_by_hash(hash) + }; + let helperstruct = GetTransactionByHashResponse { transaction }; + respond(helperstruct) + } pub async fn process_request_internal(&self, request: Request) -> Result { match request.method.as_ref() { @@ -166,6 +188,7 @@ impl JsonHandler { GET_GENESIS => self.process_get_genesis(request).await, GET_LAST_BLOCK => self.process_get_last_block(request).await, GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await, + GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await, _ => Err(RpcErr(RpcError::method_not_found(request.method))), } } @@ -315,4 +338,26 @@ mod tests { assert_eq!(response, expected_response); } + + #[actix_web::test] + async fn test_get_transaction_by_hash_for_non_existent_hash() { + let json_handler = json_handler_for_tests(); + let request = serde_json::json!({ + "jsonrpc": "2.0", + "method": "get_transaction_by_hash", + "params": { "hash": "cafe".repeat(16) }, + "id": 1 + }); + let expected_response = serde_json::json!({ + "id": 1, + "jsonrpc": "2.0", + "result": { + "transaction": Value::Null + } + }); + + let response = call_rpc_handler_with_json(json_handler, request).await; + + assert_eq!(response, expected_response); + } } From 13536e69af142a575cc96e38127bbdd5e0bf5a69 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 10:34:57 -0300 Subject: [PATCH 2/9] add test --- sequencer_rpc/src/process.rs | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 3e68598..bd197d8 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -148,7 +148,7 @@ impl JsonHandler { async fn process_get_account_balance(&self, request: Request) -> Result { let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?; let address = hex::decode(get_account_req.address) - .map_err(|_| RpcParseError("invalid address".to_string()))?; + .map_err(|_| RpcError::invalid_params("invalid address".to_string()))?; let balance = { let state = self.sequencer_state.lock().await; @@ -166,10 +166,10 @@ impl JsonHandler { async fn process_get_transaction_by_hash(&self, request: Request) -> Result { let get_transaction_req = GetTransactionByHashRequest::parse(Some(request.params))?; let bytes: Vec = hex::decode(get_transaction_req.hash) - .map_err(|_| RpcParseError("invalid hash".to_string()))?; + .map_err(|_| RpcError::invalid_params("invalid hash".to_string()))?; let hash: TreeHashType = bytes .try_into() - .map_err(|_| RpcParseError("invalid hash".to_string()))?; + .map_err(|_| RpcError::invalid_params("invalid hash".to_string()))?; let transaction = { let state = self.sequencer_state.lock().await; @@ -300,16 +300,9 @@ mod tests { "jsonrpc": "2.0", "id": 1, "error": { - "code": -32700, - "message": "Parse error", - "name": "REQUEST_VALIDATION_ERROR", - "data": "invalid address", - "cause": { - "name": "PARSE_ERROR", - "info": { - "error_message": "invalid address" - } - } + "code": -32602, + "message": "Invalid params", + "data": "invalid address" } }); let response = call_rpc_handler_with_json(json_handler, request).await; @@ -360,4 +353,28 @@ mod tests { assert_eq!(response, expected_response); } + + #[actix_web::test] + async fn test_get_transaction_by_hash_for_invalid_hash() { + let json_handler = json_handler_for_tests(); + let request = serde_json::json!({ + "jsonrpc": "2.0", + "method": "get_transaction_by_hash", + "params": { "hash": "not_a_valid_hex" }, + "id": 1 + }); + let expected_response = serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32602, + "message": "Invalid params", + "data": "invalid hash" + } + }); + + let response = call_rpc_handler_with_json(json_handler, request).await; + + assert_eq!(response, expected_response); + } } From c9574b0d094e07d6e1de43676ee7dc2c43d840f4 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 11:13:19 -0300 Subject: [PATCH 3/9] add test --- sequencer_rpc/src/process.rs | 69 +++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index bd197d8..708e771 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -199,9 +199,10 @@ mod tests { use std::sync::Arc; use crate::{rpc_handler, JsonHandler}; - use common::rpc_primitives::RpcPollingConfig; + use common::{rpc_primitives::RpcPollingConfig, transaction::Transaction}; use sequencer_core::{ config::{AccountInitialData, SequencerConfig}, + transaction_mempool::TransactionMempool, SequencerCore, }; use serde_json::Value; @@ -236,11 +237,34 @@ mod tests { fn json_handler_for_tests() -> JsonHandler { let config = sequencer_config_for_tests(); - let sequencer_core = Arc::new(Mutex::new(SequencerCore::start_from_config(config))); + let mut sequencer_core = SequencerCore::start_from_config(config); + let tx = Transaction { + tx_kind: common::transaction::TxKind::Public, + execution_input: Default::default(), + execution_output: Default::default(), + utxo_commitments_spent_hashes: Default::default(), + utxo_commitments_created_hashes: Default::default(), + nullifier_created_hashes: Default::default(), + execution_proof_private: Default::default(), + encoded_data: Default::default(), + ephemeral_pub_key: Default::default(), + commitment: Default::default(), + tweak: Default::default(), + secret_r: Default::default(), + sc_addr: Default::default(), + state_changes: Default::default(), + }; + + sequencer_core + .push_tx_into_mempool_pre_check(TransactionMempool { tx }, [[0; 32]; 2]) + .unwrap(); + sequencer_core + .produce_new_block_with_mempool_transactions() + .unwrap(); JsonHandler { polling_config: RpcPollingConfig::default(), - sequencer_state: sequencer_core, + sequencer_state: Arc::new(Mutex::new(sequencer_core)), } } @@ -345,7 +369,7 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "transaction": Value::Null + "transaction": null } }); @@ -377,4 +401,41 @@ mod tests { assert_eq!(response, expected_response); } + + #[actix_web::test] + async fn test_get_transaction_by_hash_for_existing_transaction() { + let json_handler = json_handler_for_tests(); + let request = serde_json::json!({ + "jsonrpc": "2.0", + "method": "get_transaction_by_hash", + "params": { "hash": "ca8e38269c0137d27cbe7c55d240a834b46e86e236578b9a1a3a25b3dabc5709" }, + "id": 1 + }); + let expected_response = serde_json::json!({ + "id": 1, + "jsonrpc": "2.0", + "result": { + "transaction": { + "commitment": [], + "encoded_data": [], + "ephemeral_pub_key": [], + "execution_input": [], + "execution_output": [], + "execution_proof_private": "", + "nullifier_created_hashes": [], + "sc_addr": "", + "secret_r": vec![0; 32], + "state_changes": [null, 0], + "tweak": "0".repeat(64), + "tx_kind": "Public", + "utxo_commitments_created_hashes": [], + "utxo_commitments_spent_hashes": [] + } + } + }); + + let response = call_rpc_handler_with_json(json_handler, request).await; + + assert_eq!(response, expected_response); + } } From 98631721fa94e4cfcd14f0d8de30694a618d4536 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 12:27:42 -0300 Subject: [PATCH 4/9] add test --- Cargo.lock | 1 + common/src/transaction.rs | 4 +- sequencer_core/Cargo.toml | 1 + .../src/sequencer_store/block_store.rs | 58 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78c4c62..e837cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4359,6 +4359,7 @@ dependencies = [ "serde", "serde_json", "storage", + "tempfile", ] [[package]] diff --git a/common/src/transaction.rs b/common/src/transaction.rs index 3f2dcb2..3d16fcd 100644 --- a/common/src/transaction.rs +++ b/common/src/transaction.rs @@ -15,7 +15,7 @@ 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,7 +23,7 @@ pub enum TxKind { Deshielded, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] ///General transaction object pub struct Transaction { pub tx_kind: TxKind, diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 14c047c..ebe3984 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -13,6 +13,7 @@ serde.workspace = true rand.workspace = true elliptic-curve.workspace = true k256.workspace = true +tempfile.workspace = true [dependencies.storage] path = "../storage" diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index e4611c5..1bd2bfd 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -70,3 +70,61 @@ fn block_to_transactions_map(block: &Block) -> HashMap { .map(|transaction| (transaction.hash(), block.block_id)) .collect() } + +#[cfg(test)] +mod tests { + use super::*; + use tempfile::tempdir; + + fn create_genesis_block_with_transaction() -> (Block, Transaction) { + let tx = Transaction { + tx_kind: common::transaction::TxKind::Public, + execution_input: Default::default(), + execution_output: Default::default(), + utxo_commitments_spent_hashes: Default::default(), + utxo_commitments_created_hashes: Default::default(), + nullifier_created_hashes: Default::default(), + execution_proof_private: Default::default(), + encoded_data: Default::default(), + ephemeral_pub_key: Default::default(), + commitment: Default::default(), + tweak: Default::default(), + secret_r: Default::default(), + sc_addr: Default::default(), + state_changes: Default::default(), + }; + ( + Block { + block_id: 0, + prev_block_id: 0, + prev_block_hash: [0; 32], + hash: [1; 32], + transactions: vec![tx.clone()], + data: vec![], + }, + tx, + ) + } + + #[test] + fn test_get_transaction_by_hash_for_existing_transaction() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let (block, tx) = create_genesis_block_with_transaction(); + let node_store = SequecerBlockStore::open_db_with_genesis(path, Some(block)).unwrap(); + let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); + assert_eq!(Some(tx), retrieved_tx); + } + + #[test] + fn test_get_transaction_by_hash_for_non_existent_transaction() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let (block, tx) = create_genesis_block_with_transaction(); + let node_store = SequecerBlockStore::open_db_with_genesis(path, Some(block)).unwrap(); + let retrieved_tx = node_store.get_transaction_by_hash([0; 32]); + assert_eq!(None, retrieved_tx); + } +} From 5dd7d257621d008c202699159b4c8461cef28728 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 12:58:54 -0300 Subject: [PATCH 5/9] nit --- sequencer_core/src/sequencer_store/block_store.rs | 2 +- sequencer_rpc/src/process.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 1bd2bfd..5cc2c69 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -122,7 +122,7 @@ mod tests { let temp_dir = tempdir().unwrap(); let path = temp_dir.path(); - let (block, tx) = create_genesis_block_with_transaction(); + let (block, _) = create_genesis_block_with_transaction(); let node_store = SequecerBlockStore::open_db_with_genesis(path, Some(block)).unwrap(); let retrieved_tx = node_store.get_transaction_by_hash([0; 32]); assert_eq!(None, retrieved_tx); diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 708e771..47311ef 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -4,7 +4,7 @@ use serde_json::Value; use common::{ merkle_tree_public::TreeHashType, rpc_primitives::{ - errors::{RpcError, RpcParseError}, + errors::RpcError, message::{Message, Request}, parser::RpcRequest, requests::{ From 3e54156abf2927eaf5716cf3910d22a8d8a8b79d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 13:21:33 -0300 Subject: [PATCH 6/9] improve test so that it covers the map update logic --- .../src/sequencer_store/block_store.rs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 5cc2c69..05bbc95 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -49,6 +49,7 @@ impl SequecerBlockStore { Ok(()) } + /// Returns the transaction corresponding to the given hash, if it exists in the blockchain. 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)); @@ -76,7 +77,7 @@ mod tests { use super::*; use tempfile::tempdir; - fn create_genesis_block_with_transaction() -> (Block, Transaction) { + fn create_dummy_block_with_transaction(block_id: u64) -> (Block, Transaction) { let tx = Transaction { tx_kind: common::transaction::TxKind::Public, execution_input: Default::default(), @@ -95,8 +96,8 @@ mod tests { }; ( Block { - block_id: 0, - prev_block_id: 0, + block_id, + prev_block_id: block_id - 1, prev_block_hash: [0; 32], hash: [1; 32], transactions: vec![tx.clone()], @@ -107,24 +108,28 @@ mod tests { } #[test] - fn test_get_transaction_by_hash_for_existing_transaction() { + fn test_get_transaction_by_hash() { let temp_dir = tempdir().unwrap(); let path = temp_dir.path(); - - let (block, tx) = create_genesis_block_with_transaction(); - let node_store = SequecerBlockStore::open_db_with_genesis(path, Some(block)).unwrap(); + let genesis_block = Block { + block_id: 0, + prev_block_id: 0, + 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 (block, tx) = create_dummy_block_with_transaction(1); + // Try retrieve a tx that's not in the chain yet. + let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); + assert_eq!(None, retrieved_tx); + // Add the block with the transaction + node_store.put_block_at_id(block).unwrap(); + // Try again let retrieved_tx = node_store.get_transaction_by_hash(tx.hash()); assert_eq!(Some(tx), retrieved_tx); } - - #[test] - fn test_get_transaction_by_hash_for_non_existent_transaction() { - let temp_dir = tempdir().unwrap(); - let path = temp_dir.path(); - - let (block, _) = create_genesis_block_with_transaction(); - let node_store = SequecerBlockStore::open_db_with_genesis(path, Some(block)).unwrap(); - let retrieved_tx = node_store.get_transaction_by_hash([0; 32]); - assert_eq!(None, retrieved_tx); - } } From 693aa0c9f76b7ce0b6c261a5eb9bf21952e855fb Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 13:36:12 -0300 Subject: [PATCH 7/9] improve error messages --- sequencer_rpc/src/process.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 47311ef..0068437 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -166,10 +166,10 @@ impl JsonHandler { async fn process_get_transaction_by_hash(&self, request: Request) -> Result { let get_transaction_req = GetTransactionByHashRequest::parse(Some(request.params))?; let bytes: Vec = hex::decode(get_transaction_req.hash) - .map_err(|_| RpcError::invalid_params("invalid hash".to_string()))?; + .map_err(|_| RpcError::invalid_params("invalid hex".to_string()))?; let hash: TreeHashType = bytes .try_into() - .map_err(|_| RpcError::invalid_params("invalid hash".to_string()))?; + .map_err(|_| RpcError::invalid_params("invalid length".to_string()))?; let transaction = { let state = self.sequencer_state.lock().await; @@ -379,7 +379,7 @@ mod tests { } #[actix_web::test] - async fn test_get_transaction_by_hash_for_invalid_hash() { + async fn test_get_transaction_by_hash_for_invalid_hex() { let json_handler = json_handler_for_tests(); let request = serde_json::json!({ "jsonrpc": "2.0", @@ -393,7 +393,31 @@ mod tests { "error": { "code": -32602, "message": "Invalid params", - "data": "invalid hash" + "data": "invalid hex" + } + }); + + let response = call_rpc_handler_with_json(json_handler, request).await; + + assert_eq!(response, expected_response); + } + + #[actix_web::test] + async fn test_get_transaction_by_hash_for_invalid_length() { + let json_handler = json_handler_for_tests(); + let request = serde_json::json!({ + "jsonrpc": "2.0", + "method": "get_transaction_by_hash", + "params": { "hash": "cafecafe" }, + "id": 1 + }); + let expected_response = serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "error": { + "code": -32602, + "message": "Invalid params", + "data": "invalid length" } }); From db5815ec09e44ac0757c44539bfb8fd5806e1d79 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 22 Jul 2025 13:44:52 -0300 Subject: [PATCH 8/9] add docstrings --- sequencer_rpc/src/process.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 0068437..f8dc7da 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -163,6 +163,9 @@ impl JsonHandler { respond(helperstruct) } + + /// Returns the transaction corresponding to the given hash, if it exists in the blockchain. + /// The hash must be a valid hex string of the correct length. async fn process_get_transaction_by_hash(&self, request: Request) -> Result { let get_transaction_req = GetTransactionByHashRequest::parse(Some(request.params))?; let bytes: Vec = hex::decode(get_transaction_req.hash) From 6ede7eac160d96a3c84d95354a413b14b5aad0db Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 24 Jul 2025 08:21:20 -0300 Subject: [PATCH 9/9] add TODO --- sequencer_core/src/sequencer_store/block_store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/sequencer_core/src/sequencer_store/block_store.rs b/sequencer_core/src/sequencer_store/block_store.rs index 05bbc95..02843fd 100644 --- a/sequencer_core/src/sequencer_store/block_store.rs +++ b/sequencer_core/src/sequencer_store/block_store.rs @@ -6,6 +6,7 @@ use storage::RocksDBIO; pub struct SequecerBlockStore { dbio: RocksDBIO, + // TODO: Consider adding the hashmap to the database for faster recovery. tx_hash_to_block_map: HashMap, pub genesis_id: u64, }