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) ); }