From 8d751acce3de21363c97246382488df9bf853bad Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 7 Apr 2022 20:25:13 -0300 Subject: [PATCH 1/8] implement `getaddresstxids` rpc method with dummy empty response --- zebra-chain/src/primitives.rs | 2 +- .../src/primitives/zcash_primitives.rs | 2 +- zebra-rpc/src/methods.rs | 69 ++++++++++++++ zebra-rpc/src/methods/tests/vectors.rs | 95 +++++++++++++++++++ zebra-state/src/request.rs | 8 ++ zebra-state/src/response.rs | 5 +- zebra-state/src/service.rs | 25 ++++- 7 files changed, 202 insertions(+), 4 deletions(-) diff --git a/zebra-chain/src/primitives.rs b/zebra-chain/src/primitives.rs index 0a6ce4c2a10..da310044466 100644 --- a/zebra-chain/src/primitives.rs +++ b/zebra-chain/src/primitives.rs @@ -16,4 +16,4 @@ pub use proofs::{Bctv14Proof, Groth16Proof, Halo2Proof, ZkSnarkProof}; pub mod zcash_history; pub mod zcash_note_encryption; -pub(crate) mod zcash_primitives; +pub mod zcash_primitives; diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 8ae8fc99f0b..89531664972 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -152,7 +152,7 @@ pub(crate) fn auth_digest(trans: &Transaction) -> AuthDigest { /// Return the destination address from a transparent output. /// /// Returns None if the address type is not valid or unrecognized. -pub(crate) fn transparent_output_address( +pub fn transparent_output_address( output: &transparent::Output, network: Network, ) -> Option { diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index d6ba68e8796..e4de2d49aa8 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -22,6 +22,7 @@ use zebra_chain::{ parameters::{ConsensusBranchId, Network, NetworkUpgrade}, serialization::{SerializationError, ZcashDeserialize}, transaction::{self, SerializedTransaction, Transaction}, + transparent::Address, }; use zebra_network::constants::USER_AGENT; use zebra_node_services::{mempool, BoxError}; @@ -140,6 +141,28 @@ pub trait Rpc { txid_hex: String, verbose: u8, ) -> BoxFuture>; + + /// Returns the transaction ids made by the provided transparent addresses. + /// + /// zcashd reference: [`getaddresstxids`](https://zcash.github.io/rpc/getaddresstxids.html) + /// + /// # Parameters + /// + /// - `addresses`: (json array of string, required) The addresses to get transactions from. + /// - `start`: (numeric, required) The lower height to start looking for transactions (inclusive). + /// - `end`: (numeric, required) The top height to stop looking for transactions (inclusive). + /// + /// # Notes + /// + /// Only the multi-argument format is used by lightwalletd and this is what we currently support: + /// https://github.com/zcash/lightwalletd/blob/631bb16404e3d8b045e74a7c5489db626790b2f6/common/common.go#L97-L102 + #[rpc(name = "getaddresstxids")] + fn get_address_tx_ids( + &self, + addresses: Vec, + start: u32, + end: u32, + ) -> BoxFuture>>; } /// RPC method implementations. @@ -526,6 +549,52 @@ where } .boxed() } + + fn get_address_tx_ids( + &self, + addresses: Vec, + start: u32, + end: u32, + ) -> BoxFuture>> { + let mut state = self.state.clone(); + let mut response_transactions = vec![]; + let start = Height(start); + let end = Height(end); + + async move { + if start > end { + return Err(Error::invalid_params( + "End height should be at least start height", + )); + } + + for address in addresses { + let valid_address: Address = address.parse().map_err(|_| { + Error::invalid_params(format!("Provided address is not valid: {}", address)) + })?; + + let request = + zebra_state::ReadRequest::TransactionsByAddresses(valid_address, start, end); + let response = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + match response { + zebra_state::ReadResponse::TransactionIds(hashes) => response_transactions + .append(&mut hashes.iter().map(|h| h.to_string()).collect()), + _ => unreachable!("unmatched response to a TransactionsByAddresses request"), + } + } + Ok(response_transactions) + } + .boxed() + } } /// Response to a `getinfo` RPC request. diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 9dddd5a1c2d..911b277304b 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -9,6 +9,7 @@ use zebra_chain::{ block::Block, chain_tip::NoChainTip, parameters::Network::*, + primitives::zcash_primitives, serialization::{ZcashDeserializeInto, ZcashSerialize}, transaction::{UnminedTx, UnminedTxId}, }; @@ -281,3 +282,97 @@ async fn rpc_getrawtransaction() { } } } + +#[tokio::test] +async fn rpc_getaddresstxids_invalid_arguments() { + zebra_test::init(); + + let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + let mut read_state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + + let rpc = RpcImpl::new( + "RPC test", + Buffer::new(mempool.clone(), 1), + Buffer::new(read_state.clone(), 1), + NoChainTip, + Mainnet, + ); + + // call the method with an invalid address string + let address = "11111111".to_string(); + let addresses = vec![address.clone()]; + let start: u32 = 1; + let end: u32 = 2; + let error = rpc + .get_address_tx_ids(addresses, start, end) + .await + .unwrap_err(); + assert_eq!( + error.message, + format!("Provided address is not valid: {}", address) + ); + + // call the method with an invalid start/end combination + let address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".to_string(); + let addresses = vec![address.clone()]; + let start: u32 = 2; + let end: u32 = 1; + let error = rpc + .get_address_tx_ids(addresses, start, end) + .await + .unwrap_err(); + assert_eq!( + error.message, + "End height should be at least start height".to_string() + ); + + read_state.expect_no_requests().await; + mempool.expect_no_requests().await; +} + +#[tokio::test] +async fn rpc_getaddresstxids_response() { + zebra_test::init(); + + let blocks: Vec> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS + .iter() + .map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap()) + .collect(); + + // get the first transaction of the first block + let first_block_first_transaction = &blocks[1].transactions[0]; + // get the address, this is always `t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd` + let address = zcash_primitives::transparent_output_address( + &first_block_first_transaction.outputs()[1], + Mainnet, + ) + .unwrap(); + + let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + // Create a populated state service + let (_state, read_state, _latest_chain_tip, _chain_tip_change) = + zebra_state::populated_state(blocks.clone(), Mainnet).await; + + let rpc = RpcImpl::new( + "RPC test", + Buffer::new(mempool.clone(), 1), + Buffer::new(read_state.clone(), 1), + NoChainTip, + Mainnet, + ); + + // call the method with valid arguments + let addresses = vec![address.to_string()]; + let start: u32 = 1; + let end: u32 = 1; + let response = rpc + .get_address_tx_ids(addresses, start, end) + .await + .expect("arguments are valid so no error can happen here"); + + // TODO: The lenght of the response should be 1 + // Fix in the context of #3147 + assert_eq!(response.len(), 0); + + mempool.expect_no_requests().await; +} diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 985f6932e43..89499447e77 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -401,4 +401,12 @@ pub enum ReadRequest { /// * [`Response::Transaction(Some(Arc))`](Response::Transaction) if the transaction is in the best chain; /// * [`Response::Transaction(None)`](Response::Transaction) otherwise. Transaction(transaction::Hash), + + /// Looks up transactions hashes that were made by provided address in a blockchain height range. + /// + /// Returns + /// + /// * A vector of transaction hashes. + /// * An empty vector if no transactions were found for the given arguments. + TransactionsByAddresses(transparent::Address, block::Height, block::Height), } diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index 6377142550c..0ba5903577d 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use zebra_chain::{ block::{self, Block}, - transaction::Transaction, + transaction::{Hash, Transaction}, transparent, }; @@ -53,4 +53,7 @@ pub enum ReadResponse { /// Response to [`ReadRequest::Transaction`] with the specified transaction. Transaction(Option<(Arc, block::Height)>), + + /// Response to [`ReadRequest::TransactionsByAddresses`] with the obtained transaction ids. + TransactionIds(Vec), } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 0a342d9ca63..0746456b0a2 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -958,7 +958,7 @@ impl Service for ReadStateService { .boxed() } - // For the get_raw_transaction RPC, to be implemented in #3145. + // For the get_raw_transaction RPC. ReadRequest::Transaction(hash) => { metrics::counter!( "state.requests", @@ -979,6 +979,29 @@ impl Service for ReadStateService { } .boxed() } + + // For the get_address_tx_ids RPC. + ReadRequest::TransactionsByAddresses(_address, _start, _end) => { + metrics::counter!( + "state.requests", + 1, + "service" => "read_state", + "type" => "transactionsbyaddresses", + ); + + let _state = self.clone(); + + async move { + // TODO: Respond with found transactions + // At least the following pull requests should be merged: + // - #4022 + // - #4038 + // Do the corresponding update in the context of #3147 + let transaction_ids = vec![]; + Ok(ReadResponse::TransactionIds(transaction_ids)) + } + .boxed() + } } } } From 1c2e3863b25281649a126c399dc7e6134af87824 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 10:17:40 -0300 Subject: [PATCH 2/8] use already public function --- zebra-chain/src/primitives.rs | 2 +- zebra-chain/src/primitives/zcash_primitives.rs | 2 +- zebra-rpc/src/methods/tests/vectors.rs | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/zebra-chain/src/primitives.rs b/zebra-chain/src/primitives.rs index da310044466..0a6ce4c2a10 100644 --- a/zebra-chain/src/primitives.rs +++ b/zebra-chain/src/primitives.rs @@ -16,4 +16,4 @@ pub use proofs::{Bctv14Proof, Groth16Proof, Halo2Proof, ZkSnarkProof}; pub mod zcash_history; pub mod zcash_note_encryption; -pub mod zcash_primitives; +pub(crate) mod zcash_primitives; diff --git a/zebra-chain/src/primitives/zcash_primitives.rs b/zebra-chain/src/primitives/zcash_primitives.rs index 89531664972..8ae8fc99f0b 100644 --- a/zebra-chain/src/primitives/zcash_primitives.rs +++ b/zebra-chain/src/primitives/zcash_primitives.rs @@ -152,7 +152,7 @@ pub(crate) fn auth_digest(trans: &Transaction) -> AuthDigest { /// Return the destination address from a transparent output. /// /// Returns None if the address type is not valid or unrecognized. -pub fn transparent_output_address( +pub(crate) fn transparent_output_address( output: &transparent::Output, network: Network, ) -> Option { diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 911b277304b..d7f3d536575 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -9,7 +9,6 @@ use zebra_chain::{ block::Block, chain_tip::NoChainTip, parameters::Network::*, - primitives::zcash_primitives, serialization::{ZcashDeserializeInto, ZcashSerialize}, transaction::{UnminedTx, UnminedTxId}, }; @@ -342,11 +341,9 @@ async fn rpc_getaddresstxids_response() { // get the first transaction of the first block let first_block_first_transaction = &blocks[1].transactions[0]; // get the address, this is always `t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd` - let address = zcash_primitives::transparent_output_address( - &first_block_first_transaction.outputs()[1], - Mainnet, - ) - .unwrap(); + let address = &first_block_first_transaction.outputs()[1] + .address(Mainnet) + .unwrap(); let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); // Create a populated state service From b1a69676ca75fed87863b4b6cadcb07225159d34 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 10:26:09 -0300 Subject: [PATCH 3/8] fix some docs --- zebra-state/src/request.rs | 3 +++ zebra-state/src/response.rs | 3 ++- zebra-state/src/service.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 89499447e77..2ec0de44f6e 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -408,5 +408,8 @@ pub enum ReadRequest { /// /// * A vector of transaction hashes. /// * An empty vector if no transactions were found for the given arguments. + /// + /// Returned txids are in the order they appear in blocks, which ensures that they are topologically sorted + /// (i.e. parent txids will appear before child txids). TransactionsByAddresses(transparent::Address, block::Height, block::Height), } diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index 0ba5903577d..6a0af14a018 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -54,6 +54,7 @@ pub enum ReadResponse { /// Response to [`ReadRequest::Transaction`] with the specified transaction. Transaction(Option<(Arc, block::Height)>), - /// Response to [`ReadRequest::TransactionsByAddresses`] with the obtained transaction ids. + /// Response to [`ReadRequest::TransactionsByAddresses`] with the obtained transaction ids, + /// in the order they appear in blocks. TransactionIds(Vec), } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 0746456b0a2..583e14ae6c8 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -986,7 +986,7 @@ impl Service for ReadStateService { "state.requests", 1, "service" => "read_state", - "type" => "transactionsbyaddresses", + "type" => "transactions_by_addresses", ); let _state = self.clone(); From 0948c7ee5af61c9c20900255d67f5ec5a96ec581 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 11:01:47 -0300 Subject: [PATCH 4/8] pass a list of addresses to the state request --- zebra-rpc/src/methods.rs | 37 ++++++++++++++++++++----------------- zebra-state/src/request.rs | 6 +++--- zebra-state/src/service.rs | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index e4de2d49aa8..5cbc11723c5 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -568,29 +568,32 @@ where )); } + let mut valid_addresses = vec![]; for address in addresses { let valid_address: Address = address.parse().map_err(|_| { Error::invalid_params(format!("Provided address is not valid: {}", address)) })?; + valid_addresses.push(valid_address); + } - let request = - zebra_state::ReadRequest::TransactionsByAddresses(valid_address, start, end); - let response = state - .ready() - .and_then(|service| service.call(request)) - .await - .map_err(|error| Error { - code: ErrorCode::ServerError(0), - message: error.to_string(), - data: None, - })?; - - match response { - zebra_state::ReadResponse::TransactionIds(hashes) => response_transactions - .append(&mut hashes.iter().map(|h| h.to_string()).collect()), - _ => unreachable!("unmatched response to a TransactionsByAddresses request"), - } + let request = + zebra_state::ReadRequest::TransactionsByAddresses(valid_addresses, start, end); + let response = state + .ready() + .and_then(|service| service.call(request)) + .await + .map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + match response { + zebra_state::ReadResponse::TransactionIds(hashes) => response_transactions + .append(&mut hashes.iter().map(|h| h.to_string()).collect()), + _ => unreachable!("unmatched response to a TransactionsByAddresses request"), } + Ok(response_transactions) } .boxed() diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 2ec0de44f6e..959ce0a9679 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -380,7 +380,7 @@ pub enum Request { }, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] /// A read-only query about the chain state, via the [`ReadStateService`]. pub enum ReadRequest { /// Looks up a block by hash or height in the current best chain. @@ -402,7 +402,7 @@ pub enum ReadRequest { /// * [`Response::Transaction(None)`](Response::Transaction) otherwise. Transaction(transaction::Hash), - /// Looks up transactions hashes that were made by provided address in a blockchain height range. + /// Looks up transactions hashes that were made by provided addresses in a blockchain height range. /// /// Returns /// @@ -411,5 +411,5 @@ pub enum ReadRequest { /// /// Returned txids are in the order they appear in blocks, which ensures that they are topologically sorted /// (i.e. parent txids will appear before child txids). - TransactionsByAddresses(transparent::Address, block::Height, block::Height), + TransactionsByAddresses(Vec, block::Height, block::Height), } diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 583e14ae6c8..232fe022e0e 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -981,7 +981,7 @@ impl Service for ReadStateService { } // For the get_address_tx_ids RPC. - ReadRequest::TransactionsByAddresses(_address, _start, _end) => { + ReadRequest::TransactionsByAddresses(_addresses, _start, _end) => { metrics::counter!( "state.requests", 1, From eb802af73c74516b1d156cf87bd6958d2a7ee75c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 12:29:24 -0300 Subject: [PATCH 5/8] sync range errors with zcashd --- zebra-rpc/src/methods.rs | 32 ++++++++++++++--- zebra-rpc/src/methods/tests/vectors.rs | 48 ++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 5cbc11723c5..9d78de1f619 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -561,12 +561,15 @@ where let start = Height(start); let end = Height(end); + let chain_height = self.latest_chain_tip.best_tip_height().ok_or(Error { + code: ErrorCode::ServerError(0), + message: "No blocks in state".to_string(), + data: None, + }); + async move { - if start > end { - return Err(Error::invalid_params( - "End height should be at least start height", - )); - } + // height range checks + check_height_range(start, end, chain_height?)?; let mut valid_addresses = vec![]; for address in addresses { @@ -722,3 +725,22 @@ impl GetRawTransaction { } } } + +/// Check if provided height range is valid +fn check_height_range(start: Height, end: Height, chain_height: Height) -> Result<()> { + if start == Height(0) || end == Height(0) { + return Err(Error::invalid_params( + "Start and end are expected to be greater than zero", + )); + } + if end < start { + return Err(Error::invalid_params( + "End value is expected to be greater or equal than start", + )); + } + if start > chain_height || end > chain_height { + return Err(Error::invalid_params("Start or end is outside chain range")); + } + + Ok(()) +} diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index d7f3d536575..d1265b0854e 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -287,13 +287,22 @@ async fn rpc_getaddresstxids_invalid_arguments() { zebra_test::init(); let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); - let mut read_state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); + + // Create a continuous chain of mainnet blocks from genesis + let blocks: Vec> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS + .iter() + .map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap()) + .collect(); + + // Create a populated state service + let (_state, read_state, latest_chain_tip, _chain_tip_change) = + zebra_state::populated_state(blocks.clone(), Mainnet).await; let rpc = RpcImpl::new( "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(read_state.clone(), 1), - NoChainTip, + latest_chain_tip, Mainnet, ); @@ -311,21 +320,46 @@ async fn rpc_getaddresstxids_invalid_arguments() { format!("Provided address is not valid: {}", address) ); - // call the method with an invalid start/end combination + // create a valid address let address = "t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd".to_string(); let addresses = vec![address.clone()]; + + // call the method with start greater than end let start: u32 = 2; let end: u32 = 1; + let error = rpc + .get_address_tx_ids(addresses.clone(), start, end) + .await + .unwrap_err(); + assert_eq!( + error.message, + "End value is expected to be greater or equal than start".to_string() + ); + + // call the method with start equal zero + let start: u32 = 0; + let end: u32 = 1; + let error = rpc + .get_address_tx_ids(addresses.clone(), start, end) + .await + .unwrap_err(); + assert_eq!( + error.message, + "Start and end are expected to be greater than zero".to_string() + ); + + // call the method outside the chain tip height + let start: u32 = 1; + let end: u32 = 11; let error = rpc .get_address_tx_ids(addresses, start, end) .await .unwrap_err(); assert_eq!( error.message, - "End height should be at least start height".to_string() + "Start or end is outside chain range".to_string() ); - read_state.expect_no_requests().await; mempool.expect_no_requests().await; } @@ -347,14 +381,14 @@ async fn rpc_getaddresstxids_response() { let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests(); // Create a populated state service - let (_state, read_state, _latest_chain_tip, _chain_tip_change) = + let (_state, read_state, latest_chain_tip, _chain_tip_change) = zebra_state::populated_state(blocks.clone(), Mainnet).await; let rpc = RpcImpl::new( "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(read_state.clone(), 1), - NoChainTip, + latest_chain_tip, Mainnet, ); From b0cce84289f83e71c9d6b45990cac4519ad89510 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 15:09:51 -0300 Subject: [PATCH 6/8] refactor a loop --- zebra-rpc/src/methods.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 9d78de1f619..0b43056e698 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -571,16 +571,17 @@ where // height range checks check_height_range(start, end, chain_height?)?; - let mut valid_addresses = vec![]; - for address in addresses { - let valid_address: Address = address.parse().map_err(|_| { - Error::invalid_params(format!("Provided address is not valid: {}", address)) - })?; - valid_addresses.push(valid_address); - } + let valid_addresses: Result> = addresses + .iter() + .map(|address| { + address.parse().map_err(|_| { + Error::invalid_params(format!("Provided address is not valid: {}", address)) + }) + }) + .collect(); let request = - zebra_state::ReadRequest::TransactionsByAddresses(valid_addresses, start, end); + zebra_state::ReadRequest::TransactionsByAddresses(valid_addresses?, start, end); let response = state .ready() .and_then(|service| service.call(request)) From c91e7087f2e67b83df69c75ac1cfae964021465c Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Fri, 8 Apr 2022 19:31:26 -0300 Subject: [PATCH 7/8] fix grammar --- zebra-rpc/src/methods.rs | 2 +- zebra-rpc/src/methods/tests/vectors.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 0b43056e698..470fb4b4383 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -736,7 +736,7 @@ fn check_height_range(start: Height, end: Height, chain_height: Height) -> Resul } if end < start { return Err(Error::invalid_params( - "End value is expected to be greater or equal than start", + "End value is expected to be greater than or equal to start", )); } if start > chain_height || end > chain_height { diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index d1265b0854e..c707d3893ba 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -333,7 +333,7 @@ async fn rpc_getaddresstxids_invalid_arguments() { .unwrap_err(); assert_eq!( error.message, - "End value is expected to be greater or equal than start".to_string() + "End value is expected to be greater than or equal to start".to_string() ); // call the method with start equal zero From cc376590fa924c362dd92c9be8ba7dfed71d2bf5 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 12 Apr 2022 12:08:13 -0300 Subject: [PATCH 8/8] fix tests --- zebra-rpc/src/methods/tests/vectors.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index a882b7d1f3d..f2799a6cd14 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -322,7 +322,7 @@ async fn rpc_getaddresstxids_invalid_arguments() { let (_state, read_state, latest_chain_tip, _chain_tip_change) = zebra_state::populated_state(blocks.clone(), Mainnet).await; - let rpc = RpcImpl::new( + let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new( "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(read_state.clone(), 1), @@ -385,6 +385,10 @@ async fn rpc_getaddresstxids_invalid_arguments() { ); mempool.expect_no_requests().await; + + // The queue task should continue without errors or panics + let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never(); + assert!(matches!(rpc_tx_queue_task_result, None)); } #[tokio::test] @@ -408,7 +412,7 @@ async fn rpc_getaddresstxids_response() { let (_state, read_state, latest_chain_tip, _chain_tip_change) = zebra_state::populated_state(blocks.clone(), Mainnet).await; - let rpc = RpcImpl::new( + let (rpc, rpc_tx_queue_task_handle) = RpcImpl::new( "RPC test", Buffer::new(mempool.clone(), 1), Buffer::new(read_state.clone(), 1), @@ -430,4 +434,8 @@ async fn rpc_getaddresstxids_response() { assert_eq!(response.len(), 0); mempool.expect_no_requests().await; + + // The queue task should continue without errors or panics + let rpc_tx_queue_task_result = rpc_tx_queue_task_handle.now_or_never(); + assert!(matches!(rpc_tx_queue_task_result, None)); }