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

3073 lines
107 KiB
Rust
Raw Normal View History

//! Tests for Zcash transaction consensus checks.
//
// TODO: split fixed test vectors into a `vectors` module?
use std::{collections::HashMap, sync::Arc};
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
use chrono::{DateTime, TimeZone, Utc};
use color_eyre::eyre::Report;
change(nu5): use new V5 transaction script verification API (#3799) * update librustzcash; adapt to new API * add ticket reference for removing zcash_proofs duplicated dependencies * update to new zcash_script V5 API * use zp_tx shorthand * update to Zcash 4.7.0 dependencies * update protocol versions * feat(rpc): Implement `getblockchaininfo` RPC method (#3891) * Implement `getblockchaininfo` RPC method * add a test for `get_blockchain_info` * fix tohex/fromhex * move comment * Update lightwalletd acceptance test for getblockchaininfo RPC (#3914) * change(rpc): Return getblockchaininfo network upgrades in height order (#3915) * Update lightwalletd acceptance test for getblockchaininfo RPC * Update some doc comments for network upgrades * List network upgrades in order in the getblockchaininfo RPC Also: - Use a constant for the "missing consensus branch ID" RPC value - Simplify fetching consensus branch IDs - Make RPC type derives consistent - Update RPC type documentation * Make RPC type derives consistent * Fix a confusing test comment * get hashand height at the same time * fix estimated_height * fix lint * add extra check Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * fix typo Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * split test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * fix(rpc): ignore an expected error in the RPC acceptance tests (#3961) * Add ignored regexes to test command failure regex methods * Ignore empty chain error in getblockchaininfo We expect this error when zebrad starts up with an empty state. Co-authored-by: teor <teor@riseup.net> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Make sync error logs more user-friendly (#3944) - use info level, there is nothing the user needs to do, particularly for a single error - explain that the errors are temporary - hide backtraces, because they look like crashes * Update test.patch.yml with lightwalletd job (#3970) * Update test.patch.yml with lightwalletd job * Remove a workflow condition that will always be false In general, patch workflows need the opposite conditions to the original workflow. But in this case, we know the result of the condition will always be true, so we can just delete it. Co-authored-by: teor <teor@riseup.net> * fix(doc): Fix bugs in the lightwalletd database design (#3964) * Re-order column families in design in dependency order * Minor RFC design tweaks and fixes Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Repoint zebra image links to our new zfnd.org site for now (#3949) * Repoint zebra image links to our new zfnd.org site for now * Remove images/ Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Fix typos (#3956) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * bump database version to trigger testnet rollback * reduce minimum protocol version for now (will be changed later) * update dependencies * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * update versions to match zcash 4.7.0 * deny.toml: update 'darling' Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> Co-authored-by: teor <teor@riseup.net> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com> Co-authored-by: Dimitris Apostolou <dimitris.apostolou@icloud.com>
2022-04-18 17:14:16 -07:00
use halo2::pasta::{group::ff::PrimeField, pallas};
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
use tower::{service_fn, ServiceExt};
use zebra_chain::{
amount::{Amount, NonNegative},
block::{self, Block, Height},
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
orchard::AuthorizedAction,
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
parameters::{Network, NetworkUpgrade},
primitives::{ed25519, x25519, Groth16Proof},
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
sapling,
serialization::{DateTime32, ZcashDeserialize, ZcashDeserializeInto},
sprout,
transaction::{
arbitrary::{
fake_v5_transactions_for_network, insert_fake_orchard_shielded_data, test_transactions,
transactions_from_blocks,
},
zip317, Hash, HashType, JoinSplitData, LockTime, Transaction,
},
transparent::{self, CoinbaseData},
};
2023-05-16 14:06:46 -07:00
use zebra_state::ValidateContextError;
use zebra_test::mock_service::MockService;
use crate::error::TransactionError;
use super::{check, Request, Verifier};
Add some proptests for lock time validation (#3089) * Create a strategy for block heights after Sapling Provides an arbitrary network (mainnet or testnet) and a block height between the Sapling activation height on that network and the maximum block height. * Create a helper function to select block heights Allows generating block heights inside a range using a scale factor between 0 and 1. * Allow specifying the outpoint index for mock UTXOs Avoid creating multiple transparent transfers in the same transaction with the same source UTXO, which would lead to a double spend. * Create helper function to mock multiple transfers Given relative block height scale factors, create a mock transparent transfer for each one of them. Also add a constant that serves as a guideline for the maximum number of transparent transfers to mock. * Create helper function to sanitize tx. version Make sure the arbitrary transaction version is valid for the network (testnet or mainnet) at the specified block height. * Create `mock_transparent_transaction` helper func. Creates a V4 or V5 mock transaction that only includes transparent inputs and outputs. * Create helper function for transaction validation Performs the actual tested action of verifying a transaction. It sets up the verifier and uses it to obtain the verification result. * Test if zero lock time means unlocked Generate arbitrary transactions with zero lock time, and check that they are accepted by the transaction verifier. * Allow changing the sequence number of an input Add a setter method for a `transparent::Input`'s sequence number. This setter is only available for testing. * Test if sequence numbers can disable lock time Create arbitrary transactions and set the sequence numbers of all of its inputs to `u32::MAX` to see if that disables the lock time and the transactions are accepted by the verifier. * Test block height lock times Make sure that the transaction verifier rejects transactions that are still locked at a certain block height. * Test block time lock times Test that the transaction verifier rejects a transaction that is validated at a block time that's before the transaction's lock time. * Test unlocking by block height Test that transactions unlocked at an earlier block height are accepted by the transaction verifier. * Test transactions unlocked by the block time Test that transactions that were unlocked at a previous block time are accepted by the transaction verifier. * Fix an incorrect method comment Co-authored-by: teor <teor@riseup.net>
2021-12-19 15:44:12 -08:00
#[cfg(test)]
mod prop;
#[test]
fn v5_fake_transactions() -> Result<(), Report> {
let _init_guard = 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)?;
}
}
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);
// The check will fail if the transaction has no flags
assert_eq!(
check::has_inputs_and_outputs(&transaction),
Err(TransactionError::NoInputs)
);
// If we add ENABLE_SPENDS flag it will pass the inputs check but fails with the outputs
// TODO: Avoid new calls to `insert_fake_orchard_shielded_data` for each check #2409.
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS;
assert_eq!(
check::has_inputs_and_outputs(&transaction),
Err(TransactionError::NoOutputs)
);
// If we add ENABLE_OUTPUTS flag it will pass the outputs check but fails with the inputs
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
assert_eq!(
check::has_inputs_and_outputs(&transaction),
Err(TransactionError::NoInputs)
);
// Finally make it valid by adding both required flags
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags =
zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
assert!(check::has_inputs_and_outputs(&transaction).is_ok());
}
2021-11-08 13:45:54 -08:00
#[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);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS;
2021-11-08 13:45:54 -08:00
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);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
2021-11-08 13:45:54 -08:00
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);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags =
zebra_chain::orchard::Flags::ENABLE_SPENDS | zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
2021-11-08 13:45:54 -08:00
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(
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)
);
}
#[tokio::test]
async fn mempool_request_with_missing_input_is_rejected() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let (height, tx) = transactions_from_blocks(zebra_test::vectors::MAINNET_BLOCKS.iter())
.find(|(_, tx)| !(tx.is_coinbase() || tx.inputs().is_empty()))
.expect("At least one non-coinbase transaction with transparent inputs in test vectors");
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
};
tokio::spawn(async move {
// The first non-coinbase transaction with transparent inputs in our test vectors
// does not use a lock time, so we don't see Request::BestChainNextMedianTimePast here
state
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(None));
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert_eq!(
verifier_response,
Err(TransactionError::TransparentInputNotFound)
);
}
#[tokio::test]
async fn mempool_request_with_present_input_is_accepted() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert!(
verifier_response.is_ok(),
"expected successful verification, got: {verifier_response:?}"
);
}
#[tokio::test]
async fn mempool_request_with_invalid_lock_time_is_rejected() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::max_lock_time_timestamp(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::BestChainNextMedianTimePast)
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::BestChainNextMedianTimePast(
DateTime32::from(
u32::try_from(LockTime::MIN_TIMESTAMP).expect("min time is valid"),
),
));
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert_eq!(
verifier_response,
Err(TransactionError::LockedUntilAfterBlockTime(
Utc.timestamp_opt(u32::MAX.into(), 0).unwrap()
))
);
}
#[tokio::test]
async fn mempool_request_with_unlocked_lock_time_is_accepted() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert!(
verifier_response.is_ok(),
"expected successful verification, got: {verifier_response:?}"
);
}
#[tokio::test]
async fn mempool_request_with_lock_time_max_sequence_number_is_accepted() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (mut input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Ignore the lock time.
input.set_sequence(u32::MAX);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::max_lock_time_timestamp(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert!(
verifier_response.is_ok(),
"expected successful verification, got: {verifier_response:?}"
);
}
#[tokio::test]
async fn mempool_request_with_past_lock_time_is_accepted() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::min_lock_time_timestamp(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::BestChainNextMedianTimePast)
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::BestChainNextMedianTimePast(
DateTime32::MAX,
));
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
change(mempool): Contextually validates mempool transactions in best chain (#5716) * updates comments * adds check nullifier no dup fns for transactions * Adds: - check::anchors fn for tx iter - TODO comments for unifying nullifiers and anchors checks - new state request Updates unknown anchor errors to accomodate tx-only check Calls new state fn from transaction verifier * updates check::anchors fns to use transactions updates TransactionContextualValidity request to check sprout anchors adds comment mentioning TransactionContextualValidity ignores UTXOs * conditions new state req call on is_mempool updates tests * fix doc link / lint error * checks for duplicate nullifiers with closures * Update zebra-state/src/service/check/nullifier.rs Co-authored-by: teor <teor@riseup.net> * documents find_duplicate_nullifier params moves if let statement into for loop * renames new state req/res * asserts correct response variant in tx verifier * adds CheckBestChainTipShieldedSpends call in tx verifier to async checks * re-adds tracing instrumentation to check::anchors fn renames transaction_in_state to transaction_in_chain * adds block/tx wrapper fns for anchors checks * uses UnminedTx instead of transaction.hash() deletes broken test * updates new state req/res name * updates tests and uses par_iter for anchors checks * Updates check::anchors pub fn docs. * Adds: - comments / docs - a TransactionError variant for ValidateContextError * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * moves downcast to From impl rustfmt * moves the ValidateContextError into an Arc updates comments and naming * leaves par_iter for another PR * puts io::Error in an Arc * updates anchors tests to call tx_anchors check * updates tests to call tx_no_duplicates_in_chain slightly improves formatting * Update zebra-consensus/src/error.rs Co-authored-by: teor <teor@riseup.net> * moves Arc from HistoryError to ValidateContextError Co-authored-by: teor <teor@riseup.net>
2022-11-29 20:40:15 -08:00
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert!(
verifier_response.is_ok(),
"expected successful verification, got: {verifier_response:?}"
);
}
/// Tests that calls to the transaction verifier with a mempool request that spends
/// immature coinbase outputs will return an error.
#[tokio::test]
2023-05-16 14:06:46 -07:00
async fn mempool_request_with_immature_spend_is_rejected() {
let _init_guard = zebra_test::init();
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::min_lock_time_timestamp(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
let spend_restriction = tx.coinbase_spend_restriction(height);
let coinbase_spend_height = Height(5);
let utxo = known_utxos
.get(&input_outpoint)
.map(|utxo| {
let mut utxo = utxo.utxo.clone();
utxo.height = coinbase_spend_height;
utxo.from_coinbase = true;
utxo
})
.expect("known_utxos should contain the outpoint");
let expected_error =
zebra_state::check::transparent_coinbase_spend(input_outpoint, spend_restriction, &utxo)
.map_err(Box::new)
.map_err(TransactionError::ValidateContextError)
.expect_err("check should fail");
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::BestChainNextMedianTimePast)
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::BestChainNextMedianTimePast(
DateTime32::MAX,
));
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos.get(&input_outpoint).map(|utxo| {
let mut utxo = utxo.utxo.clone();
utxo.height = coinbase_spend_height;
utxo.from_coinbase = true;
utxo
}),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await
.expect_err("verification of transaction with immature spend should fail");
assert_eq!(
verifier_response, expected_error,
"expected to fail verification, got: {verifier_response:?}"
);
}
2023-05-16 14:06:46 -07:00
/// Tests that errors from the read state service are correctly converted into
/// transaction verifier errors.
#[tokio::test]
async fn state_error_converted_correctly() {
use zebra_state::DuplicateNullifierError;
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let tx = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
let make_validate_context_error =
|| sprout::Nullifier([0; 32].into()).duplicate_nullifier_error(true);
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(Err::<zebra_state::Response, zebra_state::BoxError>(
make_validate_context_error().into(),
));
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
let transaction_error =
verifier_response.expect_err("expected failed verification, got: {verifier_response:?}");
assert_eq!(
TransactionError::from(make_validate_context_error()),
transaction_error,
"expected matching state and transaction errors"
);
let state_error = zebra_state::BoxError::from(make_validate_context_error())
.downcast::<ValidateContextError>()
.map(|boxed| TransactionError::from(*boxed))
.expect("downcast should succeed");
assert_eq!(
state_error, transaction_error,
"expected matching state and transaction errors"
);
let TransactionError::ValidateContextError(propagated_validate_context_error) = transaction_error else {
panic!("should be a ValidateContextError variant");
};
assert_eq!(
*propagated_validate_context_error,
make_validate_context_error(),
"expected matching state and transaction errors"
);
}
#[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);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS;
assert_eq!(
check::coinbase_tx_no_prevout_joinsplit_spend(&transaction),
Err(TransactionError::CoinbaseHasEnableSpendsOrchard)
);
}
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
#[tokio::test]
async fn v5_transaction_is_rejected_before_nu5_activation() {
const V5_TRANSACTION_VERSION: u32 = 5;
let canopy = NetworkUpgrade::Canopy;
let networks = vec![
(Network::Mainnet, zebra_test::vectors::MAINNET_BLOCKS.iter()),
(Network::Testnet, zebra_test::vectors::TESTNET_BLOCKS.iter()),
];
for (network, blocks) in networks {
let state_service = service_fn(|_| async { unreachable!("Service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
let transaction = fake_v5_transactions_for_network(network, blocks)
.next_back()
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
.expect("At least one fake V5 transaction in the test vectors");
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height: canopy
.activation_height(network)
.expect("Canopy activation height is specified"),
time: DateTime::<Utc>::MAX_UTC,
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::UnsupportedByNetworkUpgrade(
V5_TRANSACTION_VERSION,
canopy
))
);
}
}
#[test]
fn v5_transaction_is_accepted_after_nu5_activation_mainnet() {
v5_transaction_is_accepted_after_nu5_activation_for_network(Network::Mainnet)
Update shared NU5 dependencies, set the NU5 testnet activation network upgrade parameters (#2825) * Check return value of zcash_script_new_precomputed_tx * Set the NU5 testnet activation height to 1_590_000 * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * Update Nu5 constants to new values * Update ZIP-244 test vectors for new branch ID * Squashed commit of the following: commit bdb120a249e3e889a913114a712505defdade1d4 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:54:01 2021 -0400 Use pallas::Base::from_str_vartime() in sinsemilla tests commit e99fa4925857840fa65ccfb4a076ec412e416576 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:45:24 2021 -0400 Compiles commit a5200181146bfd2aa1e09abea2caaa7a7ceb006e Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 10:15:17 2021 -0400 Incomplete upgrade of deps * Squashed commit of the following: commit 8d1b76ec5626517817c3a4d9f3950acc90a359df Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 04:02:26 2021 +0000 Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. commit 371233628ae61e0c25d6ba8f31d9dba42823becb Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:06:20 2021 +0000 Update Zcash dependencies Update some Zcash crates: - `halo2` - `incrementalmerkletree' (patch version) - `orchard` (patch version) - `zcash_history` (patch version) - `zcash_note_encryption` (patch version) - `zcash_primitives` (patch version) And also update the `group` dependency so that the code remains compatible. commit de5cf1ec40c3fc08670fc971cdf3e65e13d9f4c7 Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:04:13 2021 +0000 Update error message assertion Use the updated message for the expected error variant. * Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: teor <teor@riseup.net>
2021-10-05 18:08:41 -07:00
}
#[test]
fn v5_transaction_is_accepted_after_nu5_activation_testnet() {
v5_transaction_is_accepted_after_nu5_activation_for_network(Network::Testnet)
Update shared NU5 dependencies, set the NU5 testnet activation network upgrade parameters (#2825) * Check return value of zcash_script_new_precomputed_tx * Set the NU5 testnet activation height to 1_590_000 * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * Update Nu5 constants to new values * Update ZIP-244 test vectors for new branch ID * Squashed commit of the following: commit bdb120a249e3e889a913114a712505defdade1d4 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:54:01 2021 -0400 Use pallas::Base::from_str_vartime() in sinsemilla tests commit e99fa4925857840fa65ccfb4a076ec412e416576 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:45:24 2021 -0400 Compiles commit a5200181146bfd2aa1e09abea2caaa7a7ceb006e Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 10:15:17 2021 -0400 Incomplete upgrade of deps * Squashed commit of the following: commit 8d1b76ec5626517817c3a4d9f3950acc90a359df Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 04:02:26 2021 +0000 Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. commit 371233628ae61e0c25d6ba8f31d9dba42823becb Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:06:20 2021 +0000 Update Zcash dependencies Update some Zcash crates: - `halo2` - `incrementalmerkletree' (patch version) - `orchard` (patch version) - `zcash_history` (patch version) - `zcash_note_encryption` (patch version) - `zcash_primitives` (patch version) And also update the `group` dependency so that the code remains compatible. commit de5cf1ec40c3fc08670fc971cdf3e65e13d9f4c7 Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:04:13 2021 +0000 Update error message assertion Use the updated message for the expected error variant. * Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: teor <teor@riseup.net>
2021-10-05 18:08:41 -07:00
}
fn v5_transaction_is_accepted_after_nu5_activation_for_network(network: Network) {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
let nu5 = NetworkUpgrade::Nu5;
let nu5_activation_height = nu5
.activation_height(network)
.expect("NU5 activation height is specified");
let blocks = match network {
Network::Mainnet => zebra_test::vectors::MAINNET_BLOCKS.iter(),
Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(),
};
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
let state_service = service_fn(|_| async { unreachable!("Service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
let mut transaction = fake_v5_transactions_for_network(network, blocks)
.next_back()
.expect("At least one fake V5 transaction in the test vectors");
if transaction
.expiry_height()
.expect("V5 must have expiry_height")
< nu5_activation_height
{
let expiry_height = transaction.expiry_height_mut();
*expiry_height = nu5_activation_height;
}
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
let expected_hash = transaction.unmined_id();
let expiry_height = transaction
.expiry_height()
.expect("V5 must have expiry_height");
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height: expiry_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
assert_eq!(
result.expect("unexpected error response").tx_id(),
expected_hash
);
})
Reject V5 transactions before NU5 activation (#2285) * Add a `Transaction::version` getter Returns the version of the transaction as a `u32`. * Add `Transaction::is_overwintered` helper method Returns if the `fOverwintered` flag should be set for the transaction's version. * Use new helpers to serialize transaction version Reduce the repeated code and make it less error-prone with future changes. * Add getter methods to `transaction::Request` type Refactor to move the type deconstruction code into the `Request` type. The main objective is to make it easier to split the call handler into methods that receive the request directly. * Refactor to create `verify_v4_transaction` helper Split the code specific to V4 transactions into a separate helper method. * Create `verify_v5_transaction` helper method Prepare a separate method to have the validation code. * Add `UnsupportedByNetworkUpgrade` error variant An error for when a transaction's version isn't supported by the network upgrade of the block it's included or for the current network upgrade if the transaction is for the mempool. * Verify a V5 transaction's network upgrade For now, only NU5 supports V5 transactions. * Test that V5 transaction is rejected on Canopy Create a fake V5 transaction and try to verify it using a block height from Canopy's activation. The verifier should reject the transaction with an error saying that the network upgrade does not support that transaction version. * Test if V5 tx. is accepted after NU5 activation Create a fake V5 transaction and pretend it is placed in a block that has a height after the NU5 activation. The test should succeed, but since the NU5 activation height has not been specified yet (neither for the testnet nor the mainnet), for now this test is marked as `should_panic`. * Add `TODO` comment to the code Add more detail to what's left to do, and link to the appropriate PRs. * Use `u32` to store transaction version Use a type consistent with how the version is specified. Co-authored-by: teor <teor@riseup.net> Co-authored-by: teor <teor@riseup.net>
2021-06-14 17:15:59 -07:00
}
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
/// Test if V4 transaction with transparent funds is accepted.
#[tokio::test]
async fn v4_transaction_with_transparent_transfer_is_accepted() {
let network = Network::Mainnet;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Create a V4 transaction
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
joinsplit_data: None,
sapling_shielded_data: None,
};
let transaction_hash = transaction.unmined_id();
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction_hash
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
}
/// Tests if a non-coinbase V4 transaction with the last valid expiry height is
/// accepted.
#[tokio::test]
async fn v4_transaction_with_last_valid_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
let block_height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// Create a non-coinbase V4 tx with the last valid expiry height.
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: block_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction.unmined_id()
);
}
/// Tests if a coinbase V4 transaction with an expiry height lower than the
/// block height is accepted.
///
/// Note that an expiry height lower than the block height is considered
/// *expired* for *non-coinbase* transactions.
#[tokio::test]
async fn v4_coinbase_transaction_with_low_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
let block_height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let (input, output) = mock_coinbase_transparent_output(block_height);
// This is a correct expiry height for coinbase V4 transactions.
let expiry_height = (block_height - 1).expect("original block height is too small");
// Create a coinbase V4 tx.
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction.unmined_id()
);
}
/// Tests if an expired non-coinbase V4 transaction is rejected.
#[tokio::test]
async fn v4_transaction_with_too_low_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
let block_height = NetworkUpgrade::Canopy
.activation_height(Network::Mainnet)
.expect("Canopy activation height is specified");
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// This expiry height is too low so that the tx should seem expired to the verifier.
let expiry_height = (block_height - 1).expect("original block height is too small");
// Create a non-coinbase V4 tx.
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::ExpiredTransaction {
expiry_height,
block_height,
transaction_hash: transaction.hash(),
})
);
}
/// Tests if a non-coinbase V4 transaction with an expiry height exceeding the
/// maximum is rejected.
#[tokio::test]
async fn v4_transaction_with_exceeding_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
let block_height = block::Height::MAX;
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// This expiry height exceeds the maximum defined by the specification.
let expiry_height = block::Height(500_000_000);
// Create a non-coinbase V4 tx.
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::MaximumExpiryHeight {
expiry_height,
is_coinbase: false,
block_height,
transaction_hash: transaction.hash(),
})
);
}
/// Tests if a coinbase V4 transaction with an expiry height exceeding the
/// maximum is rejected.
#[tokio::test]
async fn v4_coinbase_transaction_with_exceeding_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
// Use an arbitrary pre-NU5 block height.
// It can't be NU5-onward because the expiry height limit is not enforced
// for coinbase transactions (it needs to match the block height instead),
// which is what is used in this test.
let block_height = (NetworkUpgrade::Nu5
.activation_height(Network::Mainnet)
.expect("NU5 height must be set")
- 1)
.expect("will not underflow");
let (input, output) = mock_coinbase_transparent_output(block_height);
// This expiry height exceeds the maximum defined by the specification.
let expiry_height = block::Height(500_000_000);
// Create a coinbase V4 tx.
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::MaximumExpiryHeight {
expiry_height,
is_coinbase: true,
block_height,
transaction_hash: transaction.hash(),
})
);
}
/// Test if V4 coinbase transaction is accepted.
#[tokio::test]
async fn v4_coinbase_transaction_is_accepted() {
let network = Network::Mainnet;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
// Create a fake transparent coinbase that should succeed
let (input, output) = mock_coinbase_transparent_output(transaction_block_height);
// Create a V4 coinbase transaction
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: transaction_block_height,
joinsplit_data: None,
sapling_shielded_data: None,
};
let transaction_hash = transaction.unmined_id();
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction_hash
);
}
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
/// Test if V4 transaction with transparent funds is rejected if the source script prevents it.
///
/// This test simulates the case where the script verifier rejects the transaction because the
/// script prevents spending the source UTXO.
#[tokio::test]
async fn v4_transaction_with_transparent_transfer_is_rejected_by_the_script() {
let network = Network::Mainnet;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should not succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
false,
0,
Amount::try_from(1).expect("invalid value"),
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Create a V4 transaction
let transaction = Transaction::V4 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
joinsplit_data: None,
sapling_shielded_data: None,
};
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::InternalDowncastError(
"downcast to known transaction error type failed, original error: ScriptInvalid"
.to_string()
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
))
);
}
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
/// Test if V4 transaction with an internal double spend of transparent funds is rejected.
#[tokio::test]
async fn v4_transaction_with_conflicting_transparent_spend_is_rejected() {
let network = Network::Mainnet;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Create a V4 transaction
let transaction = Transaction::V4 {
inputs: vec![input.clone(), input.clone()],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
joinsplit_data: None,
sapling_shielded_data: None,
};
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
let expected_outpoint = input.outpoint().expect("Input should have an outpoint");
assert_eq!(
result,
Err(TransactionError::DuplicateTransparentSpend(
expected_outpoint
))
);
}
/// Test if V4 transaction with a joinsplit that has duplicate nullifiers is rejected.
#[test]
fn v4_transaction_with_conflicting_sprout_nullifier_inside_joinsplit_is_rejected() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let network = Network::Mainnet;
let network_upgrade = NetworkUpgrade::Canopy;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
// Create a fake Sprout join split
let (mut joinsplit_data, signing_key) = mock_sprout_join_split_data();
// Make both nullifiers the same inside the joinsplit transaction
let duplicate_nullifier = joinsplit_data.first.nullifiers[0];
joinsplit_data.first.nullifiers[1] = duplicate_nullifier;
// Create a V4 transaction
let mut transaction = Transaction::V4 {
inputs: vec![],
outputs: vec![],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
joinsplit_data: Some(joinsplit_data),
sapling_shielded_data: None,
};
// Sign the transaction
let sighash = transaction.sighash(network_upgrade, HashType::ALL, &[], None);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
match &mut transaction {
Transaction::V4 {
joinsplit_data: Some(joinsplit_data),
..
} => joinsplit_data.sig = signing_key.sign(sighash.as_ref()),
_ => unreachable!("Mock transaction was created incorrectly"),
}
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::DuplicateSproutNullifier(
duplicate_nullifier
))
);
});
}
/// Test if V4 transaction with duplicate nullifiers across joinsplits is rejected.
#[test]
fn v4_transaction_with_conflicting_sprout_nullifier_across_joinsplits_is_rejected() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let network = Network::Mainnet;
let network_upgrade = NetworkUpgrade::Canopy;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
// Create a fake Sprout join split
let (mut joinsplit_data, signing_key) = mock_sprout_join_split_data();
// Duplicate a nullifier from the created joinsplit
let duplicate_nullifier = joinsplit_data.first.nullifiers[1];
// Add a new joinsplit with the duplicate nullifier
let mut new_joinsplit = joinsplit_data.first.clone();
new_joinsplit.nullifiers[0] = duplicate_nullifier;
new_joinsplit.nullifiers[1] = sprout::note::Nullifier([2u8; 32].into());
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
joinsplit_data.rest.push(new_joinsplit);
// Create a V4 transaction
let mut transaction = Transaction::V4 {
inputs: vec![],
outputs: vec![],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
joinsplit_data: Some(joinsplit_data),
sapling_shielded_data: None,
};
// Sign the transaction
let sighash = transaction.sighash(network_upgrade, HashType::ALL, &[], None);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
match &mut transaction {
Transaction::V4 {
joinsplit_data: Some(joinsplit_data),
..
} => joinsplit_data.sig = signing_key.sign(sighash.as_ref()),
_ => unreachable!("Mock transaction was created incorrectly"),
}
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::DuplicateSproutNullifier(
duplicate_nullifier
))
);
});
}
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
/// Test if V5 transaction with transparent funds is accepted.
#[tokio::test]
async fn v5_transaction_with_transparent_transfer_is_accepted() {
let network = Network::Testnet;
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let network_upgrade = NetworkUpgrade::Nu5;
let nu5_activation_height = network_upgrade
.activation_height(network)
.expect("NU5 activation height is specified");
let transaction_block_height =
(nu5_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Create a V5 transaction
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade,
};
let transaction_hash = transaction.unmined_id();
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction_hash
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
}
/// Tests if a non-coinbase V5 transaction with the last valid expiry height is
/// accepted.
#[tokio::test]
async fn v5_transaction_with_last_valid_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Testnet, state_service);
let block_height = NetworkUpgrade::Nu5
.activation_height(Network::Testnet)
.expect("Nu5 activation height for testnet is specified");
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// Create a non-coinbase V5 tx with the last valid expiry height.
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: block_height,
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade: NetworkUpgrade::Nu5,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction.unmined_id()
);
}
/// Tests that a coinbase V5 transaction is accepted only if its expiry height
/// is equal to the height of the block the transaction belongs to.
#[tokio::test]
async fn v5_coinbase_transaction_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Testnet, state_service);
let block_height = NetworkUpgrade::Nu5
.activation_height(Network::Testnet)
.expect("Nu5 activation height for testnet is specified");
let (input, output) = mock_coinbase_transparent_output(block_height);
// Create a coinbase V5 tx with an expiry height that matches the height of
// the block. Note that this is the only valid expiry height for a V5
// coinbase tx.
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height: block_height,
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade: NetworkUpgrade::Nu5,
};
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction.unmined_id()
);
// Increment the expiry height so that it becomes invalid.
let new_expiry_height = (block_height + 1).expect("transaction block height is too large");
let mut new_transaction = transaction.clone();
*new_transaction.expiry_height_mut() = new_expiry_height;
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(new_transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::CoinbaseExpiryBlockHeight {
expiry_height: Some(new_expiry_height),
block_height,
transaction_hash: new_transaction.hash(),
})
);
// Decrement the expiry height so that it becomes invalid.
let new_expiry_height = (block_height - 1).expect("transaction block height is too low");
let mut new_transaction = transaction.clone();
*new_transaction.expiry_height_mut() = new_expiry_height;
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(new_transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::CoinbaseExpiryBlockHeight {
expiry_height: Some(new_expiry_height),
block_height,
transaction_hash: new_transaction.hash(),
})
);
// Test with matching heights again, but using a very high value
// that is greater than the limit for non-coinbase transactions,
// to ensure the limit is not being enforced for coinbase transactions.
let new_expiry_height = Height::MAX;
let mut new_transaction = transaction.clone();
*new_transaction.expiry_height_mut() = new_expiry_height;
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(new_transaction.clone()),
known_utxos: Arc::new(HashMap::new()),
height: new_expiry_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
new_transaction.unmined_id()
);
}
/// Tests if an expired non-coinbase V5 transaction is rejected.
#[tokio::test]
async fn v5_transaction_with_too_low_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Testnet, state_service);
let block_height = NetworkUpgrade::Nu5
.activation_height(Network::Testnet)
.expect("Nu5 activation height for testnet is specified");
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// This expiry height is too low so that the tx should seem expired to the verifier.
let expiry_height = (block_height - 1).expect("original block height is too small");
// Create a non-coinbase V5 tx.
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade: NetworkUpgrade::Nu5,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::ExpiredTransaction {
expiry_height,
block_height,
transaction_hash: transaction.hash(),
})
);
}
/// Tests if a non-coinbase V5 transaction with an expiry height exceeding the
/// maximum is rejected.
#[tokio::test]
async fn v5_transaction_with_exceeding_expiry_height() {
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(Network::Mainnet, state_service);
let block_height = block::Height::MAX;
let fund_height = (block_height - 1).expect("fake source fund block height is too small");
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
// This expiry height exceeds the maximum defined by the specification.
let expiry_height = block::Height(500_000_000);
// Create a non-coinbase V5 tx.
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
expiry_height,
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade: NetworkUpgrade::Nu5,
};
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction.clone()),
known_utxos: Arc::new(known_utxos),
height: block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result,
Err(TransactionError::MaximumExpiryHeight {
expiry_height,
is_coinbase: false,
block_height,
transaction_hash: transaction.hash(),
})
);
}
/// Test if V5 coinbase transaction is accepted.
#[tokio::test]
async fn v5_coinbase_transaction_is_accepted() {
let network = Network::Testnet;
let network_upgrade = NetworkUpgrade::Nu5;
let nu5_activation_height = network_upgrade
.activation_height(network)
.expect("NU5 activation height is specified");
let transaction_block_height =
(nu5_activation_height + 10).expect("transaction block height is too large");
// Create a fake transparent coinbase that should succeed
let (input, output) = mock_coinbase_transparent_output(transaction_block_height);
let known_utxos = HashMap::new();
// Create a V5 coinbase transaction
let transaction = Transaction::V5 {
network_upgrade,
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: transaction_block_height,
sapling_shielded_data: None,
orchard_shielded_data: None,
};
let transaction_hash = transaction.unmined_id();
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
transaction_hash
);
}
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
/// Test if V5 transaction with transparent funds is rejected if the source script prevents it.
///
/// This test simulates the case where the script verifier rejects the transaction because the
/// script prevents spending the source UTXO.
#[tokio::test]
async fn v5_transaction_with_transparent_transfer_is_rejected_by_the_script() {
let network = Network::Testnet;
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let network_upgrade = NetworkUpgrade::Nu5;
let nu5_activation_height = network_upgrade
.activation_height(network)
.expect("NU5 activation height is specified");
let transaction_block_height =
(nu5_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should not succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
false,
0,
Amount::try_from(1).expect("invalid value"),
);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Create a V5 transaction
let transaction = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade,
};
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::InternalDowncastError(
Update shared NU5 dependencies, set the NU5 testnet activation network upgrade parameters (#2825) * Check return value of zcash_script_new_precomputed_tx * Set the NU5 testnet activation height to 1_590_000 * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * Update Nu5 constants to new values * Update ZIP-244 test vectors for new branch ID * Squashed commit of the following: commit bdb120a249e3e889a913114a712505defdade1d4 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:54:01 2021 -0400 Use pallas::Base::from_str_vartime() in sinsemilla tests commit e99fa4925857840fa65ccfb4a076ec412e416576 Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 11:45:24 2021 -0400 Compiles commit a5200181146bfd2aa1e09abea2caaa7a7ceb006e Author: Deirdre Connolly <durumcrustulum@gmail.com> Date: Tue Oct 5 10:15:17 2021 -0400 Incomplete upgrade of deps * Squashed commit of the following: commit 8d1b76ec5626517817c3a4d9f3950acc90a359df Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 04:02:26 2021 +0000 Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. commit 371233628ae61e0c25d6ba8f31d9dba42823becb Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:06:20 2021 +0000 Update Zcash dependencies Update some Zcash crates: - `halo2` - `incrementalmerkletree' (patch version) - `orchard` (patch version) - `zcash_history` (patch version) - `zcash_note_encryption` (patch version) - `zcash_primitives` (patch version) And also update the `group` dependency so that the code remains compatible. commit de5cf1ec40c3fc08670fc971cdf3e65e13d9f4c7 Author: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Date: Tue Oct 5 03:04:13 2021 +0000 Update error message assertion Use the updated message for the expected error variant. * Update `zcash_script` to support V5 transactions Use a newer version of `zcash_script` that has been updated to support V5 transactions. Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: teor <teor@riseup.net>
2021-10-05 18:08:41 -07:00
"downcast to known transaction error type failed, original error: ScriptInvalid"
.to_string()
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
))
);
}
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
/// Test if V5 transaction with an internal double spend of transparent funds is rejected.
#[tokio::test]
async fn v5_transaction_with_conflicting_transparent_spend_is_rejected() {
let network = Network::Mainnet;
let network_upgrade = NetworkUpgrade::Nu5;
let canopy_activation_height = NetworkUpgrade::Canopy
.activation_height(network)
.expect("Canopy activation height is specified");
let transaction_block_height =
(canopy_activation_height + 10).expect("transaction block height is too large");
let fake_source_fund_height =
(transaction_block_height - 1).expect("fake source fund block height is too small");
// Create a fake transparent transfer that should succeed
let (input, output, known_utxos) = mock_transparent_transfer(
fake_source_fund_height,
true,
0,
Amount::try_from(1).expect("invalid value"),
);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Create a V4 transaction
let transaction = Transaction::V5 {
inputs: vec![input.clone(), input.clone()],
outputs: vec![output],
lock_time: LockTime::Height(block::Height(0)),
expiry_height: (transaction_block_height + 1).expect("expiry height is too large"),
sapling_shielded_data: None,
orchard_shielded_data: None,
network_upgrade,
};
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let result = verifier
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(known_utxos),
height: transaction_block_height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
let expected_outpoint = input.outpoint().expect("Input should have an outpoint");
assert_eq!(
result,
Err(TransactionError::DuplicateTransparentSpend(
expected_outpoint
))
);
}
/// Test if signed V4 transaction with a dummy [`sprout::JoinSplit`] is accepted.
///
/// This test verifies if the transaction verifier correctly accepts a signed transaction.
#[test]
fn v4_with_signed_sprout_transfer_is_accepted() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
let network = Network::Mainnet;
let (height, transaction) = test_transactions(network)
.rev()
.filter(|(_, transaction)| {
!transaction.is_coinbase() && transaction.inputs().is_empty()
})
.find(|(_, transaction)| transaction.sprout_groth16_joinsplits().next().is_some())
.expect("No transaction found with Groth16 JoinSplits");
let expected_hash = transaction.unmined_id();
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction,
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
expected_hash
);
})
}
/// Test if an V4 transaction with a modified [`sprout::JoinSplit`] is rejected.
///
/// This test verifies if the transaction verifier correctly rejects the transaction because of the
/// invalid JoinSplit.
#[test]
fn v4_with_modified_joinsplit_is_rejected() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
v4_with_joinsplit_is_rejected_for_modification(
JoinSplitModification::CorruptSignature,
// TODO: Fix error downcast
// Err(TransactionError::Ed25519(ed25519::Error::InvalidSignature))
TransactionError::InternalDowncastError(
"downcast to known transaction error type failed, original error: InvalidSignature"
.to_string(),
),
)
.await;
v4_with_joinsplit_is_rejected_for_modification(
JoinSplitModification::CorruptProof,
TransactionError::Groth16("proof verification failed".to_string()),
)
.await;
v4_with_joinsplit_is_rejected_for_modification(
JoinSplitModification::ZeroProof,
TransactionError::MalformedGroth16("invalid G1".to_string()),
)
.await;
})
}
async fn v4_with_joinsplit_is_rejected_for_modification(
modification: JoinSplitModification,
expected_error: TransactionError,
) {
let network = Network::Mainnet;
let (height, mut transaction) = test_transactions(network)
.rev()
.filter(|(_, transaction)| !transaction.is_coinbase() && transaction.inputs().is_empty())
.find(|(_, transaction)| transaction.sprout_groth16_joinsplits().next().is_some())
.expect("There should be a tx with Groth16 JoinSplits.");
let expected_error = Err(expected_error);
// Modify a JoinSplit in the transaction following the given modification type.
let tx = Arc::get_mut(&mut transaction).expect("The tx should have only one active reference.");
match tx {
Transaction::V4 {
joinsplit_data: Some(ref mut joinsplit_data),
..
} => modify_joinsplit_data(joinsplit_data, modification),
_ => unreachable!("Transaction should have some JoinSplit shielded data."),
}
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called.") });
let verifier = Verifier::new(network, state_service);
// Test the transaction verifier.
//
// Note that modifying the JoinSplit data invalidates the tx signatures. The signatures are
// checked concurrently with the ZK proofs, and when a signature check finishes before the proof
// check, the verifier reports an invalid signature instead of invalid proof. This race
// condition happens only occasionaly, so we run the verifier in a loop with a small iteration
// threshold until it returns the correct error.
let mut i = 1;
let result = loop {
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: transaction.clone(),
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
if result == expected_error || i >= 10 {
break result;
}
i += 1;
};
assert_eq!(result, expected_error);
}
/// Test if a V4 transaction with Sapling spends is accepted by the verifier.
#[test]
fn v4_with_sapling_spends() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
let network = Network::Mainnet;
let (height, transaction) = test_transactions(network)
.rev()
.filter(|(_, transaction)| {
!transaction.is_coinbase() && transaction.inputs().is_empty()
})
.find(|(_, transaction)| transaction.sapling_spends_per_anchor().next().is_some())
.expect("No transaction found with Sapling spends");
let expected_hash = transaction.unmined_id();
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction,
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
expected_hash
);
});
}
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
/// Test if a V4 transaction with a duplicate Sapling spend is rejected by the verifier.
#[test]
fn v4_with_duplicate_sapling_spends() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let network = Network::Mainnet;
let (height, mut transaction) = test_transactions(network)
.rev()
.filter(|(_, transaction)| {
!transaction.is_coinbase() && transaction.inputs().is_empty()
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.find(|(_, transaction)| transaction.sapling_spends_per_anchor().next().is_some())
.expect("No transaction found with Sapling spends");
// Duplicate one of the spends
let duplicate_nullifier = duplicate_sapling_spend(
Arc::get_mut(&mut transaction).expect("Transaction only has one active reference"),
);
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction,
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::DuplicateSaplingNullifier(
duplicate_nullifier
))
);
});
}
/// Test if a V4 transaction with Sapling outputs but no spends is accepted by the verifier.
#[test]
fn v4_with_sapling_outputs_and_no_spends() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
let network = Network::Mainnet;
let (height, transaction) = test_transactions(network)
.rev()
.filter(|(_, transaction)| {
!transaction.is_coinbase() && transaction.inputs().is_empty()
})
.find(|(_, transaction)| {
transaction.sapling_spends_per_anchor().next().is_none()
&& transaction.sapling_outputs().next().is_some()
})
2021-07-02 09:48:53 -07:00
.expect("No transaction found with Sapling outputs and no Sapling spends");
let expected_hash = transaction.unmined_id();
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction,
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
expected_hash
);
})
}
2021-07-02 09:48:53 -07:00
/// Test if a V5 transaction with Sapling spends is accepted by the verifier.
#[test]
// TODO: add NU5 mainnet test vectors with Sapling spends, then remove should_panic
2021-07-02 09:48:53 -07:00
#[should_panic]
fn v5_with_sapling_spends() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
2021-07-02 09:48:53 -07:00
let network = Network::Mainnet;
let nu5_activation = NetworkUpgrade::Nu5.activation_height(network);
2021-07-02 09:48:53 -07:00
let transaction =
fake_v5_transactions_for_network(network, zebra_test::vectors::MAINNET_BLOCKS.iter())
.rev()
.filter(|transaction| {
!transaction.is_coinbase()
&& transaction.inputs().is_empty()
&& transaction.expiry_height() >= nu5_activation
})
2021-07-02 09:48:53 -07:00
.find(|transaction| transaction.sapling_spends_per_anchor().next().is_some())
.expect("No transaction found with Sapling spends");
let expected_hash = transaction.unmined_id();
2021-07-02 09:48:53 -07:00
let height = transaction
.expiry_height()
.expect("Transaction is missing expiry height");
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
2021-07-02 09:48:53 -07:00
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
2021-07-02 09:48:53 -07:00
})
.await;
assert_eq!(
result.expect("unexpected error response").tx_id(),
expected_hash
);
2021-07-02 09:48:53 -07:00
});
}
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
/// Test if a V5 transaction with a duplicate Sapling spend is rejected by the verifier.
#[test]
fn v5_with_duplicate_sapling_spends() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let network = Network::Mainnet;
let mut transaction =
fake_v5_transactions_for_network(network, zebra_test::vectors::MAINNET_BLOCKS.iter())
.rev()
.filter(|transaction| !transaction.is_coinbase() && transaction.inputs().is_empty())
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
.find(|transaction| transaction.sapling_spends_per_anchor().next().is_some())
.expect("No transaction found with Sapling spends");
let height = transaction
.expiry_height()
.expect("Transaction is missing expiry height");
// Duplicate one of the spends
let duplicate_nullifier = duplicate_sapling_spend(&mut transaction);
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::DuplicateSaplingNullifier(
duplicate_nullifier
))
);
});
}
/// Test if a V5 transaction with a duplicate Orchard action is rejected by the verifier.
#[test]
fn v5_with_duplicate_orchard_action() {
let _init_guard = zebra_test::init();
zebra_test::MULTI_THREADED_RUNTIME.block_on(async {
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
let network = Network::Mainnet;
// Find a transaction with no inputs or outputs to use as base
let mut transaction =
fake_v5_transactions_for_network(network, 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");
let height = transaction
.expiry_height()
.expect("Transaction is missing expiry height");
// Insert fake Orchard shielded data to the transaction, which has at least one action (this is
// guaranteed structurally by `orchard::ShieldedData`)
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
// Enable spends
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS
| zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Duplicate the first action
let duplicate_action = shielded_data.actions.first().clone();
let duplicate_nullifier = duplicate_action.action.nullifier;
shielded_data.actions.push(duplicate_action);
// Initialize the verifier
let state_service =
service_fn(|_| async { unreachable!("State service should not be called") });
let verifier = Verifier::new(network, state_service);
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
// Test the transaction verifier
let result = verifier
.clone()
.oneshot(Request::Block {
transaction: Arc::new(transaction),
known_utxos: Arc::new(HashMap::new()),
height,
time: DateTime::<Utc>::MAX_UTC,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
})
.await;
assert_eq!(
result,
Err(TransactionError::DuplicateOrchardNullifier(
duplicate_nullifier
))
);
});
}
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Utility functions
/// Create a mock transparent transfer to be included in a transaction.
///
/// First, this creates a fake unspent transaction output from a fake transaction included in the
/// specified `previous_utxo_height` block height. This fake [`Utxo`] also contains a simple script
/// that can either accept or reject any spend attempt, depending on if `script_should_succeed` is
/// `true` or `false`. Since the `tx_index_in_block` is irrelevant for blocks that have already
/// been verified, it is set to `1`.
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
///
/// Then, a [`transparent::Input::PrevOut`] is created that attempts to spend the previously created fake
/// UTXO to a new [`transparent::Output`].
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
///
/// Finally, the initial fake UTXO is placed in a `known_utxos` [`HashMap`] so that it can be
/// retrieved during verification.
///
/// The function then returns the generated transparent input and output, as well as the
/// `known_utxos` map.
///
/// Note: `known_utxos` is only intended to be used for UTXOs within the same block,
/// so future verification changes might break this mocking function.
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
fn mock_transparent_transfer(
previous_utxo_height: block::Height,
script_should_succeed: bool,
Add some proptests for lock time validation (#3089) * Create a strategy for block heights after Sapling Provides an arbitrary network (mainnet or testnet) and a block height between the Sapling activation height on that network and the maximum block height. * Create a helper function to select block heights Allows generating block heights inside a range using a scale factor between 0 and 1. * Allow specifying the outpoint index for mock UTXOs Avoid creating multiple transparent transfers in the same transaction with the same source UTXO, which would lead to a double spend. * Create helper function to mock multiple transfers Given relative block height scale factors, create a mock transparent transfer for each one of them. Also add a constant that serves as a guideline for the maximum number of transparent transfers to mock. * Create helper function to sanitize tx. version Make sure the arbitrary transaction version is valid for the network (testnet or mainnet) at the specified block height. * Create `mock_transparent_transaction` helper func. Creates a V4 or V5 mock transaction that only includes transparent inputs and outputs. * Create helper function for transaction validation Performs the actual tested action of verifying a transaction. It sets up the verifier and uses it to obtain the verification result. * Test if zero lock time means unlocked Generate arbitrary transactions with zero lock time, and check that they are accepted by the transaction verifier. * Allow changing the sequence number of an input Add a setter method for a `transparent::Input`'s sequence number. This setter is only available for testing. * Test if sequence numbers can disable lock time Create arbitrary transactions and set the sequence numbers of all of its inputs to `u32::MAX` to see if that disables the lock time and the transactions are accepted by the verifier. * Test block height lock times Make sure that the transaction verifier rejects transactions that are still locked at a certain block height. * Test block time lock times Test that the transaction verifier rejects a transaction that is validated at a block time that's before the transaction's lock time. * Test unlocking by block height Test that transactions unlocked at an earlier block height are accepted by the transaction verifier. * Test transactions unlocked by the block time Test that transactions that were unlocked at a previous block time are accepted by the transaction verifier. * Fix an incorrect method comment Co-authored-by: teor <teor@riseup.net>
2021-12-19 15:44:12 -08:00
outpoint_index: u32,
previous_output_value: Amount<NonNegative>,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
) -> (
transparent::Input,
transparent::Output,
HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
) {
// A script with a single opcode that accepts the transaction (pushes true on the stack)
let accepting_script = transparent::Script::new(&[1, 1]);
// A script with a single opcode that rejects the transaction (OP_FALSE)
let rejecting_script = transparent::Script::new(&[0]);
// Mock an unspent transaction output
let previous_outpoint = transparent::OutPoint {
hash: Hash([1u8; 32]),
Add some proptests for lock time validation (#3089) * Create a strategy for block heights after Sapling Provides an arbitrary network (mainnet or testnet) and a block height between the Sapling activation height on that network and the maximum block height. * Create a helper function to select block heights Allows generating block heights inside a range using a scale factor between 0 and 1. * Allow specifying the outpoint index for mock UTXOs Avoid creating multiple transparent transfers in the same transaction with the same source UTXO, which would lead to a double spend. * Create helper function to mock multiple transfers Given relative block height scale factors, create a mock transparent transfer for each one of them. Also add a constant that serves as a guideline for the maximum number of transparent transfers to mock. * Create helper function to sanitize tx. version Make sure the arbitrary transaction version is valid for the network (testnet or mainnet) at the specified block height. * Create `mock_transparent_transaction` helper func. Creates a V4 or V5 mock transaction that only includes transparent inputs and outputs. * Create helper function for transaction validation Performs the actual tested action of verifying a transaction. It sets up the verifier and uses it to obtain the verification result. * Test if zero lock time means unlocked Generate arbitrary transactions with zero lock time, and check that they are accepted by the transaction verifier. * Allow changing the sequence number of an input Add a setter method for a `transparent::Input`'s sequence number. This setter is only available for testing. * Test if sequence numbers can disable lock time Create arbitrary transactions and set the sequence numbers of all of its inputs to `u32::MAX` to see if that disables the lock time and the transactions are accepted by the verifier. * Test block height lock times Make sure that the transaction verifier rejects transactions that are still locked at a certain block height. * Test block time lock times Test that the transaction verifier rejects a transaction that is validated at a block time that's before the transaction's lock time. * Test unlocking by block height Test that transactions unlocked at an earlier block height are accepted by the transaction verifier. * Test transactions unlocked by the block time Test that transactions that were unlocked at a previous block time are accepted by the transaction verifier. * Fix an incorrect method comment Co-authored-by: teor <teor@riseup.net>
2021-12-19 15:44:12 -08:00
index: outpoint_index,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
};
let lock_script = if script_should_succeed {
accepting_script.clone()
} else {
rejecting_script.clone()
};
let previous_output = transparent::Output {
value: previous_output_value,
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
lock_script,
};
let previous_utxo = transparent::OrderedUtxo::new(previous_output, previous_utxo_height, 1);
Validate transparent inputs and outputs in V5 transactions (#2302) * Add missing documentation Document methods to describe what they do and why. * Create an `AsyncChecks` type alias Make it simpler to write the `FuturesUnordered` type with boxed futures. This will also end up being used more when refactoring to return the checks so that the `call` method can wait on them. * Create `verify_transparent_inputs_and_outputs` Refactors the verification of the transparent inputs and outputs into a separate method. * Refactor transparent checks to use `call_all` Instead of pushing the verifications into a stream of unordered futures, use the `ServiceExt::call_all` method to build an equivalent stream after building a stream of requests. * Replace `CallAll` with `FuturesUnordered` Make it more consistent with the rest of the code, and make sure that the `len()` method is available to use for tracing. Co-authored-by: teor <teor@riseup.net> * Refactor to move wait for checks into a new method Allow the code snipped to be reused by other transaction version-specific check methods. * Verify transparent inputs in V5 transactions Use the script verifier to check the transparent inputs in a V5 transaction. * Check `has_inputs_and_outputs` for all versions Check if a transaction has inputs and outputs, independently of the transaction version. * Wait for checks in `call` method Refactor to move the repeated code into the `call` method. Now the validation methods return the set of asynchronous checks to wait for. * Add helper function to mock transparent transfers Creates a fake source UTXO, and then the input and output that represent spending that UTXO. The initial UTXO can be configured to have a script that either accepts or rejects any spend attempt. * Test if transparent V4 transaction is accepted Create a fake V4 transaction that includes a fake transparent transfer of funds. The transfer uses a script to allow any UTXO to spend it. * Test transaction V4 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Test if transparent V5 transaction is accepted Create a mock V5 transaction that includes a transparent transfer of funds. The transaction should be accepted by the verifier. * Test transaction V5 rejection based on script Create a fake transparent transfer where the source UTXO has a script that rejects spending. The script verifier should not accept this transaction. * Update `Request::upgrade` getter documentation Simplify it so that it won't become updated when #1683 is fixed. Co-authored-by: teor <teor@riseup.net>
2021-06-22 18:54:00 -07:00
// Use the `previous_outpoint` as input
let input = transparent::Input::PrevOut {
outpoint: previous_outpoint,
unlock_script: accepting_script,
sequence: 0,
};
// The output resulting from the transfer
// Using the rejecting script pretends the amount is burned because it can't be spent again
let output = transparent::Output {
value: Amount::try_from(1).expect("1 is an invalid amount"),
lock_script: rejecting_script,
};
// Cache the source of the fund so that it can be used during verification
let mut known_utxos = HashMap::new();
known_utxos.insert(previous_outpoint, previous_utxo);
(input, output, known_utxos)
}
/// Create a mock coinbase input with a transparent output.
///
/// Create a [`transparent::Input::Coinbase`] at `coinbase_height`.
/// Then create UTXO with a [`transparent::Output`] spending some coinbase funds.
///
/// Returns the generated coinbase input and transparent output.
fn mock_coinbase_transparent_output(
coinbase_height: block::Height,
) -> (transparent::Input, transparent::Output) {
// A script with a single opcode that rejects the transaction (OP_FALSE)
let rejecting_script = transparent::Script::new(&[0]);
let input = transparent::Input::Coinbase {
height: coinbase_height,
data: CoinbaseData::new(Vec::new()),
sequence: u32::MAX,
};
// The output resulting from the transfer
// Using the rejecting script pretends the amount is burned because it can't be spent again
let output = transparent::Output {
value: Amount::try_from(1).expect("1 is an invalid amount"),
lock_script: rejecting_script,
};
(input, output)
}
/// Create a mock [`sprout::JoinSplit`] and include it in a [`transaction::JoinSplitData`].
///
/// This creates a dummy join split. By itself it is invalid, but it is useful for including in a
/// transaction to check the signatures.
///
/// The [`transaction::JoinSplitData`] with the dummy [`sprout::JoinSplit`] is returned together
/// with the [`ed25519::SigningKey`] that can be used to create a signature to later add to the
/// returned join split data.
fn mock_sprout_join_split_data() -> (JoinSplitData<Groth16Proof>, ed25519::SigningKey) {
// Prepare dummy inputs for the join split
let zero_amount = 0_i32
.try_into()
.expect("Invalid JoinSplit transparent input");
let anchor = sprout::tree::Root::default();
let first_nullifier = sprout::note::Nullifier([0u8; 32].into());
let second_nullifier = sprout::note::Nullifier([1u8; 32].into());
let commitment = sprout::commitment::NoteCommitment::from([0u8; 32]);
let ephemeral_key =
x25519::PublicKey::from(&x25519::EphemeralSecret::random_from_rng(rand::thread_rng()));
let random_seed = sprout::RandomSeed::from([0u8; 32]);
let mac = sprout::note::Mac::zcash_deserialize(&[0u8; 32][..])
.expect("Failure to deserialize dummy MAC");
let zkproof = Groth16Proof([0u8; 192]);
let encrypted_note = sprout::note::EncryptedNote([0u8; 601]);
// Create an dummy join split
let joinsplit = sprout::JoinSplit {
vpub_old: zero_amount,
vpub_new: zero_amount,
anchor,
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
nullifiers: [first_nullifier, second_nullifier],
commitments: [commitment; 2],
ephemeral_key,
random_seed,
vmacs: [mac.clone(), mac],
zkproof,
enc_ciphertexts: [encrypted_note; 2],
};
// Create a usable signing key
let signing_key = ed25519::SigningKey::new(rand::thread_rng());
let verification_key = ed25519::VerificationKey::from(&signing_key);
// Populate join split data with the dummy join split.
let joinsplit_data = JoinSplitData {
first: joinsplit,
rest: vec![],
pub_key: verification_key.into(),
sig: [0u8; 64].into(),
};
(joinsplit_data, signing_key)
}
/// A type of JoinSplit modification to test.
#[derive(Clone, Copy)]
enum JoinSplitModification {
// Corrupt a signature, making it invalid.
CorruptSignature,
// Corrupt a proof, making it invalid, but still well-formed.
CorruptProof,
// Make a proof all-zeroes, making it malformed.
ZeroProof,
}
/// Modify a [`JoinSplitData`] following the given modification type.
fn modify_joinsplit_data(
joinsplit_data: &mut JoinSplitData<Groth16Proof>,
modification: JoinSplitModification,
) {
match modification {
JoinSplitModification::CorruptSignature => {
let mut sig_bytes: [u8; 64] = joinsplit_data.sig.into();
// Flip a bit from an arbitrary byte of the signature.
sig_bytes[10] ^= 0x01;
joinsplit_data.sig = sig_bytes.into();
}
JoinSplitModification::CorruptProof => {
let joinsplit = joinsplit_data
.joinsplits_mut()
.next()
.expect("must have a JoinSplit");
{
// A proof is composed of three field elements, the first and last having 48 bytes.
// (The middle one has 96 bytes.) To corrupt the proof without making it malformed,
// simply swap those first and last elements.
let (first, rest) = joinsplit.zkproof.0.split_at_mut(48);
first.swap_with_slice(&mut rest[96..144]);
}
}
JoinSplitModification::ZeroProof => {
let joinsplit = joinsplit_data
.joinsplits_mut()
.next()
.expect("must have a JoinSplit");
joinsplit.zkproof.0 = [0; 192];
}
}
}
Reject a mempool transaction if it has internal spend conflicts (#2843) * Reorder imports to follow convention Place the imports from `std` at the top. * Add transaction errors for double spends Add a variant for each pool. They represent a double spend inside a transaction. * Add `check::spend_conflicts` implementation Checks if a transaction has spend conflicts, i.e., if a transaction spends a UTXO more than once or if it reveals a nullifier more than once. * Reject transactions with internal spend conflicts The transaction verifier should reject transactions that spend the same transparent UTXO or that reveal the same nullifier. * Add transparent spend consensus rule Add it to the documentation to help with understanding and auditing it. Co-authored-by: teor <teor@riseup.net> * Use different nullifiers by default Don't use the same nullifier twice when mocking a `sprout::JoinSplitData` because it will lead to an invalid transaction. * Test transactions with repeated spend outpoints Since that represents a spend conflict, they should be rejected. * Test duplicate nullifiers in joinsplit Check if a mock transaction with a joinsplit that reveals the same nullifier twice is rejected. * Test duplicate nullifiers across joinsplits Check if a duplicate nullifier in two different joinsplits in the same transaction is rejected. * Test V4 transaction with duplicate Sapling spend Check if a V4 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Sapling spend Check if a V5 transaction that has a duplicate Sapling spend is rejected. * Test V5 transaction with duplicate Orchard actions Check if a V5 transaction that has duplicate Orchard actions is rejected by the transaction verifier. Co-authored-by: teor <teor@riseup.net>
2021-10-27 19:49:28 -07:00
/// Duplicate a Sapling spend inside a `transaction`.
///
/// Returns the nullifier of the duplicate spend.
///
/// # Panics
///
/// Will panic if the `transaction` does not have Sapling spends.
fn duplicate_sapling_spend(transaction: &mut Transaction) -> sapling::Nullifier {
match transaction {
Transaction::V4 {
sapling_shielded_data: Some(ref mut shielded_data),
..
} => duplicate_sapling_spend_in_shielded_data(shielded_data),
Transaction::V5 {
sapling_shielded_data: Some(ref mut shielded_data),
..
} => duplicate_sapling_spend_in_shielded_data(shielded_data),
_ => unreachable!("Transaction has no Sapling shielded data"),
}
}
/// Duplicates the first spend of the `shielded_data`.
///
/// Returns the nullifier of the duplicate spend.
///
/// # Panics
///
/// Will panic if `shielded_data` has no spends.
fn duplicate_sapling_spend_in_shielded_data<A: sapling::AnchorVariant + Clone>(
shielded_data: &mut sapling::ShieldedData<A>,
) -> sapling::Nullifier {
match shielded_data.transfers {
sapling::TransferData::SpendsAndMaybeOutputs { ref mut spends, .. } => {
let duplicate_spend = spends.first().clone();
let duplicate_nullifier = duplicate_spend.nullifier;
spends.push(duplicate_spend);
duplicate_nullifier
}
sapling::TransferData::JustOutputs { .. } => {
unreachable!("Sapling shielded data has no spends")
}
}
}
#[test]
fn add_to_sprout_pool_after_nu() {
let _init_guard = zebra_test::init();
// get a block that we know it haves a transaction with `vpub_old` field greater than 0.
let block: Arc<_> = zebra_chain::block::Block::zcash_deserialize(
&zebra_test::vectors::BLOCK_MAINNET_419199_BYTES[..],
)
.unwrap()
.into();
// create a block height at canopy activation.
let network = Network::Mainnet;
let block_height = NetworkUpgrade::Canopy.activation_height(network).unwrap();
// create a zero amount.
let zero = Amount::<NonNegative>::try_from(0).expect("an amount of 0 is always valid");
// the coinbase transaction should pass the check.
assert_eq!(
check::disabled_add_to_sprout_pool(&block.transactions[0], block_height, network),
Ok(())
);
// the 2nd transaction has no joinsplits, should pass the check.
assert_eq!(block.transactions[1].joinsplit_count(), 0);
assert_eq!(
check::disabled_add_to_sprout_pool(&block.transactions[1], block_height, network),
Ok(())
);
// the 5th transaction has joinsplits and the `vpub_old` cumulative is greater than 0,
// should fail the check.
assert!(block.transactions[4].joinsplit_count() > 0);
let vpub_old: Amount<NonNegative> = block.transactions[4]
Check remaining transaction value & make value balance signs match the spec (#2566) * Make Amount arithmetic more generic To modify generated amounts, we need some extra operations on `Amount`. We also need to extend existing operations to both `NonNegative` and `NegativeAllowed` amounts. * Add a constrain method for ValueBalance * Derive Eq for ValueBalance * impl Neg for ValueBalance * Make some Amount arithmetic expectations explicit * Explain why we use i128 for multiplication And expand the overflow error details. * Expand Amount::sum error details * Make amount::Error field order consistent * Rename an amount::Error variant to Constraint, so it's clearer * Add specific pool variants to ValueBalanceError * Update coinbase remaining value consensus rule comment This consensus rule was updated recently to include coinbase transactions, but Zebra doesn't check block subsidy or miner fees yet. * Add test methods for modifying transparent values and shielded value balances * Temporarily set values and value balances to zero in proptests In both generated chains and proptests that construct their own transactions. Using zero values reduces value calculation and value check test coverage. A future change will use non-zero values, and fix them so the check passes. * Add extra fields to remaining transaction value errors * Swap the transparent value balance sign to match shielded value balances This makes the signs of all the chain value pools consistent. * Use a NonNegative constraint for transparent values This fix: * makes the type signature match the consensus rules * avoids having to write code to handle negative values * Allocate total generated transaction input value to outputs If there isn't enough input value for an output, set it to zero. Temporarily reduce all generated values to avoid overflow. (We'll remove this workaround when we calculate chain value balances.) * Consistently use ValueBalanceError for ValueBalances * Make the value balance signs match the spec And rename and document methods so their signs are clearer. * Convert amount::Errors to specific pool ValueBalanceErrors * Move some error changes to the next PR * Add extra info to remaining transaction value errors (#2585) * Distinguish between overflow and negative remaining transaction value errors And make some error types cloneable. * Add methods for updating chain value pools (#2586) * Move amount::test to amount::tests:vectors * Make ValueBalance traits more consistent with Amount - implement Add and Sub variants with Result and Assign - derive Hash * Clarify some comments and expects * Create ValueBalance update methods for blocks and transactions Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
2021-08-09 10:22:26 -07:00
.output_values_to_sprout()
.fold(zero, |acc, &x| (acc + x).unwrap());
assert!(vpub_old > zero);
assert_eq!(
check::disabled_add_to_sprout_pool(&block.transactions[3], block_height, network),
Err(TransactionError::DisabledAddToSproutPool)
);
// the 8th transaction has joinsplits and the `vpub_old` cumulative is 0,
// should pass the check.
assert!(block.transactions[7].joinsplit_count() > 0);
let vpub_old: Amount<NonNegative> = block.transactions[7]
Check remaining transaction value & make value balance signs match the spec (#2566) * Make Amount arithmetic more generic To modify generated amounts, we need some extra operations on `Amount`. We also need to extend existing operations to both `NonNegative` and `NegativeAllowed` amounts. * Add a constrain method for ValueBalance * Derive Eq for ValueBalance * impl Neg for ValueBalance * Make some Amount arithmetic expectations explicit * Explain why we use i128 for multiplication And expand the overflow error details. * Expand Amount::sum error details * Make amount::Error field order consistent * Rename an amount::Error variant to Constraint, so it's clearer * Add specific pool variants to ValueBalanceError * Update coinbase remaining value consensus rule comment This consensus rule was updated recently to include coinbase transactions, but Zebra doesn't check block subsidy or miner fees yet. * Add test methods for modifying transparent values and shielded value balances * Temporarily set values and value balances to zero in proptests In both generated chains and proptests that construct their own transactions. Using zero values reduces value calculation and value check test coverage. A future change will use non-zero values, and fix them so the check passes. * Add extra fields to remaining transaction value errors * Swap the transparent value balance sign to match shielded value balances This makes the signs of all the chain value pools consistent. * Use a NonNegative constraint for transparent values This fix: * makes the type signature match the consensus rules * avoids having to write code to handle negative values * Allocate total generated transaction input value to outputs If there isn't enough input value for an output, set it to zero. Temporarily reduce all generated values to avoid overflow. (We'll remove this workaround when we calculate chain value balances.) * Consistently use ValueBalanceError for ValueBalances * Make the value balance signs match the spec And rename and document methods so their signs are clearer. * Convert amount::Errors to specific pool ValueBalanceErrors * Move some error changes to the next PR * Add extra info to remaining transaction value errors (#2585) * Distinguish between overflow and negative remaining transaction value errors And make some error types cloneable. * Add methods for updating chain value pools (#2586) * Move amount::test to amount::tests:vectors * Make ValueBalance traits more consistent with Amount - implement Add and Sub variants with Result and Assign - derive Hash * Clarify some comments and expects * Create ValueBalance update methods for blocks and transactions Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
2021-08-09 10:22:26 -07:00
.output_values_to_sprout()
.fold(zero, |acc, &x| (acc + x).unwrap());
assert_eq!(vpub_old, zero);
assert_eq!(
check::disabled_add_to_sprout_pool(&block.transactions[7], block_height, network),
Ok(())
);
}
#[test]
fn coinbase_outputs_are_decryptable_for_historical_blocks() -> Result<(), Report> {
let _init_guard = zebra_test::init();
coinbase_outputs_are_decryptable_for_historical_blocks_for_network(Network::Mainnet)?;
coinbase_outputs_are_decryptable_for_historical_blocks_for_network(Network::Testnet)?;
Ok(())
}
fn coinbase_outputs_are_decryptable_for_historical_blocks_for_network(
network: Network,
) -> Result<(), Report> {
let block_iter = match network {
Network::Mainnet => zebra_test::vectors::MAINNET_BLOCKS.iter(),
Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(),
};
let mut tested_coinbase_txs = 0;
let mut tested_non_coinbase_txs = 0;
for (height, block) in block_iter {
let block = block
.zcash_deserialize_into::<Block>()
.expect("block is structurally valid");
let height = Height(*height);
let heartwood_onward = height
>= NetworkUpgrade::Heartwood
.activation_height(network)
.unwrap();
let coinbase_tx = block
.transactions
.get(0)
.expect("must have coinbase transaction");
// Check if the coinbase outputs are decryptable with an all-zero key.
if heartwood_onward
&& (coinbase_tx.sapling_outputs().count() > 0
|| coinbase_tx.orchard_actions().count() > 0)
{
// We are only truly decrypting something if it's Heartwood-onward
// and there are relevant outputs.
tested_coinbase_txs += 1;
}
check::coinbase_outputs_are_decryptable(coinbase_tx, network, height)
.expect("coinbase outputs must be decryptable with an all-zero key");
// For remaining transactions, check if existing outputs are NOT decryptable
// with an all-zero key, if applicable.
for tx in block.transactions.iter().skip(1) {
let has_outputs = tx.sapling_outputs().count() > 0 || tx.orchard_actions().count() > 0;
if has_outputs && heartwood_onward {
tested_non_coinbase_txs += 1;
check::coinbase_outputs_are_decryptable(tx, network, height).expect_err(
"decrypting a non-coinbase output with an all-zero key should fail",
);
} else {
check::coinbase_outputs_are_decryptable(tx, network, height)
.expect("a transaction without outputs, or pre-Heartwood, must be considered 'decryptable'");
}
}
}
assert!(tested_coinbase_txs > 0, "ensure it was actually tested");
assert!(tested_non_coinbase_txs > 0, "ensure it was actually tested");
Ok(())
}
/// Given an Orchard action as a base, fill fields related to note encryption
/// from the given test vector and returned the modified action.
fn fill_action_with_note_encryption_test_vector(
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
action: &zebra_chain::orchard::Action,
v: &zebra_test::vectors::TestVector,
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
) -> zebra_chain::orchard::Action {
let mut action = action.clone();
action.cv = v.cv_net.try_into().expect("test vector must be valid");
change(nu5): use new V5 transaction script verification API (#3799) * update librustzcash; adapt to new API * add ticket reference for removing zcash_proofs duplicated dependencies * update to new zcash_script V5 API * use zp_tx shorthand * update to Zcash 4.7.0 dependencies * update protocol versions * feat(rpc): Implement `getblockchaininfo` RPC method (#3891) * Implement `getblockchaininfo` RPC method * add a test for `get_blockchain_info` * fix tohex/fromhex * move comment * Update lightwalletd acceptance test for getblockchaininfo RPC (#3914) * change(rpc): Return getblockchaininfo network upgrades in height order (#3915) * Update lightwalletd acceptance test for getblockchaininfo RPC * Update some doc comments for network upgrades * List network upgrades in order in the getblockchaininfo RPC Also: - Use a constant for the "missing consensus branch ID" RPC value - Simplify fetching consensus branch IDs - Make RPC type derives consistent - Update RPC type documentation * Make RPC type derives consistent * Fix a confusing test comment * get hashand height at the same time * fix estimated_height * fix lint * add extra check Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * fix typo Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * split test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * fix(rpc): ignore an expected error in the RPC acceptance tests (#3961) * Add ignored regexes to test command failure regex methods * Ignore empty chain error in getblockchaininfo We expect this error when zebrad starts up with an empty state. Co-authored-by: teor <teor@riseup.net> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Make sync error logs more user-friendly (#3944) - use info level, there is nothing the user needs to do, particularly for a single error - explain that the errors are temporary - hide backtraces, because they look like crashes * Update test.patch.yml with lightwalletd job (#3970) * Update test.patch.yml with lightwalletd job * Remove a workflow condition that will always be false In general, patch workflows need the opposite conditions to the original workflow. But in this case, we know the result of the condition will always be true, so we can just delete it. Co-authored-by: teor <teor@riseup.net> * fix(doc): Fix bugs in the lightwalletd database design (#3964) * Re-order column families in design in dependency order * Minor RFC design tweaks and fixes Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Repoint zebra image links to our new zfnd.org site for now (#3949) * Repoint zebra image links to our new zfnd.org site for now * Remove images/ Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Fix typos (#3956) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * bump database version to trigger testnet rollback * reduce minimum protocol version for now (will be changed later) * update dependencies * Apply suggestions from code review Co-authored-by: teor <teor@riseup.net> * update versions to match zcash 4.7.0 * deny.toml: update 'darling' Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> Co-authored-by: teor <teor@riseup.net> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Deirdre Connolly <durumcrustulum@gmail.com> Co-authored-by: Dimitris Apostolou <dimitris.apostolou@icloud.com>
2022-04-18 17:14:16 -07:00
action.cm_x = pallas::Base::from_repr(v.cmx).unwrap();
action.nullifier = v.rho.try_into().expect("test vector must be valid");
action.ephemeral_key = v
.ephemeral_key
.try_into()
.expect("test vector must be valid");
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
action.out_ciphertext = v.c_out.into();
action.enc_ciphertext = v.c_enc.into();
action
}
/// Test if shielded coinbase outputs are decryptable with an all-zero outgoing
/// viewing key.
#[test]
fn coinbase_outputs_are_decryptable_for_fake_v5_blocks() {
let network = Network::Testnet;
for v in zebra_test::vectors::ORCHARD_NOTE_ENCRYPTION_ZERO_VECTOR.iter() {
// Find a transaction with no inputs or outputs to use as base
let mut transaction =
fake_v5_transactions_for_network(network, zebra_test::vectors::TESTNET_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");
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS
| zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
let action =
fill_action_with_note_encryption_test_vector(&shielded_data.actions[0].action, v);
let sig = shielded_data.actions[0].spend_auth_sig;
shielded_data.actions = vec![AuthorizedAction::from_parts(action, sig)]
.try_into()
.unwrap();
assert_eq!(
check::coinbase_outputs_are_decryptable(
&transaction,
network,
NetworkUpgrade::Nu5.activation_height(network).unwrap(),
),
Ok(())
);
}
}
/// Test if random shielded outputs are NOT decryptable with an all-zero outgoing
/// viewing key.
#[test]
fn shielded_outputs_are_not_decryptable_for_fake_v5_blocks() {
let network = Network::Testnet;
for v in zebra_test::vectors::ORCHARD_NOTE_ENCRYPTION_VECTOR.iter() {
// Find a transaction with no inputs or outputs to use as base
let mut transaction =
fake_v5_transactions_for_network(network, zebra_test::vectors::TESTNET_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");
let shielded_data = insert_fake_orchard_shielded_data(&mut transaction);
Async Halo2 verifier service (#2645) * First pass at async Halo2 verification service Stubs out a batch verifier for the future. The dependencies for orchard, halo2, librustzcash, zcash_primitives, have not been resolved. * Halo2 verifier service and test * Remove redundant conversion * Test async halo2 verifier service with pre-computed Orchard shielded data test vectors * Fix typo Co-authored-by: Conrado Gouvea <conrado@zfnd.org> * Assert future result is_ok() in Halo2 verifier test Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> * Shorten tower::Service trait constraints for Halo2 verifier tests * Remove commented out trait constraints * .expect() vs .unwrap() to parse orchard::redpallas::VerificationKey * Use .to_vec() for some test vectors * Fix self-referential Display impl * Fix deps * Distinguish orchard vs zebra_chain::orchard imports * Add test that halo2 verifier fails with malformed proof inputs * Use thiserror for Halo2Error * Use ZcashFoundation/orchard instead of dconnolly/orchard * Add a link to the issue to remove the zfnd fork of orchard crate * Update zebra-consensus/Cargo.toml Co-authored-by: teor <teor@riseup.net> * Add note * Move artificial Orchard shielded data test vectors to zebra-test * Align brackets * Tidy some trait constraints and debug statements Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: Conrado Gouvea <conrado@zfnd.org> Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com> Co-authored-by: teor <teor@riseup.net>
2021-11-16 19:26:15 -08:00
shielded_data.flags = zebra_chain::orchard::Flags::ENABLE_SPENDS
| zebra_chain::orchard::Flags::ENABLE_OUTPUTS;
let action =
fill_action_with_note_encryption_test_vector(&shielded_data.actions[0].action, v);
let sig = shielded_data.actions[0].spend_auth_sig;
shielded_data.actions = vec![AuthorizedAction::from_parts(action, sig)]
.try_into()
.unwrap();
assert_eq!(
check::coinbase_outputs_are_decryptable(
&transaction,
network,
NetworkUpgrade::Nu5.activation_height(network).unwrap(),
),
Err(TransactionError::CoinbaseOutputsNotDecryptable)
);
}
}
#[tokio::test]
async fn mempool_zip317_error() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Nu5
.activation_height(Network::Mainnet)
.expect("Nu5 activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
// Will produce a small enough miner fee to fail the check.
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10).expect("invalid value"),
);
// Create a non-coinbase V5 tx.
let tx = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
network_upgrade: NetworkUpgrade::Nu5,
expiry_height: height,
sapling_shielded_data: None,
orchard_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
// Mempool refuses to add this transaction into storage.
assert!(verifier_response.is_err());
assert_eq!(
verifier_response.err(),
Some(TransactionError::Zip317(zip317::Error::FeeBelowMinimumRate))
);
}
#[tokio::test]
async fn mempool_zip317_ok() {
let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests();
let verifier = Verifier::new(Network::Mainnet, state.clone());
let height = NetworkUpgrade::Nu5
.activation_height(Network::Mainnet)
.expect("Nu5 activation height is specified");
let fund_height = (height - 1).expect("fake source fund block height is too small");
// Will produce a big enough miner fee to pass the check.
let (input, output, known_utxos) = mock_transparent_transfer(
fund_height,
true,
0,
Amount::try_from(10001).expect("invalid value"),
);
// Create a non-coinbase V5 tx.
let tx = Transaction::V5 {
inputs: vec![input],
outputs: vec![output],
lock_time: LockTime::unlocked(),
network_upgrade: NetworkUpgrade::Nu5,
expiry_height: height,
sapling_shielded_data: None,
orchard_shielded_data: None,
};
let input_outpoint = match tx.inputs()[0] {
transparent::Input::PrevOut { outpoint, .. } => outpoint,
transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"),
};
tokio::spawn(async move {
state
.expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint))
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::UnspentBestChainUtxo(
known_utxos
.get(&input_outpoint)
.map(|utxo| utxo.utxo.clone()),
));
state
.expect_request_that(|req| {
matches!(
req,
zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_)
)
})
.await
.expect("verifier should call mock state service with correct request")
.respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors);
});
let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;
assert!(
verifier_response.is_ok(),
"expected successful verification, got: {verifier_response:?}"
);
}