1
0
mirror of https://github.com/romanz/electrs.git synced 2024-11-19 18:10:51 +01:00

Move query-related code into a separate module

This commit is contained in:
Roman Zeyde 2018-04-30 14:56:00 +03:00
parent 68feda0baa
commit 95da35ac80
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB
5 changed files with 122 additions and 112 deletions

View File

@ -6,7 +6,7 @@ extern crate indexrs;
use argparse::{ArgumentParser, StoreFalse}; use argparse::{ArgumentParser, StoreFalse};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use indexrs::{daemon, index, rpc, store, waiter}; use indexrs::{daemon, index, query, rpc, store, waiter};
fn setup_logging() { fn setup_logging() {
use simplelog::*; use simplelog::*;
@ -51,7 +51,7 @@ fn run_server(config: Config) {
} }
let store = store::Store::open(DB_PATH, store::StoreOptions { auto_compact: true }); let store = store::Store::open(DB_PATH, store::StoreOptions { auto_compact: true });
let query = index::Query::new(&store, &daemon); let query = query::Query::new(&store, &daemon);
crossbeam::scope(|scope| { crossbeam::scope(|scope| {
scope.spawn(|| rpc::serve("localhost:50001", &query)); scope.spawn(|| rpc::serve("localhost:50001", &query));

View File

@ -1,12 +1,11 @@
use bincode; use bincode;
use bitcoin::blockdata::block::{Block, BlockHeader}; use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::transaction::{Transaction, TxIn, TxOut}; use bitcoin::blockdata::transaction::{TxIn, TxOut};
use bitcoin::network::serialize::BitcoinHash; use bitcoin::network::serialize::BitcoinHash;
use bitcoin::network::serialize::{deserialize, serialize}; use bitcoin::network::serialize::{deserialize, serialize};
use bitcoin::util::hash::Sha256dHash; use bitcoin::util::hash::Sha256dHash;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use itertools::enumerate;
use pbr; use pbr;
use std::io::{stderr, Stderr}; use std::io::{stderr, Stderr};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -17,12 +16,12 @@ use store::{Row, Store};
use types::{Bytes, HeaderMap}; use types::{Bytes, HeaderMap};
const HASH_LEN: usize = 32; const HASH_LEN: usize = 32;
const HASH_PREFIX_LEN: usize = 8; pub const HASH_PREFIX_LEN: usize = 8;
type FullHash = [u8; HASH_LEN]; type FullHash = [u8; HASH_LEN];
type HashPrefix = [u8; HASH_PREFIX_LEN]; pub type HashPrefix = [u8; HASH_PREFIX_LEN];
fn hash_prefix(hash: &[u8]) -> HashPrefix { pub fn hash_prefix(hash: &[u8]) -> HashPrefix {
array_ref![hash, 0, HASH_PREFIX_LEN].clone() array_ref![hash, 0, HASH_PREFIX_LEN].clone()
} }
@ -31,43 +30,43 @@ fn full_hash(hash: &[u8]) -> FullHash {
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct TxInKey { pub struct TxInKey {
code: u8, pub code: u8,
prev_hash_prefix: HashPrefix, pub prev_hash_prefix: HashPrefix,
prev_index: u16, pub prev_index: u16,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct TxInRow { pub struct TxInRow {
key: TxInKey, key: TxInKey,
txid_prefix: HashPrefix, pub txid_prefix: HashPrefix,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct TxOutKey { pub struct TxOutKey {
code: u8, code: u8,
script_hash_prefix: HashPrefix, script_hash_prefix: HashPrefix,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct TxOutRow { pub struct TxOutRow {
key: TxOutKey, key: TxOutKey,
txid_prefix: HashPrefix, pub txid_prefix: HashPrefix,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct TxKey { pub struct TxKey {
code: u8, code: u8,
txid: FullHash, pub txid: FullHash,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct BlockKey { pub struct BlockKey {
code: u8, code: u8,
hash: FullHash, hash: FullHash,
} }
fn digest(data: &[u8]) -> FullHash { pub fn compute_script_hash(data: &[u8]) -> FullHash {
let mut hash = FullHash::default(); let mut hash = FullHash::default();
let mut sha2 = Sha256::new(); let mut sha2 = Sha256::new();
sha2.input(data); sha2.input(data);
@ -94,7 +93,7 @@ fn txout_row(output: &TxOut, txid: &Sha256dHash) -> Row {
key: bincode::serialize(&TxOutRow { key: bincode::serialize(&TxOutRow {
key: TxOutKey { key: TxOutKey {
code: b'O', code: b'O',
script_hash_prefix: hash_prefix(&digest(&output.script_pubkey[..])), script_hash_prefix: hash_prefix(&compute_script_hash(&output.script_pubkey[..])),
}, },
txid_prefix: hash_prefix(&txid[..]), txid_prefix: hash_prefix(&txid[..]),
}).unwrap(), }).unwrap(),
@ -315,91 +314,3 @@ impl Index {
self.headers = Some(current_headers) self.headers = Some(current_headers)
} }
} }
pub struct Query<'a> {
store: &'a Store,
daemon: &'a Daemon,
}
impl<'a> Query<'a> {
pub fn new(store: &'a Store, daemon: &'a Daemon) -> Query<'a> {
Query { store, daemon }
}
fn load_txns(&self, prefixes: Vec<HashPrefix>) -> Vec<Transaction> {
let mut txns = Vec::new();
for txid_prefix in prefixes {
for row in self.store.scan(&[b"T", &txid_prefix[..]].concat()) {
let key: TxKey = bincode::deserialize(&row.key).unwrap();
let txid: Sha256dHash = deserialize(&key.txid).unwrap();
let txn_bytes = self.daemon.get(&format!("tx/{}.bin", txid.be_hex_string()));
let txn: Transaction = deserialize(&txn_bytes).unwrap();
txns.push(txn)
}
}
txns
}
fn find_spending_txn(&self, txid: &Sha256dHash, output_index: u32) -> Option<Transaction> {
let spend_key = bincode::serialize(&TxInKey {
code: b'I',
prev_hash_prefix: hash_prefix(&txid[..]),
prev_index: output_index as u16,
}).unwrap();
let mut spending: Vec<Transaction> = self.load_txns(
self.store
.scan(&spend_key)
.iter()
.map(|row| {
bincode::deserialize::<TxInRow>(&row.key)
.unwrap()
.txid_prefix
})
.collect(),
);
spending.retain(|item| {
item.input
.iter()
.any(|input| input.prev_hash == *txid && input.prev_index == output_index)
});
assert!(spending.len() <= 1);
if spending.len() == 1 {
Some(spending.remove(0))
} else {
None
}
}
pub fn balance(&self, script_hash: &[u8]) -> f64 {
let mut funding: Vec<Transaction> = self.load_txns(
self.store
.scan(&[b"O", &script_hash[..HASH_PREFIX_LEN]].concat())
.iter()
.map(|row| {
bincode::deserialize::<TxOutRow>(&row.key)
.unwrap()
.txid_prefix
})
.collect(),
);
funding.retain(|item| {
item.output
.iter()
.any(|output| digest(&output.script_pubkey[..]) == script_hash)
});
let mut balance = 0u64;
let mut spending = Vec::<Transaction>::new();
for txn in &funding {
let txid = txn.txid();
for (index, output) in enumerate(&txn.output) {
if let Some(spent) = self.find_spending_txn(&txid, index as u32) {
spending.push(spent); // TODO: may contain duplicate TXNs
} else {
balance += output.value;
}
}
}
balance as f64 / 100_000_000f64
}
}

View File

@ -24,6 +24,7 @@ extern crate serde_json;
pub mod daemon; pub mod daemon;
pub mod index; pub mod index;
pub mod query;
pub mod rpc; pub mod rpc;
pub mod store; pub mod store;
pub mod waiter; pub mod waiter;

98
src/query.rs Normal file
View File

@ -0,0 +1,98 @@
use bincode;
use bitcoin::blockdata::transaction::Transaction;
use bitcoin::network::serialize::deserialize;
use bitcoin::util::hash::Sha256dHash;
use itertools::enumerate;
use daemon::Daemon;
use index::{compute_script_hash, hash_prefix, HashPrefix, TxInKey, TxInRow, TxKey, TxOutRow,
HASH_PREFIX_LEN};
use store::Store;
pub struct Query<'a> {
store: &'a Store,
daemon: &'a Daemon,
}
impl<'a> Query<'a> {
pub fn new(store: &'a Store, daemon: &'a Daemon) -> Query<'a> {
Query { store, daemon }
}
fn load_txns(&self, prefixes: Vec<HashPrefix>) -> Vec<Transaction> {
let mut txns = Vec::new();
for txid_prefix in prefixes {
for row in self.store.scan(&[b"T", &txid_prefix[..]].concat()) {
let key: TxKey = bincode::deserialize(&row.key).unwrap();
let txid: Sha256dHash = deserialize(&key.txid).unwrap();
let txn_bytes = self.daemon.get(&format!("tx/{}.bin", txid.be_hex_string()));
let txn: Transaction = deserialize(&txn_bytes).unwrap();
txns.push(txn)
}
}
txns
}
fn find_spending_txn(&self, txid: &Sha256dHash, output_index: u32) -> Option<Transaction> {
let spend_key = bincode::serialize(&TxInKey {
code: b'I',
prev_hash_prefix: hash_prefix(&txid[..]),
prev_index: output_index as u16,
}).unwrap();
let mut spending: Vec<Transaction> = self.load_txns(
self.store
.scan(&spend_key)
.iter()
.map(|row| {
bincode::deserialize::<TxInRow>(&row.key)
.unwrap()
.txid_prefix
})
.collect(),
);
spending.retain(|item| {
item.input
.iter()
.any(|input| input.prev_hash == *txid && input.prev_index == output_index)
});
assert!(spending.len() <= 1);
if spending.len() == 1 {
Some(spending.remove(0))
} else {
None
}
}
pub fn balance(&self, script_hash: &[u8]) -> f64 {
let mut funding: Vec<Transaction> = self.load_txns(
self.store
.scan(&[b"O", &script_hash[..HASH_PREFIX_LEN]].concat())
.iter()
.map(|row| {
bincode::deserialize::<TxOutRow>(&row.key)
.unwrap()
.txid_prefix
})
.collect(),
);
funding.retain(|item| {
item.output
.iter()
.any(|output| compute_script_hash(&output.script_pubkey[..]) == script_hash)
});
let mut balance = 0u64;
let mut spending = Vec::<Transaction>::new();
for txn in &funding {
let txid = txn.txid();
for (index, output) in enumerate(&txn.output) {
if let Some(spent) = self.find_spending_txn(&txid, index as u32) {
spending.push(spent); // TODO: may contain duplicate TXNs
} else {
balance += output.value;
}
}
}
balance as f64 / 100_000_000f64
}
}

View File

@ -3,12 +3,12 @@ use serde_json::{from_str, Number, Value};
use std::io::{BufRead, BufReader, Write}; use std::io::{BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpListener, TcpStream}; use std::net::{SocketAddr, TcpListener, TcpStream};
use index; use query::Query;
error_chain!{} error_chain!{}
struct Handler<'a> { struct Handler<'a> {
query: &'a index::Query<'a>, query: &'a Query<'a>,
} }
impl<'a> Handler<'a> { impl<'a> Handler<'a> {
@ -129,7 +129,7 @@ impl<'a> Handler<'a> {
} }
} }
pub fn serve(addr: &str, query: &index::Query) { pub fn serve(addr: &str, query: &Query) {
let listener = TcpListener::bind(addr).unwrap(); let listener = TcpListener::bind(addr).unwrap();
info!("RPC server running on {}", addr); info!("RPC server running on {}", addr);
loop { loop {