From 5b8eadc66efbebfb6a1cfc2f6180a82fe40b0ed5 Mon Sep 17 00:00:00 2001 From: arya2 Date: Thu, 10 Nov 2022 18:53:57 -0500 Subject: [PATCH] adds read::unspent_utxo fn --- zebra-consensus/src/transaction.rs | 8 +++++--- zebra-consensus/src/transaction/tests.rs | 4 ++-- zebra-state/src/request.rs | 18 +++++++----------- zebra-state/src/response.rs | 13 +++++-------- zebra-state/src/service.rs | 16 ++++++++++------ zebra-state/src/service/read.rs | 4 +++- zebra-state/src/service/read/block.rs | 16 ++++++++++++++++ 7 files changed, 48 insertions(+), 31 deletions(-) diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 07953fdbe9c..55e6539ca91 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -486,11 +486,13 @@ where tracing::trace!("UXTO in known_utxos, discarding query"); output.utxo.clone() } else if is_mempool { - let query = state.clone().oneshot(zs::Request::BestChainUtxo(*outpoint)); - if let zebra_state::Response::BestChainUtxo(utxo) = query.await? { + let query = state + .clone() + .oneshot(zs::Request::UnspentBestChainUtxo(*outpoint)); + if let zebra_state::Response::UnspentBestChainUtxo(utxo) = query.await? { utxo.ok_or(TransactionError::TransparentInputNotFound)? } else { - unreachable!("BestChainUtxo always responds with Option") + unreachable!("UnspentBestChainUtxo always responds with Option") } } else { let query = state diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index 3c94178d74a..4adcb9e91fa 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -189,7 +189,7 @@ async fn mempool_request_with_missing_input_is_rejected() { .find(|(_, tx)| !(tx.is_coinbase() || tx.inputs().is_empty())) .expect("At least one non-coinbase transaction with transparent inputs in test vectors"); - let expected_state_request = zebra_state::Request::BestChainUtxo(match tx.inputs()[0] { + let expected_state_request = zebra_state::Request::UnspentBestChainUtxo(match tx.inputs()[0] { transparent::Input::PrevOut { outpoint, .. } => outpoint, transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"), }); @@ -199,7 +199,7 @@ async fn mempool_request_with_missing_input_is_rejected() { .expect_request(expected_state_request) .await .expect("verifier should call mock state service") - .respond(zebra_state::Response::BestChainUtxo(None)); + .respond(zebra_state::Response::UnspentBestChainUtxo(None)); }); let verifier_response = verifier diff --git a/zebra-state/src/request.rs b/zebra-state/src/request.rs index 25fca397677..151bb769c39 100644 --- a/zebra-state/src/request.rs +++ b/zebra-state/src/request.rs @@ -462,10 +462,7 @@ pub enum Request { /// returning `None` immediately if it is unknown. /// /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain. - /// - /// This request is purely informational, there is no guarantee that - /// the UTXO remains unspent in the best chain. - BestChainUtxo(transparent::OutPoint), + UnspentBestChainUtxo(transparent::OutPoint), /// Looks up a block by hash or height in the current best chain. /// @@ -554,7 +551,7 @@ impl Request { Request::Tip => "tip", Request::BlockLocator => "block_locator", Request::Transaction(_) => "transaction", - Request::BestChainUtxo { .. } => "best_chain_utxo", + Request::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo", Request::Block(_) => "block", Request::FindBlockHashes { .. } => "find_block_hashes", Request::FindBlockHeaders { .. } => "find_block_headers", @@ -623,10 +620,7 @@ pub enum ReadRequest { /// returning `None` immediately if it is unknown. /// /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain. - /// - /// This request is purely informational, there is no guarantee that - /// the UTXO remains unspent in the best chain. - BestChainUtxo(transparent::OutPoint), + UnspentBestChainUtxo(transparent::OutPoint), /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint), /// returning `None` immediately if it is unknown. @@ -760,7 +754,7 @@ impl ReadRequest { ReadRequest::Block(_) => "block", ReadRequest::Transaction(_) => "transaction", ReadRequest::TransactionIdsForBlock(_) => "transaction_ids_for_block", - ReadRequest::BestChainUtxo { .. } => "best_chain_utxo", + ReadRequest::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo", ReadRequest::AnyChainUtxo { .. } => "any_chain_utxo", ReadRequest::BlockLocator => "block_locator", ReadRequest::FindBlockHashes { .. } => "find_block_hashes", @@ -799,7 +793,9 @@ impl TryFrom for ReadRequest { Request::Block(hash_or_height) => Ok(ReadRequest::Block(hash_or_height)), Request::Transaction(tx_hash) => Ok(ReadRequest::Transaction(tx_hash)), - Request::BestChainUtxo(outpoint) => Ok(ReadRequest::BestChainUtxo(outpoint)), + Request::UnspentBestChainUtxo(outpoint) => { + Ok(ReadRequest::UnspentBestChainUtxo(outpoint)) + } Request::BlockLocator => Ok(ReadRequest::BlockLocator), Request::FindBlockHashes { known_blocks, stop } => { diff --git a/zebra-state/src/response.rs b/zebra-state/src/response.rs index 8ea09f1837c..71d9ff10576 100644 --- a/zebra-state/src/response.rs +++ b/zebra-state/src/response.rs @@ -36,8 +36,8 @@ pub enum Response { /// Response to [`Request::Transaction`] with the specified transaction. Transaction(Option>), - /// Response to [`Request::BestChainUtxo`] with the UTXO - BestChainUtxo(Option), + /// Response to [`Request::UnspentBestChainUtxo`] with the UTXO + UnspentBestChainUtxo(Option), /// Response to [`Request::Block`] with the specified block. Block(Option>), @@ -84,12 +84,9 @@ pub enum ReadResponse { /// The response to a `FindBlockHeaders` request. BlockHeaders(Vec), - /// The response to a `BestChainUtxo` request, from verified blocks in the + /// The response to a `UnspentBestChainUtxo` request, from verified blocks in the /// _best_ non-finalized chain, or the finalized chain. - /// - /// This response is purely informational, there is no guarantee that - /// the UTXO remains unspent in the best chain. - BestChainUtxo(Option), + UnspentBestChainUtxo(Option), /// The response to an `AnyChainUtxo` request, from verified blocks in /// _any_ non-finalized chain, or the finalized chain. @@ -135,7 +132,7 @@ impl TryFrom for Response { ReadResponse::Transaction(tx_and_height) => { Ok(Response::Transaction(tx_and_height.map(|(tx, _height)| tx))) } - ReadResponse::BestChainUtxo(utxo) => Ok(Response::BestChainUtxo(utxo)), + ReadResponse::UnspentBestChainUtxo(utxo) => Ok(Response::UnspentBestChainUtxo(utxo)), ReadResponse::AnyChainUtxo(_) => Err("ReadService does not track pending UTXOs. \ diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index b583d6198fb..0f45aa0f448 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -1021,7 +1021,7 @@ impl Service for StateService { | Request::Tip | Request::BlockLocator | Request::Transaction(_) - | Request::BestChainUtxo(_) + | Request::UnspentBestChainUtxo(_) | Request::Block(_) | Request::FindBlockHashes { .. } | Request::FindBlockHeaders { .. } => { @@ -1218,7 +1218,7 @@ impl Service for ReadStateService { } // Currently unused. - ReadRequest::BestChainUtxo(outpoint) => { + ReadRequest::UnspentBestChainUtxo(outpoint) => { let timer = CodeTimer::start(); let state = self.clone(); @@ -1228,17 +1228,21 @@ impl Service for ReadStateService { span.in_scope(move || { let utxo = state.non_finalized_state_receiver.with_watch_data( |non_finalized_state| { - read::utxo(non_finalized_state.best_chain(), &state.db, outpoint) + read::unspent_utxo( + non_finalized_state.best_chain(), + &state.db, + outpoint, + ) }, ); // The work is done in the future. - timer.finish(module_path!(), line!(), "ReadRequest::BestChainUtxo"); + timer.finish(module_path!(), line!(), "ReadRequest::UnspentBestChainUtxo"); - Ok(ReadResponse::BestChainUtxo(utxo)) + Ok(ReadResponse::UnspentBestChainUtxo(utxo)) }) }) - .map(|join_result| join_result.expect("panic in ReadRequest::BestChainUtxo")) + .map(|join_result| join_result.expect("panic in ReadRequest::UnspentBestChainUtxo")) .boxed() } diff --git a/zebra-state/src/service/read.rs b/zebra-state/src/service/read.rs index debff2fd3b2..b0579c83779 100644 --- a/zebra-state/src/service/read.rs +++ b/zebra-state/src/service/read.rs @@ -28,7 +28,9 @@ pub use address::{ utxo::{address_utxos, AddressUtxos, ADDRESS_HEIGHTS_FULL_RANGE}, }; -pub use block::{any_utxo, block, block_header, transaction, transaction_hashes_for_block, utxo}; +pub use block::{ + any_utxo, block, block_header, transaction, transaction_hashes_for_block, unspent_utxo, utxo, +}; #[cfg(feature = "getblocktemplate-rpcs")] pub use block::hash; diff --git a/zebra-state/src/service/read/block.rs b/zebra-state/src/service/read/block.rs index dfa9ba125d2..3cb69686d4c 100644 --- a/zebra-state/src/service/read/block.rs +++ b/zebra-state/src/service/read/block.rs @@ -141,6 +141,22 @@ where .or_else(|| db.utxo(&outpoint).map(|utxo| utxo.utxo)) } +/// Returns the [`Utxo`] for [`transparent::OutPoint`], if it exists and is unspent in the +/// non-finalized `chain` or finalized `db`. +pub fn unspent_utxo( + chain: Option, + db: &ZebraDb, + outpoint: transparent::OutPoint, +) -> Option +where + C: AsRef, +{ + match chain { + Some(chain) if chain.as_ref().spent_utxos.contains(&outpoint) => None, + chain => utxo(chain, db, outpoint), + } +} + /// Returns the [`Utxo`] for [`transparent::OutPoint`], if it exists in any chain /// in the `non_finalized_state`, or in the finalized `db`. ///