query.rs 6.38 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::collections::HashMap;
4
use std::sync::{Arc, RwLock};
5

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

Roman Zeyde's avatar
Roman Zeyde committed
13

14 15 16 17
pub struct FundingOutput {
    pub txn_id: Sha256dHash,
    pub height: u32,
    pub output_index: usize,
Roman Zeyde's avatar
Roman Zeyde committed
18 19
}

20 21
type OutPoint = (Sha256dHash, usize); // (txid, output_index)

22 23
struct SpendingInput {
    txn_id: Sha256dHash,
24
    height: u32,
25
    funding_output: OutPoint,
Roman Zeyde's avatar
Roman Zeyde committed
26 27 28
}

pub struct Status {
29 30 31 32
    confirmed: (Vec<FundingOutput>, Vec<SpendingInput>),
    mempool: (Vec<FundingOutput>, Vec<SpendingInput>),
}

33
impl Status {
34 35 36 37 38 39 40 41
    fn funding(&self) -> impl Iterator<Item = &FundingOutput> {
        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())
    }

42 43
    pub fn history(&self) -> Vec<(i32, Sha256dHash)> {
        let mut txns_map = HashMap::<Sha256dHash, i32>::new();
44
        for f in self.funding() {
45
            txns_map.insert(f.txn_id, f.height as i32);
46
        }
47
        for s in self.spending() {
48
            txns_map.insert(s.txn_id, s.height as i32);
49 50 51
        }
        let mut txns: Vec<(i32, Sha256dHash)> =
            txns_map.into_iter().map(|item| (item.1, item.0)).collect();
52
        txns.sort_unstable();
53 54 55 56
        txns
    }
}

Roman Zeyde's avatar
Roman Zeyde committed
57
struct TxnHeight {
kenshin-samourai's avatar
kenshin-samourai committed
58
    txn_id: Sha256dHash,
59
    height: u32,
Roman Zeyde's avatar
Roman Zeyde committed
60 61
}

kenshin-samourai's avatar
kenshin-samourai committed
62 63 64 65
fn txrows_by_prefix(
    store: &dyn ReadStore,
    txid_prefix: HashPrefix
) -> Vec<TxRow> {
66
    store
Roman Zeyde's avatar
Roman Zeyde committed
67
        .scan(&TxRow::filter_prefix(txid_prefix))
68 69 70 71
        .iter()
        .map(|row| TxRow::from_row(row))
        .collect()
}
72

kenshin-samourai's avatar
kenshin-samourai committed
73 74 75 76
fn txoutrows_by_script_hash(
    store: &dyn ReadStore,
    script_hash: &[u8]
) -> Vec<TxOutRow> {
77 78 79
    store
        .scan(&TxOutRow::filter(script_hash))
        .iter()
kenshin-samourai's avatar
kenshin-samourai committed
80
        .map(|row| TxOutRow::from_row(row))
81 82
        .collect()
}
83

84
fn txids_by_funding_output(
85
    store: &dyn ReadStore,
86 87 88 89 90 91 92 93
    txn_id: &Sha256dHash,
    output_index: usize,
) -> Vec<HashPrefix> {
    store
        .scan(&TxInRow::filter(&txn_id, output_index))
        .iter()
        .map(|row| TxInRow::from_row(row).txid_prefix)
        .collect()
94 95
}

96 97
pub struct Query {
    app: Arc<App>,
98
    tracker: RwLock<Tracker>,
99
    txid_limit: usize,
100 101
}

102
impl Query {
103 104 105 106
    pub fn new(
        app: Arc<App>,
        txid_limit: usize,
    ) -> Arc<Query> {
107
        Arc::new(Query {
108
            app,
109
            tracker: RwLock::new(Tracker::new()),
110
            txid_limit,
111
        })
112 113
    }

114 115
    fn load_txns_by_prefix(
        &self,
116
        store: &dyn ReadStore,
117 118
        prefixes: Vec<HashPrefix>,
    ) -> Result<Vec<TxnHeight>> {
Roman Zeyde's avatar
Roman Zeyde committed
119
        let mut txns = vec![];
120
        for txid_prefix in prefixes {
Roman Zeyde's avatar
Roman Zeyde committed
121
            for tx_row in txrows_by_prefix(store, txid_prefix) {
122
                let txid: Sha256dHash = deserialize(&tx_row.key.txid).unwrap();
123
                txns.push(TxnHeight {
kenshin-samourai's avatar
kenshin-samourai committed
124
                    txn_id: txid,
125
                    height: tx_row.height,
126
                })
127 128
            }
        }
129
        Ok(txns)
130 131
    }

132 133
    fn find_spending_input(
        &self,
134
        store: &dyn ReadStore,
135
        funding: &FundingOutput,
136
    ) -> Result<Option<SpendingInput>> {
137

Roman Zeyde's avatar
Roman Zeyde committed
138
        let mut spending_inputs = vec![];
kenshin-samourai's avatar
kenshin-samourai committed
139 140
        let prefixes = txids_by_funding_output(store, &funding.txn_id, funding.output_index);
        let spending_txns = self.load_txns_by_prefix(store, prefixes)?;
141

Roman Zeyde's avatar
Roman Zeyde committed
142
        for t in &spending_txns {
kenshin-samourai's avatar
kenshin-samourai committed
143 144 145 146 147 148
            if t.txn_id == funding.txn_id {
                spending_inputs.push(SpendingInput {
                    txn_id: t.txn_id,
                    height: t.height,
                    funding_output: (funding.txn_id, funding.output_index),
                })
Roman Zeyde's avatar
Roman Zeyde committed
149 150
            }
        }
151

Roman Zeyde's avatar
Roman Zeyde committed
152
        assert!(spending_inputs.len() <= 1);
kenshin-samourai's avatar
kenshin-samourai committed
153

154
        Ok(if spending_inputs.len() == 1 {
Roman Zeyde's avatar
Roman Zeyde committed
155
            Some(spending_inputs.remove(0))
156 157
        } else {
            None
158
        })
159 160
    }

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

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

        for row in &txout_rows {
            let funding_txns = self.load_txns_by_prefix(store, vec![row.txid_prefix])?;
            for t in &funding_txns {
173
                result.push(FundingOutput {
kenshin-samourai's avatar
kenshin-samourai committed
174
                    txn_id: t.txn_id,
175
                    height: t.height,
kenshin-samourai's avatar
kenshin-samourai committed
176
                    output_index: row.vout as usize,
177 178 179
                })
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
180 181

        Ok(result)
182 183
    }

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

        let funding_outputs = self.find_funding_outputs(read_store, script_hash)?;
        funding.extend(funding_outputs);

195
        for funding_output in &funding {
196
            if let Some(spent) = self.find_spending_input(read_store, &funding_output)? {
197
                spending.push(spent);
198 199
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
200

201
        Ok((funding, spending))
202 203
    }

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

        let funding_outputs = self.find_funding_outputs(tracker.index(), script_hash)?;
        funding.extend(funding_outputs);

216
        for funding_output in funding.iter().chain(confirmed_funding.iter()) {
217
            if let Some(spent) = self.find_spending_input(tracker.index(), &funding_output)? {
218
                spending.push(spent);
Roman Zeyde's avatar
Roman Zeyde committed
219 220
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
221

222
        Ok((funding, spending))
223
    }
224

225
    pub fn status(&self, script_hash: &[u8]) -> Result<Status> {
Roman Zeyde's avatar
Roman Zeyde committed
226 227
        let confirmed = self
            .confirmed_status(script_hash)
228
            .chain_err(|| "failed to get confirmed status")?;
229

Roman Zeyde's avatar
Roman Zeyde committed
230 231
        let mempool = self
            .mempool_status(script_hash, &confirmed.0)
232
            .chain_err(|| "failed to get mempool status")?;
233

234
        Ok(Status { confirmed, mempool })
235 236
    }

Roman Zeyde's avatar
Roman Zeyde committed
237
    pub fn update_mempool(&self) -> Result<()> {
Roman Zeyde's avatar
Roman Zeyde committed
238
        self.tracker.write().unwrap().update(self.app.daemon())
239
    }
240
}