Merge branch 'main' into feat/support-usdc-quote
# Conflicts: # examples/pumpswap_trading/src/main.rs # examples/raydium_cpmm_trading/src/main.rs # src/trading/core/executor.rs
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
//! 并行执行器
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use solana_hash::Hash;
|
||||
use solana_sdk::message::AddressLookupTableAccount;
|
||||
use solana_sdk::{
|
||||
instruction::Instruction, pubkey::Pubkey, signature::Keypair, signature::Signature,
|
||||
};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::{str::FromStr, sync::Arc, time::Instant};
|
||||
|
||||
use crate::{
|
||||
common::nonce_cache::DurableNonceInfo,
|
||||
common::{GasFeeStrategy, SolanaRpcClient},
|
||||
swqos::{SwqosClient, SwqosType, TradeType},
|
||||
trading::{common::build_transaction, MiddlewareManager},
|
||||
};
|
||||
|
||||
#[repr(align(64))]
|
||||
struct TaskResult {
|
||||
success: bool,
|
||||
signature: Signature,
|
||||
_error: Option<anyhow::Error>,
|
||||
}
|
||||
|
||||
struct ResultCollector {
|
||||
results: Arc<ArrayQueue<TaskResult>>,
|
||||
success_flag: Arc<AtomicBool>,
|
||||
completed_count: Arc<AtomicUsize>,
|
||||
total_tasks: usize,
|
||||
}
|
||||
|
||||
impl ResultCollector {
|
||||
fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
results: Arc::new(ArrayQueue::new(capacity)),
|
||||
success_flag: Arc::new(AtomicBool::new(false)),
|
||||
completed_count: Arc::new(AtomicUsize::new(0)),
|
||||
total_tasks: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
fn submit(&self, result: TaskResult) {
|
||||
// 🚀 优化:ArrayQueue 内部已保证同步,无需额外 fence
|
||||
let is_success = result.success;
|
||||
|
||||
let _ = self.results.push(result);
|
||||
|
||||
if is_success {
|
||||
self.success_flag.store(true, Ordering::Release); // Release 确保 push 可见
|
||||
}
|
||||
|
||||
self.completed_count.fetch_add(1, Ordering::Release);
|
||||
}
|
||||
|
||||
async fn wait_for_success(&self) -> Option<(bool, Signature)> {
|
||||
let start = Instant::now();
|
||||
let timeout = std::time::Duration::from_secs(30);
|
||||
|
||||
loop {
|
||||
// 🚀 Acquire 确保看到 push 的内容
|
||||
if self.success_flag.load(Ordering::Acquire) {
|
||||
while let Some(result) = self.results.pop() {
|
||||
if result.success {
|
||||
return Some((true, result.signature));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let completed = self.completed_count.load(Ordering::Acquire);
|
||||
if completed >= self.total_tasks {
|
||||
while let Some(result) = self.results.pop() {
|
||||
return Some((result.success, result.signature));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if start.elapsed() > timeout {
|
||||
return None;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_first(&self) -> Option<(bool, Signature)> {
|
||||
if let Some(result) = self.results.pop() {
|
||||
Some((result.success, result.signature))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute_parallel(
|
||||
swqos_clients: Vec<Arc<SwqosClient>>,
|
||||
payer: Arc<Keypair>,
|
||||
rpc: Option<Arc<SolanaRpcClient>>,
|
||||
instructions: Vec<Instruction>,
|
||||
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
recent_blockhash: Option<Hash>,
|
||||
durable_nonce: Option<DurableNonceInfo>,
|
||||
data_size_limit: u32,
|
||||
middleware_manager: Option<Arc<MiddlewareManager>>,
|
||||
protocol_name: &'static str,
|
||||
is_buy: bool,
|
||||
wait_transaction_confirmed: bool,
|
||||
with_tip: bool,
|
||||
gas_fee_strategy: GasFeeStrategy,
|
||||
) -> Result<(bool, Signature)> {
|
||||
let _exec_start = Instant::now();
|
||||
|
||||
if swqos_clients.is_empty() {
|
||||
return Err(anyhow!("swqos_clients is empty"));
|
||||
}
|
||||
|
||||
if !with_tip
|
||||
&& swqos_clients
|
||||
.iter()
|
||||
.find(|swqos| matches!(swqos.get_swqos_type(), SwqosType::Default))
|
||||
.is_none()
|
||||
{
|
||||
return Err(anyhow!("No Rpc Default Swqos configured."));
|
||||
}
|
||||
|
||||
let cores = core_affinity::get_core_ids().unwrap();
|
||||
let instructions = Arc::new(instructions);
|
||||
|
||||
// 预先计算所有有效的组合
|
||||
let task_configs: Vec<_> = swqos_clients
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, swqos_client)| {
|
||||
with_tip || matches!(swqos_client.get_swqos_type(), SwqosType::Default)
|
||||
})
|
||||
.flat_map(|(i, swqos_client)| {
|
||||
let gas_fee_strategy_configs = gas_fee_strategy.get_strategies(if is_buy {
|
||||
TradeType::Buy
|
||||
} else {
|
||||
TradeType::Sell
|
||||
});
|
||||
gas_fee_strategy_configs
|
||||
.into_iter()
|
||||
.filter(|config| config.0.eq(&swqos_client.get_swqos_type()))
|
||||
.map(move |config| (i, swqos_client.clone(), config))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if task_configs.is_empty() {
|
||||
return Err(anyhow!("No available gas fee strategy configs"));
|
||||
}
|
||||
|
||||
// Task preparation completed
|
||||
|
||||
let collector = Arc::new(ResultCollector::new(task_configs.len()));
|
||||
let _spawn_start = Instant::now();
|
||||
|
||||
for (i, swqos_client, gas_fee_strategy_config) in task_configs {
|
||||
let core_id = cores[i % cores.len()];
|
||||
let payer = payer.clone();
|
||||
let instructions = instructions.clone();
|
||||
let middleware_manager = middleware_manager.clone();
|
||||
let swqos_type = swqos_client.get_swqos_type();
|
||||
let tip_account_str = swqos_client.get_tip_account()?;
|
||||
let tip_account = Arc::new(Pubkey::from_str(&tip_account_str).unwrap_or_default());
|
||||
let collector = collector.clone();
|
||||
|
||||
let tip = gas_fee_strategy_config.2.tip;
|
||||
let unit_limit = gas_fee_strategy_config.2.cu_limit;
|
||||
let unit_price = gas_fee_strategy_config.2.cu_price;
|
||||
let rpc = rpc.clone();
|
||||
let durable_nonce = durable_nonce.clone();
|
||||
let address_lookup_table_account = address_lookup_table_account.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let _task_start = Instant::now();
|
||||
core_affinity::set_for_current(core_id);
|
||||
|
||||
let tip_amount = if with_tip { tip } else { 0.0 };
|
||||
|
||||
let _build_start = Instant::now();
|
||||
let transaction = match build_transaction(
|
||||
payer,
|
||||
rpc,
|
||||
unit_limit,
|
||||
unit_price,
|
||||
instructions.as_ref().clone(),
|
||||
address_lookup_table_account,
|
||||
recent_blockhash,
|
||||
data_size_limit,
|
||||
middleware_manager,
|
||||
protocol_name,
|
||||
is_buy,
|
||||
swqos_type != SwqosType::Default,
|
||||
&tip_account,
|
||||
tip_amount,
|
||||
durable_nonce,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(tx) => tx,
|
||||
Err(e) => {
|
||||
// Build transaction failed
|
||||
collector.submit(TaskResult {
|
||||
success: false,
|
||||
signature: Signature::default(),
|
||||
_error: Some(e),
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Transaction built
|
||||
|
||||
let _send_start = Instant::now();
|
||||
let success = match swqos_client
|
||||
.send_transaction(
|
||||
if is_buy { TradeType::Buy } else { TradeType::Sell },
|
||||
&transaction,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(()) => true,
|
||||
Err(_e) => {
|
||||
// Send transaction failed
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
// Transaction sent
|
||||
|
||||
if let Some(signature) = transaction.signatures.first() {
|
||||
collector.submit(TaskResult { success, signature: *signature, _error: None });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// All tasks spawned
|
||||
|
||||
if !wait_transaction_confirmed {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
|
||||
if let Some(result) = collector.get_first() {
|
||||
return Ok(result);
|
||||
}
|
||||
return Err(anyhow!("No transaction signature available"));
|
||||
}
|
||||
|
||||
if let Some(result) = collector.wait_for_success().await {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(anyhow!("All transactions failed"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
//! 执行模块
|
||||
|
||||
use anyhow::Result;
|
||||
use solana_sdk::{
|
||||
instruction::Instruction,
|
||||
pubkey::Pubkey,
|
||||
signature::Keypair,
|
||||
};
|
||||
|
||||
use crate::perf::{
|
||||
hardware_optimizations::BranchOptimizer,
|
||||
simd::SIMDMemory,
|
||||
};
|
||||
|
||||
/// 预取工具
|
||||
pub struct Prefetch;
|
||||
|
||||
impl Prefetch {
|
||||
#[inline(always)]
|
||||
pub fn instructions(instructions: &[Instruction]) {
|
||||
if instructions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 预取第一条指令
|
||||
unsafe {
|
||||
BranchOptimizer::prefetch_read_data(&instructions[0]);
|
||||
}
|
||||
|
||||
// 预取中间指令
|
||||
if instructions.len() > 2 {
|
||||
unsafe {
|
||||
BranchOptimizer::prefetch_read_data(&instructions[instructions.len() / 2]);
|
||||
}
|
||||
}
|
||||
|
||||
// 预取最后一条指令
|
||||
if instructions.len() > 1 {
|
||||
unsafe {
|
||||
BranchOptimizer::prefetch_read_data(&instructions[instructions.len() - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pubkey(pubkey: &Pubkey) {
|
||||
unsafe {
|
||||
BranchOptimizer::prefetch_read_data(pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn keypair(keypair: &Keypair) {
|
||||
unsafe {
|
||||
BranchOptimizer::prefetch_read_data(keypair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 内存操作
|
||||
pub struct MemoryOps;
|
||||
|
||||
impl MemoryOps {
|
||||
#[inline(always)]
|
||||
pub unsafe fn copy(dst: *mut u8, src: *const u8, len: usize) {
|
||||
// 优先使用 AVX2 SIMD 加速
|
||||
SIMDMemory::copy_avx2(dst, src, len);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn compare(a: *const u8, b: *const u8, len: usize) -> bool {
|
||||
// 优先使用 AVX2 SIMD 比较
|
||||
SIMDMemory::compare_avx2(a, b, len)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn zero(ptr: *mut u8, len: usize) {
|
||||
// 优先使用 AVX2 SIMD 清零
|
||||
SIMDMemory::zero_avx2(ptr, len);
|
||||
}
|
||||
}
|
||||
|
||||
/// 指令处理器
|
||||
pub struct InstructionProcessor;
|
||||
|
||||
impl InstructionProcessor {
|
||||
#[inline(always)]
|
||||
pub fn preprocess(instructions: &[Instruction]) -> Result<()> {
|
||||
// 分支预测: 大概率指令不为空
|
||||
if BranchOptimizer::unlikely(instructions.is_empty()) {
|
||||
return Err(anyhow::anyhow!("Instructions empty"));
|
||||
}
|
||||
|
||||
// 预取所有指令到缓存
|
||||
Prefetch::instructions(instructions);
|
||||
|
||||
// 分支预测: 大概率指令数量合理
|
||||
if BranchOptimizer::unlikely(instructions.len() > 64) {
|
||||
log::warn!("Large instruction count: {}", instructions.len());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn calculate_size(instructions: &[Instruction]) -> usize {
|
||||
let mut total_size = 0;
|
||||
|
||||
for instr in instructions {
|
||||
// 预取下一条指令
|
||||
unsafe {
|
||||
if let Some(next_instr) = instructions.get(total_size + 1) {
|
||||
BranchOptimizer::prefetch_read_data(next_instr);
|
||||
}
|
||||
}
|
||||
|
||||
total_size += instr.data.len();
|
||||
total_size += instr.accounts.len() * 32; // 每个账户 32 字节
|
||||
}
|
||||
|
||||
total_size
|
||||
}
|
||||
}
|
||||
|
||||
/// 执行路径
|
||||
pub struct ExecutionPath;
|
||||
|
||||
impl ExecutionPath {
|
||||
#[inline(always)]
|
||||
pub fn is_buy(input_mint: &Pubkey) -> bool {
|
||||
// 分支预测: 大概率是买入
|
||||
let is_buy = input_mint == &crate::constants::SOL_TOKEN_ACCOUNT
|
||||
|| input_mint == &crate::constants::WSOL_TOKEN_ACCOUNT
|
||||
|| input_mint == &crate::constants::USD1_TOKEN_ACCOUNT
|
||||
|| input_mint == &crate::constants::USDC_TOKEN_ACCOUNT;
|
||||
|
||||
if BranchOptimizer::likely(is_buy) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn select<T>(
|
||||
condition: bool,
|
||||
fast_path: impl FnOnce() -> T,
|
||||
slow_path: impl FnOnce() -> T,
|
||||
) -> T {
|
||||
if BranchOptimizer::likely(condition) {
|
||||
fast_path()
|
||||
} else {
|
||||
slow_path()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,25 @@ use anyhow::Result;
|
||||
use solana_sdk::signature::Signature;
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
use crate::trading::core::{
|
||||
parallel::{buy_parallel_execute, sell_parallel_execute},
|
||||
traits::TradeExecutor,
|
||||
use crate::{
|
||||
perf::syscall_bypass::SystemCallBypassManager,
|
||||
trading::core::{
|
||||
async_executor::execute_parallel,
|
||||
execution::{Prefetch, InstructionProcessor, ExecutionPath},
|
||||
traits::TradeExecutor,
|
||||
},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::{params::SwapParams, traits::InstructionBuilder};
|
||||
|
||||
/// 🚀 全局系统调用绕过管理器
|
||||
static SYSCALL_BYPASS: Lazy<SystemCallBypassManager> = Lazy::new(|| {
|
||||
use crate::perf::syscall_bypass::SyscallBypassConfig;
|
||||
SystemCallBypassManager::new(SyscallBypassConfig::default())
|
||||
.expect("Failed to create SystemCallBypassManager")
|
||||
});
|
||||
|
||||
/// Generic trade executor implementation
|
||||
pub struct GenericTradeExecutor {
|
||||
instruction_builder: Arc<dyn InstructionBuilder>,
|
||||
@@ -20,42 +32,91 @@ impl GenericTradeExecutor {
|
||||
instruction_builder: Arc<dyn InstructionBuilder>,
|
||||
protocol_name: &'static str,
|
||||
) -> Self {
|
||||
Self { instruction_builder, protocol_name }
|
||||
Self {
|
||||
instruction_builder,
|
||||
protocol_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TradeExecutor for GenericTradeExecutor {
|
||||
async fn swap(&self, params: SwapParams) -> Result<(bool, Signature)> {
|
||||
let start = Instant::now();
|
||||
// 暂时支持这三种。后续重构扩展builder 支持所有的 swap
|
||||
let is_buy = params.input_mint == crate::constants::SOL_TOKEN_ACCOUNT
|
||||
|| params.input_mint == crate::constants::WSOL_TOKEN_ACCOUNT
|
||||
|| params.input_mint == crate::constants::USDC_TOKEN_ACCOUNT
|
||||
let total_start = Instant::now();
|
||||
|
||||
// 判断买卖方向
|
||||
let is_buy = ExecutionPath::is_buy(¶ms.input_mint)
|
||||
|| (params.input_mint == crate::constants::USD1_TOKEN_ACCOUNT
|
||||
&& params.output_mint != crate::constants::WSOL_TOKEN_ACCOUNT);
|
||||
// Build instructions directly from params to avoid unnecessary cloning
|
||||
|
||||
// CPU 预取
|
||||
Prefetch::keypair(¶ms.payer);
|
||||
|
||||
// 构建指令
|
||||
let build_start = Instant::now();
|
||||
let instructions = if is_buy {
|
||||
self.instruction_builder.build_buy_instructions(¶ms).await?
|
||||
} else {
|
||||
self.instruction_builder.build_sell_instructions(¶ms).await?
|
||||
};
|
||||
let build_elapsed = build_start.elapsed();
|
||||
|
||||
// 指令预处理
|
||||
InstructionProcessor::preprocess(&instructions)?;
|
||||
|
||||
// 中间件处理
|
||||
let final_instructions = match ¶ms.middleware_manager {
|
||||
Some(middleware_manager) => middleware_manager
|
||||
.apply_middlewares_process_protocol_instructions(
|
||||
instructions,
|
||||
self.protocol_name.to_string(),
|
||||
is_buy,
|
||||
)?,
|
||||
None => instructions,
|
||||
Some(middleware_manager) => {
|
||||
middleware_manager
|
||||
.apply_middlewares_process_protocol_instructions(
|
||||
instructions,
|
||||
self.protocol_name.to_string(),
|
||||
is_buy,
|
||||
)?
|
||||
}
|
||||
None => instructions
|
||||
};
|
||||
println!("Building swap transaction instructions time cost: {:?}", start.elapsed());
|
||||
// Execute transactions in parallel
|
||||
if is_buy {
|
||||
buy_parallel_execute(params, final_instructions, self.protocol_name).await
|
||||
} else {
|
||||
sell_parallel_execute(params, final_instructions, self.protocol_name).await
|
||||
}
|
||||
|
||||
// 提交前耗时
|
||||
let before_submit_elapsed = total_start.elapsed();
|
||||
|
||||
// 并行发送交易
|
||||
let send_start = Instant::now();
|
||||
let result = execute_parallel(
|
||||
params.swqos_clients.clone(),
|
||||
params.payer,
|
||||
params.rpc,
|
||||
final_instructions,
|
||||
params.address_lookup_table_account,
|
||||
params.recent_blockhash,
|
||||
params.durable_nonce,
|
||||
if is_buy { params.data_size_limit } else { 0 },
|
||||
params.middleware_manager,
|
||||
self.protocol_name,
|
||||
is_buy,
|
||||
params.wait_transaction_confirmed,
|
||||
if is_buy { true } else { params.with_tip },
|
||||
params.gas_fee_strategy,
|
||||
)
|
||||
.await;
|
||||
let send_elapsed = send_start.elapsed();
|
||||
let total_elapsed = total_start.elapsed();
|
||||
|
||||
// 使用快速时间戳获取性能指标
|
||||
let timestamp_ns = SYSCALL_BYPASS.fast_timestamp_nanos();
|
||||
|
||||
// 在完成后一次性打印所有耗时,避免阻塞关键路径
|
||||
println!("[时间戳] {}ns", timestamp_ns);
|
||||
println!("[构建指令] 耗时: {:.3}ms ({:.0}μs)",
|
||||
build_elapsed.as_micros() as f64 / 1000.0, build_elapsed.as_micros());
|
||||
println!("[提交前耗时] {:.3}ms ({:.0}μs)",
|
||||
before_submit_elapsed.as_micros() as f64 / 1000.0, before_submit_elapsed.as_micros());
|
||||
println!("[发送交易] 耗时: {:.3}ms ({:.0}μs)",
|
||||
send_elapsed.as_micros() as f64 / 1000.0, send_elapsed.as_micros());
|
||||
println!("[总耗时] {:.3}ms ({:.0}μs)",
|
||||
total_elapsed.as_micros() as f64 / 1000.0, total_elapsed.as_micros());
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn protocol_name(&self) -> &'static str {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
pub mod params;
|
||||
pub mod traits;
|
||||
pub mod executor;
|
||||
pub mod parallel;
|
||||
pub mod async_executor;
|
||||
pub mod transaction_pool;
|
||||
pub mod execution;
|
||||
@@ -6,6 +6,7 @@ use solana_sdk::{
|
||||
use std::{str::FromStr, sync::Arc, time::Instant};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
use log::{info, debug};
|
||||
|
||||
use crate::{
|
||||
common::nonce_cache::DurableNonceInfo,
|
||||
@@ -87,7 +88,9 @@ async fn parallel_execute(
|
||||
{
|
||||
return Err(anyhow!("No Rpc Default Swqos configured."));
|
||||
}
|
||||
// 🚀 获取 CPU 核心并优化亲和性分配
|
||||
let cores = core_affinity::get_core_ids().unwrap();
|
||||
let _num_cores = cores.len();
|
||||
let mut handles: Vec<JoinHandle<Result<(bool, Signature, Option<anyhow::Error>)>>> =
|
||||
Vec::with_capacity(swqos_clients.len());
|
||||
|
||||
@@ -161,7 +164,7 @@ async fn parallel_execute(
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!(
|
||||
debug!(
|
||||
"[{:?}] - [{:?}] - Building transaction instructions: {:?}",
|
||||
swqos_type,
|
||||
gas_fee_strategy_config.1,
|
||||
@@ -186,7 +189,7 @@ async fn parallel_execute(
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
debug!(
|
||||
"[{:?}] - [{:?}] - Submitting transaction instructions: {:?}",
|
||||
swqos_type,
|
||||
gas_fee_strategy_config.1,
|
||||
@@ -247,6 +250,6 @@ async fn parallel_execute(
|
||||
}
|
||||
}
|
||||
|
||||
println!("All transactions failed: {:?}", errors);
|
||||
info!("All transactions failed: {:?}", errors);
|
||||
return Ok((false, last_signature.unwrap()));
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ use super::traits::ProtocolParams;
|
||||
use crate::common::bonding_curve::BondingCurveAccount;
|
||||
use crate::common::nonce_cache::DurableNonceInfo;
|
||||
use crate::common::spl_associated_token_account::get_associated_token_address_with_program_id;
|
||||
use crate::common::SolanaRpcClient;
|
||||
use crate::common::{GasFeeStrategy, SolanaRpcClient};
|
||||
use crate::constants::TOKEN_PROGRAM;
|
||||
use crate::swqos::{SwqosClient, TradeType};
|
||||
use crate::trading::common::get_multi_token_balances;
|
||||
use crate::trading::MiddlewareManager;
|
||||
use solana_hash::Hash;
|
||||
use solana_sdk::message::AddressLookupTableAccount;
|
||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -23,7 +24,7 @@ pub struct SwapParams {
|
||||
pub output_token_program: Option<Pubkey>,
|
||||
pub input_amount: Option<u64>,
|
||||
pub slippage_basis_points: Option<u64>,
|
||||
pub lookup_table_key: Option<Pubkey>,
|
||||
pub address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
pub recent_blockhash: Option<Hash>,
|
||||
pub data_size_limit: u32,
|
||||
pub wait_transaction_confirmed: bool,
|
||||
@@ -38,6 +39,7 @@ pub struct SwapParams {
|
||||
pub create_output_mint_ata: bool,
|
||||
pub close_output_mint_ata: bool,
|
||||
pub fixed_output_amount: Option<u64>,
|
||||
pub gas_fee_strategy: GasFeeStrategy,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SwapParams {
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
//! 🚀 交易构建器对象池
|
||||
//!
|
||||
//! 预分配交易构建器,避免运行时分配:
|
||||
//! - 对象池重用
|
||||
//! - 零分配构建
|
||||
//! - 零拷贝 I/O
|
||||
//! - 内存预热
|
||||
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use once_cell::sync::Lazy;
|
||||
use solana_sdk::{
|
||||
hash::Hash, instruction::Instruction, message::{v0, AddressLookupTableAccount, Message, VersionedMessage}, pubkey::Pubkey
|
||||
};
|
||||
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(32), // 预分配32条指令空间
|
||||
lookup_tables: Vec::with_capacity(8), // 预分配8个查找表空间
|
||||
}
|
||||
}
|
||||
|
||||
/// 重置构建器 (清空但保留容量)
|
||||
#[inline(always)]
|
||||
fn reset(&mut self) {
|
||||
self.instructions.clear();
|
||||
self.lookup_tables.clear();
|
||||
}
|
||||
|
||||
/// 🚀 零分配构建交易
|
||||
///
|
||||
/// # 交易版本自动选择
|
||||
///
|
||||
/// - **有地址查找表** (`lookup_table = Some`): 使用 `VersionedMessage::V0`
|
||||
/// - 支持地址查找表压缩
|
||||
/// - 减少交易大小
|
||||
/// - 需要 RPC 支持 V0
|
||||
///
|
||||
/// - **无地址查找表** (`lookup_table = None`): 使用 `VersionedMessage::Legacy`
|
||||
/// - 兼容所有 RPC 节点
|
||||
/// - 无需地址查找表支持
|
||||
/// - 适用于简单交易
|
||||
///
|
||||
/// # 示例
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// // 无查找表 -> Legacy 消息
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, None, blockhash);
|
||||
/// assert!(matches!(msg, VersionedMessage::Legacy(_)));
|
||||
///
|
||||
/// // 有查找表 -> V0 消息
|
||||
/// let msg = builder.build_zero_alloc(&payer, &ixs, Some(table_key), blockhash);
|
||||
/// assert!(matches!(msg, VersionedMessage::V0(_)));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn build_zero_alloc(
|
||||
&mut self,
|
||||
payer: &Pubkey,
|
||||
instructions: &[Instruction],
|
||||
address_lookup_table_account: Option<AddressLookupTableAccount>,
|
||||
recent_blockhash: Hash,
|
||||
) -> VersionedMessage {
|
||||
// 重用已分配的 vector
|
||||
self.reset();
|
||||
self.instructions.extend_from_slice(instructions);
|
||||
|
||||
// ✅ 如果有查找表,使用 V0 消息
|
||||
if let Some(address_lookup_table_account) = address_lookup_table_account {
|
||||
// self.lookup_tables.push(v0::MessageAddressTableLookup {
|
||||
// account_key: table_key,
|
||||
// writable_indexes: vec![],
|
||||
// readonly_indexes: vec![],
|
||||
// });
|
||||
|
||||
// // 使用 Message::new 创建 legacy 消息,然后提取编译后的指令
|
||||
// let legacy_msg = Message::new(&self.instructions, Some(payer));
|
||||
|
||||
// // 构建 V0 消息
|
||||
// let message = v0::Message {
|
||||
// header: legacy_msg.header,
|
||||
// account_keys: legacy_msg.account_keys,
|
||||
// recent_blockhash,
|
||||
// instructions: legacy_msg.instructions,
|
||||
// address_table_lookups: self.lookup_tables.clone(),
|
||||
// };
|
||||
|
||||
let message = v0::Message::try_compile(
|
||||
payer,
|
||||
&self.instructions,
|
||||
&[address_lookup_table_account],
|
||||
recent_blockhash,
|
||||
).expect("v0 message compile failed");
|
||||
|
||||
|
||||
VersionedMessage::V0(message)
|
||||
} else {
|
||||
// ✅ 没有查找表,使用 Legacy 消息(兼容所有 RPC)
|
||||
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(1000); // 1000个预分配构建器
|
||||
|
||||
// 预填充池
|
||||
for _ in 0..100 {
|
||||
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())
|
||||
}
|
||||
|
||||
/// 🚀 RAII 构建器包装器 (自动归还)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user