consensus: add transaction::check module
This commit is contained in:
parent
3748623d92
commit
612148fbdd
|
|
@ -17,12 +17,15 @@ use zebra_chain::{
|
||||||
parameters::NetworkUpgrade,
|
parameters::NetworkUpgrade,
|
||||||
primitives::{ed25519, redjubjub},
|
primitives::{ed25519, redjubjub},
|
||||||
transaction::{self, HashType, Transaction},
|
transaction::{self, HashType, Transaction},
|
||||||
|
transparent::{self, Script},
|
||||||
};
|
};
|
||||||
|
|
||||||
use zebra_state as zs;
|
use zebra_state as zs;
|
||||||
|
|
||||||
use crate::{script, BoxError};
|
use crate::{script, BoxError};
|
||||||
|
|
||||||
|
mod check;
|
||||||
|
|
||||||
/// Asynchronous transaction verification.
|
/// Asynchronous transaction verification.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Verifier<ZS> {
|
pub struct Verifier<ZS> {
|
||||||
|
|
@ -48,6 +51,8 @@ where
|
||||||
pub enum VerifyTransactionError {
|
pub enum VerifyTransactionError {
|
||||||
/// Only V4 and later transactions can be verified.
|
/// Only V4 and later transactions can be verified.
|
||||||
WrongVersion,
|
WrongVersion,
|
||||||
|
/// A transaction MUST move money around.
|
||||||
|
NoTransfer,
|
||||||
/// Could not verify a transparent script
|
/// Could not verify a transparent script
|
||||||
Script(#[from] zebra_script::Error),
|
Script(#[from] zebra_script::Error),
|
||||||
/// Could not verify a Groth16 proof of a JoinSplit/Spend/Output description
|
/// 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(
|
let sighash = tx.sighash(
|
||||||
NetworkUpgrade::Sapling, // TODO: pass this in
|
NetworkUpgrade::Sapling, // TODO: pass this in
|
||||||
HashType::ALL, // TODO: check these
|
HashType::ALL, // TODO: check these
|
||||||
None, // 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
|
// XXX create a method on JoinSplitData
|
||||||
// that prepares groth16::Items with the correct proofs
|
// that prepares groth16::Items with the correct proofs
|
||||||
// and proof inputs, handling interstitial treestates
|
// and proof inputs, handling interstitial treestates
|
||||||
|
|
@ -168,10 +176,7 @@ where
|
||||||
|
|
||||||
// Then, pass those items to self.joinsplit to verify them.
|
// Then, pass those items to self.joinsplit to verify them.
|
||||||
|
|
||||||
// XXX refactor this into a nicely named check function
|
check::validate_joinsplit_sig(joinsplit_data, sighash.as_bytes())?;
|
||||||
// ed25519::VerificationKey::try_from(joinsplit_data.pub_key)
|
|
||||||
// .and_then(|vk| vk.verify(&joinsplit_data.sig, sighash))
|
|
||||||
// .map_err(VerifyTransactionError::Ed25519)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(shielded_data) = shielded_data {
|
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