diff --git a/Cargo.lock b/Cargo.lock index 346da0458..398349c1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2744,7 +2744,7 @@ dependencies = [ [[package]] name = "tower" version = "0.4.0" -source = "git+https://github.com/tower-rs/tower?rev=5e1e07744820028877654c336a3b9fe057bf46f1#5e1e07744820028877654c336a3b9fe057bf46f1" +source = "git+https://github.com/tower-rs/tower?rev=d4d1c67c6a0e4213a52abcc2b9df6cc58276ee39#d4d1c67c6a0e4213a52abcc2b9df6cc58276ee39" dependencies = [ "futures-core", "futures-util", @@ -2789,7 +2789,7 @@ dependencies = [ [[package]] name = "tower-layer" version = "0.3.0" -source = "git+https://github.com/tower-rs/tower?rev=5e1e07744820028877654c336a3b9fe057bf46f1#5e1e07744820028877654c336a3b9fe057bf46f1" +source = "git+https://github.com/tower-rs/tower?rev=d4d1c67c6a0e4213a52abcc2b9df6cc58276ee39#d4d1c67c6a0e4213a52abcc2b9df6cc58276ee39" [[package]] name = "tower-service" diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index bcd3a7d6d..acdd56538 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -8,6 +8,7 @@ //! verification, where it may be accepted or rejected. use std::{ + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -25,6 +26,7 @@ use zebra_chain::{ block::{self, Block}, parameters::Network, parameters::NetworkUpgrade, + transparent, work::equihash, }; use zebra_state as zs; @@ -165,13 +167,16 @@ where let mut async_checks = FuturesUnordered::new(); + let known_utxos = known_utxos(&block); for transaction in &block.transactions { - let req = transaction::Request::Block(transaction.clone()); let rsp = transaction_verifier .ready_and() .await .expect("transaction verifier is always ready") - .call(req); + .call(transaction::Request::Block { + transaction: transaction.clone(), + known_utxos: known_utxos.clone(), + }); async_checks.push(rsp); } tracing::trace!(len = async_checks.len(), "built async tx checks"); @@ -207,3 +212,16 @@ where .boxed() } } + +fn known_utxos(block: &Block) -> Arc> { + let mut map = HashMap::default(); + for transaction in &block.transactions { + let hash = transaction.hash(); + for (index, output) in transaction.outputs().iter().cloned().enumerate() { + let index = index as u32; + map.insert(transparent::OutPoint { hash, index }, output); + } + } + + Arc::new(map) +} diff --git a/zebra-consensus/src/script.rs b/zebra-consensus/src/script.rs index ee0876e70..36b986a16 100644 --- a/zebra-consensus/src/script.rs +++ b/zebra-consensus/src/script.rs @@ -1,4 +1,4 @@ -use std::{future::Future, pin::Pin, sync::Arc}; +use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; use tracing::Instrument; @@ -40,6 +40,7 @@ impl Verifier { pub struct Request { pub transaction: Arc, pub input_index: usize, + pub known_utxos: Arc>, } impl tower::Service for Verifier @@ -62,32 +63,35 @@ where fn call(&mut self, req: Request) -> Self::Future { use futures_util::FutureExt; - let input = &req.transaction.inputs()[req.input_index]; + let Request { + transaction, + input_index, + known_utxos, + } = req; + let input = &transaction.inputs()[input_index]; match input { transparent::Input::PrevOut { outpoint, .. } => { let outpoint = *outpoint; - let transaction = req.transaction; let branch_id = self.branch; - let input_index = req.input_index; let span = tracing::trace_span!("script", ?outpoint); - let output = + let query = span.in_scope(|| self.state.call(zebra_state::Request::AwaitUtxo(outpoint))); async move { tracing::trace!("awaiting outpoint lookup"); - let previous_output = match output.await? { - zebra_state::Response::Utxo(output) => output, - _ => unreachable!("AwaitUtxo always responds with Utxo"), + let output = if let Some(output) = known_utxos.get(&outpoint) { + tracing::trace!("UXTO in known_utxos, discarding query"); + output.clone() + } else if let zebra_state::Response::Utxo(output) = query.await? { + output + } else { + unreachable!("AwaitUtxo always responds with Utxo") }; - tracing::trace!(?previous_output, "got UTXO"); + tracing::trace!(?output, "got UTXO"); - zebra_script::is_valid( - transaction, - branch_id, - (input_index as u32, previous_output), - )?; + zebra_script::is_valid(transaction, branch_id, (input_index as u32, output))?; tracing::trace!("script verification succeeded"); Ok(()) diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 39ca1a665..dd33d327b 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, future::Future, pin::Pin, sync::Arc, @@ -15,6 +16,7 @@ use tracing::Instrument; use zebra_chain::{ parameters::NetworkUpgrade, transaction::{self, HashType, Transaction}, + transparent, }; use zebra_state as zs; @@ -51,9 +53,17 @@ where #[allow(dead_code)] pub enum Request { /// Verify the supplied transaction as part of a block. - Block(Arc), + Block { + transaction: Arc, + /// Additional UTXOs which are known at the time of verification. + known_utxos: Arc>, + }, /// Verify the supplied transaction as part of the mempool. - Mempool(Arc), + Mempool { + transaction: Arc, + /// Additional UTXOs which are known at the time of verification. + known_utxos: Arc>, + }, } impl Service for Verifier @@ -73,17 +83,23 @@ where // TODO: break up each chunk into its own method fn call(&mut self, req: Request) -> Self::Future { let is_mempool = match req { - Request::Block(_) => false, - Request::Mempool(_) => true, + Request::Block { .. } => false, + Request::Mempool { .. } => true, }; if is_mempool { // XXX determine exactly which rules apply to mempool transactions unimplemented!(); } - let tx = match req { - Request::Block(tx) => tx, - Request::Mempool(tx) => tx, + let (tx, known_utxos) = match req { + Request::Block { + transaction, + known_utxos, + } => (transaction, known_utxos), + Request::Mempool { + transaction, + known_utxos, + } => (transaction, known_utxos), }; let mut redjubjub_verifier = crate::primitives::redjubjub::VERIFIER.clone(); @@ -119,6 +135,7 @@ where // feed all of the inputs to the script verifier for input_index in 0..inputs.len() { let rsp = script_verifier.ready_and().await?.call(script::Request { + known_utxos: known_utxos.clone(), transaction: tx.clone(), input_index, });