Create simple mempool tracker

This commit is contained in:
Roman Zeyde 2018-05-14 13:41:11 +03:00
parent c8a5cb9c56
commit eab64181bb
No known key found for this signature in database
GPG Key ID: 87CAE5FA46917CBB
7 changed files with 118 additions and 19 deletions

View File

@ -34,6 +34,17 @@ pub struct Daemon {
cookie_b64: String,
}
pub struct MempoolEntry {
fee: u64, // in satoshis
size: u32, // in bytes
}
impl MempoolEntry {
pub fn fee_per_byte(&self) -> f32 {
self.fee as f32 / self.size as f32
}
}
impl Daemon {
pub fn new(addr: &str) -> Daemon {
Daemon {
@ -165,6 +176,35 @@ impl Daemon {
)
}
pub fn getmempooltxids(&self) -> Result<Vec<Sha256dHash>> {
let txids: Value = self.request("getrawmempool", json!([/*verbose=*/ false]))?;
let mut result = Vec::new();
for value in txids.as_array().chain_err(|| "non-array result")? {
result.push(parse_hash(&value).chain_err(|| "invalid txid")?);
}
Ok(result)
}
pub fn getmempoolentry(&self, txid: &Sha256dHash) -> Result<MempoolEntry> {
let entry = self.request("getmempoolentry", json!([txid.be_hex_string()]))?;
let fees = entry
.get("fees")
.chain_err(|| "missing fees section")?
.as_object()
.chain_err(|| "non-object fees")?;
Ok(MempoolEntry {
fee: (fees.get("base")
.chain_err(|| "missing base fee")?
.as_f64()
.chain_err(|| "non-float fee")? * 100_000_000f64) as u64,
size: entry
.get("size")
.chain_err(|| "missing size")?
.as_u64()
.chain_err(|| "non-integer size")? as u32,
})
}
pub fn get_all_headers(&self) -> Result<HeaderMap> {
let info: Value = self.request("getblockchaininfo", json!([]))?;
let max_height = info.get("blocks")

View File

@ -17,22 +17,7 @@ use time;
use daemon::Daemon;
use store::{Row, Store};
use types::HeaderMap;
// TODO: consolidate serialization/deserialize code for bincode/bitcoin.
const HASH_LEN: usize = 32;
pub const HASH_PREFIX_LEN: usize = 8;
pub type FullHash = [u8; HASH_LEN];
pub type HashPrefix = [u8; HASH_PREFIX_LEN];
pub fn hash_prefix(hash: &[u8]) -> HashPrefix {
array_ref![hash, 0, HASH_PREFIX_LEN].clone()
}
fn full_hash(hash: &[u8]) -> FullHash {
array_ref![hash, 0, HASH_LEN].clone()
}
use types::{full_hash, hash_prefix, FullHash, HashPrefix, HeaderMap};
// TODO: move to a separate file (to break index<->daemon dependency)
#[derive(Eq, PartialEq, Clone)]

View File

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

58
src/mempool.rs Normal file
View File

@ -0,0 +1,58 @@
use bitcoin::blockdata::transaction::Transaction;
use bitcoin::util::hash::Sha256dHash;
use daemon::{Daemon, MempoolEntry};
use std::collections::hash_map::Entry;
use std::collections::{HashMap, HashSet};
use std::iter::FromIterator;
use query::{FundingOutput, SpendingInput};
use types::FullHash;
error_chain!{}
pub struct Stats {
tx: Transaction,
entry: MempoolEntry,
}
pub struct Tracker<'a> {
txids: HashMap<Sha256dHash, Stats>,
daemon: &'a Daemon,
}
impl<'a> Tracker<'a> {
pub fn new(daemon: &Daemon) -> Tracker {
Tracker {
txids: HashMap::new(),
daemon: daemon,
}
}
pub fn update_from_daemon(&mut self) -> Result<()> {
let new_txids = HashSet::<Sha256dHash>::from_iter(self.daemon
.getmempooltxids()
.chain_err(|| "failed to update mempool from daemon")?);
self.txids.retain(|txid, _| new_txids.contains(txid)); // remove old TXNs
for txid in new_txids {
if let Entry::Vacant(map_entry) = self.txids.entry(txid) {
let tx = match self.daemon.gettransaction(&txid) {
Ok(tx) => tx,
Err(err) => {
warn!("missing tx {}: {}", txid, err);
continue;
}
};
let entry = match self.daemon.getmempoolentry(&txid) {
Ok(entry) => entry,
Err(err) => {
warn!("no mempool entry {}: {}", txid, err);
continue;
}
};
debug!("new mempool tx: {}, {:.3}", txid, entry.fee_per_byte());
map_entry.insert(Stats { tx, entry });
}
}
Ok(())
}
}

View File

@ -6,9 +6,9 @@ use bitcoin::util::hash::Sha256dHash;
use itertools::enumerate;
use daemon::Daemon;
use index::{compute_script_hash, hash_prefix, HashPrefix, HeaderEntry, Index, TxInKey, TxInRow,
TxKey, TxOutRow, HASH_PREFIX_LEN};
use index::{compute_script_hash, HeaderEntry, Index, TxInKey, TxInRow, TxKey, TxOutRow};
use store::Store;
use types::{hash_prefix, HashPrefix, HASH_PREFIX_LEN};
pub struct Query<'a> {
store: &'a Store,

View File

@ -12,8 +12,8 @@ use std::io::{BufRead, BufReader, Write};
use std::net::{SocketAddr, TcpListener, TcpStream};
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use index::FullHash;
use query::{Query, Status};
use types::FullHash;
error_chain!{}

View File

@ -5,3 +5,18 @@ pub use bitcoin::util::hash::Sha256dHash;
pub type Bytes = Vec<u8>;
pub type HeaderMap = HashMap<Sha256dHash, BlockHeader>;
// TODO: consolidate serialization/deserialize code for bincode/bitcoin.
const HASH_LEN: usize = 32;
pub const HASH_PREFIX_LEN: usize = 8;
pub type FullHash = [u8; HASH_LEN];
pub type HashPrefix = [u8; HASH_PREFIX_LEN];
pub fn hash_prefix(hash: &[u8]) -> HashPrefix {
array_ref![hash, 0, HASH_PREFIX_LEN].clone()
}
pub fn full_hash(hash: &[u8]) -> FullHash {
array_ref![hash, 0, HASH_LEN].clone()
}