Skip to content

Commit 6cbb18d

Browse files
committed
[bdk_chain_redesign] MOVE: IndexedTxGraph into submodule
1 parent 784cd34 commit 6cbb18d

File tree

3 files changed

+250
-242
lines changed

3 files changed

+250
-242
lines changed
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
use core::convert::Infallible;
2+
3+
use alloc::collections::BTreeSet;
4+
use bitcoin::{OutPoint, Transaction, TxOut};
5+
6+
use crate::{
7+
sparse_chain::ChainPosition,
8+
tx_graph::{Additions, TxGraph, TxInGraph},
9+
BlockAnchor, ChainOracle, FullTxOut, ObservedIn, TxIndex, TxIndexAdditions,
10+
};
11+
12+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
13+
pub struct TxInChain<'a, T, A> {
14+
pub observed_in: ObservedIn<&'a A>,
15+
pub tx: TxInGraph<'a, T, A>,
16+
}
17+
18+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
19+
pub struct TxOutInChain<'a, I, A> {
20+
pub spk_index: &'a I,
21+
pub txout: FullTxOut<ObservedIn<&'a A>>,
22+
}
23+
24+
pub struct IndexedAdditions<A, D> {
25+
pub graph_additions: Additions<A>,
26+
pub index_delta: D,
27+
}
28+
29+
impl<A, D: Default> Default for IndexedAdditions<A, D> {
30+
fn default() -> Self {
31+
Self {
32+
graph_additions: Default::default(),
33+
index_delta: Default::default(),
34+
}
35+
}
36+
}
37+
38+
impl<A: BlockAnchor, D: TxIndexAdditions> TxIndexAdditions for IndexedAdditions<A, D> {
39+
fn append_additions(&mut self, other: Self) {
40+
let Self {
41+
graph_additions,
42+
index_delta,
43+
} = other;
44+
self.graph_additions.append(graph_additions);
45+
self.index_delta.append_additions(index_delta);
46+
}
47+
}
48+
49+
pub struct IndexedTxGraph<A, I> {
50+
graph: TxGraph<A>,
51+
index: I,
52+
}
53+
54+
impl<A, I: Default> Default for IndexedTxGraph<A, I> {
55+
fn default() -> Self {
56+
Self {
57+
graph: Default::default(),
58+
index: Default::default(),
59+
}
60+
}
61+
}
62+
63+
impl<A: BlockAnchor, I: TxIndex> IndexedTxGraph<A, I> {
64+
/// Get a reference of the internal transaction graph.
65+
pub fn graph(&self) -> &TxGraph<A> {
66+
&self.graph
67+
}
68+
69+
/// Get a reference of the internal transaction index.
70+
pub fn index(&self) -> &I {
71+
&self.index
72+
}
73+
74+
/// Insert a `txout` that exists in `outpoint` with the given `observation`.
75+
pub fn insert_txout(
76+
&mut self,
77+
outpoint: OutPoint,
78+
txout: &TxOut,
79+
observation: ObservedIn<A>,
80+
) -> IndexedAdditions<A, I::Additions> {
81+
IndexedAdditions {
82+
graph_additions: {
83+
let mut graph_additions = self.graph.insert_txout(outpoint, txout.clone());
84+
graph_additions.append(match observation {
85+
ObservedIn::Block(anchor) => self.graph.insert_anchor(outpoint.txid, anchor),
86+
ObservedIn::Mempool(seen_at) => {
87+
self.graph.insert_seen_at(outpoint.txid, seen_at)
88+
}
89+
});
90+
graph_additions
91+
},
92+
index_delta: <I as TxIndex>::index_txout(&mut self.index, outpoint, txout),
93+
}
94+
}
95+
96+
pub fn insert_tx(
97+
&mut self,
98+
tx: &Transaction,
99+
observation: ObservedIn<A>,
100+
) -> IndexedAdditions<A, I::Additions> {
101+
let txid = tx.txid();
102+
IndexedAdditions {
103+
graph_additions: {
104+
let mut graph_additions = self.graph.insert_tx(tx.clone());
105+
graph_additions.append(match observation {
106+
ObservedIn::Block(anchor) => self.graph.insert_anchor(txid, anchor),
107+
ObservedIn::Mempool(seen_at) => self.graph.insert_seen_at(txid, seen_at),
108+
});
109+
graph_additions
110+
},
111+
index_delta: <I as TxIndex>::index_tx(&mut self.index, tx),
112+
}
113+
}
114+
115+
pub fn filter_and_insert_txs<'t, T>(
116+
&mut self,
117+
txs: T,
118+
observation: ObservedIn<A>,
119+
) -> IndexedAdditions<A, I::Additions>
120+
where
121+
T: Iterator<Item = &'t Transaction>,
122+
{
123+
txs.filter_map(|tx| {
124+
if self.index.is_tx_relevant(tx) {
125+
Some(self.insert_tx(tx, observation.clone()))
126+
} else {
127+
None
128+
}
129+
})
130+
.fold(IndexedAdditions::default(), |mut acc, other| {
131+
acc.append_additions(other);
132+
acc
133+
})
134+
}
135+
136+
pub fn relevant_heights(&self) -> BTreeSet<u32> {
137+
self.graph.relevant_heights()
138+
}
139+
140+
pub fn try_list_chain_txs<'a, C>(
141+
&'a self,
142+
chain: C,
143+
) -> impl Iterator<Item = Result<TxInChain<'a, Transaction, A>, C::Error>>
144+
where
145+
C: ChainOracle + 'a,
146+
{
147+
self.graph
148+
.full_transactions()
149+
.filter(|tx| self.index.is_tx_relevant(tx))
150+
.filter_map(move |tx| {
151+
self.graph
152+
.try_get_chain_position(&chain, tx.txid)
153+
.map(|v| v.map(|observed_in| TxInChain { observed_in, tx }))
154+
.transpose()
155+
})
156+
}
157+
158+
pub fn list_chain_txs<'a, C>(
159+
&'a self,
160+
chain: C,
161+
) -> impl Iterator<Item = TxInChain<'a, Transaction, A>>
162+
where
163+
C: ChainOracle<Error = Infallible> + 'a,
164+
{
165+
self.try_list_chain_txs(chain)
166+
.map(|r| r.expect("error is infallible"))
167+
}
168+
169+
pub fn try_list_chain_txouts<'a, C>(
170+
&'a self,
171+
chain: C,
172+
) -> impl Iterator<Item = Result<TxOutInChain<'a, I::SpkIndex, A>, C::Error>>
173+
where
174+
C: ChainOracle + 'a,
175+
ObservedIn<A>: ChainPosition,
176+
{
177+
self.index.relevant_txouts().iter().filter_map(
178+
move |(op, (spk_i, txout))| -> Option<Result<_, C::Error>> {
179+
let graph_tx = self.graph.get_tx(op.txid)?;
180+
181+
let is_on_coinbase = graph_tx.is_coin_base();
182+
183+
let chain_position = match self.graph.try_get_chain_position(&chain, op.txid) {
184+
Ok(Some(observed_at)) => observed_at,
185+
Ok(None) => return None,
186+
Err(err) => return Some(Err(err)),
187+
};
188+
189+
let spent_by = match self.graph.try_get_spend_in_chain(&chain, *op) {
190+
Ok(spent_by) => spent_by,
191+
Err(err) => return Some(Err(err)),
192+
};
193+
194+
let full_txout = FullTxOut {
195+
outpoint: *op,
196+
txout: txout.clone(),
197+
chain_position,
198+
spent_by,
199+
is_on_coinbase,
200+
};
201+
202+
let txout_in_chain = TxOutInChain {
203+
spk_index: spk_i,
204+
txout: full_txout,
205+
};
206+
207+
Some(Ok(txout_in_chain))
208+
},
209+
)
210+
}
211+
212+
pub fn list_chain_txouts<'a, C>(
213+
&'a self,
214+
chain: C,
215+
) -> impl Iterator<Item = TxOutInChain<'a, I::SpkIndex, A>>
216+
where
217+
C: ChainOracle<Error = Infallible> + 'a,
218+
ObservedIn<A>: ChainPosition,
219+
{
220+
self.try_list_chain_txouts(chain)
221+
.map(|r| r.expect("error in infallible"))
222+
}
223+
224+
/// Return relevant unspents.
225+
pub fn try_list_chain_utxos<'a, C>(
226+
&'a self,
227+
chain: C,
228+
) -> impl Iterator<Item = Result<TxOutInChain<'a, I::SpkIndex, A>, C::Error>>
229+
where
230+
C: ChainOracle + 'a,
231+
ObservedIn<A>: ChainPosition,
232+
{
233+
self.try_list_chain_txouts(chain)
234+
.filter(|r| !matches!(r, Ok(txo) if txo.txout.spent_by.is_none()))
235+
}
236+
237+
pub fn list_chain_utxos<'a, C>(
238+
&'a self,
239+
chain: C,
240+
) -> impl Iterator<Item = TxOutInChain<'a, I::SpkIndex, A>>
241+
where
242+
C: ChainOracle<Error = Infallible> + 'a,
243+
ObservedIn<A>: ChainPosition,
244+
{
245+
self.try_list_chain_utxos(chain)
246+
.map(|r| r.expect("error is infallible"))
247+
}
248+
}

crates/chain/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod spk_txout_index;
2424
pub use spk_txout_index::*;
2525
mod chain_data;
2626
pub use chain_data::*;
27+
pub mod indexed_tx_graph;
2728
pub mod keychain;
2829
pub mod sparse_chain;
2930
mod tx_data_traits;

0 commit comments

Comments
 (0)