consensus: add transaction::check module
This commit is contained in:
parent
3748623d92
commit
612148fbdd
|
@ -17,12 +17,15 @@ use zebra_chain::{
|
|||
parameters::NetworkUpgrade,
|
||||
primitives::{ed25519, redjubjub},
|
||||
transaction::{self, HashType, Transaction},
|
||||
transparent::{self, Script},
|
||||
};
|
||||
|
||||
use zebra_state as zs;
|
||||
|
||||
use crate::{script, BoxError};
|
||||
|
||||
mod check;
|
||||
|
||||
/// Asynchronous transaction verification.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Verifier<ZS> {
|
||||
|
@ -48,6 +51,8 @@ where
|
|||
pub enum VerifyTransactionError {
|
||||
/// Only V4 and later transactions can be verified.
|
||||
WrongVersion,
|
||||
/// A transaction MUST move money around.
|
||||
NoTransfer,
|
||||
/// Could not verify a transparent script
|
||||
Script(#[from] zebra_script::Error),
|
||||
/// Could not verify a Groth16 proof of a JoinSplit/Spend/Output description
|
||||
|
@ -154,13 +159,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
check::some_money_is_spent(&tx)?;
|
||||
check::any_coinbase_inputs_no_transparent_outputs(&tx)?;
|
||||
|
||||
let sighash = tx.sighash(
|
||||
NetworkUpgrade::Sapling, // TODO: pass this in
|
||||
HashType::ALL, // TODO: check these
|
||||
None, // TODO: check these
|
||||
);
|
||||
|
||||
if let Some(_joinsplit_data) = joinsplit_data {
|
||||
if let Some(joinsplit_data) = joinsplit_data {
|
||||
// XXX create a method on JoinSplitData
|
||||
// that prepares groth16::Items with the correct proofs
|
||||
// and proof inputs, handling interstitial treestates
|
||||
|
@ -168,10 +176,7 @@ where
|
|||
|
||||
// Then, pass those items to self.joinsplit to verify them.
|
||||
|
||||
// XXX refactor this into a nicely named check function
|
||||
// ed25519::VerificationKey::try_from(joinsplit_data.pub_key)
|
||||
// .and_then(|vk| vk.verify(&joinsplit_data.sig, sighash))
|
||||
// .map_err(VerifyTransactionError::Ed25519)
|
||||
check::validate_joinsplit_sig(joinsplit_data, sighash.as_bytes())?;
|
||||
}
|
||||
|
||||
if let Some(shielded_data) = shielded_data {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use zebra_chain::{
|
||||
amount::Amount,
|
||||
primitives::{
|
||||
ed25519,
|
||||
redjubjub::{self, Binding},
|
||||
Groth16Proof,
|
||||
},
|
||||
sapling::ValueCommitment,
|
||||
transaction::{JoinSplitData, ShieldedData, Transaction},
|
||||
};
|
||||
|
||||
use crate::transaction::VerifyTransactionError;
|
||||
|
||||
/// Validate the JoinSplit binding signature.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/canopy.pdf#sproutnonmalleability
|
||||
/// https://zips.z.cash/protocol/canopy.pdf#txnencodingandconsensus
|
||||
pub fn validate_joinsplit_sig(
|
||||
joinsplit_data: &JoinSplitData<Groth16Proof>,
|
||||
sighash: &[u8],
|
||||
) -> Result<(), VerifyTransactionError> {
|
||||
ed25519::VerificationKey::try_from(joinsplit_data.pub_key)
|
||||
.and_then(|vk| vk.verify(&joinsplit_data.sig, sighash))
|
||||
.map_err(VerifyTransactionError::Ed25519)
|
||||
}
|
||||
|
||||
/// Check that at least one of tx_in_count, nShieldedSpend, and nJoinSplit MUST
|
||||
/// be non-zero.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/canopy.pdf#txnencodingandconsensus
|
||||
pub fn some_money_is_spent(tx: &Transaction) -> Result<(), VerifyTransactionError> {
|
||||
match tx {
|
||||
Transaction::V4 {
|
||||
inputs,
|
||||
joinsplit_data: Some(joinsplit_data),
|
||||
shielded_data: Some(shielded_data),
|
||||
..
|
||||
} => {
|
||||
if inputs.len() > 0
|
||||
|| joinsplit_data.joinsplits().count() > 0
|
||||
|| shielded_data.spends().count() > 0
|
||||
{
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(VerifyTransactionError::NoTransfer);
|
||||
}
|
||||
}
|
||||
_ => return Err(VerifyTransactionError::WrongVersion),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that a transaction with one or more transparent inputs from coinbase
|
||||
/// transactions has no transparent outputs.
|
||||
///
|
||||
/// Note that inputs from coinbase transactions include Founders’ Reward
|
||||
/// outputs.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/canopy.pdf#consensusfrombitcoin
|
||||
pub fn any_coinbase_inputs_no_transparent_outputs(
|
||||
tx: &Transaction,
|
||||
) -> Result<(), VerifyTransactionError> {
|
||||
match tx {
|
||||
Transaction::V4 {
|
||||
inputs, outputs, ..
|
||||
} => {
|
||||
if !tx.contains_coinbase_input() {
|
||||
return Ok(());
|
||||
} else if outputs.len() == 0 {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(VerifyTransactionError::NoTransfer);
|
||||
}
|
||||
}
|
||||
_ => return Err(VerifyTransactionError::WrongVersion),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue