Skip to content

Commit

Permalink
Don't deserialize transactions if not needed
Browse files Browse the repository at this point in the history
After filtering, we need the serialized bytes for caching only.
  • Loading branch information
romanz committed Dec 26, 2024
1 parent fc8f756 commit b2ab18a
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 28 deletions.
12 changes: 6 additions & 6 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use bitcoin::{Transaction, Txid};
use bitcoin::Txid;
use parking_lot::RwLock;

use std::collections::HashMap;
Expand All @@ -7,7 +7,7 @@ use std::sync::Arc;
use crate::metrics::{self, Histogram, Metrics};

pub(crate) struct Cache {
txs: Arc<RwLock<HashMap<Txid, Transaction>>>,
txs: Arc<RwLock<HashMap<Txid, Box<[u8]>>>>,

// stats
txs_size: Histogram,
Expand All @@ -26,18 +26,18 @@ impl Cache {
}
}

pub fn add_tx(&self, txid: Txid, f: impl FnOnce() -> Transaction) {
pub fn add_tx(&self, txid: Txid, f: impl FnOnce() -> Box<[u8]>) {
self.txs.write().entry(txid).or_insert_with(|| {
let tx = f();
self.txs_size.observe("serialized", tx.total_size() as f64);
self.txs_size.observe("serialized", tx.len() as f64);
tx
});
}

pub fn get_tx<F, T>(&self, txid: &Txid, f: F) -> Option<T>
where
F: FnOnce(&Transaction) -> T,
F: FnOnce(&[u8]) -> T,
{
self.txs.read().get(txid).map(f)
self.txs.read().get(txid).map(|tx_bytes| f(tx_bytes))
}
}
8 changes: 6 additions & 2 deletions src/electrum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use anyhow::{bail, Context, Result};
use bitcoin::{
consensus::{deserialize, encode::serialize_hex},
hashes::hex::FromHex,
hex::DisplayHex,
BlockHash, Txid,
};
use crossbeam_channel::Receiver;
Expand Down Expand Up @@ -373,8 +374,11 @@ impl Rpc {
.map(|(blockhash, _tx)| blockhash);
return self.daemon.get_transaction_info(&txid, blockhash);
}
if let Some(tx) = self.cache.get_tx(&txid, serialize_hex) {
return Ok(json!(tx));
if let Some(tx_hex) = self
.cache
.get_tx(&txid, |tx_bytes| tx_bytes.to_lower_hex_string())
{
return Ok(json!(tx_hex));
}
debug!("tx cache miss: txid={}", txid);
// use internal index to load confirmed transaction without an RPC
Expand Down
36 changes: 16 additions & 20 deletions src/status.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;
use bitcoin::{
consensus::Decodable,
consensus::serialize,
hashes::{sha256, Hash, HashEngine},
Amount, BlockHash, OutPoint, SignedAmount, Transaction, Txid,
};
Expand Down Expand Up @@ -337,7 +337,7 @@ impl ScriptHashStatus {
self.for_new_blocks(funding_blockhashes, daemon, |blockhash, block| {
let block_entries = result.entry(blockhash).or_default();
for filtered_outputs in filter_block_txs_outputs(block, scripthash) {
cache.add_tx(filtered_outputs.txid, move || filtered_outputs.tx);
cache.add_tx(filtered_outputs.txid, move || filtered_outputs.tx_bytes);
outpoints.extend(make_outpoints(
filtered_outputs.txid,
&filtered_outputs.result,
Expand All @@ -355,7 +355,7 @@ impl ScriptHashStatus {
self.for_new_blocks(spending_blockhashes, daemon, |blockhash, block| {
let block_entries = result.entry(blockhash).or_default();
for filtered_inputs in filter_block_txs_inputs(&block, outpoints) {
cache.add_tx(filtered_inputs.txid, move || filtered_inputs.tx);
cache.add_tx(filtered_inputs.txid, move || filtered_inputs.tx_bytes);
block_entries
.entry(filtered_inputs.pos)
.or_insert_with(|| TxEntry::new(filtered_inputs.txid))
Expand Down Expand Up @@ -394,7 +394,7 @@ impl ScriptHashStatus {
.entry(entry.txid)
.or_insert_with(|| TxEntry::new(entry.txid))
.outputs = funding_outputs;
cache.add_tx(entry.txid, || entry.tx.clone());
cache.add_tx(entry.txid, || serialize(&entry.tx).into_boxed_slice());
}
for entry in outpoints
.iter()
Expand All @@ -406,7 +406,7 @@ impl ScriptHashStatus {
.entry(entry.txid)
.or_insert_with(|| TxEntry::new(entry.txid))
.spent = spent_outpoints;
cache.add_tx(entry.txid, || entry.tx.clone());
cache.add_tx(entry.txid, || serialize(&entry.tx).into_boxed_slice());
}
result.into_values().collect()
}
Expand Down Expand Up @@ -501,7 +501,7 @@ fn compute_status_hash(history: &[HistoryEntry]) -> Option<StatusHash> {
}

struct FilteredTx<T> {
tx: Transaction,
tx_bytes: Box<[u8]>,
txid: Txid,
pos: usize,
result: Vec<T>,
Expand All @@ -515,22 +515,20 @@ fn filter_block_txs_outputs(block: SerBlock, scripthash: ScriptHash) -> Vec<Filt
pos: usize,
}
impl Visitor for FindOutputs {
// Called after all TxOuts are visited
fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> {
if !self.buffer.is_empty() {
let result = std::mem::take(&mut self.buffer);
let txid = bsl_txid(tx);
let tx = bitcoin::Transaction::consensus_decode(&mut tx.as_ref())
.expect("transaction was already validated");
self.result.push(FilteredTx::<TxOutput> {
tx,
txid,
tx_bytes: tx.as_ref().into(),
txid: bsl_txid(tx),
pos: self.pos,
result,
result: std::mem::take(&mut self.buffer), // clear buffer for next tx
});
}
self.pos += 1;
ControlFlow::Continue(())
}
// Keep only relevant outputs
fn visit_tx_out(&mut self, vout: usize, tx_out: &bsl::TxOut) -> ControlFlow<()> {
let current = ScriptHash::hash(tx_out.script_pubkey());
if current == self.scripthash {
Expand Down Expand Up @@ -566,22 +564,20 @@ fn filter_block_txs_inputs(
}

impl Visitor for FindInputs<'_> {
// Called after all TxIns are visited
fn visit_transaction(&mut self, tx: &bsl::Transaction) -> ControlFlow<()> {
if !self.buffer.is_empty() {
let result = std::mem::take(&mut self.buffer);
let txid = bsl_txid(tx);
let tx = bitcoin::Transaction::consensus_decode(&mut tx.as_ref())
.expect("transaction was already validated");
self.result.push(FilteredTx::<OutPoint> {
tx,
txid,
tx_bytes: tx.as_ref().into(),
txid: bsl_txid(tx),
pos: self.pos,
result,
result: std::mem::take(&mut self.buffer), // clear buffer for next tx
});
}
self.pos += 1;
ControlFlow::Continue(())
}
// Keep only relevant outpoints
fn visit_tx_in(&mut self, _vin: usize, tx_in: &bsl::TxIn) -> ControlFlow<()> {
let current: OutPoint = tx_in.prevout().into();
if self.outpoints.contains(&current) {
Expand Down

0 comments on commit b2ab18a

Please sign in to comment.