mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-25 09:14:47 -07:00
global: snapshot
This commit is contained in:
18
crates/brk_monitor/Cargo.toml
Normal file
18
crates/brk_monitor/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "brk_monitor"
|
||||
description = "A Bitcoin mempool monitor with real-time synchronization"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
bitcoin = { workspace = true }
|
||||
bitcoincore-rpc = { workspace = true }
|
||||
brk_structs = { workspace = true }
|
||||
log = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
rustc-hash = "2.1.1"
|
||||
30
crates/brk_monitor/README.md
Normal file
30
crates/brk_monitor/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# brk_monitor
|
||||
|
||||
A lightweight, thread-safe Rust library for maintaining a live, in-memory snapshot of the Bitcoin mempool.
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Real-time synchronization**: Polls Bitcoin Core RPC every second to track mempool state
|
||||
- **Thread-safe access**: Uses `RwLock` for concurrent reads with minimal contention
|
||||
- **Efficient updates**: Only fetches new transactions, with configurable rate limiting (10,000 tx/cycle)
|
||||
- **Zero-copy reads**: Exposes mempool via read guards for lock-free iteration
|
||||
- **Optimized data structures**: Uses `FxHashMap` for fast lookups and minimal hashing overhead
|
||||
- **Automatic cleanup**: Removes confirmed/dropped transactions on each update
|
||||
|
||||
## Design Principles
|
||||
|
||||
- **Minimal lock duration**: Lock held only during HashSet operations, never during I/O
|
||||
- **Memory efficient**: Stores only missing txids during fetch phase
|
||||
- **Simple API**: Just `new()`, `start()`, and `get_txs()`
|
||||
- **Production-ready**: Error handling with logging, graceful degradation
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Fee estimation and mempool analysis
|
||||
- Transaction monitoring and alerts
|
||||
- Block template prediction
|
||||
- Network research and statistics
|
||||
|
||||
## Description
|
||||
|
||||
A clean, performant way to keep Bitcoin's mempool state available in your Rust application without repeatedly querying RPC. Perfect for applications that need frequent mempool access with low latency.
|
||||
8
crates/brk_monitor/build.rs
Normal file
8
crates/brk_monitor/build.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap_or_default();
|
||||
|
||||
if profile == "release" {
|
||||
println!("cargo:rustc-flag=-C");
|
||||
println!("cargo:rustc-flag=target-cpu=native");
|
||||
}
|
||||
}
|
||||
70
crates/brk_monitor/src/lib.rs
Normal file
70
crates/brk_monitor/src/lib.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use bitcoin::{Transaction, Txid, consensus::encode};
|
||||
use bitcoincore_rpc::{Client, RpcApi};
|
||||
use log::error;
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
const MAX_FETCHES_PER_CYCLE: usize = 10_000;
|
||||
|
||||
pub struct Mempool {
|
||||
rpc: &'static Client,
|
||||
txs: RwLock<FxHashMap<Txid, Transaction>>,
|
||||
}
|
||||
|
||||
impl Mempool {
|
||||
pub fn new(rpc: &'static Client) -> Self {
|
||||
Self {
|
||||
rpc,
|
||||
txs: RwLock::new(FxHashMap::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_txs(&self) -> RwLockReadGuard<'_, FxHashMap<Txid, Transaction>> {
|
||||
self.txs.read()
|
||||
}
|
||||
|
||||
pub fn start(&self) {
|
||||
loop {
|
||||
if let Err(e) = self.update() {
|
||||
error!("Error updating mempool: {}", e);
|
||||
}
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let txids = self
|
||||
.rpc
|
||||
.get_raw_mempool()?
|
||||
.into_iter()
|
||||
.collect::<FxHashSet<_>>();
|
||||
|
||||
let missing_txids = {
|
||||
let txs = self.txs.read();
|
||||
txids
|
||||
.iter()
|
||||
.filter(|txid| !txs.contains_key(*txid))
|
||||
.take(MAX_FETCHES_PER_CYCLE)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let new_txs = missing_txids
|
||||
.into_iter()
|
||||
.filter_map(|txid| {
|
||||
self.rpc
|
||||
.get_raw_transaction_hex(txid, None)
|
||||
.ok()
|
||||
.and_then(|hex| encode::deserialize_hex(&hex).ok())
|
||||
.map(|tx| (*txid, tx))
|
||||
})
|
||||
.collect::<FxHashMap<_, _>>();
|
||||
|
||||
let mut txs = self.txs.write();
|
||||
txs.retain(|txid, _| txids.contains(txid));
|
||||
txs.extend(new_txs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
35
crates/brk_monitor/src/main.rs
Normal file
35
crates/brk_monitor/src/main.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::{path::Path, sync::Arc, thread, time::Duration};
|
||||
|
||||
use brk_monitor::Mempool;
|
||||
|
||||
fn main() {
|
||||
// Connect to Bitcoin Core
|
||||
let bitcoin_dir = Path::new(&std::env::var("HOME").unwrap())
|
||||
.join("Library")
|
||||
.join("Application Support")
|
||||
.join("Bitcoin");
|
||||
// let bitcoin_dir = Path::new("/Volumes/WD_BLACK/bitcoin");
|
||||
|
||||
let rpc = Box::leak(Box::new(
|
||||
bitcoincore_rpc::Client::new(
|
||||
"http://localhost:8332",
|
||||
bitcoincore_rpc::Auth::CookieFile(bitcoin_dir.join(".cookie")),
|
||||
)
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let mempool = Arc::new(Mempool::new(rpc));
|
||||
|
||||
// Spawn monitoring thread
|
||||
let mempool_clone = Arc::clone(&mempool);
|
||||
thread::spawn(move || {
|
||||
mempool_clone.start();
|
||||
});
|
||||
|
||||
// Access from main thread
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
let txs = mempool.get_txs();
|
||||
println!("mempool_tx_count: {}", txs.len());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user