Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change(consensus) verify that mempool transaction UTXOs are in the best chain #5616

Merged
merged 6 commits into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
adds missing input test
  • Loading branch information
arya2 committed Nov 11, 2022
commit 10b73bc271d45723d531d1df2a53c5ac84cdcb02
30 changes: 13 additions & 17 deletions zebra-chain/src/transaction/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,16 +930,7 @@ pub fn test_transactions(
Network::Testnet => zebra_test::vectors::TESTNET_BLOCKS.iter(),
};

blocks.flat_map(|(&block_height, &block_bytes)| {
let block = block_bytes
.zcash_deserialize_into::<block::Block>()
.expect("block is structurally valid");

block
.transactions
.into_iter()
.map(move |transaction| (block::Height(block_height), transaction))
})
transactions_from_blocks(blocks)
}

/// Generate an iterator over fake V5 transactions.
Expand All @@ -950,18 +941,23 @@ pub fn fake_v5_transactions_for_network<'b>(
network: Network,
blocks: impl DoubleEndedIterator<Item = (&'b u32, &'b &'static [u8])> + 'b,
) -> impl DoubleEndedIterator<Item = Transaction> + 'b {
blocks.flat_map(move |(height, original_bytes)| {
let original_block = original_bytes
transactions_from_blocks(blocks)
.map(move |(height, transaction)| transaction_to_fake_v5(&transaction, network, height))
}

/// Generate an iterator over ([`block::Height`], [`Arc<Transaction>`]).
pub fn transactions_from_blocks<'a>(
blocks: impl DoubleEndedIterator<Item = (&'a u32, &'a &'static [u8])> + 'a,
) -> impl DoubleEndedIterator<Item = (block::Height, Arc<Transaction>)> + 'a {
blocks.flat_map(|(&block_height, &block_bytes)| {
let block = block_bytes
.zcash_deserialize_into::<block::Block>()
.expect("block is structurally valid");

original_block
block
.transactions
.into_iter()
.map(move |transaction| {
transaction_to_fake_v5(&transaction, network, block::Height(*height))
})
.map(Transaction::from)
.map(move |transaction| (block::Height(block_height), transaction))
})
}

Expand Down
35 changes: 35 additions & 0 deletions zebra-consensus/src/transaction/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ use zebra_chain::{
transaction::{
arbitrary::{
fake_v5_transactions_for_network, insert_fake_orchard_shielded_data, test_transactions,
transactions_from_blocks,
},
Hash, HashType, JoinSplitData, LockTime, Transaction,
},
transparent::{self, CoinbaseData},
};

use zebra_test::mock_service::MockService;

use crate::error::TransactionError;

use super::{check, Request, Verifier};
Expand Down Expand Up @@ -177,6 +180,38 @@ fn v5_transaction_with_no_inputs_fails_validation() {
);
}

#[tokio::test]
async fn mempool_request_with_missing_input_is_rejected() {
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
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");

let expected_state_request = zebra_state::Request::BestChainUtxo(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(expected_state_request)
.await
.expect("verifier should call mock state service")
.respond(zebra_state::Response::BestChainUtxo(None));
});

let verifier_response = verifier
.oneshot(Request::Mempool {
transaction: tx.into(),
height,
})
.await;

assert_eq!(verifier_response, Err(TransactionError::InputNotFound));
}

#[test]
fn v5_transaction_with_no_outputs_fails_validation() {
let transaction = fake_v5_transactions_for_network(
Expand Down