mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-19 06:14:47 -07:00
query: fixes
This commit is contained in:
@@ -153,7 +153,7 @@ pub enum Error {
|
||||
#[error("Request weight {requested} exceeds maximum {max}")]
|
||||
WeightExceeded { requested: usize, max: usize },
|
||||
|
||||
#[error("Too many unspent transaction outputs (>500). Contact support to raise limits.")]
|
||||
#[error("Too many unspent transaction outputs (>1000).")]
|
||||
TooManyUtxos,
|
||||
|
||||
#[error("Deserialization error: {0}")]
|
||||
|
||||
@@ -143,13 +143,8 @@ impl Sfl {
|
||||
if nrate <= rate {
|
||||
continue;
|
||||
}
|
||||
match picked {
|
||||
None => picked = Some((add, nf, nv, nrate)),
|
||||
Some((_, _, _, prate)) => {
|
||||
if nrate > prate {
|
||||
picked = Some((add, nf, nv, nrate));
|
||||
}
|
||||
}
|
||||
if picked.is_none_or(|(_, _, _, prate)| nrate > prate) {
|
||||
picked = Some((add, nf, nv, nrate));
|
||||
}
|
||||
}
|
||||
match picked {
|
||||
|
||||
@@ -43,6 +43,15 @@ impl TxGraveyard {
|
||||
})
|
||||
}
|
||||
|
||||
/// Every `Replaced` tombstone, yielded as (predecessor_txid,
|
||||
/// replacer_txid). Caller walks the replacer chain forward to find
|
||||
/// each tree's terminal replacer.
|
||||
pub fn replaced_iter(&self) -> impl Iterator<Item = (&Txid, &Txid)> {
|
||||
self.tombstones
|
||||
.iter()
|
||||
.filter_map(|(txid, ts)| ts.replaced_by().map(|by| (txid, by)))
|
||||
}
|
||||
|
||||
pub fn bury(&mut self, txid: Txid, tx: Transaction, entry: TxEntry, removal: TxRemoval) {
|
||||
let now = Instant::now();
|
||||
self.tombstones
|
||||
|
||||
2
crates/brk_query/.gitignore
vendored
2
crates/brk_query/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
*.txt
|
||||
/*.md
|
||||
!README.md
|
||||
|
||||
@@ -173,9 +173,8 @@ impl Query {
|
||||
|
||||
let prefix = u32::from(type_index).to_be_bytes();
|
||||
|
||||
// Match mempool.space's electrs cap: refuse addresses with >500 UTXOs.
|
||||
// Bounds worst-case work and response size, prevents heavy-address DDoS.
|
||||
const MAX_UTXOS: usize = 500;
|
||||
const MAX_UTXOS: usize = 1000;
|
||||
let outpoints: Vec<(TxIndex, Vout)> = store
|
||||
.prefix(prefix)
|
||||
.map(|(key, _): (AddrIndexOutPoint, Unit)| (key.tx_index(), key.vout()))
|
||||
|
||||
@@ -378,6 +378,50 @@ impl Query {
|
||||
})
|
||||
}
|
||||
|
||||
/// Recent RBF replacements across the whole mempool, matching
|
||||
/// mempool.space's `GET /api/v1/replacements` and
|
||||
/// `GET /api/v1/fullrbf/replacements`. Each entry is a complete
|
||||
/// replacement tree rooted at the latest replacer; same shape as
|
||||
/// `tx_rbf().replacements`. Sorted most-recent-first by root
|
||||
/// `time`. When `full_rbf_only` is true, only trees with at least
|
||||
/// one non-signaling predecessor are returned.
|
||||
pub fn recent_replacements(&self, full_rbf_only: bool) -> Result<Vec<ReplacementNode>> {
|
||||
let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?;
|
||||
let txs = mempool.txs();
|
||||
let entries = mempool.entries();
|
||||
let graveyard = mempool.graveyard();
|
||||
|
||||
// Collect every distinct tree-root replacer. A predecessor's
|
||||
// `by` may itself have been replaced; walk forward through
|
||||
// chained Replaced tombstones until reaching a tx that's no
|
||||
// longer flagged as replaced (live, Vanished, or unknown).
|
||||
let mut roots: FxHashSet<Txid> = FxHashSet::default();
|
||||
for (_, by) in graveyard.replaced_iter() {
|
||||
let mut root = by.clone();
|
||||
while let Some(TxRemoval::Replaced { by: next }) =
|
||||
graveyard.get(&root).map(TxTombstone::reason)
|
||||
{
|
||||
root = next.clone();
|
||||
}
|
||||
roots.insert(root);
|
||||
}
|
||||
|
||||
let mut trees: Vec<ReplacementNode> = roots
|
||||
.iter()
|
||||
.filter_map(|root| {
|
||||
Self::build_rbf_node(root, None, &txs, &entries, &graveyard).map(|mut node| {
|
||||
node.tx.full_rbf = Some(node.full_rbf);
|
||||
node.interval = None;
|
||||
node
|
||||
})
|
||||
})
|
||||
.filter(|node| !full_rbf_only || node.full_rbf)
|
||||
.collect();
|
||||
|
||||
trees.sort_by(|a, b| b.time.cmp(&a.time));
|
||||
Ok(trees)
|
||||
}
|
||||
|
||||
pub fn transaction_times(&self, txids: &[Txid]) -> Result<Vec<u64>> {
|
||||
let mempool = self.mempool().ok_or(Error::MempoolNotAvailable)?;
|
||||
let entries = mempool.entries();
|
||||
|
||||
@@ -9,7 +9,7 @@ use brk_mempool::Mempool;
|
||||
use brk_reader::Reader;
|
||||
use brk_rpc::Client;
|
||||
use brk_types::{BlockHash, BlockHashPrefix, Height, SyncStatus};
|
||||
use vecdb::{AnyVec, ReadOnlyClone, ReadableVec, Ro};
|
||||
use vecdb::{ReadOnlyClone, ReadableVec, Ro};
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
mod r#async;
|
||||
@@ -63,8 +63,7 @@ impl Query {
|
||||
|
||||
/// Current computed height (series)
|
||||
pub fn computed_height(&self) -> Height {
|
||||
let len = self.computer().distribution.supply_state.len();
|
||||
Height::from(len.saturating_sub(1))
|
||||
Height::from(self.computer().distribution.supply_state.stamp())
|
||||
}
|
||||
|
||||
/// Minimum of indexed and computed heights
|
||||
@@ -73,11 +72,13 @@ impl Query {
|
||||
}
|
||||
|
||||
/// Tip block hash, cached in the indexer.
|
||||
#[inline]
|
||||
pub fn tip_blockhash(&self) -> BlockHash {
|
||||
self.indexer().tip_blockhash()
|
||||
}
|
||||
|
||||
/// Tip block hash prefix for cache etags.
|
||||
#[inline]
|
||||
pub fn tip_hash_prefix(&self) -> BlockHashPrefix {
|
||||
BlockHashPrefix::from(&self.tip_blockhash())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user