diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index ff4251cd7..aa46a5acb 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -55,6 +55,8 @@ pub enum VerifyTransactionError { NoTransfer, /// The balance of money moving around doesn't compute. BadBalance, + /// Violation of coinbase rules. + Coinbase, /// Could not verify a transparent script Script(#[from] zebra_script::Error), /// Could not verify a Groth16 proof of a JoinSplit/Spend/Output description @@ -148,6 +150,7 @@ where #[allow(clippy::if_same_then_else)] // delete when filled in if tx.is_coinbase() { // do something special for coinbase transactions + check::coinbase_tx_does_not_spend_shielded(&tx)?; } else { // otherwise, check no coinbase inputs // feed all of the inputs to the script verifier diff --git a/zebra-consensus/src/transaction/check.rs b/zebra-consensus/src/transaction/check.rs index 7f7e1b0a2..0f1289721 100644 --- a/zebra-consensus/src/transaction/check.rs +++ b/zebra-consensus/src/transaction/check.rs @@ -92,3 +92,27 @@ pub fn shielded_balances_match( return Err(VerifyTransactionError::BadBalance); } } + +/// Check that a coinbase tx does not have any JoinSplit or Spend descriptions. +/// +/// https://zips.z.cash/protocol/canopy.pdf#consensusfrombitcoin +pub fn coinbase_tx_does_not_spend_shielded(tx: &Transaction) -> Result<(), VerifyTransactionError> { + match tx { + Transaction::V4 { + joinsplit_data: Some(joinsplit_data), + shielded_data: Some(shielded_data), + .. + } => { + if !tx.is_coinbase() { + return Ok(()); + } else if joinsplit_data.joinsplits().count() == 0 + && shielded_data.spends().count() == 0 + { + return Ok(()); + } else { + return Err(VerifyTransactionError::Coinbase); + } + } + _ => return Err(VerifyTransactionError::WrongVersion), + } +}