Create simple mempool tracker
This commit is contained in:
parent
c8a5cb9c56
commit
eab64181bb
|
@ -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")
|
||||
|
|
17
src/index.rs
17
src/index.rs
|
@ -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)]
|
||||
|
|
|
@ -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
58
src/mempool.rs
Normal 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(())
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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!{}
|
||||
|
||||
|
|
15
src/types.rs
15
src/types.rs
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user