consensus: add transaction::check module

This commit is contained in:
Deirdre Connolly 2020-10-16 14:40:00 -07:00 committed by Deirdre Connolly
parent 3748623d92
commit 612148fbdd
2 changed files with 88 additions and 5 deletions

View File

@ -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 {

View File

@ -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),
}
}