sol_parser_sdk/logs/
optimized_matcher.rs

1//! 优化的日志匹配器 - 高性能日志识别
2//!
3//! 使用预计算的字符串常量和优化的匹配策略
4
5use crate::core::events::DexEvent;
6use crate::grpc::types::{EventType, EventTypeFilter};
7use solana_sdk::signature::Signature;
8use memchr::memmem;
9use once_cell::sync::Lazy;
10use super::perf_hints::{likely, unlikely};
11
12/// SIMD 优化的字符串查找器 - 预编译一次,重复使用
13static PUMPFUN_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"));
14static RAYDIUM_AMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"));
15static RAYDIUM_CLMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6"));
16static RAYDIUM_CPMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD"));
17static BONK_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b"));
18static PROGRAM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program"));
19static PROGRAM_DATA_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: "));
20static PUMPFUN_CREATE_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"Program data: GB7IKAUcB3c"));
21static WHIRL_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"whirL"));
22static METEORA_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"meteora"));
23static METEORA_LB_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"LB"));
24static METEORA_DLMM_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"DLMM"));
25static PUMPSWAP_LOWER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"pumpswap"));
26static PUMPSWAP_UPPER_FINDER: Lazy<memmem::Finder> = Lazy::new(|| memmem::Finder::new(b"PumpSwap"));
27
28/// 预计算的程序 ID 字符串常量
29pub mod program_id_strings {
30    pub const PUMPFUN_INVOKE: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P invoke";
31    pub const PUMPFUN_SUCCESS: &str = "Program 6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P success";
32    pub const PUMPFUN_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";
33
34    pub const BONK_INVOKE: &str = "Program Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b invoke";
35    pub const BONK_SUCCESS: &str = "Program Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b success";
36    pub const BONK_ID: &str = "Bxby5A7E8xPDGGc3FyJw7m5eK5aqNVLU83H2zLTQDH1b";
37
38    pub const RAYDIUM_CLMM_INVOKE: &str = "Program CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6 invoke";
39    pub const RAYDIUM_CLMM_SUCCESS: &str = "Program CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6 success";
40    pub const RAYDIUM_CLMM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUQpMdRBFSzKNT3t4ivN6";
41
42    pub const RAYDIUM_CPMM_INVOKE: &str = "Program CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD invoke";
43    pub const RAYDIUM_CPMM_SUCCESS: &str = "Program CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD success";
44    pub const RAYDIUM_CPMM_ID: &str = "CPMDWBwJDtYax9qKcQP3CtKz7tHjJsN3H8hGrYVD9mZD";
45
46    pub const RAYDIUM_AMM_V4_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
47
48    // 常用的日志模式
49    pub const PROGRAM_DATA: &str = "Program data: ";
50    pub const PROGRAM_LOG: &str = "Program log: ";
51
52    // PumpFun 事件 discriminator (base64)
53    pub const PUMPFUN_CREATE_DISCRIMINATOR: &str = "GB7IKAUcB3c";  // [24, 30, 200, 40, 5, 28, 7, 119]
54}
55
56/// 快速日志类型枚举
57#[derive(Debug, Copy, Clone, PartialEq)]
58pub enum LogType {
59    PumpFun,
60    RaydiumLaunchpad,
61    PumpAmm,
62    RaydiumClmm,
63    RaydiumCpmm,
64    RaydiumAmm,
65    OrcaWhirlpool,
66    MeteoraAmm,
67    MeteoraDamm,
68    MeteoraDlmm,
69    Unknown,
70}
71
72/// SIMD 优化的日志类型检测器 - 激进早期退出
73#[inline(always)]
74pub fn detect_log_type(log: &str) -> LogType {
75    let log_bytes = log.as_bytes();
76
77    // 第一步:快速长度检查 - 太短的日志直接跳过
78    if log_bytes.len() < 20 {
79        return LogType::Unknown;
80    }
81
82    // 第二步:检查是否有 "Program data:" - 这是事件日志的标志
83    let has_program_data = PROGRAM_DATA_FINDER.find(log_bytes).is_some();
84
85    // 只有 "Program data:" 日志才可能是交易事件
86    if unlikely(!has_program_data) {
87        return LogType::Unknown;
88    }
89
90    // 第三步:使用 SIMD 快速检测具体协议
91    // Raydium AMM - 高频,有明确程序ID(最常见)
92    if likely(RAYDIUM_AMM_FINDER.find(log_bytes).is_some()) {
93        return LogType::RaydiumAmm;
94    }
95
96    // Raydium CLMM
97    if RAYDIUM_CLMM_FINDER.find(log_bytes).is_some() {
98        return LogType::RaydiumClmm;
99    }
100
101    // Raydium CPMM
102    if RAYDIUM_CPMM_FINDER.find(log_bytes).is_some() {
103        return LogType::RaydiumCpmm;
104    }
105
106    // Raydium Launchpad (Bonk)
107    if BONK_FINDER.find(log_bytes).is_some() {
108        return LogType::RaydiumLaunchpad;
109    }
110
111    // Orca Whirlpool
112    if WHIRL_FINDER.find(log_bytes).is_some() {
113        return LogType::OrcaWhirlpool;
114    }
115
116    // Meteora - SIMD 优化
117    if let Some(pos) = METEORA_FINDER.find(log_bytes) {
118        let rest = &log_bytes[pos..];
119        if METEORA_LB_FINDER.find(rest).is_some() {
120            return LogType::MeteoraDamm;
121        } else if METEORA_DLMM_FINDER.find(rest).is_some() {
122            return LogType::MeteoraDlmm;
123        } else {
124            return LogType::MeteoraAmm;
125        }
126    }
127
128    // Pump AMM
129    if PUMPSWAP_LOWER_FINDER.find(log_bytes).is_some() || PUMPSWAP_UPPER_FINDER.find(log_bytes).is_some() {
130        return LogType::PumpAmm;
131    }
132
133    // PumpFun - 特殊处理:可能有程序ID,也可能直接是base64数据
134    // 1. 先检查是否包含程序ID(高频事件)
135    if likely(PUMPFUN_FINDER.find(log_bytes).is_some()) {
136        return LogType::PumpFun;
137    }
138
139    // 2. 兜底:有 "Program data:" 但无法识别协议的,尝试作为 PumpFun 解析
140    // PumpFun的日志格式:Program data: <base64>
141    // 只要日志够长且包含Program data,就认为可能是PumpFun
142    if log.len() > 30 {
143        return LogType::PumpFun;
144    }
145
146    LogType::Unknown
147}
148
149/// 优化的统一日志解析器(带事件类型过滤)
150#[inline(always)]
151pub fn parse_log_optimized(
152    log: &str,
153    signature: Signature,
154    slot: u64,
155    tx_index: u64,
156    block_time: Option<i64>,
157    grpc_recv_us: i64,
158    event_type_filter: Option<&EventTypeFilter>,
159    is_created_buy: bool,
160) -> Option<DexEvent> {
161    // 快速类型检测
162    let log_type = detect_log_type(log);
163
164    // 提前过滤和解析
165    if let Some(filter) = event_type_filter {
166        if let Some(ref include_only) = filter.include_only {
167            // PumpFun Trade 超快路径(最常见情况)
168            if likely(include_only.len() == 1 && include_only[0] == EventType::PumpFunTrade) {
169                if likely(log_type == LogType::PumpFun) {
170                    // 使用优化解析器:栈分配,无堆分配,内联函数
171                    return crate::logs::parse_pumpfun_trade(
172                        log, signature, slot, tx_index, block_time, grpc_recv_us, is_created_buy
173                    );
174                } else {
175                    return None;
176                }
177            }
178
179            // 提前过滤:如果该协议的所有事件都不在过滤范围内,直接跳过解析
180            let should_parse = match log_type {
181                LogType::PumpFun => include_only.iter().any(|t| matches!(t,
182                    EventType::PumpFunTrade | EventType::PumpFunCreate |
183                    EventType::PumpFunComplete | EventType::PumpFunMigrate)),
184                LogType::RaydiumAmm => include_only.iter().any(|t| matches!(t,
185                    EventType::RaydiumAmmV4Swap | EventType::RaydiumAmmV4Deposit |
186                    EventType::RaydiumAmmV4Withdraw | EventType::RaydiumAmmV4Initialize2 |
187                    EventType::RaydiumAmmV4WithdrawPnl)),
188                LogType::RaydiumClmm => include_only.iter().any(|t| matches!(t,
189                    EventType::RaydiumClmmSwap | EventType::RaydiumClmmCreatePool |
190                    EventType::RaydiumClmmOpenPosition | EventType::RaydiumClmmClosePosition |
191                    EventType::RaydiumClmmIncreaseLiquidity | EventType::RaydiumClmmDecreaseLiquidity |
192                    EventType::RaydiumClmmOpenPositionWithTokenExtNft | EventType::RaydiumClmmCollectFee)),
193                LogType::RaydiumCpmm => include_only.iter().any(|t| matches!(t,
194                    EventType::RaydiumCpmmSwap | EventType::RaydiumCpmmDeposit |
195                    EventType::RaydiumCpmmWithdraw | EventType::RaydiumCpmmInitialize)),
196                _ => true,
197            };
198
199            if unlikely(!should_parse) {
200                return None;
201            }
202        }
203    }
204
205    // 根据类型直接调用相应的解析器,传入grpc_recv_us
206    let event = match log_type {
207        LogType::PumpFun => crate::logs::parse_pumpfun_log(log, signature, slot, tx_index, block_time, grpc_recv_us, is_created_buy),
208        LogType::RaydiumLaunchpad => crate::logs::parse_raydium_launchpad_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
209        LogType::PumpAmm => crate::logs::parse_pump_amm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
210        LogType::RaydiumClmm => crate::logs::parse_raydium_clmm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
211        LogType::RaydiumCpmm => crate::logs::parse_raydium_cpmm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
212        LogType::RaydiumAmm => crate::logs::parse_raydium_amm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
213        LogType::OrcaWhirlpool => crate::logs::parse_orca_whirlpool_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
214        LogType::MeteoraAmm => crate::logs::parse_meteora_amm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
215        LogType::MeteoraDamm => crate::logs::parse_meteora_damm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
216        LogType::MeteoraDlmm => crate::logs::parse_meteora_dlmm_log(log, signature, slot, tx_index, block_time, grpc_recv_us),
217        LogType::Unknown => None,
218    };
219
220    // 应用精确的事件类型过滤
221    if let Some(event) = event {
222        if let Some(filter) = event_type_filter {
223            let event_type = match &event {
224                DexEvent::PumpFunTrade(_) => EventType::PumpFunTrade,
225                DexEvent::PumpFunCreate(_) => EventType::PumpFunCreate,
226                DexEvent::PumpFunComplete(_) => EventType::PumpFunComplete,
227                DexEvent::PumpFunMigrate(_) => EventType::PumpFunMigrate,
228                DexEvent::RaydiumAmmV4Swap(_) => EventType::RaydiumAmmV4Swap,
229                DexEvent::RaydiumClmmSwap(_) => EventType::RaydiumClmmSwap,
230                DexEvent::RaydiumCpmmSwap(_) => EventType::RaydiumCpmmSwap,
231                _ => return Some(event),
232            };
233
234            if likely(filter.should_include(event_type)) {
235                return Some(event);
236            } else {
237                return None;
238            }
239        }
240        Some(event)
241    } else {
242        None
243    }
244}
245
246/// SIMD 优化的 PumpFun Create 事件检测(扫描所有日志)
247#[inline]
248pub fn detect_pumpfun_create(logs: &[String]) -> bool {
249    logs.iter().any(|log| {
250        PUMPFUN_CREATE_FINDER.find(log.as_bytes()).is_some()
251    })
252}
253
254/// 性能测试辅助函数
255#[cfg(test)]
256pub mod performance_tests {
257    use super::*;
258    use std::time::Instant;
259
260    pub fn benchmark_log_detection(log: &str, iterations: usize) -> (u128, u128) {
261        // 测试原始方法
262        let start = Instant::now();
263        for _ in 0..iterations {
264            let _ = crate::logs::parse_log_unified(log, Default::default(), 0, None);
265        }
266        let old_time = start.elapsed().as_nanos();
267
268        // 测试优化方法
269        let start = Instant::now();
270        for _ in 0..iterations {
271            let _ = parse_log_optimized(log, Default::default(), 0, None);
272        }
273        let new_time = start.elapsed().as_nanos();
274
275        (old_time, new_time)
276    }
277}