sapio_base/
txindex.rs

1// Copyright Judica, Inc 2021
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4//  License, v. 2.0. If a copy of the MPL was not distributed with this
5//  file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use bitcoin::hash_types::*;
8use std::collections::BTreeMap;
9use std::sync::Arc;
10use std::sync::Mutex;
11
12/// Errors in resolving a TXIndex
13#[derive(Debug)]
14pub enum TxIndexError {
15    /// Network Error
16    NetworkError(std::io::Error),
17    /// TXID Could not be resolved
18    UnknownTxid(Txid),
19    /// TXID exists, but the vout index was too high
20    IndexTooHigh(u32),
21    /// Error in the Rpc System
22    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
33/// Generic interface for any txindex
34pub trait TxIndex {
35    /// lookup a tx
36    fn lookup_tx(&self, b: &Txid) -> Result<Arc<bitcoin::Transaction>>;
37    /// lookup a particular output
38    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    /// locally add a tx for tracking
46    fn add_tx(&self, tx: Arc<bitcoin::Transaction>) -> Result<Txid>;
47}
48
49/// a TxIndex which just tracks what it's seen and has no network
50pub struct TxIndexLogger {
51    map: Mutex<BTreeMap<Txid, Arc<bitcoin::Transaction>>>,
52}
53impl TxIndexLogger {
54    /// create a new default instance
55    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
83/// a cached txindex checks a cache first and then a primary txindex
84pub struct CachedTxIndex<Cache: TxIndex, Primary: TxIndex> {
85    /// the cache txindex
86    pub cache: Cache,
87    /// the main txindex
88    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}