diff --git a/crates/chain/Cargo.toml b/crates/chain/Cargo.toml index 8ff444385..b5c0018d4 100644 --- a/crates/chain/Cargo.toml +++ b/crates/chain/Cargo.toml @@ -27,7 +27,7 @@ rusqlite = { version = "0.31.0", features = ["bundled"], optional = true } [dev-dependencies] rand = "0.8" proptest = "1.2.0" -bdk_testenv = { path = "../testenv", default-features = false } +bdk_testenv = { path = "../testenv", default-features = false, features = ["miniscript"] } criterion = { version = "0.2" } [features] diff --git a/crates/chain/benches/canonicalization.rs b/crates/chain/benches/canonicalization.rs index 6893e6df8..c607af73f 100644 --- a/crates/chain/benches/canonicalization.rs +++ b/crates/chain/benches/canonicalization.rs @@ -1,248 +1,274 @@ -use bdk_chain::{keychain_txout::KeychainTxOutIndex, local_chain::LocalChain, IndexedTxGraph}; -use bdk_core::{BlockId, CheckPoint}; -use bdk_core::{ConfirmationBlockTime, TxUpdate}; -use bdk_testenv::hash; -use bitcoin::{ - absolute, constants, hashes::Hash, key::Secp256k1, transaction, Amount, BlockHash, Network, - OutPoint, ScriptBuf, Transaction, TxIn, TxOut, -}; -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use miniscript::{Descriptor, DescriptorPublicKey}; -use std::sync::Arc; - -type Keychain = (); -type KeychainTxGraph = IndexedTxGraph>; - -/// New tx guaranteed to have at least one output -fn new_tx(lt: u32) -> Transaction { - Transaction { - version: transaction::Version::TWO, - lock_time: absolute::LockTime::from_consensus(lt), - input: vec![], - output: vec![TxOut::NULL], - } +use bdk_chain::local_chain::LocalChain; +use bdk_chain::spk_txout::SpkTxOutIndex; +use bdk_chain::TxGraph; +use bdk_core::BlockId; +use bdk_testenv::tx_template::{init_graph, TxInTemplate, TxOutTemplate, TxTemplate}; +use bdk_testenv::{block_id, hash, local_chain}; +use bitcoin::Amount; +use criterion::{criterion_group, criterion_main, Criterion}; +use std::borrow::Cow; + +fn filter_chain_unspents( + tx_graph: &TxGraph, + spk_index: &SpkTxOutIndex, + chain: &LocalChain, + exp_txos: usize, +) { + let utxos = tx_graph.filter_chain_unspents( + chain, + chain.tip().block_id(), + spk_index.outpoints().clone(), + ); + assert_eq!(utxos.count(), exp_txos); } -fn spk_at_index(txout_index: &KeychainTxOutIndex, index: u32) -> ScriptBuf { - txout_index - .get_descriptor(()) - .unwrap() - .at_derivation_index(index) - .unwrap() - .script_pubkey() +fn filter_chain_txouts( + tx_graph: &TxGraph, + spk_index: &SpkTxOutIndex, + chain: &LocalChain, + exp_txos: usize, +) { + let utxos = + tx_graph.filter_chain_txouts(chain, chain.tip().block_id(), spk_index.outpoints().clone()); + assert_eq!(utxos.count(), exp_txos); } -fn genesis_block_id() -> BlockId { - BlockId { - height: 0, - hash: constants::genesis_block(Network::Regtest).block_hash(), - } +fn list_canonical_txs(tx_graph: &TxGraph, chain: &LocalChain, exp_txs: usize) { + let txs = tx_graph.list_canonical_txs(chain, chain.tip().block_id()); + assert_eq!(txs.count(), exp_txs); } -fn tip_block_id() -> BlockId { - BlockId { - height: 100, - hash: BlockHash::all_zeros(), +fn setup_many_conflicting_unconfirmed( + tx_count: u32, +) -> (TxGraph, SpkTxOutIndex, LocalChain) { + let chain = local_chain![(0, hash!("genesis")), (100, hash!("abcd"))]; + let mut templates = Vec::new(); + + templates.push(TxTemplate { + tx_name: Cow::Borrowed("ancestor_tx"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(Amount::ONE_BTC.to_sat(), Some(0))]), + anchors: Cow::Owned(vec![block_id!(100, "abcd")]), + last_seen: None, + }); + + for i in 1..=tx_count { + templates.push(TxTemplate { + tx_name: format!("conflict_tx_{}", i).into(), + inputs: Cow::Owned(vec![TxInTemplate::PrevTx("ancestor_tx".into(), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new( + Amount::ONE_BTC.to_sat() - (i as u64 * 10), + Some(1), + )]), + last_seen: Some(i as u64), + ..Default::default() + }); } + + let (tx_graph, spk_index, _) = init_graph(templates); + (tx_graph, spk_index, chain) } -/// Add ancestor tx confirmed at `block_id` with `locktime` (used for uniqueness). -/// The transaction always pays 1 BTC to SPK 0. -fn add_ancestor_tx(graph: &mut KeychainTxGraph, block_id: BlockId, locktime: u32) -> OutPoint { - let spk_0 = spk_at_index(&graph.index, 0); - let tx = Transaction { - input: vec![TxIn { - previous_output: OutPoint::new(hash!("bogus"), locktime), +/// chain of unconfirmed transactions +fn setup_many_chained_unconfirmed( + tx_chain_count: u32, +) -> (TxGraph, SpkTxOutIndex, LocalChain) { + let chain = local_chain![(0, hash!("genesis"))]; + let mut templates = Vec::new(); + + templates.push(TxTemplate { + tx_name: Cow::Borrowed("ancestor_tx"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(Amount::ONE_BTC.to_sat(), Some(0))]), + anchors: Cow::Owned(vec![block_id!(100, "abcd")]), + last_seen: None, + }); + + for i in 0..tx_chain_count { + templates.push(TxTemplate { + tx_name: format!("chain_tx_{}", i).into(), + inputs: if i == 0 { + Cow::Owned(vec![TxInTemplate::PrevTx("ancestor_tx".into(), 0)]) + } else { + Cow::Owned(vec![TxInTemplate::PrevTx( + format!("chain_tx_{}", i - 1).into(), + 0, + )]) + }, + last_seen: Some(i as u64), ..Default::default() - }], - output: vec![TxOut { - value: Amount::ONE_BTC, - script_pubkey: spk_0, - }], - ..new_tx(locktime) - }; - let txid = tx.compute_txid(); - let _ = graph.insert_tx(tx); - let _ = graph.insert_anchor( - txid, - ConfirmationBlockTime { - block_id, - confirmation_time: 100, - }, - ); - OutPoint { txid, vout: 0 } + }); + } + + let (tx_graph, spk_index, _) = init_graph(templates); + (tx_graph, spk_index, chain) } -fn setup(f: F) -> (KeychainTxGraph, LocalChain) { - const DESC: &str = "tr([ab28dc00/86h/1h/0h]tpubDCdDtzAMZZrkwKBxwNcGCqe4FRydeD9rfMisoi7qLdraG79YohRfPW4YgdKQhpgASdvh612xXNY5xYzoqnyCgPbkpK4LSVcH5Xv4cK7johH/0/*)"; - let cp = CheckPoint::from_block_ids([genesis_block_id(), tip_block_id()]) - .expect("blocks must be chronological"); - let chain = LocalChain::from_tip(cp).unwrap(); +/// graph with nested conflicting transactions +fn setup_nested_conflicts( + graph_depth: usize, + conflicts_per_output: usize, +) -> (TxGraph, SpkTxOutIndex, LocalChain) { + let chain = local_chain![(0, hash!("genesis"))]; + let mut templates = Vec::new(); - let (desc, _) = - >::parse_descriptor(&Secp256k1::new(), DESC).unwrap(); - let mut index = KeychainTxOutIndex::new(10); - index.insert_descriptor((), desc).unwrap(); - let mut tx_graph = KeychainTxGraph::new(index); + templates.push(TxTemplate { + tx_name: "ancestor_tx".into(), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(Amount::ONE_BTC.to_sat(), Some(0))]), + anchors: Cow::Owned(vec![block_id!(100, "abcd")]), + last_seen: None, + }); - f(&mut tx_graph, &chain); - (tx_graph, chain) -} + let mut previous_outputs = vec!["ancestor_tx".to_string()]; -fn run_list_canonical_txs(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_txs: usize) { - let txs = tx_graph - .graph() - .list_canonical_txs(chain, chain.tip().block_id()); - assert_eq!(txs.count(), exp_txs); -} + for depth in 1..graph_depth { + let mut next_outputs = Vec::new(); -fn run_filter_chain_txouts(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_txos: usize) { - let utxos = tx_graph.graph().filter_chain_txouts( - chain, - chain.tip().block_id(), - tx_graph.index.outpoints().clone(), - ); - assert_eq!(utxos.count(), exp_txos); -} + for (parent_index, previous_output_name) in previous_outputs.drain(..).enumerate() { + for conflict_i in 1..=conflicts_per_output { + let tx_name = format!( + "depth_{}_parent_{}_conflict_{}", + depth, parent_index, conflict_i + ); -fn run_filter_chain_unspents(tx_graph: &KeychainTxGraph, chain: &LocalChain, exp_utxos: usize) { - let utxos = tx_graph.graph().filter_chain_unspents( - chain, - chain.tip().block_id(), - tx_graph.index.outpoints().clone(), - ); - assert_eq!(utxos.count(), exp_utxos); -} + let last_seen = depth as u64 * conflict_i as u64; -pub fn many_conflicting_unconfirmed(c: &mut Criterion) { - const CONFLICTING_TX_COUNT: u32 = 2100; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { - let previous_output = add_ancestor_tx(tx_graph, tip_block_id(), 0); - // Create conflicting txs that spend from `previous_output`. - let spk_1 = spk_at_index(&tx_graph.index, 1); - for i in 1..=CONFLICTING_TX_COUNT { - let tx = Transaction { - input: vec![TxIn { - previous_output, + templates.push(TxTemplate { + tx_name: tx_name.clone().into(), + inputs: Cow::Owned(vec![TxInTemplate::PrevTx( + previous_output_name.clone().into(), + 0, + )]), + outputs: Cow::Owned(vec![TxOutTemplate::new( + Amount::ONE_BTC.to_sat() - (depth as u64 * 200 - conflict_i as u64), + Some(0), + )]), + last_seen: Some(last_seen), ..Default::default() - }], - output: vec![TxOut { - value: Amount::ONE_BTC - Amount::from_sat(i as u64 * 10), - script_pubkey: spk_1.clone(), - }], - ..new_tx(i) - }; - let mut update = TxUpdate::default(); - update.seen_ats = [(tx.compute_txid(), i as u64)].into(); - update.txs = vec![Arc::new(tx)]; - let _ = tx_graph.apply_update(update); + }); + + next_outputs.push(tx_name); + } } - })); + + previous_outputs = next_outputs; + } + + let (tx_graph, spk_index, _) = init_graph(templates); + (tx_graph, spk_index, chain) +} + +/// Benchmark for many conflicting unconfirmed transactions +fn bench_many_conflicting_unconfirmed(c: &mut Criterion) { + const CONFLICTING_TX_COUNT: u32 = 2100; + + let (tx_graph, spk_index, chain) = setup_many_conflicting_unconfirmed(CONFLICTING_TX_COUNT); + c.bench_function("many_conflicting_unconfirmed::list_canonical_txs", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_list_canonical_txs(&tx_graph, &chain, 2)) + let tx_graph = tx_graph.clone(); + let chain = chain.clone(); + move |b| { + b.iter(|| list_canonical_txs(&tx_graph, &chain, 2)); + } }); + c.bench_function("many_conflicting_unconfirmed::filter_chain_txouts", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_txouts(&tx_graph, &chain, 2)) - }); - c.bench_function("many_conflicting_unconfirmed::filter_chain_unspents", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_unspents(&tx_graph, &chain, 1)) + let tx_graph = tx_graph.clone(); + let spk_index = spk_index.clone(); + let chain = chain.clone(); + move |b| { + b.iter(|| filter_chain_txouts(&tx_graph, &spk_index, &chain, 2)); + } }); + + c.bench_function( + "many_conflicting_unconfirmed::filter_chain_unspents", + move |b| { + b.iter(|| { + filter_chain_unspents(&tx_graph, &spk_index, &chain, 1); + }); + }, + ); } -pub fn many_chained_unconfirmed(c: &mut Criterion) { +/// Benchmark for many chained unconfirmed transactions +pub fn bench_many_chained_unconfirmed(c: &mut Criterion) { const TX_CHAIN_COUNT: u32 = 2100; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { - let mut previous_output = add_ancestor_tx(tx_graph, tip_block_id(), 0); - // Create a chain of unconfirmed txs where each subsequent tx spends the output of the - // previous one. - for i in 0..TX_CHAIN_COUNT { - // Create tx. - let tx = Transaction { - input: vec![TxIn { - previous_output, - ..Default::default() - }], - ..new_tx(i) - }; - let txid = tx.compute_txid(); - let mut update = TxUpdate::default(); - update.seen_ats = [(txid, i as u64)].into(); - update.txs = vec![Arc::new(tx)]; - let _ = tx_graph.apply_update(update); - // Store the next prevout. - previous_output = OutPoint::new(txid, 0); - } - })); + + let (tx_graph, spk_index, chain) = setup_many_chained_unconfirmed(TX_CHAIN_COUNT); + c.bench_function("many_chained_unconfirmed::list_canonical_txs", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_list_canonical_txs(&tx_graph, &chain, 2101)) + let tx_graph = tx_graph.clone(); + let chain = chain.clone(); + move |b| { + b.iter(|| { + list_canonical_txs(&tx_graph, &chain, (TX_CHAIN_COUNT + 1).try_into().unwrap()); + }); + } }); + c.bench_function("many_chained_unconfirmed::filter_chain_txouts", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_txouts(&tx_graph, &chain, 1)) + let tx_graph = tx_graph.clone(); + let chain = chain.clone(); + let spk_index = spk_index.clone(); + move |b| { + b.iter(|| { + filter_chain_txouts(&tx_graph, &spk_index, &chain, 1); + }); + } }); + c.bench_function("many_chained_unconfirmed::filter_chain_unspents", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_unspents(&tx_graph, &chain, 0)) + move |b| { + b.iter(|| { + filter_chain_unspents(&tx_graph, &spk_index, &chain, 0); + }); + } }); } -pub fn nested_conflicts(c: &mut Criterion) { +/// Benchmark for nested conflicts +pub fn bench_nested_conflicts(c: &mut Criterion) { const CONFLICTS_PER_OUTPUT: usize = 3; const GRAPH_DEPTH: usize = 7; - let (tx_graph, chain) = black_box(setup(|tx_graph, _chain| { - let mut prev_ops = core::iter::once(add_ancestor_tx(tx_graph, tip_block_id(), 0)) - .collect::>(); - for depth in 1..GRAPH_DEPTH { - for previous_output in core::mem::take(&mut prev_ops) { - for conflict_i in 1..=CONFLICTS_PER_OUTPUT { - let mut last_seen = depth * conflict_i; - if last_seen % 2 == 0 { - last_seen /= 2; - } - let ((_, script_pubkey), _) = tx_graph.index.next_unused_spk(()).unwrap(); - let value = - Amount::ONE_BTC - Amount::from_sat(depth as u64 * 200 - conflict_i as u64); - let tx = Transaction { - input: vec![TxIn { - previous_output, - ..Default::default() - }], - output: vec![TxOut { - value, - script_pubkey, - }], - ..new_tx(conflict_i as _) - }; - let txid = tx.compute_txid(); - prev_ops.push(OutPoint::new(txid, 0)); - let _ = tx_graph.insert_seen_at(txid, last_seen as _); - let _ = tx_graph.insert_tx(tx); - } - } - } - })); + + let (tx_graph, spk_index, chain) = setup_nested_conflicts(GRAPH_DEPTH, CONFLICTS_PER_OUTPUT); + c.bench_function("nested_conflicts_unconfirmed::list_canonical_txs", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_list_canonical_txs(&tx_graph, &chain, GRAPH_DEPTH)) + let tx_graph = tx_graph.clone(); + let chain = chain.clone(); + move |b| { + b.iter(|| { + list_canonical_txs(&tx_graph, &chain, GRAPH_DEPTH); + }); + } }); + c.bench_function("nested_conflicts_unconfirmed::filter_chain_txouts", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_txouts(&tx_graph, &chain, GRAPH_DEPTH)) + let tx_graph = tx_graph.clone(); + let chain = chain.clone(); + let spk_index = spk_index.clone(); + move |b| { + b.iter(|| { + filter_chain_txouts(&tx_graph, &spk_index, &chain, GRAPH_DEPTH); + }); + } }); + c.bench_function("nested_conflicts_unconfirmed::filter_chain_unspents", { - let (tx_graph, chain) = (tx_graph.clone(), chain.clone()); - move |b| b.iter(|| run_filter_chain_unspents(&tx_graph, &chain, 1)) + move |b| { + b.iter(|| { + filter_chain_unspents(&tx_graph, &spk_index, &chain, 1); + }); + } }); } criterion_group!( benches, - many_conflicting_unconfirmed, - many_chained_unconfirmed, - nested_conflicts, + bench_many_conflicting_unconfirmed, + bench_many_chained_unconfirmed, + bench_nested_conflicts, ); criterion_main!(benches); diff --git a/crates/chain/tests/common/mod.rs b/crates/chain/tests/common/mod.rs deleted file mode 100644 index cb3ee66f3..000000000 --- a/crates/chain/tests/common/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![cfg(feature = "miniscript")] - -mod tx_template; -#[allow(unused_imports)] -pub use tx_template::*; diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index 1e28eb6a2..900b304b0 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -1,8 +1,5 @@ #![cfg(feature = "miniscript")] -#[macro_use] -mod common; - use std::{collections::BTreeSet, sync::Arc}; use bdk_chain::{ diff --git a/crates/chain/tests/test_tx_graph.rs b/crates/chain/tests/test_tx_graph.rs index 446147821..c4bcc7b21 100644 --- a/crates/chain/tests/test_tx_graph.rs +++ b/crates/chain/tests/test_tx_graph.rs @@ -1,7 +1,5 @@ #![cfg(feature = "miniscript")] -#[macro_use] -mod common; use bdk_chain::{collections::*, BlockId, ConfirmationBlockTime}; use bdk_chain::{ local_chain::LocalChain, @@ -9,14 +7,18 @@ use bdk_chain::{ tx_graph::{ChangeSet, TxGraph}, Anchor, ChainOracle, ChainPosition, Merge, }; -use bdk_testenv::{block_id, hash, utils::new_tx}; +use bdk_testenv::{ + block_id, hash, + tx_template::{init_graph, TxInTemplate, TxOutTemplate, TxTemplate}, + utils::new_tx, +}; use bitcoin::{ absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount, Transaction, TxIn, TxOut, Txid, }; -use common::*; use core::iter; use rand::RngCore; +use std::borrow::Cow; use std::sync::Arc; use std::vec; @@ -1136,28 +1138,28 @@ fn call_map_anchors_with_non_deterministic_anchor() { let template = [ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(1))], - anchors: &[block_id!(1, "A")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(1))]), + anchors: Cow::Owned(vec![block_id!(1, "A")]), last_seen: None, }, TxTemplate { - tx_name: "tx2", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(20000, Some(2))], - anchors: &[block_id!(2, "B")], + tx_name: Cow::Borrowed("tx2"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(2, "B")]), ..Default::default() }, TxTemplate { - tx_name: "tx3", - inputs: &[TxInTemplate::PrevTx("tx2", 0)], - outputs: &[TxOutTemplate::new(30000, Some(3))], - anchors: &[block_id!(3, "C"), block_id!(4, "D")], + tx_name: Cow::Borrowed("tx3"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx2"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), + anchors: Cow::Owned(vec![block_id!(3, "C"), block_id!(4, "D")]), ..Default::default() }, ]; - let (graph, _, _) = init_graph(&template); + let (graph, _, _) = init_graph(template); let new_graph = graph.clone().map_anchors(|a| NonDeterministicAnchor { anchor_block: a, // A non-deterministic value diff --git a/crates/chain/tests/test_tx_graph_conflicts.rs b/crates/chain/tests/test_tx_graph_conflicts.rs index ff4c8b1f9..d42e27bbe 100644 --- a/crates/chain/tests/test_tx_graph_conflicts.rs +++ b/crates/chain/tests/test_tx_graph_conflicts.rs @@ -1,20 +1,22 @@ #![cfg(feature = "miniscript")] -#[macro_use] -mod common; - use bdk_chain::{Balance, BlockId}; -use bdk_testenv::{block_id, hash, local_chain}; +use bdk_testenv::{ + block_id, hash, local_chain, + tx_template::{init_graph, TxInTemplate, TxOutTemplate, TxTemplate}, +}; use bitcoin::{Amount, OutPoint, ScriptBuf}; -use common::*; -use std::collections::{BTreeSet, HashSet}; +use std::{ + borrow::Cow, + collections::{BTreeSet, HashSet}, +}; #[allow(dead_code)] struct Scenario<'a> { /// Name of the test scenario name: &'a str, /// Transaction templates - tx_templates: &'a [TxTemplate<'a, BlockId>], + tx_templates: &'a [TxTemplate], /// Names of txs that must exist in the output of `list_canonical_txs` exp_chain_txs: HashSet<&'a str>, /// Outpoints that must exist in the output of `filter_chain_txouts` @@ -48,32 +50,32 @@ fn test_tx_conflict_handling() { name: "coinbase tx cannot be in mempool and be unconfirmed", tx_templates: &[ TxTemplate { - tx_name: "unconfirmed_coinbase", - inputs: &[TxInTemplate::Coinbase], - outputs: &[TxOutTemplate::new(5000, Some(0))], + tx_name: Cow::Borrowed("unconfirmed_coinbase"), + inputs: Cow::Borrowed(&[TxInTemplate::Coinbase]), + outputs: Cow::Owned(vec![TxOutTemplate::new(5000, Some(0))]), ..Default::default() }, TxTemplate { - tx_name: "confirmed_genesis", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(1))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("confirmed_genesis"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(1))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "unconfirmed_conflict", - inputs: &[ - TxInTemplate::PrevTx("confirmed_genesis", 0), - TxInTemplate::PrevTx("unconfirmed_coinbase", 0) - ], - outputs: &[TxOutTemplate::new(20000, Some(2))], + tx_name: Cow::Borrowed("unconfirmed_conflict"), + inputs: Cow::Borrowed(&[ + TxInTemplate::PrevTx(Cow::Borrowed("confirmed_genesis"), 0), + TxInTemplate::PrevTx(Cow::Borrowed("unconfirmed_coinbase"), 0) + ]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), ..Default::default() }, TxTemplate { - tx_name: "confirmed_conflict", - inputs: &[TxInTemplate::PrevTx("confirmed_genesis", 0)], - outputs: &[TxOutTemplate::new(20000, Some(3))], - anchors: &[block_id!(4, "E")], + tx_name: Cow::Borrowed("confirmed_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("confirmed_genesis"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(3))]), + anchors: Cow::Owned(vec![block_id!(4, "E")]), ..Default::default() }, ], @@ -89,23 +91,23 @@ fn test_tx_conflict_handling() { name: "2 unconfirmed txs with same last_seens conflict", tx_templates: &[ TxTemplate { - tx_name: "tx1", - outputs: &[TxOutTemplate::new(40000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + outputs: Cow::Owned(vec![TxOutTemplate::new(40000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(20000, Some(2))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), last_seen: Some(300), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_2", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(30000, Some(3))], + tx_name: Cow::Borrowed("tx_conflict_2"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), last_seen: Some(300), ..Default::default() }, @@ -125,23 +127,23 @@ fn test_tx_conflict_handling() { name: "2 unconfirmed txs with different last_seens conflict", tx_templates: &[ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0)), TxOutTemplate::new(10000, Some(1))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0)), TxOutTemplate::new(10000, Some(1))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(2))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_2", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::PrevTx("tx1", 1)], - outputs: &[TxOutTemplate::new(30000, Some(3))], + tx_name: Cow::Borrowed("tx_conflict_2"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 1)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), last_seen: Some(300), ..Default::default() }, @@ -160,30 +162,30 @@ fn test_tx_conflict_handling() { name: "3 unconfirmed txs with different last_seens conflict", tx_templates: &[ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Owned(vec![TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_2", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(30000, Some(2))], + tx_name: Cow::Borrowed("tx_conflict_2"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(2))]), last_seen: Some(300), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_3", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(40000, Some(3))], + tx_name: Cow::Borrowed("tx_conflict_3"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(40000, Some(3))]), last_seen: Some(400), ..Default::default() }, @@ -202,24 +204,24 @@ fn test_tx_conflict_handling() { name: "unconfirmed tx conflicts with tx in orphaned block, orphaned higher last_seen", tx_templates: &[ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "tx_orphaned_conflict", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(30000, Some(2))], - anchors: &[block_id!(4, "Orphaned Block")], + tx_name: Cow::Borrowed("tx_orphaned_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(4, "Orphaned Block")]), last_seen: Some(300), }, ], @@ -237,24 +239,24 @@ fn test_tx_conflict_handling() { name: "unconfirmed tx conflicts with tx in orphaned block, orphaned lower last_seen", tx_templates: &[ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "tx_orphaned_conflict", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(30000, Some(2))], - anchors: &[block_id!(4, "Orphaned Block")], + tx_name: Cow::Borrowed("tx_orphaned_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(4, "Orphaned Block")]), last_seen: Some(100), }, ], @@ -272,38 +274,38 @@ fn test_tx_conflict_handling() { name: "multiple unconfirmed txs conflict with a confirmed tx", tx_templates: &[ TxTemplate { - tx_name: "tx1", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx1"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "tx_conflict_1", - inputs: &[TxInTemplate::PrevTx("tx1", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("tx_conflict_1"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_2", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(30000, Some(2))], + tx_name: Cow::Borrowed("tx_conflict_2"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(2))]), last_seen: Some(300), ..Default::default() }, TxTemplate { - tx_name: "tx_conflict_3", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(40000, Some(3))], + tx_name: Cow::Borrowed("tx_conflict_3"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(40000, Some(3))]), last_seen: Some(400), ..Default::default() }, TxTemplate { - tx_name: "tx_confirmed_conflict", - inputs: &[TxInTemplate::PrevTx("tx1", 0)], - outputs: &[TxOutTemplate::new(50000, Some(4))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("tx_confirmed_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("tx1"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(50000, Some(4))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, ], @@ -321,30 +323,30 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, C spends B, all the transactions are unconfirmed, B' has higher last_seen than B", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), last_seen: Some(22), ..Default::default() }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(23), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(2))], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), last_seen: Some(24), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[TxInTemplate::PrevTx("B", 0)], - outputs: &[TxOutTemplate::new(30000, Some(3))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("B"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), last_seen: Some(25), ..Default::default() }, @@ -366,29 +368,29 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, C spends B, A and B' are in best chain", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(2))], - anchors: &[block_id!(4, "E")], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(4, "E")]), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[TxInTemplate::PrevTx("B", 0)], - outputs: &[TxOutTemplate::new(30000, Some(3))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Owned(vec![TxInTemplate::PrevTx(Cow::Borrowed("B"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), ..Default::default() }, ], @@ -407,30 +409,30 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, C spends B', A and B' are in best chain", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(2), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(20000, Some(2))], - anchors: &[block_id!(4, "E")], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(4, "E")]), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[TxInTemplate::PrevTx("B'", 0)], - outputs: &[TxOutTemplate::new(30000, Some(3))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("B'"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(3))]), last_seen: Some(1), ..Default::default() }, @@ -454,33 +456,33 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, C spends both B and B', A is in best chain", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Owned(vec![TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Owned(vec![TxInTemplate::PrevTx(Cow::Borrowed("A"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(30000, Some(2))], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Owned(vec![TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(30000, Some(2))]), last_seen: Some(300), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[ - TxInTemplate::PrevTx("B", 0), - TxInTemplate::PrevTx("B'", 0), - ], - outputs: &[TxOutTemplate::new(20000, Some(3))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Owned(vec![ + TxInTemplate::PrevTx(Cow::Borrowed("B"), 0), + TxInTemplate::PrevTx(Cow::Borrowed("B'"), 0), + ]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(3))]), ..Default::default() }, ], @@ -499,33 +501,33 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, B' is confirmed, C spends both B and B', A is in best chain", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(50000, Some(4))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(50000, Some(4))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[ - TxInTemplate::PrevTx("B", 0), - TxInTemplate::PrevTx("B'", 0), - ], - outputs: &[TxOutTemplate::new(20000, Some(5))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Borrowed(&[ + TxInTemplate::PrevTx(Cow::Borrowed("B"), 0), + TxInTemplate::PrevTx(Cow::Borrowed("B'"), 0), + ]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(5))]), ..Default::default() }, ], @@ -544,39 +546,39 @@ fn test_tx_conflict_handling() { name: "B and B' spend A and conflict, B' is confirmed, C spends both B and B', D spends C, A is in best chain", tx_templates: &[ TxTemplate { - tx_name: "A", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("A"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), last_seen: None, }, TxTemplate { - tx_name: "B", - inputs: &[TxInTemplate::PrevTx("A", 0), TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(20000, Some(1))], + tx_name: Cow::Borrowed("B"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0), TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(1))]), last_seen: Some(200), ..Default::default() }, TxTemplate { - tx_name: "B'", - inputs: &[TxInTemplate::PrevTx("A", 0)], - outputs: &[TxOutTemplate::new(50000, Some(4))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("B'"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("A"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(50000, Some(4))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, TxTemplate { - tx_name: "C", - inputs: &[ - TxInTemplate::PrevTx("B", 0), - TxInTemplate::PrevTx("B'", 0), - ], - outputs: &[TxOutTemplate::new(20000, Some(5))], + tx_name: Cow::Borrowed("C"), + inputs: Cow::Borrowed(&[ + TxInTemplate::PrevTx(Cow::Borrowed("B"), 0), + TxInTemplate::PrevTx(Cow::Borrowed("B'"), 0), + ]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(5))]), ..Default::default() }, TxTemplate { - tx_name: "D", - inputs: &[TxInTemplate::PrevTx("C", 0)], - outputs: &[TxOutTemplate::new(20000, Some(6))], + tx_name: Cow::Borrowed("D"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("C"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(20000, Some(6))]), ..Default::default() }, ], @@ -595,22 +597,22 @@ fn test_tx_conflict_handling() { name: "transitively confirmed ancestors", tx_templates: &[ TxTemplate { - tx_name: "first", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(1000, Some(0))], + tx_name: Cow::Borrowed("first"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(1000, Some(0))]), ..Default::default() }, TxTemplate { - tx_name: "second", - inputs: &[TxInTemplate::PrevTx("first", 0)], - outputs: &[TxOutTemplate::new(900, Some(0))], + tx_name: Cow::Borrowed("second"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("first"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(900, Some(0))]), ..Default::default() }, TxTemplate { - tx_name: "anchored", - inputs: &[TxInTemplate::PrevTx("second", 0)], - outputs: &[TxOutTemplate::new(800, Some(0))], - anchors: &[block_id!(3, "D")], + tx_name: Cow::Borrowed("anchored"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("second"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(800, Some(0))]), + anchors: Cow::Owned(vec![block_id!(3, "D")]), ..Default::default() }, ], @@ -628,31 +630,31 @@ fn test_tx_conflict_handling() { name: "transitively anchored txs should have priority over last seen", tx_templates: &[ TxTemplate { - tx_name: "root", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10_000, Some(0))], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("root"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10_000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, TxTemplate { - tx_name: "last_seen_conflict", - inputs: &[TxInTemplate::PrevTx("root", 0)], - outputs: &[TxOutTemplate::new(9900, Some(1))], + tx_name: Cow::Borrowed("last_seen_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("root"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(9900, Some(1))]), last_seen: Some(1000), ..Default::default() }, TxTemplate { - tx_name: "transitively_anchored_conflict", - inputs: &[TxInTemplate::PrevTx("root", 0)], - outputs: &[TxOutTemplate::new(9000, Some(1))], + tx_name: Cow::Borrowed("transitively_anchored_conflict"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("root"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(9000, Some(1))]), last_seen: Some(100), ..Default::default() }, TxTemplate { - tx_name: "anchored", - inputs: &[TxInTemplate::PrevTx("transitively_anchored_conflict", 0)], - outputs: &[TxOutTemplate::new(8000, Some(2))], - anchors: &[block_id!(4, "E")], + tx_name: Cow::Borrowed("anchored"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("transitively_anchored_conflict"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(8000, Some(2))]), + anchors: Cow::Owned(vec![block_id!(4, "E")]), ..Default::default() }, ], @@ -668,17 +670,17 @@ fn test_tx_conflict_handling() { name: "tx anchored in orphaned block and not seen in mempool should be canon", tx_templates: &[ TxTemplate { - tx_name: "root", - inputs: &[TxInTemplate::Bogus], - outputs: &[TxOutTemplate::new(10_000, None)], - anchors: &[block_id!(1, "B")], + tx_name: Cow::Borrowed("root"), + inputs: Cow::Borrowed(&[TxInTemplate::Bogus]), + outputs: Cow::Owned(vec![TxOutTemplate::new(10_000, None)]), + anchors: Cow::Owned(vec![block_id!(1, "B")]), ..Default::default() }, TxTemplate { - tx_name: "tx", - inputs: &[TxInTemplate::PrevTx("root", 0)], - outputs: &[TxOutTemplate::new(9000, Some(0))], - anchors: &[block_id!(6, "not G")], + tx_name: Cow::Borrowed("tx"), + inputs: Cow::Borrowed(&[TxInTemplate::PrevTx(Cow::Borrowed("root"), 0)]), + outputs: Cow::Owned(vec![TxOutTemplate::new(9000, Some(0))]), + anchors: Cow::Owned(vec![block_id!(6, "not G")]), ..Default::default() }, ], @@ -690,7 +692,7 @@ fn test_tx_conflict_handling() { ]; for scenario in scenarios { - let (tx_graph, spk_index, exp_tx_ids) = init_graph(scenario.tx_templates.iter()); + let (tx_graph, spk_index, exp_tx_ids) = init_graph(scenario.tx_templates.iter().cloned()); let txs = tx_graph .list_canonical_txs(&local_chain, chain_tip) @@ -699,7 +701,7 @@ fn test_tx_conflict_handling() { let exp_txs = scenario .exp_chain_txs .iter() - .map(|txid| *exp_tx_ids.get(txid).expect("txid must exist")) + .map(|txid| *exp_tx_ids.get(*txid).expect("txid must exist")) .collect::>(); assert_eq!( txs, exp_txs, @@ -719,7 +721,7 @@ fn test_tx_conflict_handling() { .exp_chain_txouts .iter() .map(|(txid, vout)| OutPoint { - txid: *exp_tx_ids.get(txid).expect("txid must exist"), + txid: *exp_tx_ids.get(*txid).expect("txid must exist"), vout: *vout, }) .collect::>(); @@ -741,7 +743,7 @@ fn test_tx_conflict_handling() { .exp_unspents .iter() .map(|(txid, vout)| OutPoint { - txid: *exp_tx_ids.get(txid).expect("txid must exist"), + txid: *exp_tx_ids.get(*txid).expect("txid must exist"), vout: *vout, }) .collect::>(); diff --git a/crates/testenv/Cargo.toml b/crates/testenv/Cargo.toml index 48f4d242c..a4447f499 100644 --- a/crates/testenv/Cargo.toml +++ b/crates/testenv/Cargo.toml @@ -18,15 +18,14 @@ workspace = true [dependencies] bdk_chain = { path = "../chain", version = "0.21.1", default-features = false } electrsd = { version = "0.28.0", features = [ "legacy" ], default-features = false } - -[dev-dependencies] -bdk_testenv = { path = "." } +rand = {version = "0.8", optional = true} [features] default = ["std", "download"] download = ["electrsd/bitcoind_25_0", "electrsd/esplora_a33e97e1"] std = ["bdk_chain/std"] serde = ["bdk_chain/serde"] +miniscript = ["bdk_chain/miniscript", "rand"] [package.metadata.docs.rs] -no-default-features = true \ No newline at end of file +no-default-features = true diff --git a/crates/testenv/src/lib.rs b/crates/testenv/src/lib.rs index 2c0f15f64..9b932d072 100644 --- a/crates/testenv/src/lib.rs +++ b/crates/testenv/src/lib.rs @@ -1,3 +1,4 @@ +pub mod tx_template; pub mod utils; use bdk_chain::{ diff --git a/crates/chain/tests/common/tx_template.rs b/crates/testenv/src/tx_template.rs similarity index 82% rename from crates/chain/tests/common/tx_template.rs rename to crates/testenv/src/tx_template.rs index 0b0e2fd9e..beb9ae047 100644 --- a/crates/chain/tests/common/tx_template.rs +++ b/crates/testenv/src/tx_template.rs @@ -1,33 +1,41 @@ #![cfg(feature = "miniscript")] -use bdk_testenv::utils::DESCRIPTORS; +use crate::utils::DESCRIPTORS; +use bdk_chain::{ + bitcoin::{ + self, locktime::absolute::LockTime, secp256k1::Secp256k1, transaction, Amount, OutPoint, + ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness, + }, + miniscript::Descriptor, + spk_txout::SpkTxOutIndex, + tx_graph::TxGraph, + Anchor, +}; use rand::distributions::{Alphanumeric, DistString}; +use std::borrow::Cow; use std::collections::HashMap; -use bdk_chain::{spk_txout::SpkTxOutIndex, tx_graph::TxGraph, Anchor}; -use bitcoin::{ - locktime::absolute::LockTime, secp256k1::Secp256k1, transaction, Amount, OutPoint, ScriptBuf, - Sequence, Transaction, TxIn, TxOut, Txid, Witness, -}; -use miniscript::Descriptor; - /// Template for creating a transaction in `TxGraph`. /// /// The incentive for transaction templates is to create a transaction history in a simple manner to /// avoid having to explicitly hash previous transactions to form previous outpoints of later /// transactions. -#[derive(Clone, Copy, Default)] -pub struct TxTemplate<'a, A> { +#[derive(Clone, Default)] +pub struct TxTemplate +where + A: Clone + 'static, +{ /// Uniquely identifies the transaction, before it can have a txid. - pub tx_name: &'a str, - pub inputs: &'a [TxInTemplate<'a>], - pub outputs: &'a [TxOutTemplate], - pub anchors: &'a [A], + pub tx_name: Cow<'static, str>, + pub inputs: Cow<'static, [TxInTemplate]>, + pub outputs: Cow<'static, [TxOutTemplate]>, + pub anchors: Cow<'static, [A]>, pub last_seen: Option, } #[allow(dead_code)] -pub enum TxInTemplate<'a> { +#[derive(Clone)] +pub enum TxInTemplate { /// This will give a random txid and vout. Bogus, @@ -36,9 +44,10 @@ pub enum TxInTemplate<'a> { /// Contains the `tx_name` and `vout` that we are spending. The rule is that we must only spend /// from tx of a previous `TxTemplate`. - PrevTx(&'a str, usize), + PrevTx(Cow<'static, str>, usize), } +#[derive(Clone)] pub struct TxOutTemplate { pub value: u64, pub spk_index: Option, // some = get spk from SpkTxOutIndex, none = random spk @@ -52,9 +61,13 @@ impl TxOutTemplate { } #[allow(dead_code)] -pub fn init_graph<'a, A: Anchor + Clone + 'a>( - tx_templates: impl IntoIterator>, -) -> (TxGraph, SpkTxOutIndex, HashMap<&'a str, Txid>) { +pub fn init_graph( + tx_templates: impl IntoIterator>, +) -> ( + TxGraph, + SpkTxOutIndex, + HashMap, Txid>, +) { let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTORS[2]).unwrap(); let mut graph = TxGraph::::default(); @@ -68,7 +81,7 @@ pub fn init_graph<'a, A: Anchor + Clone + 'a>( .script_pubkey(), ); }); - let mut tx_ids = HashMap::<&'a str, Txid>::new(); + let mut tx_ids = HashMap::, Txid>::new(); for (bogus_txin_vout, tx_tmp) in tx_templates.into_iter().enumerate() { let tx = Transaction {