validate orchard flags in v5 (#3035)
This commit is contained in:
parent
c393b9a488
commit
f7c1907fb6
|
@ -266,6 +266,21 @@ impl Transaction {
|
|||
.contains(orchard::Flags::ENABLE_OUTPUTS))
|
||||
}
|
||||
|
||||
/// Does this transaction has at least one flag when we have at least one orchard action?
|
||||
///
|
||||
/// [NU5 onward] If effectiveVersion >= 5 and nActionsOrchard > 0, then at least one
|
||||
/// of enableSpendsOrchard and enableOutputsOrchard MUST be 1.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#txnconsensus
|
||||
pub fn has_enough_orchard_flags(&self) -> bool {
|
||||
if self.version() < 5 || self.orchard_actions().count() == 0 {
|
||||
return true;
|
||||
}
|
||||
self.orchard_flags()
|
||||
.unwrap_or_else(orchard::Flags::empty)
|
||||
.intersects(orchard::Flags::ENABLE_SPENDS | orchard::Flags::ENABLE_OUTPUTS)
|
||||
}
|
||||
|
||||
/// Returns the [`CoinbaseSpendRestriction`] for this transaction,
|
||||
/// assuming it is mined at `spend_height`.
|
||||
pub fn coinbase_spend_restriction(
|
||||
|
|
|
@ -116,6 +116,9 @@ pub enum TransactionError {
|
|||
|
||||
#[error("orchard double-spend: duplicate nullifier: {_0:?}")]
|
||||
DuplicateOrchardNullifier(orchard::Nullifier),
|
||||
|
||||
#[error("must have at least one active orchard flag")]
|
||||
NotEnoughFlags,
|
||||
}
|
||||
|
||||
impl From<BoxError> for TransactionError {
|
||||
|
|
|
@ -265,6 +265,7 @@ where
|
|||
|
||||
// Do basic checks first
|
||||
check::has_inputs_and_outputs(&tx)?;
|
||||
check::has_enough_orchard_flags(&tx)?;
|
||||
|
||||
if req.is_mempool() && tx.has_any_coinbase_inputs() {
|
||||
return Err(TransactionError::CoinbaseInMempool);
|
||||
|
|
|
@ -40,6 +40,20 @@ pub fn has_inputs_and_outputs(tx: &Transaction) -> Result<(), TransactionError>
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks that the transaction has enough orchard flags.
|
||||
///
|
||||
/// For `Transaction::V5` only:
|
||||
/// * If `orchard_actions_count` > 0 then at least one of
|
||||
/// `ENABLE_SPENDS|ENABLE_OUTPUTS` must be active.
|
||||
///
|
||||
/// https://zips.z.cash/protocol/protocol.pdf#txnencodingandconsensus
|
||||
pub fn has_enough_orchard_flags(tx: &Transaction) -> Result<(), TransactionError> {
|
||||
if !tx.has_enough_orchard_flags() {
|
||||
return Err(TransactionError::NotEnoughFlags);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that a coinbase transaction has no PrevOut inputs, JoinSplits, or spends.
|
||||
///
|
||||
/// A coinbase transaction MUST NOT have any transparent inputs, JoinSplit descriptions,
|
||||
|
|
|
@ -114,6 +114,49 @@ fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() {
|
|||
assert!(check::has_inputs_and_outputs(&transaction).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fake_v5_transaction_with_orchard_actions_has_flags() {
|
||||
// Find a transaction with no inputs or outputs to use as base
|
||||
let mut transaction = fake_v5_transactions_for_network(
|
||||
Network::Mainnet,
|
||||
zebra_test::vectors::MAINNET_BLOCKS.iter(),
|
||||
)
|
||||
.rev()
|
||||
.find(|transaction| {
|
||||
transaction.inputs().is_empty()
|
||||
&& transaction.outputs().is_empty()
|
||||
&& transaction.sapling_spends_per_anchor().next().is_none()
|
||||
&& transaction.sapling_outputs().next().is_none()
|
||||
&& transaction.joinsplit_count() == 0
|
||||
})
|
||||
.expect("At least one fake V5 transaction with no inputs and no outputs");
|
||||
|
||||
// Insert fake Orchard shielded data to the transaction, which has at least one action (this is
|
||||
// guaranteed structurally by `orchard::ShieldedData`)
|
||||
insert_fake_orchard_shielded_data(&mut transaction);
|
||||
|
||||
// The check will fail if the transaction has no flags
|
||||
assert_eq!(
|
||||
check::has_enough_orchard_flags(&transaction),
|
||||
Err(TransactionError::NotEnoughFlags)
|
||||
);
|
||||
|
||||
// If we add ENABLE_SPENDS flag it will pass.
|
||||
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
|
||||
shielded_data.flags = orchard::Flags::ENABLE_SPENDS;
|
||||
assert!(check::has_enough_orchard_flags(&transaction).is_ok());
|
||||
|
||||
// If we add ENABLE_OUTPUTS flag instead, it will pass.
|
||||
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
|
||||
shielded_data.flags = orchard::Flags::ENABLE_OUTPUTS;
|
||||
assert!(check::has_enough_orchard_flags(&transaction).is_ok());
|
||||
|
||||
// If we add BOTH ENABLE_SPENDS and ENABLE_OUTPUTS flags it will pass.
|
||||
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
|
||||
shielded_data.flags = orchard::Flags::ENABLE_SPENDS | orchard::Flags::ENABLE_OUTPUTS;
|
||||
assert!(check::has_enough_orchard_flags(&transaction).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v5_transaction_with_no_inputs_fails_validation() {
|
||||
let transaction = fake_v5_transactions_for_network(
|
||||
|
|
Loading…
Reference in New Issue