Skip to content

Commit 59a2403

Browse files
test(chain): Introduce TxTemplate
Co-authored-by: Wei Chen <[email protected]>
1 parent 6e51147 commit 59a2403

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

crates/chain/tests/common/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
mod tx_template;
2+
pub use tx_template::*;
3+
14
#[allow(unused_macros)]
25
macro_rules! block_id {
36
($height:expr, $hash:literal) => {{
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use rand::distributions::{Alphanumeric, DistString};
2+
use std::collections::HashMap;
3+
4+
use bdk_chain::{tx_graph::TxGraph, BlockId, SpkTxOutIndex};
5+
use bitcoin::{
6+
locktime::absolute::LockTime, secp256k1::Secp256k1, OutPoint, ScriptBuf, Sequence, Transaction,
7+
TxIn, TxOut, Txid, Witness,
8+
};
9+
use miniscript::Descriptor;
10+
11+
/// Template for creating a transaction in `TxGraph`.
12+
///
13+
/// The incentive for transaction templates is to create a transaction history in a simple manner to
14+
/// avoid having to explicitly hash previous transactions to form previous outpoints of later
15+
/// transactions.
16+
#[derive(Clone, Copy, Default)]
17+
pub struct TxTemplate<'a, A> {
18+
/// Uniquely identifies the transaction, before it can have a txid.
19+
pub tx_name: &'a str,
20+
pub inputs: &'a [TxInTemplate<'a>],
21+
pub outputs: &'a [TxOutTemplate],
22+
pub anchors: &'a [A],
23+
pub last_seen: Option<u64>,
24+
}
25+
26+
#[allow(dead_code)]
27+
pub enum TxInTemplate<'a> {
28+
/// This will give a random txid and vout.
29+
Bogus,
30+
31+
/// This is used for coinbase transactions because they do not have previous outputs.
32+
Coinbase,
33+
34+
/// Contains the `tx_name` and `vout` that we are spending. The rule is that we must only spend
35+
/// from tx of a previous `TxTemplate`.
36+
PrevTx(&'a str, usize),
37+
}
38+
39+
pub struct TxOutTemplate {
40+
pub value: u64,
41+
pub spk_index: Option<u32>, // some = get spk from SpkTxOutIndex, none = random spk
42+
}
43+
44+
#[allow(unused)]
45+
impl TxOutTemplate {
46+
pub fn new(value: u64, spk_index: Option<u32>) -> Self {
47+
TxOutTemplate { value, spk_index }
48+
}
49+
}
50+
51+
#[allow(dead_code)]
52+
pub fn init_graph<'a>(
53+
tx_templates: impl IntoIterator<Item = &'a TxTemplate<'a, BlockId>>,
54+
) -> (TxGraph<BlockId>, SpkTxOutIndex<u32>, HashMap<&'a str, Txid>) {
55+
let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/0/*)").unwrap();
56+
let mut graph = TxGraph::<BlockId>::default();
57+
let mut spk_index = SpkTxOutIndex::default();
58+
(0..10).for_each(|index| {
59+
spk_index.insert_spk(
60+
index,
61+
descriptor
62+
.at_derivation_index(index)
63+
.unwrap()
64+
.script_pubkey(),
65+
);
66+
});
67+
let mut tx_ids = HashMap::<&'a str, Txid>::new();
68+
69+
for (bogus_txin_vout, tx_tmp) in tx_templates.into_iter().enumerate() {
70+
let tx = Transaction {
71+
version: 0,
72+
lock_time: LockTime::ZERO,
73+
input: tx_tmp
74+
.inputs
75+
.iter()
76+
.map(|input| match input {
77+
TxInTemplate::Bogus => TxIn {
78+
previous_output: OutPoint::new(
79+
bitcoin::hashes::Hash::hash(
80+
Alphanumeric
81+
.sample_string(&mut rand::thread_rng(), 20)
82+
.as_bytes(),
83+
),
84+
bogus_txin_vout as u32,
85+
),
86+
script_sig: ScriptBuf::new(),
87+
sequence: Sequence::default(),
88+
witness: Witness::new(),
89+
},
90+
TxInTemplate::Coinbase => TxIn {
91+
previous_output: OutPoint::null(),
92+
script_sig: ScriptBuf::new(),
93+
sequence: Sequence::MAX,
94+
witness: Witness::new(),
95+
},
96+
TxInTemplate::PrevTx(prev_name, prev_vout) => {
97+
let prev_txid = tx_ids.get(prev_name).expect(
98+
"txin template must spend from tx of template that comes before",
99+
);
100+
TxIn {
101+
previous_output: OutPoint::new(*prev_txid, *prev_vout as _),
102+
script_sig: ScriptBuf::new(),
103+
sequence: Sequence::default(),
104+
witness: Witness::new(),
105+
}
106+
}
107+
})
108+
.collect(),
109+
output: tx_tmp
110+
.outputs
111+
.iter()
112+
.map(|output| match &output.spk_index {
113+
None => TxOut {
114+
value: output.value,
115+
script_pubkey: ScriptBuf::new(),
116+
},
117+
Some(index) => TxOut {
118+
value: output.value,
119+
script_pubkey: spk_index.spk_at_index(index).unwrap().to_owned(),
120+
},
121+
})
122+
.collect(),
123+
};
124+
125+
tx_ids.insert(tx_tmp.tx_name, tx.txid());
126+
spk_index.scan(&tx);
127+
let _ = graph.insert_tx(tx.clone());
128+
for anchor in tx_tmp.anchors.iter() {
129+
let _ = graph.insert_anchor(tx.txid(), *anchor);
130+
}
131+
if let Some(seen_at) = tx_tmp.last_seen {
132+
let _ = graph.insert_seen_at(tx.txid(), seen_at);
133+
}
134+
}
135+
(graph, spk_index, tx_ids)
136+
}

0 commit comments

Comments
 (0)