zebra/zebra-consensus/src/transaction/tests.rs

163 lines
5.5 KiB
Rust
Raw Normal View History

use zebra_chain::{
orchard,
parameters::Network,
transaction::{
arbitrary::{fake_v5_transactions_for_network, insert_fake_orchard_shielded_data},
Transaction,
},
};
use super::check;
use crate::error::TransactionError;
use color_eyre::eyre::Report;
#[test]
fn v5_fake_transactions() -> Result<(), Report> {
zebra_test::init();
let networks = vec![
(Network::Mainnet, zebra_test::vectors::MAINNET_BLOCKS.iter()),
(Network::Testnet, zebra_test::vectors::TESTNET_BLOCKS.iter()),
];
for (network, blocks) in networks {
for transaction in fake_v5_transactions_for_network(network, blocks) {
match check::has_inputs_and_outputs(&transaction) {
Ok(()) => (),
Err(TransactionError::NoInputs) | Err(TransactionError::NoOutputs) => (),
Err(_) => panic!("error must be NoInputs or NoOutputs"),
};
// make sure there are no joinsplits nor spends in coinbase
check::coinbase_tx_no_prevout_joinsplit_spend(&transaction)?;
// validate the sapling shielded data
match transaction {
Transaction::V5 {
sapling_shielded_data,
..
} => {
if let Some(s) = sapling_shielded_data {
for spend in s.spends_per_anchor() {
check::spend_cv_rk_not_small_order(&spend)?
}
for output in s.outputs() {
check::output_cv_epk_not_small_order(&output)?;
}
}
}
_ => panic!("we should have no tx other than 5"),
}
}
}
Ok(())
}
#[test]
fn fake_v5_transaction_with_orchard_actions_has_inputs_and_outputs() {
// 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);
// If a transaction has at least one Orchard shielded action, it should be considered to have
// inputs and/or outputs
assert!(check::has_inputs_and_outputs(&transaction).is_ok());
}
#[test]
fn v5_transaction_with_no_inputs_fails_validation() {
let transaction = fake_v5_transactions_for_network(
Network::Mainnet,
zebra_test::vectors::MAINNET_BLOCKS.iter(),
)
.rev()
.find(|transaction| {
transaction.inputs().is_empty()
&& transaction.sapling_spends_per_anchor().next().is_none()
&& transaction.orchard_actions().next().is_none()
&& transaction.joinsplit_count() == 0
&& (!transaction.outputs().is_empty() || transaction.sapling_outputs().next().is_some())
})
.expect("At least one fake v5 transaction with no inputs in the test vectors");
assert_eq!(
check::has_inputs_and_outputs(&transaction),
Err(TransactionError::NoInputs)
);
}
#[test]
fn v5_transaction_with_no_outputs_fails_validation() {
let transaction = fake_v5_transactions_for_network(
Network::Mainnet,
zebra_test::vectors::MAINNET_BLOCKS.iter(),
)
.rev()
.find(|transaction| {
transaction.outputs().is_empty()
&& transaction.sapling_outputs().next().is_none()
&& transaction.orchard_actions().next().is_none()
&& transaction.joinsplit_count() == 0
&& (!transaction.inputs().is_empty()
|| transaction.sapling_spends_per_anchor().next().is_some())
})
.expect("At least one fake v5 transaction with no outputs in the test vectors");
assert_eq!(
check::has_inputs_and_outputs(&transaction),
Err(TransactionError::NoOutputs)
);
}
#[test]
fn v5_coinbase_transaction_without_enable_spends_flag_passes_validation() {
let mut transaction = fake_v5_transactions_for_network(
Network::Mainnet,
zebra_test::vectors::MAINNET_BLOCKS.iter(),
)
.rev()
.find(|transaction| transaction.is_coinbase())
.expect("At least one fake V5 coinbase transaction in the test vectors");
insert_fake_orchard_shielded_data(&mut transaction);
assert!(check::coinbase_tx_no_prevout_joinsplit_spend(&transaction).is_ok(),);
}
#[test]
fn v5_coinbase_transaction_with_enable_spends_flag_fails_validation() {
let mut transaction = fake_v5_transactions_for_network(
Network::Mainnet,
zebra_test::vectors::MAINNET_BLOCKS.iter(),
)
.rev()
.find(|transaction| transaction.is_coinbase())
.expect("At least one fake V5 coinbase transaction in the test vectors");
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
shielded_data.flags = orchard::Flags::ENABLE_SPENDS;
assert_eq!(
check::coinbase_tx_no_prevout_joinsplit_spend(&transaction),
Err(TransactionError::CoinbaseHasEnableSpendsOrchard)
);
}