Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.d/shared-template-txs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Changed

- `getblocktemplate` and the SV2 Template Provider select transactions as
the mempool's own shared bodies instead of copying every selected
transaction on each call.
7 changes: 5 additions & 2 deletions crates/rbitcoin-mempool/src/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1901,15 +1901,18 @@ impl ActiveMempool {

/// Mining-order live txs that fit `budget` (best chunks first) with
/// `prioritisetransaction` deltas, each with its [`Selected`] meta.
///
/// RAM trade: the bodies are the pool's own `Arc`s, not copies. A caller
/// that holds one past eviction or replacement keeps that body alive.
pub fn select_block_template(
&self,
budget: SelectBudget,
delta: impl Fn(Txid) -> i64,
) -> Vec<(Transaction, Selected)> {
) -> Vec<(Arc<Transaction>, Selected)> {
self.graph
.select_block_template(budget, delta)
.into_iter()
.filter_map(|s| self.get_tx(&s.txid).map(|tx| (tx.clone(), s)))
.filter_map(|s| self.bodies.get(&s.txid).map(|tx| (Arc::clone(tx), s)))
.collect()
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/rbitcoin-net/src/tx_relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2809,7 +2809,7 @@ impl MempoolHub {
/// (best chunks first, `prioritisetransaction` deltas applied). Base fee
/// and sigop cost come from the same read lock as the selection, so a tx
/// evicted afterwards still reports what it was selected with.
pub fn select_block_template(&self, budget: SelectBudget) -> Vec<(Transaction, Selected)> {
pub fn select_block_template(&self, budget: SelectBudget) -> Vec<(Arc<Transaction>, Selected)> {
let deltas = self.fee_deltas.lock().unwrap().clone();
let g = self.lock_read();
g.select_block_template(budget, |id| deltas.get(&id).copied().unwrap_or(0))
Expand Down Expand Up @@ -4616,7 +4616,7 @@ mod tests {
hub.accept_tx(&tx).expect("16004 sigop cost fits a block");
let picked = hub.select_block_template(hub.template_budget(0));
assert_eq!(picked.len(), 1);
assert_eq!(picked[0].0, tx);
assert_eq!(*picked[0].0, tx);
assert_eq!(
(picked[0].1.fee_sat, picked[0].1.sigop_cost),
(100_000, 16_004)
Expand Down Expand Up @@ -5147,7 +5147,7 @@ mod tests {
assert_eq!(budget.reserved_sigops, 0);
let picked = hub.select_block_template(budget);
assert_eq!(picked.len(), 1);
assert_eq!(picked[0].0, tx);
assert_eq!(*picked[0].0, tx);
assert_eq!(
(picked[0].1.fee_sat, picked[0].1.sigop_cost),
(4_999_999_001, 79_920)
Expand Down
5 changes: 3 additions & 2 deletions crates/rbitcoin-rpc/src/methods/mine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rbitcoin_primitives::{Height, Network};
use serde_json::{json, Value};
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::Arc;

pub(crate) fn require_regtest(ctx: &RpcContext, method: &str) -> Result<(), Value> {
if ctx.network != Network::Regtest {
Expand Down Expand Up @@ -124,7 +125,7 @@ pub(crate) fn hashes_json(hashes: &[BlockHash]) -> Value {
json!(hashes.iter().map(|h| h.to_string()).collect::<Vec<_>>())
}

pub(crate) fn mempool_block_txs(ctx: &RpcContext) -> Vec<(Transaction, Selected)> {
pub(crate) fn mempool_block_txs(ctx: &RpcContext) -> Vec<(Arc<Transaction>, Selected)> {
let min = ctx
.chain
.as_ref()
Expand Down Expand Up @@ -152,7 +153,7 @@ pub(crate) fn generate_with_mempool(
let miner = require_regtest_miner(ctx, "generate")?;
let extras: Vec<Transaction> = mempool_block_txs(ctx)
.into_iter()
.map(|(tx, _)| tx)
.map(|(tx, _)| Transaction::clone(&tx))
.collect();
let hashes = miner
.generate_to_script(nblocks, script, extras.clone())
Expand Down
2 changes: 1 addition & 1 deletion crates/rbitcoin-rpc/src/regtest_chain_ops_journey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ fn chain_ops_sigop_adjusted_entry_and_min_fee(ctx: &RpcContext, cbs: &mut TrueCo
.set_block_min_tx_fee_sat_kvb(min);
crate::methods::mine::mempool_block_txs(ctx)
.into_iter()
.map(|(tx, _)| tx)
.map(|(tx, _)| Transaction::clone(&tx))
.collect::<Vec<_>>()
};
// 2_000 sat is 0.5 sat/vB at 4_000 vB.
Expand Down
4 changes: 2 additions & 2 deletions crates/rbitcoin-sv2/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ impl Session {
}
let mut txdata = Vec::with_capacity(1 + r.t.txs.len());
txdata.push(coinbase);
txdata.extend(r.t.txs.iter().cloned());
txdata.extend(r.t.txs.iter().map(|tx| Transaction::clone(tx)));
let block = Block { header, txdata };
let hash = block.block_hash();
let c = Arc::clone(&self.chain);
Expand Down Expand Up @@ -497,7 +497,7 @@ async fn on_request_transaction_data(
let raw: Vec<Vec<u8>> = t
.txs
.iter()
.map(bitcoin::consensus::encode::serialize)
.map(|tx| bitcoin::consensus::encode::serialize(tx.as_ref()))
.collect();
let wire = |e: binary_sv2::Error| {
io::Error::other(format!("sv2 RequestTransactionData.Success: {e:?}"))
Expand Down
6 changes: 4 additions & 2 deletions crates/rbitcoin-sv2/src/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rbitcoin_consensus::{
use rbitcoin_net::{ChainHub, SelectBudget};
use rbitcoin_primitives::Height;
use std::io;
use std::sync::Arc;
use template_distribution_sv2::{NewTemplate, SetNewPrevHash};

/// Coinbase witness reserved value the template's witness commitment is
Expand All @@ -35,8 +36,9 @@ pub(crate) struct Template {
pub n_bits: u32,
/// `n_bits` expanded, little-endian (no weak-block target).
pub target: [u8; 32],
/// Non-coinbase txs in block order, kept past mempool eviction.
pub txs: Vec<Transaction>,
/// Non-coinbase txs in block order: the mempool's own bodies, kept alive
/// past eviction.
pub txs: Vec<Arc<Transaction>>,
}

impl Template {
Expand Down
4 changes: 3 additions & 1 deletion docs/sv2-template-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ selected with.
returns `Vec<Selected { txid, fee_sat, sigop_cost }>` in mining order.
`fee_sat` is the base fee, not the `prioritisetransaction` delta.
`MempoolHub::select_block_template` applies the node's deltas under the
same lock and returns `Vec<(Transaction, Selected)>`. A larger
same lock and returns `Vec<(Arc<Transaction>, Selected)>`: the pool's own
bodies, not copies, so a template costs a pointer per tx and a body it
holds outlives eviction or replacement. A larger
`reserved_sigops` or a smaller `max_weight_wu` drops what no longer fits
and still takes a later chunk. A running sigop cost of exactly 80_000
fits; a chunk that would pass 80_000 is skipped.
Expand Down
Loading