query.rs 10.6 KB
Newer Older
Roman Zeyde's avatar
Roman Zeyde committed
1
use bitcoin::blockdata::block::{Block, BlockHeader};
2
use bitcoin::blockdata::transaction::Transaction;
Roman Zeyde's avatar
Roman Zeyde committed
3
use bitcoin::network::serialize::deserialize;
4
use bitcoin::util::hash::Sha256dHash;
5 6 7
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use std::collections::HashMap;
8
use std::sync::{Arc, RwLock};
9

10 11
use app::App;
use index::{compute_script_hash, TxInRow, TxOutRow, TxRow};
12
use mempool::Tracker;
13
use store::ReadStore;
14
use util::{FullHash, HashPrefix, HeaderEntry};
15

Roman Zeyde's avatar
Roman Zeyde committed
16
use errors::*;
Roman Zeyde's avatar
Roman Zeyde committed
17

18 19
struct FundingOutput {
    txn_id: Sha256dHash,
20
    height: u32,
21 22
    output_index: usize,
    value: u64,
Roman Zeyde's avatar
Roman Zeyde committed
23 24
}

25 26
struct SpendingInput {
    txn_id: Sha256dHash,
27
    height: u32,
28
    value: u64,
Roman Zeyde's avatar
Roman Zeyde committed
29 30 31
}

pub struct Status {
32 33 34 35 36 37 38
    confirmed: (Vec<FundingOutput>, Vec<SpendingInput>),
    mempool: (Vec<FundingOutput>, Vec<SpendingInput>),
}

fn calc_balance((funding, spending): &(Vec<FundingOutput>, Vec<SpendingInput>)) -> u64 {
    let total = funding.iter().fold(0, |acc, output| acc + output.value);
    spending.iter().fold(total, |acc, input| acc - input.value)
Roman Zeyde's avatar
Roman Zeyde committed
39 40
}

41
impl Status {
42 43 44 45 46 47 48 49 50 51 52 53 54 55
    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())
    }

    pub fn confirmed_balance(&self) -> u64 {
        calc_balance(&self.confirmed)
    }

    pub fn mempool_balance(&self) -> u64 {
        calc_balance(&self.mempool)
56 57
    }

58 59
    pub fn history(&self) -> Vec<(i32, Sha256dHash)> {
        let mut txns_map = HashMap::<Sha256dHash, i32>::new();
60
        for f in self.funding() {
61
            txns_map.insert(f.txn_id, f.height as i32);
62
        }
63
        for s in self.spending() {
64
            txns_map.insert(s.txn_id, s.height as i32);
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
        }
        let mut txns: Vec<(i32, Sha256dHash)> =
            txns_map.into_iter().map(|item| (item.1, item.0)).collect();
        txns.sort();
        txns
    }

    pub fn hash(&self) -> Option<FullHash> {
        let txns = self.history();
        if txns.is_empty() {
            None
        } else {
            let mut hash = FullHash::default();
            let mut sha2 = Sha256::new();
            for (height, txn_id) in txns {
                let part = format!("{}:{}:", txn_id.be_hex_string(), height);
                sha2.input(part.as_bytes());
            }
            sha2.result(&mut hash);
            Some(hash)
        }
    }
}

Roman Zeyde's avatar
Roman Zeyde committed
89 90
struct TxnHeight {
    txn: Transaction,
91
    height: u32,
Roman Zeyde's avatar
Roman Zeyde committed
92 93
}

Roman Zeyde's avatar
Roman Zeyde committed
94 95 96 97 98
fn merklize(left: Sha256dHash, right: Sha256dHash) -> Sha256dHash {
    let data = [&left[..], &right[..]].concat();
    Sha256dHash::from_data(&data)
}

99 100
// TODO: the 3 functions below can be part of ReadStore.
fn txrows_by_prefix(store: &ReadStore, txid_prefix: &HashPrefix) -> Vec<TxRow> {
101 102 103 104 105 106
    store
        .scan(&TxRow::filter(&txid_prefix))
        .iter()
        .map(|row| TxRow::from_row(row))
        .collect()
}
107

108
fn txids_by_script_hash(store: &ReadStore, script_hash: &[u8]) -> Vec<HashPrefix> {
109 110 111 112 113 114
    store
        .scan(&TxOutRow::filter(script_hash))
        .iter()
        .map(|row| TxOutRow::from_row(row).txid_prefix)
        .collect()
}
115

116
fn txids_by_funding_output(
117
    store: &ReadStore,
118 119 120 121 122 123 124 125
    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()
126 127
}

128 129
pub struct Query {
    app: Arc<App>,
130 131 132
    tracker: RwLock<Tracker>,
}

133 134
impl Query {
    pub fn new(app: Arc<App>) -> Query {
135
        Query {
136
            app,
137
            tracker: RwLock::new(Tracker::new()),
138
        }
139 140
    }

141
    fn load_txns(&self, store: &ReadStore, prefixes: Vec<HashPrefix>) -> Result<Vec<TxnHeight>> {
142 143
        let mut txns = Vec::new();
        for txid_prefix in prefixes {
144
            for tx_row in txrows_by_prefix(store, &txid_prefix) {
145
                let txid: Sha256dHash = deserialize(&tx_row.key.txid).unwrap();
146
                let txn: Transaction = self.get_tx(&txid)?;
147 148
                txns.push(TxnHeight {
                    txn,
149
                    height: tx_row.height,
150
                })
151 152
            }
        }
153
        Ok(txns)
154 155
    }

156 157 158 159
    fn find_spending_input(
        &self,
        store: &ReadStore,
        funding: &FundingOutput,
160
    ) -> Result<Option<SpendingInput>> {
161
        let spending_txns: Vec<TxnHeight> = self.load_txns(
162
            store,
163
            txids_by_funding_output(store, &funding.txn_id, funding.output_index),
164
        )?;
Roman Zeyde's avatar
Roman Zeyde committed
165 166
        let mut spending_inputs = Vec::new();
        for t in &spending_txns {
167
            for input in t.txn.input.iter() {
Roman Zeyde's avatar
Roman Zeyde committed
168 169 170 171 172 173
                if input.prev_hash == funding.txn_id
                    && input.prev_index == funding.output_index as u32
                {
                    spending_inputs.push(SpendingInput {
                        txn_id: t.txn.txid(),
                        height: t.height,
174
                        value: funding.value,
Roman Zeyde's avatar
Roman Zeyde committed
175 176 177 178 179
                    })
                }
            }
        }
        assert!(spending_inputs.len() <= 1);
180
        Ok(if spending_inputs.len() == 1 {
Roman Zeyde's avatar
Roman Zeyde committed
181
            Some(spending_inputs.remove(0))
182 183
        } else {
            None
184
        })
185 186
    }

187 188 189
    fn find_funding_outputs(&self, t: &TxnHeight, script_hash: &[u8]) -> Vec<FundingOutput> {
        let mut result = Vec::new();
        let txn_id = t.txn.txid();
Roman Zeyde's avatar
Roman Zeyde committed
190
        for (index, output) in t.txn.output.iter().enumerate() {
191 192 193 194 195 196 197 198 199 200 201 202
            if compute_script_hash(&output.script_pubkey[..]) == script_hash {
                result.push(FundingOutput {
                    txn_id: txn_id,
                    height: t.height,
                    output_index: index,
                    value: output.value,
                })
            }
        }
        result
    }

203 204 205 206
    fn confirmed_status(
        &self,
        script_hash: &[u8],
    ) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
207 208 209
        let mut funding = vec![];
        let mut spending = vec![];
        for t in self.load_txns(
210 211
            self.app.store(),
            txids_by_script_hash(self.app.store(), script_hash),
212
        )? {
213
            funding.extend(self.find_funding_outputs(&t, script_hash));
214
        }
215
        for funding_output in &funding {
216
            if let Some(spent) = self.find_spending_input(self.app.store(), &funding_output)? {
217
                spending.push(spent);
218 219
            }
        }
220
        Ok((funding, spending))
221 222
    }

223 224 225 226
    fn mempool_status(
        &self,
        script_hash: &[u8],
        confirmed_funding: &[FundingOutput],
227
    ) -> Result<(Vec<FundingOutput>, Vec<SpendingInput>)> {
228 229
        let mut funding = vec![];
        let mut spending = vec![];
230
        let tracker = self.tracker.read().unwrap();
231
        for t in self.load_txns(
232 233
            tracker.index(),
            txids_by_script_hash(tracker.index(), script_hash),
234
        )? {
235
            funding.extend(self.find_funding_outputs(&t, script_hash));
236
        }
237
        // // TODO: dedup outputs (somehow) both confirmed and in mempool (e.g. reorg?)
238
        for funding_output in funding.iter().chain(confirmed_funding.iter()) {
239
            if let Some(spent) = self.find_spending_input(tracker.index(), &funding_output)? {
240
                spending.push(spent);
Roman Zeyde's avatar
Roman Zeyde committed
241 242
            }
        }
243
        Ok((funding, spending))
244
    }
245

246 247 248 249
    pub fn status(&self, script_hash: &[u8]) -> Result<Status> {
        let confirmed = self.confirmed_status(script_hash)?;
        let mempool = self.mempool_status(script_hash, &confirmed.0)?;
        Ok(Status { confirmed, mempool })
250 251
    }

252 253
    pub fn get_tx(&self, tx_hash: &Sha256dHash) -> Result<Transaction> {
        self.app.daemon().gettransaction(tx_hash)
254
    }
255

Roman Zeyde's avatar
Roman Zeyde committed
256
    pub fn get_headers(&self, heights: &[usize]) -> Vec<BlockHeader> {
257
        let headers_list = self.app.index().headers_list();
258 259
        let headers = headers_list.headers();
        let mut result = Vec::new();
Roman Zeyde's avatar
Roman Zeyde committed
260 261 262 263 264
        for height in heights {
            let header: &BlockHeader = match headers.get(*height) {
                Some(header) => header.header(),
                None => break,
            };
Roman Zeyde's avatar
Roman Zeyde committed
265
            result.push(*header);
266 267 268
        }
        result
    }
Roman Zeyde's avatar
Roman Zeyde committed
269

270
    pub fn get_best_header(&self) -> Result<HeaderEntry> {
271
        let header_list = self.app.index().headers_list();
272 273
        let last_header = header_list.headers().last();
        Ok(last_header.chain_err(|| "no headers indexed")?.clone())
Roman Zeyde's avatar
Roman Zeyde committed
274
    }
Roman Zeyde's avatar
Roman Zeyde committed
275 276 277 278 279

    pub fn get_merkle_proof(
        &self,
        tx_hash: &Sha256dHash,
        height: usize,
280
    ) -> Result<(Vec<Sha256dHash>, usize)> {
281
        let header_list = self.app.index().headers_list();
282 283 284 285 286
        let blockhash = header_list
            .headers()
            .get(height)
            .chain_err(|| format!("missing block #{}", height))?
            .hash();
287
        let block: Block = self.app.daemon().getblock(&blockhash).unwrap();
Roman Zeyde's avatar
Roman Zeyde committed
288
        let mut txids: Vec<Sha256dHash> = block.txdata.iter().map(|tx| tx.txid()).collect();
289 290 291 292
        let pos = txids
            .iter()
            .position(|txid| txid == tx_hash)
            .chain_err(|| format!("missing txid {}", tx_hash))?;
Roman Zeyde's avatar
Roman Zeyde committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
        let mut merkle = Vec::new();
        let mut index = pos;
        while txids.len() > 1 {
            if txids.len() % 2 != 0 {
                let last = txids.last().unwrap().clone();
                txids.push(last);
            }
            index = if index % 2 == 0 { index + 1 } else { index - 1 };
            merkle.push(txids[index]);
            index = index / 2;
            txids = txids
                .chunks(2)
                .map(|pair| merklize(pair[0], pair[1]))
                .collect()
        }
308
        Ok((merkle, pos))
Roman Zeyde's avatar
Roman Zeyde committed
309
    }
310

311
    pub fn broadcast(&self, txn: &Transaction) -> Result<Sha256dHash> {
312
        self.app.daemon().broadcast(txn)
313 314
    }

Roman Zeyde's avatar
Roman Zeyde committed
315
    pub fn update_mempool(&self) -> Result<()> {
Roman Zeyde's avatar
Roman Zeyde committed
316
        self.tracker.write().unwrap().update(self.app.daemon())
317
    }
318

319
    /// Returns [vsize, fee_rate] pairs (measured in vbytes and satoshis).
320
    pub fn get_fee_histogram(&self) -> Vec<(f32, u32)> {
321 322 323
        self.tracker.read().unwrap().fee_histogram().clone()
    }

324 325 326 327 328 329 330 331 332 333 334 335 336
    // Fee rate [BTC/kB] to be confirmed in `blocks` from now.
    pub fn estimate_fee(&self, blocks: usize) -> f32 {
        let mut total_vsize = 0u32;
        let mut last_fee_rate = 0.0;
        let blocks_in_vbytes = (blocks * 1_000_000) as u32; // assume ~1MB blocks
        for (fee_rate, vsize) in self.tracker.read().unwrap().fee_histogram() {
            last_fee_rate = *fee_rate;
            total_vsize += vsize;
            if total_vsize >= blocks_in_vbytes {
                break; // under-estimate the fee rate a bit
            }
        }
        last_fee_rate * 1e-5 // [BTC/kB] = 10^5 [sat/B]
337
    }
338
}