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
82pub fn verify_pubkeys(input: &[String]) -> SurfpoolResult<Vec<Pubkey>> {
83 input
84 .iter()
85 .enumerate()
86 .map(|(i, s)| {
87 verify_pubkey(s)
88 .map_err(|e| SurfpoolError::invalid_pubkey_at_index(s, i, e.to_string()))
89 })
90 .collect::<SurfpoolResult<Vec<_>>>()
91}
92
93fn verify_hash(input: &str) -> Result<Hash> {
94 input
95 .parse()
96 .map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
97}
98
99fn verify_signature(input: &str) -> Result<Signature> {
100 input
101 .parse()
102 .map_err(|e| Error::invalid_params(format!("Invalid param: {e:?}")))
103}
104
105fn verify_token_account_filter(
106 token_account_filter: RpcTokenAccountsFilter,
107) -> Result<TokenAccountsFilter> {
108 match token_account_filter {
109 RpcTokenAccountsFilter::Mint(mint_str) => {
110 let mint = verify_pubkey(&mint_str)?;
111 Ok(TokenAccountsFilter::Mint(mint))
112 }
113 RpcTokenAccountsFilter::ProgramId(program_id_str) => {
114 let program_id = verify_pubkey(&program_id_str)?;
115 Ok(TokenAccountsFilter::ProgramId(program_id))
116 }
117 }
118}
119
120fn verify_and_parse_signatures_for_address_params(
121 address: String,
122 before: Option<String>,
123 until: Option<String>,
124 limit: Option<usize>,
125) -> Result<(Pubkey, Option<Signature>, Option<Signature>, usize)> {
126 let address = verify_pubkey(&address)?;
127 let before = before
128 .map(|ref before| verify_signature(before))
129 .transpose()?;
130 let until = until.map(|ref until| verify_signature(until)).transpose()?;
131 let limit = limit.unwrap_or(MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT);
132
133 if limit == 0 || limit > MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT {
134 return Err(Error::invalid_params(format!(
135 "Invalid limit; max {MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT}"
136 )));
137 }
138 Ok((address, before, until, limit))
139}
140
141const MAX_BASE58_SIZE: usize = 1683; const MAX_BASE64_SIZE: usize = 1644; pub fn decode_and_deserialize<T>(
144 encoded: String,
145 encoding: TransactionBinaryEncoding,
146) -> Result<(Vec<u8>, T)>
147where
148 T: serde::de::DeserializeOwned,
149{
150 let wire_output = match encoding {
151 TransactionBinaryEncoding::Base58 => {
152 if encoded.len() > MAX_BASE58_SIZE {
153 return Err(Error::invalid_params(format!(
154 "base58 encoded {} too large: {} bytes (max: encoded/raw {}/{})",
155 type_name::<T>(),
156 encoded.len(),
157 MAX_BASE58_SIZE,
158 PACKET_DATA_SIZE,
159 )));
160 }
161 bs58::decode(encoded)
162 .into_vec()
163 .map_err(|e| Error::invalid_params(format!("invalid base58 encoding: {e:?}")))?
164 }
165 TransactionBinaryEncoding::Base64 => {
166 if encoded.len() > MAX_BASE64_SIZE {
167 return Err(Error::invalid_params(format!(
168 "base64 encoded {} too large: {} bytes (max: encoded/raw {}/{})",
169 type_name::<T>(),
170 encoded.len(),
171 MAX_BASE64_SIZE,
172 PACKET_DATA_SIZE,
173 )));
174 }
175 BASE64_STANDARD
176 .decode(encoded)
177 .map_err(|e| Error::invalid_params(format!("invalid base64 encoding: {e:?}")))?
178 }
179 };
180 if wire_output.len() > PACKET_DATA_SIZE {
181 return Err(Error::invalid_params(format!(
182 "decoded {} too large: {} bytes (max: {} bytes)",
183 type_name::<T>(),
184 wire_output.len(),
185 PACKET_DATA_SIZE
186 )));
187 }
188 bincode::options()
189 .with_limit(PACKET_DATA_SIZE as u64)
190 .with_fixint_encoding()
191 .allow_trailing_bytes()
192 .deserialize_from(&wire_output[..])
193 .map_err(|err| {
194 Error::invalid_params(format!(
195 "failed to deserialize {}: {}",
196 type_name::<T>(),
197 &err.to_string()
198 ))
199 })
200 .map(|output| (wire_output, output))
201}
202
203pub fn transform_tx_metadata_to_ui_accounts(
204 meta: &TransactionMetadata,
205) -> Vec<UiInnerInstructions> {
206 meta.inner_instructions
207 .iter()
208 .enumerate()
209 .map(|(i, ixs)| {
210 InnerInstructions {
211 index: i as u8,
212 instructions: ixs
213 .iter()
214 .map(|ix| InnerInstruction {
215 instruction: ix.instruction.clone(),
216 stack_height: Some(ix.stack_height as u32),
217 })
218 .collect(),
219 }
220 .into()
221 })
222 .collect()
223}