query.rs 6.23 KB
Newer Older
1
use bitcoin::consensus::encode::deserialize;
Roman Zeyde's avatar
Roman Zeyde committed
2
use bitcoin_hashes::sha256d::Hash as Sha256dHash;
3
use std::sync::{Arc, RwLock};
4

Roman Zeyde's avatar
Roman Zeyde committed
5 6
use crate::app::App;
use crate::errors::*;
kenshin-samourai's avatar
kenshin-samourai committed
7
use crate::index::{TxInRow, TxOutRow, TxRow};
Roman Zeyde's avatar
Roman Zeyde committed
8
use crate::mempool::Tracker;
kenshin-samourai's avatar
kenshin-samourai committed
9 10 11
use crate::store::ReadStore;
use crate::util::HashPrefix;

kenshin-samourai's avatar
kenshin-samourai committed
12 13 14 15 16 17
//
// Output of a Transaction
//
pub struct Txo {
    pub txid: Sha256dHash,
    pub vout: usize,
Roman Zeyde's avatar
Roman Zeyde committed
18 19
}

kenshin-samourai's avatar
kenshin-samourai committed
20 21 22 23
//
// Input of a Transaction
//
type OutPoint = (Sha256dHash, usize); // (txid, vout)
24

25
struct SpendingInput {
kenshin-samourai's avatar
kenshin-samourai committed
26 27
    txid: Sha256dHash,
    outpoint: OutPoint,
Roman Zeyde's avatar
Roman Zeyde committed
28 29
}

kenshin-samourai's avatar
kenshin-samourai committed
30 31 32 33
//
// Status of an Address
// (vectors of confirmed and unconfirmed outputs and inputs)
//
Roman Zeyde's avatar
Roman Zeyde committed
34
pub struct Status {
kenshin-samourai's avatar
kenshin-samourai committed
35 36
    confirmed: (Vec<Txo>, Vec<SpendingInput>),
    mempool: (Vec<Txo>, Vec<SpendingInput>),
37 38
}

39
impl Status {
kenshin-samourai's avatar
kenshin-samourai committed
40
    fn funding(&self) -> impl Iterator<Item = &Txo> {
41 42 43 44 45 46 47
        self.confirmed.0.iter().chain(self.mempool.0.iter())
    }

    fn spending(&self) -> impl Iterator<Item = &SpendingInput> {
        self.confirmed.1.iter().chain(self.mempool.1.iter())
    }

kenshin-samourai's avatar
kenshin-samourai committed
48 49
    pub fn history(&self) -> Vec<Sha256dHash> {
        let mut txns = vec![];
50
        for f in self.funding() {
kenshin-samourai's avatar
kenshin-samourai committed
51
            txns.push(f.txid);
52
        }
53
        for s in self.spending() {
kenshin-samourai's avatar
kenshin-samourai committed
54
            txns.push(s.txid);
55
        }
56
        txns.sort_unstable();
57 58 59 60
        txns
    }
}

kenshin-samourai's avatar
kenshin-samourai committed
61 62 63
//
// QUery tool for the indexer
//
64 65
pub struct Query {
    app: Arc<App>,
66
    tracker: RwLock<Tracker>,
67
    txid_limit: usize,
68 69
}

70
impl Query {
71 72 73 74
    pub fn new(
        app: Arc<App>,
        txid_limit: usize,
    ) -> Arc<Query> {
75
        Arc::new(Query {
76
            app,
77
            tracker: RwLock::new(Tracker::new()),
78
            txid_limit,
79
        })
80 81
    }

kenshin-samourai's avatar
kenshin-samourai committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
    fn get_txrows_by_prefix(
        &self,
        store: &dyn ReadStore,
        prefix: HashPrefix
    ) -> Vec<TxRow> {
        store
            .scan(&TxRow::filter_prefix(prefix))
            .iter()
            .map(|row| TxRow::from_row(row))
            .collect()
    }

    fn get_txoutrows_by_script_hash(
        &self,
        store: &dyn ReadStore,
        script_hash: &[u8]
    ) -> Vec<TxOutRow> {
        store
            .scan(&TxOutRow::filter(script_hash))
            .iter()
            .map(|row| TxOutRow::from_row(row))
            .collect()
    }

    fn get_prefixes_by_funding_txo(
        &self,
        store: &dyn ReadStore,
        txid: &Sha256dHash,
        vout: usize,
    ) -> Vec<HashPrefix> {
        store
            .scan(&TxInRow::filter(&txid, vout))
            .iter()
            .map(|row| TxInRow::from_row(row).txid_prefix)
            .collect()
    }

    fn get_txids_by_prefix(
120
        &self,
121
        store: &dyn ReadStore,
122
        prefixes: Vec<HashPrefix>,
kenshin-samourai's avatar
kenshin-samourai committed
123
    ) -> Result<Vec<Sha256dHash>> {
Roman Zeyde's avatar
Roman Zeyde committed
124
        let mut txns = vec![];
kenshin-samourai's avatar
kenshin-samourai committed
125 126
        for prefix in prefixes {
            for tx_row in self.get_txrows_by_prefix(store, prefix) {
127
                let txid: Sha256dHash = deserialize(&tx_row.key.txid).unwrap();
kenshin-samourai's avatar
kenshin-samourai committed
128
                txns.push(txid)
129 130
            }
        }
131
        Ok(txns)
132 133
    }

134 135
    fn find_spending_input(
        &self,
136
        store: &dyn ReadStore,
kenshin-samourai's avatar
kenshin-samourai committed
137
        txo: &Txo,
138
    ) -> Result<Option<SpendingInput>> {
139

kenshin-samourai's avatar
kenshin-samourai committed
140 141 142
        let mut spendings = vec![];
        let prefixes = self.get_prefixes_by_funding_txo(store, &txo.txid, txo.vout);
        let txids = self.get_txids_by_prefix(store, prefixes)?;
143

kenshin-samourai's avatar
kenshin-samourai committed
144 145 146 147 148
        for txid in &txids {
            spendings.push(SpendingInput {
                txid: *txid,
                outpoint: (txo.txid, txo.vout),
            })
Roman Zeyde's avatar
Roman Zeyde committed
149
        }
150

kenshin-samourai's avatar
kenshin-samourai committed
151
        assert!(spendings.len() <= 1);
kenshin-samourai's avatar
kenshin-samourai committed
152

kenshin-samourai's avatar
kenshin-samourai committed
153 154
        Ok(if spendings.len() == 1 {
            Some(spendings.remove(0))
155 156
        } else {
            None
157
        })
158 159
    }

kenshin-samourai's avatar
kenshin-samourai committed
160 161 162 163
    fn find_funding_outputs(
        &self,
        store: &dyn ReadStore,
        script_hash: &[u8]
kenshin-samourai's avatar
kenshin-samourai committed
164 165
    ) -> Result<Vec<Txo>> {
        let txout_rows = self.get_txoutrows_by_script_hash(store, script_hash);
kenshin-samourai's avatar
kenshin-samourai committed
166

Roman Zeyde's avatar
Roman Zeyde committed
167
        let mut result = vec![];
kenshin-samourai's avatar
kenshin-samourai committed
168 169

        for row in &txout_rows {
kenshin-samourai's avatar
kenshin-samourai committed
170 171 172 173 174
            let txids = self.get_txids_by_prefix(store, vec![row.txid_prefix])?;
            for txid in &txids {
                result.push(Txo {
                    txid: *txid,
                    vout: row.vout as usize,
175 176 177
                })
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
178 179

        Ok(result)
180 181
    }

182 183 184
    fn confirmed_status(
        &self,
        script_hash: &[u8],
kenshin-samourai's avatar
kenshin-samourai committed
185
    ) -> Result<(Vec<Txo>, Vec<SpendingInput>)> {
186 187
        let mut funding = vec![];
        let mut spending = vec![];
188
        let read_store = self.app.read_store();
kenshin-samourai's avatar
kenshin-samourai committed
189

kenshin-samourai's avatar
kenshin-samourai committed
190 191 192 193 194 195 196 197
        let txos = self.find_funding_outputs(read_store, script_hash)?;
        if self.txid_limit > 0 && txos.len() > self.txid_limit {
            bail!(
                "{}+ transactions found, query may take a long time",
                txos.len()
            );
        }
        funding.extend(txos);
kenshin-samourai's avatar
kenshin-samourai committed
198

kenshin-samourai's avatar
kenshin-samourai committed
199 200
        for txo in &funding {
            if let Some(spent) = self.find_spending_input(read_store, &txo)? {
201
                spending.push(spent);
202 203
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
204

205
        Ok((funding, spending))
206 207
    }

208 209 210
    fn mempool_status(
        &self,
        script_hash: &[u8],
kenshin-samourai's avatar
kenshin-samourai committed
211 212
        confirmed_funding: &[Txo],
    ) -> Result<(Vec<Txo>, Vec<SpendingInput>)> {
213 214
        let mut funding = vec![];
        let mut spending = vec![];
kenshin-samourai's avatar
kenshin-samourai committed
215

216
        let tracker = self.tracker.read().unwrap();
kenshin-samourai's avatar
kenshin-samourai committed
217

kenshin-samourai's avatar
kenshin-samourai committed
218 219 220 221 222 223 224 225
        let txos = self.find_funding_outputs(tracker.index(), script_hash)?;
        if self.txid_limit > 0 && txos.len() > self.txid_limit {
            bail!(
                "{}+ transactions found, query may take a long time",
                txos.len()
            );
        }
        funding.extend(txos);
kenshin-samourai's avatar
kenshin-samourai committed
226

kenshin-samourai's avatar
kenshin-samourai committed
227 228
        for txo in funding.iter().chain(confirmed_funding.iter()) {
            if let Some(spent) = self.find_spending_input(tracker.index(), &txo)? {
229
                spending.push(spent);
Roman Zeyde's avatar
Roman Zeyde committed
230 231
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
232

233
        Ok((funding, spending))
234
    }
235

236
    pub fn status(&self, script_hash: &[u8]) -> Result<Status> {
Roman Zeyde's avatar
Roman Zeyde committed
237 238
        let confirmed = self
            .confirmed_status(script_hash)
239
            .chain_err(|| "failed to get confirmed status")?;
240

Roman Zeyde's avatar
Roman Zeyde committed
241 242
        let mempool = self
            .mempool_status(script_hash, &confirmed.0)
243
            .chain_err(|| "failed to get mempool status")?;
244

245
        Ok(Status { confirmed, mempool })
246 247
    }

Roman Zeyde's avatar
Roman Zeyde committed
248
    pub fn update_mempool(&self) -> Result<()> {
Roman Zeyde's avatar
Roman Zeyde committed
249
        self.tracker.write().unwrap().update(self.app.daemon())
250
    }
251
}