query: fixes

This commit is contained in:
nym21
2026-04-30 19:19:09 +02:00
parent 9b42b40a36
commit 1068ad4e8f
10 changed files with 165 additions and 35 deletions

View File

@@ -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}")]

View File

@@ -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 {

View File

@@ -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

View File

@@ -1 +1,3 @@
*.txt
/*.md
!README.md

View File

@@ -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()))

View File

@@ -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();

View File

@@ -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())
}