mirror of
https://github.com/romanz/electrs.git
synced 2024-11-19 09:54:09 +01:00
Move query-related code into a separate module
This commit is contained in:
parent
68feda0baa
commit
95da35ac80
@ -6,7 +6,7 @@ extern crate indexrs;
|
||||
|
||||
use argparse::{ArgumentParser, StoreFalse};
|
||||
use std::fs::OpenOptions;
|
||||
use indexrs::{daemon, index, rpc, store, waiter};
|
||||
use indexrs::{daemon, index, query, rpc, store, waiter};
|
||||
|
||||
fn setup_logging() {
|
||||
use simplelog::*;
|
||||
@ -51,7 +51,7 @@ fn run_server(config: Config) {
|
||||
}
|
||||
|
||||
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| {
|
||||
scope.spawn(|| rpc::serve("localhost:50001", &query));
|
||||
|
125
src/index.rs
125
src/index.rs
@ -1,12 +1,11 @@
|
||||
use bincode;
|
||||
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::{deserialize, serialize};
|
||||
use bitcoin::util::hash::Sha256dHash;
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha256;
|
||||
use itertools::enumerate;
|
||||
use pbr;
|
||||
use std::io::{stderr, Stderr};
|
||||
use std::time::{Duration, Instant};
|
||||
@ -17,12 +16,12 @@ use store::{Row, Store};
|
||||
use types::{Bytes, HeaderMap};
|
||||
|
||||
const HASH_LEN: usize = 32;
|
||||
const HASH_PREFIX_LEN: usize = 8;
|
||||
pub const HASH_PREFIX_LEN: usize = 8;
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@ -31,43 +30,43 @@ fn full_hash(hash: &[u8]) -> FullHash {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TxInKey {
|
||||
code: u8,
|
||||
prev_hash_prefix: HashPrefix,
|
||||
prev_index: u16,
|
||||
pub struct TxInKey {
|
||||
pub code: u8,
|
||||
pub prev_hash_prefix: HashPrefix,
|
||||
pub prev_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TxInRow {
|
||||
pub struct TxInRow {
|
||||
key: TxInKey,
|
||||
txid_prefix: HashPrefix,
|
||||
pub txid_prefix: HashPrefix,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TxOutKey {
|
||||
pub struct TxOutKey {
|
||||
code: u8,
|
||||
script_hash_prefix: HashPrefix,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TxOutRow {
|
||||
pub struct TxOutRow {
|
||||
key: TxOutKey,
|
||||
txid_prefix: HashPrefix,
|
||||
pub txid_prefix: HashPrefix,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct TxKey {
|
||||
pub struct TxKey {
|
||||
code: u8,
|
||||
txid: FullHash,
|
||||
pub txid: FullHash,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct BlockKey {
|
||||
pub struct BlockKey {
|
||||
code: u8,
|
||||
hash: FullHash,
|
||||
}
|
||||
|
||||
fn digest(data: &[u8]) -> FullHash {
|
||||
pub fn compute_script_hash(data: &[u8]) -> FullHash {
|
||||
let mut hash = FullHash::default();
|
||||
let mut sha2 = Sha256::new();
|
||||
sha2.input(data);
|
||||
@ -94,7 +93,7 @@ fn txout_row(output: &TxOut, txid: &Sha256dHash) -> Row {
|
||||
key: bincode::serialize(&TxOutRow {
|
||||
key: TxOutKey {
|
||||
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[..]),
|
||||
}).unwrap(),
|
||||
@ -315,91 +314,3 @@ impl Index {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ extern crate serde_json;
|
||||
|
||||
pub mod daemon;
|
||||
pub mod index;
|
||||
pub mod query;
|
||||
pub mod rpc;
|
||||
pub mod store;
|
||||
pub mod waiter;
|
||||
|
98
src/query.rs
Normal file
98
src/query.rs
Normal 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
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@ use serde_json::{from_str, Number, Value};
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::net::{SocketAddr, TcpListener, TcpStream};
|
||||
|
||||
use index;
|
||||
use query::Query;
|
||||
|
||||
error_chain!{}
|
||||
|
||||
struct Handler<'a> {
|
||||
query: &'a index::Query<'a>,
|
||||
query: &'a Query<'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();
|
||||
info!("RPC server running on {}", addr);
|
||||
loop {
|
||||
|
Loading…
Reference in New Issue
Block a user