Skip to content

Commit

Permalink
add precomputation
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg committed May 29, 2024
1 parent b8bee9b commit a89a5a0
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 96 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5798,6 +5798,7 @@ dependencies = [
"ff",
"fpe",
"group",
"hdwallet",
"hex",
"incrementalmerkletree",
"jubjub",
Expand All @@ -5807,7 +5808,9 @@ dependencies = [
"rand 0.8.5",
"rand_core 0.6.4",
"redjubjub",
"ripemd",
"sapling-crypto",
"secp256k1",
"sha2",
"subtle",
"tracing",
Expand Down Expand Up @@ -5893,6 +5896,7 @@ dependencies = [
[[package]]
name = "zcash_script"
version = "0.1.16"
source = "git+https://github.com/ZcashFoundation/zcash_script.git?rev=73f28bed340e698ea0eaad608548508ae4dcbc59#73f28bed340e698ea0eaad608548508ae4dcbc59"
dependencies = [
"bindgen",
"cc",
Expand Down
66 changes: 46 additions & 20 deletions zebra-chain/src/primitives/zcash_primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use std::{io, ops::Deref};

use zcash_primitives::transaction as zp_tx;
use zcash_primitives::transaction::{self as zp_tx, TxDigests};

use crate::{
amount::{Amount, NonNegative},
Expand Down Expand Up @@ -231,6 +231,41 @@ impl From<Script> for zcash_primitives::legacy::Script {
}
}

/// Precomputed data used for sighash or txid computation.
#[derive(Debug)]
pub(crate) struct PrecomputedTxData<'a> {
tx_data: zp_tx::TransactionData<PrecomputedAuth<'a>>,
txid_parts: TxDigests<blake2b_simd::Hash>,
all_previous_outputs: &'a [transparent::Output],
}

impl<'a> PrecomputedTxData<'a> {
pub(crate) fn new(
tx: &'a Transaction,
branch_id: ConsensusBranchId,
all_previous_outputs: &'a [transparent::Output],
) -> PrecomputedTxData<'a> {
let alt_tx = convert_tx_to_librustzcash(tx, branch_id)
.expect("zcash_primitives and Zebra transaction formats must be compatible");
let txid_parts = alt_tx.deref().digest(zp_tx::txid::TxIdDigester);

let f_transparent = MapTransparent {
auth: TransparentAuth {
all_prev_outputs: all_previous_outputs,
},
};
let tx_data: zp_tx::TransactionData<PrecomputedAuth> = alt_tx
.into_data()
.map_authorization(f_transparent, IdentityMap, IdentityMap);

PrecomputedTxData {
tx_data,
txid_parts,
all_previous_outputs,
}
}
}

/// Compute a signature hash using librustzcash.
///
/// # Inputs
Expand All @@ -243,21 +278,16 @@ impl From<Script> for zcash_primitives::legacy::Script {
/// previous transaction, the input itself, and the index of the input in
/// the transaction.
pub(crate) fn sighash(
trans: &Transaction,
precomputed_tx_data: &PrecomputedTxData,
hash_type: HashType,
branch_id: ConsensusBranchId,
all_previous_outputs: &[transparent::Output],
input_index: Option<usize>,
script_code: Option<Vec<u8>>,
) -> SigHash {
let alt_tx = convert_tx_to_librustzcash(trans, branch_id)
.expect("zcash_primitives and Zebra transaction formats must be compatible");

let lock_script: zcash_primitives::legacy::Script;
let unlock_script: zcash_primitives::legacy::Script;
let signable_input = match input_index {
Some(input_index) => {
let output = &all_previous_outputs[input_index];
let output = &precomputed_tx_data.all_previous_outputs[input_index];
lock_script = output.lock_script.clone().into();
unlock_script = zcash_primitives::legacy::Script(script_code.unwrap());
zp_tx::sighash::SignableInput::Transparent {
Expand All @@ -274,18 +304,14 @@ pub(crate) fn sighash(
None => zp_tx::sighash::SignableInput::Shielded,
};

let txid_parts = alt_tx.deref().digest(zp_tx::txid::TxIdDigester);
let f_transparent = MapTransparent {
auth: TransparentAuth {
all_prev_outputs: all_previous_outputs,
},
};
let txdata: zp_tx::TransactionData<PrecomputedAuth> =
alt_tx
.into_data()
.map_authorization(f_transparent, IdentityMap, IdentityMap);

SigHash(*zp_tx::sighash::signature_hash(&txdata, &signable_input, &txid_parts).as_ref())
SigHash(
*zp_tx::sighash::signature_hash(
&precomputed_tx_data.tx_data,
&signable_input,
&precomputed_tx_data.txid_parts,
)
.as_ref(),
)
}

/// Compute the authorizing data commitment of this transaction as specified
Expand Down
32 changes: 22 additions & 10 deletions zebra-chain/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub use serialize::{
SerializedTransaction, MIN_TRANSPARENT_TX_SIZE, MIN_TRANSPARENT_TX_V4_SIZE,
MIN_TRANSPARENT_TX_V5_SIZE,
};
pub use sighash::{HashType, SigHash};
pub use sighash::{HashType, SigHash, SigHasher};
pub use unmined::{
zip317, UnminedTx, UnminedTxId, VerifiedUnminedTx, MEMPOOL_TRANSACTION_COST_THRESHOLD,
};
Expand Down Expand Up @@ -194,14 +194,21 @@ impl Transaction {
UnminedTxId::from(self)
}

/// Calculate the sighash for the current transaction
/// Calculate the sighash for the current transaction.
///
/// If you need to compute multiple sighashes for the same transactions,
/// it's more efficient to use [`Transaction::sighasher()`].
///
/// # Details
///
/// The `input` argument indicates the transparent Input for which we are
/// producing a sighash. It is comprised of the index identifying the
/// transparent::Input within the transaction and the transparent::Output
/// representing the UTXO being spent by that input.
/// producing a sighash, or None if it's a shielded input. It is comprised
/// of the index identifying the transparent::Input within the transaction
/// and the transparent::Output representing the UTXO being spent by that
/// input.
///
/// The `script_code` argument indicates the script code being validated
/// for transparent inputs, or None if it's a shielded input.
///
/// # Panics
///
Expand All @@ -217,15 +224,20 @@ impl Transaction {
input: Option<usize>,
script_code: Option<Vec<u8>>,
) -> SigHash {
sighash::SigHasher::new(
self,
sighash::SigHasher::new(self, branch_id, all_previous_outputs).sighash(
hash_type,
branch_id,
all_previous_outputs,
input,
script_code,
)
.sighash()
}

/// Return a [`SigHasher`] for this transaction.
pub fn sighasher<'a>(
&'a self,
branch_id: ConsensusBranchId,
all_previous_outputs: &'a [transparent::Output],
) -> sighash::SigHasher {
sighash::SigHasher::new(self, branch_id, all_previous_outputs)
}

/// Compute the authorizing data commitment of this transaction as specified
Expand Down
66 changes: 37 additions & 29 deletions zebra-chain/src/transaction/sighash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::Transaction;
use crate::parameters::ConsensusBranchId;
use crate::transparent;

use crate::primitives::zcash_primitives::sighash;
use crate::primitives::zcash_primitives::{sighash, PrecomputedTxData};

bitflags::bitflags! {
/// The different SigHash types, as defined in <https://zips.z.cash/zip-0143>
Expand Down Expand Up @@ -39,47 +39,55 @@ impl AsRef<[u8]> for SigHash {
}
}

pub(super) struct SigHasher<'a> {
trans: &'a Transaction,
hash_type: HashType,
branch_id: ConsensusBranchId,
all_previous_outputs: &'a [transparent::Output],
input_index: Option<usize>,
script_code: Option<Vec<u8>>,
/// A SigHasher context which stores precomputed data that is reused
/// between sighash computations for the same transaction.
pub struct SigHasher<'a> {
precomputed_tx_data: PrecomputedTxData<'a>,
}

impl<'a> SigHasher<'a> {
/// Create a new SigHasher for the given transaction.
pub fn new(
trans: &'a Transaction,
hash_type: HashType,
branch_id: ConsensusBranchId,
all_previous_outputs: &'a [transparent::Output],
input_index: Option<usize>,
script_code: Option<Vec<u8>>,
) -> Self {
let precomputed_tx_data = PrecomputedTxData::new(trans, branch_id, all_previous_outputs);
SigHasher {
trans,
hash_type,
branch_id,
all_previous_outputs,
input_index,
script_code,
precomputed_tx_data,
}
}

pub(super) fn sighash(self) -> SigHash {
self.hash_sighash_librustzcash()
}

/// Compute a signature hash using librustzcash.
fn hash_sighash_librustzcash(&self) -> SigHash {
/// Calculate the sighash for the current transaction.
///
/// # Details
///
/// The `input` argument indicates the transparent Input for which we are
/// producing a sighash, or None if it's a shielded input. It is comprised
/// of the index identifying the transparent::Input within the transaction
/// and the transparent::Output representing the UTXO being spent by that
/// input.
///
/// The `script_code` argument indicates the script code being validated
/// for transparent inputs, or None if it's a shielded input.
///
/// # Panics
///
/// - if passed in any NetworkUpgrade from before NetworkUpgrade::Overwinter
/// - if called on a v1 or v2 transaction
/// - if the input index points to a transparent::Input::CoinBase
/// - if the input index is out of bounds for self.inputs()
pub fn sighash(
&self,
hash_type: HashType,
input_index: Option<usize>,
script_code: Option<Vec<u8>>,
) -> SigHash {
sighash(
self.trans,
self.hash_type,
self.branch_id,
self.all_previous_outputs,
self.input_index,
self.script_code.clone(),
&self.precomputed_tx_data,
hash_type,
input_index,
script_code,
)
}
}
Loading

0 comments on commit a89a5a0

Please sign in to comment.