query.rs 5.86 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;

Roman Zeyde's avatar
Roman Zeyde committed
12

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

18 19
type OutPoint = (Sha256dHash, usize); // (txid, output_index)

20 21
struct SpendingInput {
    txn_id: Sha256dHash,
22
    funding_output: OutPoint,
Roman Zeyde's avatar
Roman Zeyde committed
23 24 25
}

pub struct Status {
26 27 28 29
    confirmed: (Vec<FundingOutput>, Vec<SpendingInput>),
    mempool: (Vec<FundingOutput>, Vec<SpendingInput>),
}

30
impl Status {
31 32 33 34 35 36 37 38
    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())
    }

kenshin-samourai's avatar
kenshin-samourai committed
39 40
    pub fn history(&self) -> Vec<Sha256dHash> {
        let mut txns = vec![];
41
        for f in self.funding() {
kenshin-samourai's avatar
kenshin-samourai committed
42
            txns.push(f.txn_id);
43
        }
44
        for s in self.spending() {
kenshin-samourai's avatar
kenshin-samourai committed
45
            txns.push(s.txn_id);
46
        }
47
        txns.sort_unstable();
48 49 50 51
        txns
    }
}

kenshin-samourai's avatar
kenshin-samourai committed
52 53 54 55
fn txrows_by_prefix(
    store: &dyn ReadStore,
    txid_prefix: HashPrefix
) -> Vec<TxRow> {
56
    store
Roman Zeyde's avatar
Roman Zeyde committed
57
        .scan(&TxRow::filter_prefix(txid_prefix))
58 59 60 61
        .iter()
        .map(|row| TxRow::from_row(row))
        .collect()
}
62

kenshin-samourai's avatar
kenshin-samourai committed
63 64 65 66
fn txoutrows_by_script_hash(
    store: &dyn ReadStore,
    script_hash: &[u8]
) -> Vec<TxOutRow> {
67 68 69
    store
        .scan(&TxOutRow::filter(script_hash))
        .iter()
kenshin-samourai's avatar
kenshin-samourai committed
70
        .map(|row| TxOutRow::from_row(row))
71 72
        .collect()
}
73

74
fn txids_by_funding_output(
75
    store: &dyn ReadStore,
76 77 78 79 80 81 82 83
    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()
84 85
}

86 87
pub struct Query {
    app: Arc<App>,
88
    tracker: RwLock<Tracker>,
89
    txid_limit: usize,
90 91
}

92
impl Query {
93 94 95 96
    pub fn new(
        app: Arc<App>,
        txid_limit: usize,
    ) -> Arc<Query> {
97
        Arc::new(Query {
98
            app,
99
            tracker: RwLock::new(Tracker::new()),
100
            txid_limit,
101
        })
102 103
    }

104 105
    fn load_txns_by_prefix(
        &self,
106
        store: &dyn ReadStore,
107
        prefixes: Vec<HashPrefix>,
kenshin-samourai's avatar
kenshin-samourai committed
108
    ) -> Result<Vec<Sha256dHash>> {
Roman Zeyde's avatar
Roman Zeyde committed
109
        let mut txns = vec![];
110
        for txid_prefix in prefixes {
Roman Zeyde's avatar
Roman Zeyde committed
111
            for tx_row in txrows_by_prefix(store, txid_prefix) {
112
                let txid: Sha256dHash = deserialize(&tx_row.key.txid).unwrap();
kenshin-samourai's avatar
kenshin-samourai committed
113
                txns.push(txid)
114 115
            }
        }
116
        Ok(txns)
117 118
    }

119 120
    fn find_spending_input(
        &self,
121
        store: &dyn ReadStore,
122
        funding: &FundingOutput,
123
    ) -> Result<Option<SpendingInput>> {
124

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

Roman Zeyde's avatar
Roman Zeyde committed
129
        for t in &spending_txns {
kenshin-samourai's avatar
kenshin-samourai committed
130
            if *t == funding.txn_id {
kenshin-samourai's avatar
kenshin-samourai committed
131
                spending_inputs.push(SpendingInput {
kenshin-samourai's avatar
kenshin-samourai committed
132
                    txn_id: *t,
kenshin-samourai's avatar
kenshin-samourai committed
133 134
                    funding_output: (funding.txn_id, funding.output_index),
                })
Roman Zeyde's avatar
Roman Zeyde committed
135 136
            }
        }
137

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

140
        Ok(if spending_inputs.len() == 1 {
Roman Zeyde's avatar
Roman Zeyde committed
141
            Some(spending_inputs.remove(0))
142 143
        } else {
            None
144
        })
145 146
    }

kenshin-samourai's avatar
kenshin-samourai committed
147 148 149 150 151 152 153
    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
154
        let mut result = vec![];
kenshin-samourai's avatar
kenshin-samourai committed
155 156 157 158

        for row in &txout_rows {
            let funding_txns = self.load_txns_by_prefix(store, vec![row.txid_prefix])?;
            for t in &funding_txns {
159
                result.push(FundingOutput {
kenshin-samourai's avatar
kenshin-samourai committed
160
                    txn_id: *t,
kenshin-samourai's avatar
kenshin-samourai committed
161
                    output_index: row.vout as usize,
162 163 164
                })
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
165 166

        Ok(result)
167 168
    }

169 170 171 172
    fn confirmed_status(
        &self,
        script_hash: &[u8],
    ) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
173 174
        let mut funding = vec![];
        let mut spending = vec![];
175
        let read_store = self.app.read_store();
kenshin-samourai's avatar
kenshin-samourai committed
176 177 178 179

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

180
        for funding_output in &funding {
181
            if let Some(spent) = self.find_spending_input(read_store, &funding_output)? {
182
                spending.push(spent);
183 184
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
185

186
        Ok((funding, spending))
187 188
    }

189 190 191 192
    fn mempool_status(
        &self,
        script_hash: &[u8],
        confirmed_funding: &[FundingOutput],
193
    ) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
194 195
        let mut funding = vec![];
        let mut spending = vec![];
196
        let tracker = self.tracker.read().unwrap();
kenshin-samourai's avatar
kenshin-samourai committed
197 198 199 200

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

201
        for funding_output in funding.iter().chain(confirmed_funding.iter()) {
202
            if let Some(spent) = self.find_spending_input(tracker.index(), &funding_output)? {
203
                spending.push(spent);
Roman Zeyde's avatar
Roman Zeyde committed
204 205
            }
        }
kenshin-samourai's avatar
kenshin-samourai committed
206

207
        Ok((funding, spending))
208
    }
209

210
    pub fn status(&self, script_hash: &[u8]) -> Result<Status> {
Roman Zeyde's avatar
Roman Zeyde committed
211 212
        let confirmed = self
            .confirmed_status(script_hash)
213
            .chain_err(|| "failed to get confirmed status")?;
214

Roman Zeyde's avatar
Roman Zeyde committed
215 216
        let mempool = self
            .mempool_status(script_hash, &confirmed.0)
217
            .chain_err(|| "failed to get mempool status")?;
218

219
        Ok(Status { confirmed, mempool })
220 221
    }

Roman Zeyde's avatar
Roman Zeyde committed
222
    pub fn update_mempool(&self) -> Result<()> {
Roman Zeyde's avatar
Roman Zeyde committed
223
        self.tracker.write().unwrap().update(self.app.daemon())
224
    }
225
}