diff --git a/Cargo.toml b/Cargo.toml index c2d9e32..b1f94b1 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -112,7 +112,7 @@ sha2 = "0.10" tonic-prost = "0.14.2" # 须含 runtime-tokio,否则 quinn::Endpoint::client 报 no async runtime found,QUIC(Speedlanding/Soyas)无法初始化 quinn = { version = "0.11", default-features = false, features = ["rustls", "runtime-tokio"] } -lunar-lander-quic-client = "0.1" +lunar-lander-quic-client = "0.4" rcgen = "0.13" uuid = "1.11" diff --git a/src/swqos/lunarlander.rs b/src/swqos/lunarlander.rs index f513d25..a198c2e 100644 --- a/src/swqos/lunarlander.rs +++ b/src/swqos/lunarlander.rs @@ -15,8 +15,7 @@ use crate::{common::SolanaRpcClient, constants::swqos::LUNARLANDER_TIP_ACCOUNTS} use std::sync::atomic::{AtomicBool, Ordering}; use tokio::task::JoinHandle; -use lunar_lander_quic_client::LunarLanderQuicClient; -use tokio::sync::Mutex; +use lunar_lander_quic_client::{ClientOptions, LunarLanderQuicClient}; #[derive(Clone)] pub enum LunarLanderBackend { @@ -27,7 +26,7 @@ pub enum LunarLanderBackend { ping_handle: Arc>>>, stop_ping: Arc, }, - Quic(Arc>), + Quic(Arc), } #[derive(Clone)] @@ -98,12 +97,22 @@ impl LunarLanderClient { } /// Create a QUIC client (port 16888, cert CN = api_key, fire-and-forget unidirectional streams). - pub async fn new_quic(rpc_url: String, quic_endpoint: &str, api_key: String) -> Result { + pub async fn new_quic( + rpc_url: String, + quic_endpoint: &str, + api_key: String, + mev_protection: bool, + ) -> Result { let rpc_client = SolanaRpcClient::new(rpc_url); - let quic_client = LunarLanderQuicClient::connect(quic_endpoint, &api_key).await?; + let quic_client = LunarLanderQuicClient::connect_with_options( + quic_endpoint, + api_key, + quic_client_options(mev_protection), + ) + .await?; Ok(Self { rpc_client: Arc::new(rpc_client), - backend: LunarLanderBackend::Quic(Arc::new(Mutex::new(quic_client))), + backend: LunarLanderBackend::Quic(Arc::new(quic_client)), }) } @@ -155,7 +164,7 @@ impl LunarLanderClient { endpoint: &str, auth_token: &str, ) -> Result<()> { - let url = format!("{}/ping", endpoint); + let url = format!("{}/ping", endpoint.trim_end_matches('/')); let response = http_client .get(&url) .header("x-api-key", auth_token) @@ -173,13 +182,16 @@ impl LunarLanderClient { wait_confirmation: bool, ) -> Result<()> { let start_time = Instant::now(); - let signature = transaction.signatures[0]; + let signature = *transaction + .signatures + .first() + .ok_or_else(|| anyhow::anyhow!("LunarLander transaction has no signature"))?; let body_bytes = bincode_serialize(transaction) .map_err(|e| anyhow::anyhow!("LunarLander binary serialize failed: {}", e))?; match &self.backend { LunarLanderBackend::Http { endpoint, auth_token, http_client, .. } => { - let url = format!("{}/send-bin", endpoint); + let url = format!("{}/send-bin", endpoint.trim_end_matches('/')); let response = http_client .post(&url) .header("x-api-key", auth_token) @@ -210,25 +222,7 @@ impl LunarLanderClient { } } LunarLanderBackend::Quic(quic) => { - let send_result = { - let client = quic.lock().await; - client.send_transaction(&body_bytes).await - }; - if let Err(e) = send_result { - // Attempt reconnect on failure. - let mut client = quic.lock().await; - if let Err(re) = client.reconnect().await { - if crate::common::sdk_log::sdk_log_enabled() { - crate::common::sdk_log::log_swqos_submission_failed( - "LunarLander", - trade_type, - start_time.elapsed(), - format!("QUIC send failed: {}, reconnect failed: {}", e, re), - ); - } - } - return Err(anyhow::anyhow!("LunarLander QUIC send failed: {}", e)); - } + quic.send_transaction(&body_bytes).await?; if crate::common::sdk_log::sdk_log_enabled() { crate::common::sdk_log::log_swqos_submitted( "LunarLander", @@ -275,16 +269,28 @@ impl Drop for LunarLanderClient { match &self.backend { LunarLanderBackend::Http { stop_ping, ping_handle, .. } => { stop_ping.store(true, Ordering::Relaxed); - let ping_handle = ping_handle.clone(); - tokio::spawn(async move { - let mut guard = ping_handle.lock().await; - if let Some(handle) = guard.as_ref() { + if let Ok(mut guard) = ping_handle.try_lock() { + if let Some(handle) = guard.take() { handle.abort(); } - *guard = None; - }); + } } LunarLanderBackend::Quic(_) => {} } } } + +fn quic_client_options(mev_protection: bool) -> ClientOptions { + ClientOptions { mev_protect: mev_protection, ..ClientOptions::default() } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn quic_options_follow_sdk_mev_protection() { + assert!(!quic_client_options(false).mev_protect); + assert!(quic_client_options(true).mev_protect); + } +} diff --git a/src/swqos/mod.rs b/src/swqos/mod.rs index 39a417c..ea71863 100755 --- a/src/swqos/mod.rs +++ b/src/swqos/mod.rs @@ -553,9 +553,13 @@ impl SwqosConfig { let quic_endpoint = url.unwrap_or_else(|| { SWQOS_ENDPOINTS_LUNARLANDER_QUIC[region as usize].to_string() }); - let lunarlander_client = - LunarLanderClient::new_quic(rpc_url.clone(), &quic_endpoint, api_key) - .await?; + let lunarlander_client = LunarLanderClient::new_quic( + rpc_url.clone(), + &quic_endpoint, + api_key, + mev_protection, + ) + .await?; Ok(Arc::new(lunarlander_client)) } else { let endpoint = SwqosConfig::get_endpoint(SwqosType::LunarLander, region, url);