const TX_BUILDER_INSTRUCTION_CAP: usize = 32;
const TX_BUILDER_LOOKUP_TABLE_CAP: usize = 8;
const TX_BUILDER_POOL_CAP: usize = 1000;
const PARALLEL_SENDER_COUNT: usize = 18;
const TX_BUILDER_POOL_PREFILL: usize = 64;
use crossbeam_queue::ArrayQueue;
use once_cell::sync::Lazy;
use solana_sdk::{
hash::Hash,
instruction::Instruction,
message::{v0, Message, VersionedMessage},
pubkey::Pubkey,
};
use solana_message::AddressLookupTableAccount;
use std::sync::Arc;
pub struct PreallocatedTxBuilder {
instructions: Vec<Instruction>,
lookup_tables: Vec<v0::MessageAddressTableLookup>,
}
impl PreallocatedTxBuilder {
fn new() -> Self {
Self {
instructions: Vec::with_capacity(TX_BUILDER_INSTRUCTION_CAP),
lookup_tables: Vec::with_capacity(TX_BUILDER_LOOKUP_TABLE_CAP),
}
}
#[inline(always)]
fn reset(&mut self) {
self.instructions.clear();
self.lookup_tables.clear();
}
#[inline(always)]
pub fn build_zero_alloc(
&mut self,
payer: &Pubkey,
instructions: &[Instruction],
address_lookup_table_account: Option<&AddressLookupTableAccount>,
recent_blockhash: Hash,
) -> VersionedMessage {
self.reset();
self.instructions.extend_from_slice(instructions);
if let Some(alt) = address_lookup_table_account {
let message = v0::Message::try_compile(
payer,
&self.instructions,
std::slice::from_ref(alt),
recent_blockhash,
)
.expect("v0 message compile failed");
VersionedMessage::V0(message)
} else {
let message =
Message::new_with_blockhash(&self.instructions, Some(payer), &recent_blockhash);
VersionedMessage::Legacy(message)
}
}
}
static TX_BUILDER_POOL: Lazy<Arc<ArrayQueue<PreallocatedTxBuilder>>> = Lazy::new(|| {
let pool = ArrayQueue::new(TX_BUILDER_POOL_CAP);
let prefill = TX_BUILDER_POOL_PREFILL.max(PARALLEL_SENDER_COUNT);
for _ in 0..prefill {
let _ = pool.push(PreallocatedTxBuilder::new());
}
Arc::new(pool)
});
#[inline(always)]
pub fn acquire_builder() -> PreallocatedTxBuilder {
TX_BUILDER_POOL.pop().unwrap_or_else(|| PreallocatedTxBuilder::new())
}
#[inline(always)]
pub fn release_builder(mut builder: PreallocatedTxBuilder) {
builder.reset();
let _ = TX_BUILDER_POOL.push(builder);
}
pub fn get_pool_stats() -> (usize, usize) {
(TX_BUILDER_POOL.len(), TX_BUILDER_POOL.capacity())
}
pub struct TxBuilderGuard {
builder: Option<PreallocatedTxBuilder>,
}
impl TxBuilderGuard {
pub fn new() -> Self {
Self { builder: Some(acquire_builder()) }
}
pub fn get_mut(&mut self) -> &mut PreallocatedTxBuilder {
self.builder.as_mut().unwrap()
}
}
impl Drop for TxBuilderGuard {
fn drop(&mut self) {
if let Some(builder) = self.builder.take() {
release_builder(builder);
}
}
}