From d3d0ba8554ae31090756ef84ef1ea70da47ca64d Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 1 Feb 2024 10:29:58 -0300 Subject: [PATCH 1/2] implement Results service call --- .../src/scan_service/request.rs | 4 +- .../src/scan_service/response.rs | 13 +++- zebra-scan/src/service.rs | 36 +++++++-- zebra-scan/src/service/tests.rs | 76 +++++++++++++++++++ 4 files changed, 118 insertions(+), 11 deletions(-) diff --git a/zebra-node-services/src/scan_service/request.rs b/zebra-node-services/src/scan_service/request.rs index c36202cdcf0..dbf0a2490cb 100644 --- a/zebra-node-services/src/scan_service/request.rs +++ b/zebra-node-services/src/scan_service/request.rs @@ -15,8 +15,8 @@ pub enum Request { /// Deletes viewing keys and their results from the database. DeleteKeys(Vec), - /// TODO: Accept `KeyHash`es and return `Transaction`s - Results(Vec<()>), + /// Accept keys and return transaction data + Results(Vec), /// TODO: Accept `KeyHash`es and return a channel receiver SubscribeResults(Vec<()>), diff --git a/zebra-node-services/src/scan_service/response.rs b/zebra-node-services/src/scan_service/response.rs index 7ba8b10e00a..fcb6b11b3af 100644 --- a/zebra-node-services/src/scan_service/response.rs +++ b/zebra-node-services/src/scan_service/response.rs @@ -1,8 +1,11 @@ //! `zebra_scan::service::ScanService` response types. -use std::sync::{mpsc, Arc}; +use std::{ + collections::BTreeMap, + sync::{mpsc, Arc}, +}; -use zebra_chain::{block::Height, transaction::Transaction}; +use zebra_chain::{block::Height, transaction::Hash}; #[derive(Debug)] /// Response types for `zebra_scan::service::ScanService` @@ -14,11 +17,13 @@ pub enum Response { }, /// Response to Results request - Results(Vec), + /// + /// We use the nested `BTreeMap` so we don't repeat any piece of response data. + Results(BTreeMap>>), /// Response to DeleteKeys request DeletedKeys, /// Response to SubscribeResults request - SubscribeResults(mpsc::Receiver>), + SubscribeResults(mpsc::Receiver>), } diff --git a/zebra-scan/src/service.rs b/zebra-scan/src/service.rs index 37c679faad2..b349aaba9ae 100644 --- a/zebra-scan/src/service.rs +++ b/zebra-scan/src/service.rs @@ -1,11 +1,12 @@ //! [`tower::Service`] for zebra-scan. -use std::{future::Future, pin::Pin, task::Poll, time::Duration}; +use std::{collections::BTreeMap, future::Future, pin::Pin, task::Poll, time::Duration}; use futures::future::FutureExt; use tower::Service; -use zebra_chain::parameters::Network; +use zebra_chain::{parameters::Network, transaction::Hash}; + use zebra_state::ChainTipChange; use crate::{init::ScanTask, scan, storage::Storage, Config, Request, Response}; @@ -130,8 +131,33 @@ impl Service for ScanService { .boxed(); } - Request::Results(_key_hashes) => { - // TODO: read results from db + Request::Results(keys) => { + let db = self.db.clone(); + + return async move { + if keys.len() > MAX_REQUEST_KEYS { + return Err(format!( + "maximum number of keys per request is {MAX_REQUEST_KEYS}" + ) + .into()); + } + + let mut final_result = BTreeMap::new(); + for key in keys { + let mut heights_and_transactions = BTreeMap::new(); + let txs = db.sapling_results_for_key(&key); + txs.iter().for_each(|(k, v)| { + heights_and_transactions + .entry(*k) + .or_insert_with(Vec::new) + .extend(v.iter().map(|x| Hash::from(*x))); + }); + final_result.entry(key).or_insert(heights_and_transactions); + } + + Ok(Response::Results(final_result)) + } + .boxed(); } Request::SubscribeResults(_key_hashes) => { @@ -143,6 +169,6 @@ impl Service for ScanService { } } - async move { Ok(Response::Results(vec![])) }.boxed() + async move { Ok(Response::Results(BTreeMap::new())) }.boxed() } } diff --git a/zebra-scan/src/service/tests.rs b/zebra-scan/src/service/tests.rs index caee08dada5..a2870bca901 100644 --- a/zebra-scan/src/service/tests.rs +++ b/zebra-scan/src/service/tests.rs @@ -77,3 +77,79 @@ pub async fn scan_service_deletes_keys_correctly() -> Result<()> { Ok(()) } + +/// Tests that results for key are returned correctly +#[tokio::test] +pub async fn scan_service_get_results_for_key_correctly() -> Result<()> { + let mut db = new_test_storage(Network::Mainnet); + + let zec_pages_sapling_efvk = ZECPAGES_SAPLING_VIEWING_KEY.to_string(); + + for fake_result_height in [Height::MIN, Height(1), Height::MAX] { + db.insert_sapling_results( + &zec_pages_sapling_efvk, + fake_result_height, + fake_sapling_results([ + TransactionIndex::MIN, + TransactionIndex::from_index(40), + TransactionIndex::MAX, + ]), + ); + } + + assert!( + db.sapling_results(&zec_pages_sapling_efvk).len() == 3, + "there should be 3 heights for this key in the db" + ); + + for (_height, transactions) in db.sapling_results(&zec_pages_sapling_efvk) { + assert!( + transactions.len() == 3, + "there should be 3 transactions for each height for this key in the db" + ); + } + + // We don't need to send any command to the scanner for this call. + let (mut scan_service, _cmd_receiver) = ScanService::new_with_mock_scanner(db); + + let response_fut = scan_service + .ready() + .await + .map_err(|err| eyre!(err))? + .call(Request::Results(vec![zec_pages_sapling_efvk.clone()])); + + match response_fut.await.map_err(|err| eyre!(err))? { + Response::Results(results) => { + assert!( + results.contains_key(&zec_pages_sapling_efvk), + "results should contain the requested key" + ); + assert!(results.len() == 1, "values are only for 1 key"); + + assert!( + results + .get_key_value(&zec_pages_sapling_efvk) + .unwrap() + .1 + .len() + == 3, + "we should have 3 heights for the given key " + ); + + for transactions in results + .get_key_value(&zec_pages_sapling_efvk) + .unwrap() + .1 + .values() + { + assert!( + transactions.len() == 3, + "there should be 3 transactions for each height for this key" + ); + } + } + _ => panic!("scan service returned unexpected response variant"), + }; + + Ok(()) +} From 80a429d727100ac3832031c97d3111e1b757ab4a Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Thu, 1 Feb 2024 18:18:45 -0300 Subject: [PATCH 2/2] call `sapling_results_for_key` from a blocking thread Co-authored-by: Arya --- zebra-scan/src/service.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zebra-scan/src/service.rs b/zebra-scan/src/service.rs index b349aaba9ae..a957c897422 100644 --- a/zebra-scan/src/service.rs +++ b/zebra-scan/src/service.rs @@ -144,8 +144,13 @@ impl Service for ScanService { let mut final_result = BTreeMap::new(); for key in keys { + let db = db.clone(); let mut heights_and_transactions = BTreeMap::new(); - let txs = db.sapling_results_for_key(&key); + let txs = { + let key = key.clone(); + tokio::task::spawn_blocking(move || db.sapling_results_for_key(&key)) + } + .await?; txs.iter().for_each(|(k, v)| { heights_and_transactions .entry(*k)