diff --git a/zebra-consensus/src/block.rs b/zebra-consensus/src/block.rs index c86c3983de3..f1623b7897e 100644 --- a/zebra-consensus/src/block.rs +++ b/zebra-consensus/src/block.rs @@ -25,7 +25,6 @@ use tracing::Instrument; use zebra_chain::{ block::{self, Block}, parameters::Network, - parameters::NetworkUpgrade, transparent, work::equihash, }; @@ -83,9 +82,8 @@ where S::Future: Send + 'static, { pub fn new(network: Network, state_service: S) -> Self { - let branch = NetworkUpgrade::Sapling.branch_id().unwrap(); - let script_verifier = script::Verifier::new(state_service.clone(), branch); - let transaction_verifier = transaction::Verifier::new(script_verifier); + let transaction_verifier = + transaction::Verifier::new(network, script::Verifier::new(state_service.clone())); Self { network, @@ -176,6 +174,7 @@ where .call(transaction::Request::Block { transaction: transaction.clone(), known_utxos: known_utxos.clone(), + height, }); async_checks.push(rsp); } diff --git a/zebra-consensus/src/script.rs b/zebra-consensus/src/script.rs index cad3c6b093e..a4bc2e5c592 100644 --- a/zebra-consensus/src/script.rs +++ b/zebra-consensus/src/script.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; use tracing::Instrument; -use zebra_chain::{parameters::ConsensusBranchId, transaction::Transaction, transparent}; +use zebra_chain::{parameters::NetworkUpgrade, transaction::Transaction, transparent}; use zebra_state::Utxo; use crate::BoxError; @@ -21,27 +21,34 @@ use crate::BoxError; #[derive(Debug, Clone)] pub struct Verifier { state: ZS, - branch: ConsensusBranchId, } impl Verifier { - pub fn new(state: ZS, branch: ConsensusBranchId) -> Self { - Self { state, branch } + pub fn new(state: ZS) -> Self { + Self { state } } } /// A script verification request. -/// -/// Ideally, this would supply only an `Outpoint` and the unlock script, -/// rather than the entire `Transaction`, but we call a C++ -/// implementation, and its FFI requires the entire transaction. -/// At some future point, we could investigate reducing the size of the -/// request. #[derive(Debug)] pub struct Request { + /// Ideally, this would supply only an `Outpoint` and the unlock script, + /// rather than the entire `Transaction`, but we call a C++ + /// implementation, and its FFI requires the entire transaction. + /// + /// This causes quadratic script verification behavior, so + /// at some future point, we need to reform this data. pub transaction: Arc, pub input_index: usize, + /// A set of additional UTXOs known in the context of this verification request. + /// + /// This allows specifying additional UTXOs that are not already known to the chain state. pub known_utxos: Arc>, + /// The network upgrade active in the context of this verification request. + /// + /// Because the consensus branch ID changes with each network upgrade, + /// it has to be specified on a per-request basis. + pub upgrade: NetworkUpgrade, } impl tower::Service for Verifier @@ -68,13 +75,16 @@ where transaction, input_index, known_utxos, + upgrade, } = req; let input = &transaction.inputs()[input_index]; + let branch_id = upgrade + .branch_id() + .expect("post-Sapling NUs have a consensus branch ID"); match input { transparent::Input::PrevOut { outpoint, .. } => { let outpoint = *outpoint; - let branch_id = self.branch; let span = tracing::trace_span!("script", ?outpoint); let query = diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 894e6450548..27c6417a52c 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -14,7 +14,8 @@ use tower::{Service, ServiceExt}; use tracing::Instrument; use zebra_chain::{ - parameters::NetworkUpgrade, + block, + parameters::{Network, NetworkUpgrade}, transaction::{self, HashType, Transaction}, transparent, }; @@ -28,6 +29,7 @@ mod check; /// Asynchronous transaction verification. #[derive(Debug, Clone)] pub struct Verifier { + network: Network, script_verifier: script::Verifier, // spend_verifier: groth16::Verifier, // output_verifier: groth16::Verifier, @@ -40,8 +42,11 @@ where ZS::Future: Send + 'static, { // XXX: how should this struct be constructed? - pub fn new(script_verifier: script::Verifier) -> Self { - Self { script_verifier } + pub fn new(network: Network, script_verifier: script::Verifier) -> Self { + Self { + network, + script_verifier, + } } } @@ -54,15 +59,22 @@ where pub enum Request { /// Verify the supplied transaction as part of a block. Block { + /// The transaction itself. transaction: Arc, /// Additional UTXOs which are known at the time of verification. known_utxos: Arc>, + /// The height of the block containing this transaction, used to + /// determine the applicable network upgrade. + height: block::Height, }, /// Verify the supplied transaction as part of the mempool. Mempool { + /// The transaction itself. transaction: Arc, /// Additional UTXOs which are known at the time of verification. known_utxos: Arc>, + /// The active NU in the context of this verification. + upgrade: NetworkUpgrade, }, } @@ -91,15 +103,20 @@ where unimplemented!(); } - let (tx, known_utxos) = match req { + let (tx, known_utxos, upgrade) = match req { Request::Block { transaction, known_utxos, - } => (transaction, known_utxos), + height, + } => { + let upgrade = NetworkUpgrade::current(self.network, height); + (transaction, known_utxos, upgrade) + } Request::Mempool { transaction, known_utxos, - } => (transaction, known_utxos), + upgrade, + } => (transaction, known_utxos, upgrade), }; let mut redjubjub_verifier = crate::primitives::redjubjub::VERIFIER.clone(); @@ -135,6 +152,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 { + upgrade, known_utxos: known_utxos.clone(), transaction: tx.clone(), input_index, @@ -146,10 +164,11 @@ where check::has_inputs_and_outputs(&tx)?; + // TODO: rework this code let sighash = tx.sighash( - NetworkUpgrade::Sapling, // TODO: pass this in - HashType::ALL, // TODO: check these - None, // TODO: check these + upgrade, + HashType::ALL, // TODO: check these + None, // TODO: check these ); if let Some(joinsplit_data) = joinsplit_data {