2021-04-27 17:43:00 -07:00
|
|
|
use zebra_chain::{
|
2021-06-06 19:02:18 -07:00
|
|
|
orchard,
|
2021-05-09 18:31:45 -07:00
|
|
|
parameters::Network,
|
2021-06-06 19:02:18 -07:00
|
|
|
transaction::{
|
|
|
|
arbitrary::{fake_v5_transactions_for_network, insert_fake_orchard_shielded_data},
|
|
|
|
Transaction,
|
|
|
|
},
|
2021-04-27 17:43:00 -07:00
|
|
|
};
|
|
|
|
|
2021-06-01 18:32:52 -07:00
|
|
|
use super::check;
|
|
|
|
|
|
|
|
use crate::error::TransactionError;
|
2021-04-27 17:43:00 -07:00
|
|
|
use color_eyre::eyre::Report;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn v5_fake_transactions() -> Result<(), Report> {
|
|
|
|
zebra_test::init();
|
|
|
|
|
2021-06-01 18:32:52 -07:00
|
|
|
let networks = vec![
|
|
|
|
(Network::Mainnet, zebra_test::vectors::MAINNET_BLOCKS.iter()),
|
|
|
|
(Network::Testnet, zebra_test::vectors::TESTNET_BLOCKS.iter()),
|
|
|
|
];
|
2021-04-27 17:43:00 -07:00
|
|
|
|
2021-06-01 18:32:52 -07:00
|
|
|
for (network, blocks) in networks {
|
|
|
|
for transaction in fake_v5_transactions_for_network(network, blocks) {
|
|
|
|
match check::has_inputs_and_outputs(&transaction) {
|
2021-04-27 17:43:00 -07:00
|
|
|
Ok(()) => (),
|
2021-06-01 18:32:52 -07:00
|
|
|
Err(TransactionError::NoInputs) | Err(TransactionError::NoOutputs) => (),
|
|
|
|
Err(_) => panic!("error must be NoInputs or NoOutputs"),
|
2021-04-27 17:43:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// make sure there are no joinsplits nor spends in coinbase
|
2021-06-01 18:32:52 -07:00
|
|
|
check::coinbase_tx_no_prevout_joinsplit_spend(&transaction)?;
|
2021-04-27 17:43:00 -07:00
|
|
|
|
|
|
|
// 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() {
|
2021-06-01 18:32:52 -07:00
|
|
|
check::spend_cv_rk_not_small_order(&spend)?
|
2021-04-27 17:43:00 -07:00
|
|
|
}
|
|
|
|
for output in s.outputs() {
|
2021-06-01 18:32:52 -07:00
|
|
|
check::output_cv_epk_not_small_order(&output)?;
|
2021-04-27 17:43:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("we should have no tx other than 5"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-01 18:32:52 -07:00
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-06-01 18:32:52 -07:00
|
|
|
|
2021-06-06 19:02:18 -07:00
|
|
|
#[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());
|
|
|
|
}
|
|
|
|
|
2021-06-01 18:32:52 -07:00
|
|
|
#[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)
|
|
|
|
);
|
|
|
|
}
|
2021-06-06 19:02:18 -07:00
|
|
|
|
|
|
|
#[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)
|
|
|
|
);
|
|
|
|
}
|