1use bitcoin::hash_types::*;
8use std::collections::BTreeMap;
9use std::sync::Arc;
10use std::sync::Mutex;
11
12#[derive(Debug)]
14pub enum TxIndexError {
15 NetworkError(std::io::Error),
17 UnknownTxid(Txid),
19 IndexTooHigh(u32),
21 RpcError(Box<dyn std::error::Error>),
23}
24impl std::error::Error for TxIndexError {}
25
26impl std::fmt::Display for TxIndexError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "{:?}", self)
29 }
30}
31type Result<T> = std::result::Result<T, TxIndexError>;
32
33pub trait TxIndex {
35 fn lookup_tx(&self, b: &Txid) -> Result<Arc<bitcoin::Transaction>>;
37 fn lookup_output(&self, b: &bitcoin::OutPoint) -> Result<bitcoin::TxOut> {
39 self.lookup_tx(&b.txid)?
40 .output
41 .get(b.vout as usize)
42 .cloned()
43 .ok_or(TxIndexError::IndexTooHigh(b.vout))
44 }
45 fn add_tx(&self, tx: Arc<bitcoin::Transaction>) -> Result<Txid>;
47}
48
49pub struct TxIndexLogger {
51 map: Mutex<BTreeMap<Txid, Arc<bitcoin::Transaction>>>,
52}
53impl TxIndexLogger {
54 pub fn new() -> TxIndexLogger {
56 Self::default()
57 }
58}
59
60impl Default for TxIndexLogger {
61 fn default() -> Self {
62 TxIndexLogger {
63 map: Mutex::new(BTreeMap::new()),
64 }
65 }
66}
67impl TxIndex for TxIndexLogger {
68 fn lookup_tx(&self, b: &Txid) -> Result<Arc<bitcoin::Transaction>> {
69 self.map
70 .lock()
71 .unwrap()
72 .get(b)
73 .cloned()
74 .ok_or(TxIndexError::UnknownTxid(*b))
75 }
76 fn add_tx(&self, tx: Arc<bitcoin::Transaction>) -> Result<Txid> {
77 let txid = tx.txid();
78 self.map.lock().unwrap().insert(txid, tx);
79 Ok(txid)
80 }
81}
82
83pub struct CachedTxIndex<Cache: TxIndex, Primary: TxIndex> {
85 pub cache: Cache,
87 pub primary: Primary,
89}
90
91impl<Cache, Primary> TxIndex for CachedTxIndex<Cache, Primary>
92where
93 Cache: TxIndex,
94 Primary: TxIndex,
95{
96 fn lookup_tx(&self, b: &Txid) -> Result<Arc<bitcoin::Transaction>> {
97 if let Ok(ent) = self.cache.lookup_tx(b) {
98 Ok(ent)
99 } else {
100 let ent = self.primary.lookup_tx(b)?;
101 self.cache.add_tx(ent.clone())?;
102 Ok(ent)
103 }
104 }
105 fn add_tx(&self, tx: Arc<bitcoin::Transaction>) -> Result<Txid> {
106 let txid = tx.txid();
107 if self.cache.lookup_tx(&txid).is_ok() {
108 Ok(txid)
109 } else {
110 self.primary.add_tx(tx.clone())?;
111 self.cache.add_tx(tx)
112 }
113 }
114}