surfpool_core/rpc/
utils.rs1#![allow(dead_code)]
2
3use std::{any::type_name, sync::Arc};
4
5use base64::prelude::*;
6use bincode::Options;
7use jsonrpc_core::{Error, Result};
8use litesvm::types::TransactionMetadata;
9use solana_client::{
10 rpc_config::RpcTokenAccountsFilter,
11 rpc_custom_error::RpcCustomError,
12 rpc_filter::RpcFilterType,
13 rpc_request::{TokenAccountsFilter, MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT},
14};
15use solana_hash::Hash;
16use solana_packet::PACKET_DATA_SIZE;
17use solana_pubkey::{ParsePubkeyError, Pubkey};
18use solana_runtime::verify_precompiles::verify_precompiles;
19use solana_signature::Signature;
20use solana_transaction::sanitized::SanitizedTransaction;
21use solana_transaction_status::{
22 InnerInstruction, InnerInstructions, TransactionBinaryEncoding, UiInnerInstructions,
23};
24
25use crate::error::{SurfpoolError, SurfpoolResult};
26
27pub fn convert_transaction_metadata_from_canonical(
28 transaction_metadata: &TransactionMetadata,
29) -> surfpool_types::TransactionMetadata {
30 surfpool_types::TransactionMetadata {
31 signature: transaction_metadata.signature,
32 logs: transaction_metadata.logs.clone(),
33 inner_instructions: transaction_metadata.inner_instructions.clone(),
34 compute_units_consumed: transaction_metadata.compute_units_consumed,
35 return_data: transaction_metadata.return_data.clone(),
36 }
37}
38
39fn optimize_filters(filters: &mut [RpcFilterType]) {
40 filters.iter_mut().for_each(|filter_type| {
41 if let RpcFilterType::Memcmp(compare) = filter_type {
42 if let Err(err) = compare.convert_to_raw_bytes() {
43 warn!("Invalid filter: bytes could not be decoded, {err}");
45 }
46 }
47 })
48}
49
50fn verify_transaction(
51 transaction: &SanitizedTransaction,
52 feature_set: &Arc<solana_feature_set::FeatureSet>,
53) -> Result<()> {
54 #[allow(clippy::question_mark)]
55 if transaction.verify().is_err() {
56 return Err(RpcCustomError::TransactionSignatureVerificationFailure.into());
57 }
58
59 let move_precompile_verification_to_svm =
60 feature_set.is_active(&solana_feature_set::move_precompile_verification_to_svm::id());
61 if !move_precompile_verification_to_svm {
62 if let Err(e) = verify_precompiles(transaction, feature_set) {
63 return Err(RpcCustomError::TransactionPrecompileVerificationFailure(e).into());
64 }
65 }
66
67 Ok(())
68}
69
70fn verify_filter(input: &RpcFilterType) -> Result<()> {
71 input
72 .verify()
73 .map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
74}
75
76pub fn verify_pubkey(input: &str) -> SurfpoolResult<Pubkey> {
77 input
78 .parse()
79 .map_err(|e: ParsePubkeyError| SurfpoolError::invalid_pubkey(input, e.to_string()))
80}
81
82fn verify_hash(input: &str) -> Result<Hash> {
83 input
84 .parse()
85 .map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
86}
87
88fn verify_signature(input: &str) -> Result<Signature> {
89 input
90 .parse()
91 .map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
92}
93
94fn verify_token_account_filter(
95 token_account_filter: RpcTokenAccountsFilter,
96) -> Result<TokenAccountsFilter> {
97 match token_account_filter {
98 RpcTokenAccountsFilter::Mint(mint_str) => {
99 let mint = verify_pubkey(&mint_str)?;
100 Ok(TokenAccountsFilter::Mint(mint))
101 }
102 RpcTokenAccountsFilter::ProgramId(program_id_str) => {
103 let program_id = verify_pubkey(&program_id_str)?;
104 Ok(TokenAccountsFilter::ProgramId(program_id))
105 }
106 }
107}
108
109fn verify_and_parse_signatures_for_address_params(
110 address: String,
111 before: Option<String>,
112 until: Option<String>,
113 limit: Option<usize>,
114) -> Result<(Pubkey, Option<Signature>, Option<Signature>, usize)> {
115 let address = verify_pubkey(&address)?;
116 let before = before
117 .map(|ref before| verify_signature(before))
118 .transpose()?;
119 let until = until.map(|ref until| verify_signature(until)).transpose()?;
120 let limit = limit.unwrap_or(MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT);
121
122 if limit == 0 || limit > MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT {
123 return Err(Error::invalid_params(format!(
124 "Invalid limit; max {MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT}"
125 )));
126 }
127 Ok((address, before, until, limit))
128}
129
130const MAX_BASE58_SIZE: usize = 1683; const MAX_BASE64_SIZE: usize = 1644; pub fn decode_and_deserialize<T>(
133 encoded: String,
134 encoding: TransactionBinaryEncoding,
135) -> Result<(Vec<u8>, T)>
136where
137 T: serde::de::DeserializeOwned,
138{
139 let wire_output = match encoding {
140 TransactionBinaryEncoding::Base58 => {
141 if encoded.len() > MAX_BASE58_SIZE {
142 return Err(Error::invalid_params(format!(
143 "base58 encoded {} too large: {} bytes (max: encoded/raw {}/{})",
144 type_name::<T>(),
145 encoded.len(),
146 MAX_BASE58_SIZE,
147 PACKET_DATA_SIZE,
148 )));
149 }
150 bs58::decode(encoded)
151 .into_vec()
152 .map_err(|e| Error::invalid_params(format!("invalid base58 encoding: {e:?}")))?
153 }
154 TransactionBinaryEncoding::Base64 => {
155 if encoded.len() > MAX_BASE64_SIZE {
156 return Err(Error::invalid_params(format!(
157 "base64 encoded {} too large: {} bytes (max: encoded/raw {}/{})",
158 type_name::<T>(),
159 encoded.len(),
160 MAX_BASE64_SIZE,
161 PACKET_DATA_SIZE,
162 )));
163 }
164 BASE64_STANDARD
165 .decode(encoded)
166 .map_err(|e| Error::invalid_params(format!("invalid base64 encoding: {e:?}")))?
167 }
168 };
169 if wire_output.len() > PACKET_DATA_SIZE {
170 return Err(Error::invalid_params(format!(
171 "decoded {} too large: {} bytes (max: {} bytes)",
172 type_name::<T>(),
173 wire_output.len(),
174 PACKET_DATA_SIZE
175 )));
176 }
177 bincode::options()
178 .with_limit(PACKET_DATA_SIZE as u64)
179 .with_fixint_encoding()
180 .allow_trailing_bytes()
181 .deserialize_from(&wire_output[..])
182 .map_err(|err| {
183 Error::invalid_params(format!(
184 "failed to deserialize {}: {}",
185 type_name::<T>(),
186 &err.to_string()
187 ))
188 })
189 .map(|output| (wire_output, output))
190}
191
192pub fn transform_tx_metadata_to_ui_accounts(
193 meta: &TransactionMetadata,
194) -> Vec<UiInnerInstructions> {
195 meta.inner_instructions
196 .iter()
197 .enumerate()
198 .map(|(i, ixs)| {
199 InnerInstructions {
200 index: i as u8,
201 instructions: ixs
202 .iter()
203 .map(|ix| InnerInstruction {
204 instruction: ix.instruction.clone(),
205 stack_height: Some(ix.stack_height as u32),
206 })
207 .collect(),
208 }
209 .into()
210 })
211 .collect()
212}