query.rs 9.59 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::RwLock;
9

Roman Zeyde's avatar
Roman Zeyde committed
10
use daemon::Daemon;
11
use index::{compute_script_hash, Index, TxInRow, TxOutRow, TxRow};
12
use mempool::Tracker;
13
use store::Store;
14
use util::{FullHash, HashPrefix, HeaderEntry};
15

Roman Zeyde's avatar
Roman Zeyde committed
16 17
error_chain!{}

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
    funding: Vec<FundingOutput>,
    spending: Vec<SpendingInput>,
Roman Zeyde's avatar
Roman Zeyde committed
34 35
}

36
impl Status {
37
    pub fn balance(&self) -> u64 {
38 39 40 41 42 43
        let total = self.funding
            .iter()
            .fold(0, |acc, output| acc + output.value);
        self.spending
            .iter()
            .fold(total, |acc, input| acc - input.value)
44 45
    }

46 47 48
    pub fn history(&self) -> Vec<(i32, Sha256dHash)> {
        let mut txns_map = HashMap::<Sha256dHash, i32>::new();
        for f in &self.funding {
49
            txns_map.insert(f.txn_id, f.height as i32);
50 51
        }
        for s in &self.spending {
52
            txns_map.insert(s.txn_id, s.height as i32);
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
        }
        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
77 78
struct TxnHeight {
    txn: Transaction,
79
    height: u32,
Roman Zeyde's avatar
Roman Zeyde committed
80 81
}

Roman Zeyde's avatar
Roman Zeyde committed
82 83 84 85 86
fn merklize(left: Sha256dHash, right: Sha256dHash) -> Sha256dHash {
    let data = [&left[..], &right[..]].concat();
    Sha256dHash::from_data(&data)
}

Roman Zeyde's avatar
Roman Zeyde committed
87
// TODO: the 3 functions below can be part of Store.
88 89 90 91 92 93 94
fn txrows_by_prefix(store: &Store, txid_prefix: &HashPrefix) -> Vec<TxRow> {
    store
        .scan(&TxRow::filter(&txid_prefix))
        .iter()
        .map(|row| TxRow::from_row(row))
        .collect()
}
95

96 97 98 99 100 101 102
fn txids_by_script_hash(store: &Store, script_hash: &[u8]) -> Vec<HashPrefix> {
    store
        .scan(&TxOutRow::filter(script_hash))
        .iter()
        .map(|row| TxOutRow::from_row(row).txid_prefix)
        .collect()
}
103

104 105 106 107 108 109 110 111 112 113
fn txids_by_funding_output(
    store: &Store,
    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()
114 115
}

116 117 118
pub struct Query<'a> {
    daemon: &'a Daemon,
    index: &'a Index,
119
    index_store: &'a Store, // TODO: should be a part of index
120 121 122
    tracker: RwLock<Tracker>,
}

123
// TODO: return errors instead of panics
124
impl<'a> Query<'a> {
125
    pub fn new(index_store: &'a Store, daemon: &'a Daemon, index: &'a Index) -> Query<'a> {
126 127 128
        Query {
            daemon,
            index,
129
            index_store,
130
            tracker: RwLock::new(Tracker::new()),
131
        }
132 133
    }

134 135 136 137
    pub fn daemon(&self) -> &Daemon {
        self.daemon
    }

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

153
    fn find_spending_input(&self, store: &Store, funding: &FundingOutput) -> Option<SpendingInput> {
154
        let spending_txns: Vec<TxnHeight> = self.load_txns(
155
            store,
156 157
            txids_by_funding_output(store, &funding.txn_id, funding.output_index),
        );
Roman Zeyde's avatar
Roman Zeyde committed
158 159
        let mut spending_inputs = Vec::new();
        for t in &spending_txns {
160
            for input in t.txn.input.iter() {
Roman Zeyde's avatar
Roman Zeyde committed
161 162 163 164 165 166
                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,
167
                        value: funding.value,
Roman Zeyde's avatar
Roman Zeyde committed
168 169 170 171 172 173 174
                    })
                }
            }
        }
        assert!(spending_inputs.len() <= 1);
        if spending_inputs.len() == 1 {
            Some(spending_inputs.remove(0))
175 176 177 178 179
        } else {
            None
        }
    }

180 181 182
    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
183
        for (index, output) in t.txn.output.iter().enumerate() {
184 185 186 187 188 189 190 191 192 193 194 195
            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
    }

196
    fn confirmed_status(&self, script_hash: &[u8]) -> Status {
197 198 199 200 201 202 203
        let mut funding = vec![];
        let mut spending = vec![];
        for t in self.load_txns(
            self.index_store,
            txids_by_script_hash(self.index_store, script_hash),
        ) {
            funding.extend(self.find_funding_outputs(&t, script_hash));
204
        }
205 206 207
        for funding_output in &funding {
            if let Some(spent) = self.find_spending_input(self.index_store, &funding_output) {
                spending.push(spent);
208 209
            }
        }
210
        Status { funding, spending }
211 212 213
    }

    fn mempool_status(&self, script_hash: &[u8], confirmed_status: &Status) -> Status {
214 215
        let mut funding = vec![];
        let mut spending = vec![];
Roman Zeyde's avatar
Roman Zeyde committed
216
        // TODO: build index once per Tracker::update()
217
        let mempool_store = self.tracker.read().unwrap().build_index();
218 219 220 221 222
        for t in self.load_txns(
            &*mempool_store,
            txids_by_script_hash(&*mempool_store, script_hash),
        ) {
            funding.extend(self.find_funding_outputs(&t, script_hash));
223
        }
224 225
        // // TODO: dedup outputs (somehow) both confirmed and in mempool (e.g. reorg?)
        for funding_output in funding.iter().chain(confirmed_status.funding.iter()) {
226
            if let Some(spent) = self.find_spending_input(&*mempool_store, &funding_output) {
227
                spending.push(spent);
Roman Zeyde's avatar
Roman Zeyde committed
228 229
            }
        }
Roman Zeyde's avatar
Roman Zeyde committed
230 231
        // TODO: update height to -1 for txns with any unconfirmed input
        // (https://electrumx.readthedocs.io/en/latest/protocol-basics.html#status)
232
        Status { funding, spending }
233
    }
234

235 236 237 238 239 240 241 242
    pub fn status(&self, script_hash: &[u8]) -> Status {
        let mut status = self.confirmed_status(script_hash);
        let mempool_status = self.mempool_status(script_hash, &status);
        status.funding.extend(mempool_status.funding);
        status.spending.extend(mempool_status.spending);
        status
    }

243
    pub fn get_tx(&self, tx_hash: &Sha256dHash) -> Transaction {
244
        self.daemon
245 246
            .gettransaction(tx_hash)
            .expect(&format!("failed to load tx {}", tx_hash))
247
    }
248

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

    pub fn get_best_header(&self) -> Option<HeaderEntry> {
        let header_list = self.index.headers_list();
        Some(header_list.headers().last()?.clone())
    }
Roman Zeyde's avatar
Roman Zeyde committed
267 268 269 270 271 272 273 274 275

    // TODO: add error-handling logic
    pub fn get_merkle_proof(
        &self,
        tx_hash: &Sha256dHash,
        height: usize,
    ) -> Option<(Vec<Sha256dHash>, usize)> {
        let header_list = self.index.headers_list();
        let blockhash = header_list.headers().get(height)?.hash();
276
        let block: Block = self.daemon.getblock(&blockhash).unwrap();
Roman Zeyde's avatar
Roman Zeyde committed
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
        let mut txids: Vec<Sha256dHash> = block.txdata.iter().map(|tx| tx.txid()).collect();
        let pos = txids.iter().position(|txid| txid == tx_hash)?;
        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()
        }
        Some((merkle, pos))
    }
296

Roman Zeyde's avatar
Roman Zeyde committed
297
    pub fn update_mempool(&self) -> Result<()> {
298 299 300 301
        self.tracker
            .write()
            .unwrap()
            .update(self.daemon)
Roman Zeyde's avatar
Roman Zeyde committed
302
            .chain_err(|| "failed to update mempool")
303
    }
304 305 306 307

    pub fn get_fee_histogram(&self) -> Vec<(f32, u32)> {
        self.tracker.read().unwrap().fee_histogram()
    }
308
}