Refactor: Add transaction binary encoding enum (#23546)

This commit is contained in:
Justin Starry 2022-03-09 16:09:08 +08:00 committed by GitHub
parent 65e2d9b2f2
commit 249d926d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 42 deletions

View File

@ -39,7 +39,9 @@ use {
system_program, system_program,
transaction::Transaction, transaction::Transaction,
}, },
solana_transaction_status::{Encodable, EncodedTransaction, UiTransactionEncoding}, solana_transaction_status::{
Encodable, EncodedTransaction, TransactionBinaryEncoding, UiTransactionEncoding,
},
std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc}, std::{fmt::Write as FmtWrite, fs::File, io::Write, sync::Arc},
}; };
@ -189,7 +191,7 @@ impl WalletSubCommands for App<'_, '_> {
Arg::with_name("encoding") Arg::with_name("encoding")
.index(2) .index(2)
.value_name("ENCODING") .value_name("ENCODING")
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum .possible_values(&["base58", "base64"]) // Variants of `TransactionBinaryEncoding` enum
.default_value("base58") .default_value("base58")
.takes_value(true) .takes_value(true)
.required(true) .required(true)
@ -341,13 +343,13 @@ pub fn parse_balance(
pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> { pub fn parse_decode_transaction(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let blob = value_t_or_exit!(matches, "transaction", String); let blob = value_t_or_exit!(matches, "transaction", String);
let encoding = match matches.value_of("encoding").unwrap() { let binary_encoding = match matches.value_of("encoding").unwrap() {
"base58" => UiTransactionEncoding::Base58, "base58" => TransactionBinaryEncoding::Base58,
"base64" => UiTransactionEncoding::Base64, "base64" => TransactionBinaryEncoding::Base64,
_ => unreachable!(), _ => unreachable!(),
}; };
let encoded_transaction = EncodedTransaction::Binary(blob, encoding); let encoded_transaction = EncodedTransaction::Binary(blob, binary_encoding);
if let Some(transaction) = encoded_transaction.decode() { if let Some(transaction) = encoded_transaction.decode() {
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::DecodeTransaction(transaction), command: CliCommand::DecodeTransaction(transaction),

View File

@ -32,9 +32,9 @@ use {
}, },
solana_transaction_status::{ solana_transaction_status::{
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction, EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, EncodedTransaction,
EncodedTransactionWithStatusMeta, Rewards, TransactionConfirmationStatus, EncodedTransactionWithStatusMeta, Rewards, TransactionBinaryEncoding,
TransactionStatus, UiCompiledInstruction, UiMessage, UiRawMessage, UiTransaction, TransactionConfirmationStatus, TransactionStatus, UiCompiledInstruction, UiMessage,
UiTransactionEncoding, UiTransactionStatusMeta, UiRawMessage, UiTransaction, UiTransactionStatusMeta,
}, },
solana_version::Version, solana_version::Version,
std::{collections::HashMap, net::SocketAddr, str::FromStr, sync::RwLock}, std::{collections::HashMap, net::SocketAddr, str::FromStr, sync::RwLock},
@ -381,7 +381,7 @@ impl RpcSender for MockSender {
pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\ pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\
hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK" hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK"
.to_string(), .to_string(),
UiTransactionEncoding::Base58, TransactionBinaryEncoding::Base58,
), ),
meta: None, meta: None,
version: Some(TransactionVersion::LEGACY), version: Some(TransactionVersion::LEGACY),

View File

@ -82,8 +82,8 @@ use {
solana_transaction_status::{ solana_transaction_status::{
BlockEncodingOptions, ConfirmedBlock, ConfirmedTransactionStatusWithSignature, BlockEncodingOptions, ConfirmedBlock, ConfirmedTransactionStatusWithSignature,
ConfirmedTransactionWithStatusMeta, EncodedConfirmedTransactionWithStatusMeta, Reward, ConfirmedTransactionWithStatusMeta, EncodedConfirmedTransactionWithStatusMeta, Reward,
RewardType, TransactionConfirmationStatus, TransactionStatus, UiConfirmedBlock, RewardType, TransactionBinaryEncoding, TransactionConfirmationStatus, TransactionStatus,
UiTransactionEncoding, UiConfirmedBlock, UiTransactionEncoding,
}, },
solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}, solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY},
spl_token::{ spl_token::{
@ -3468,9 +3468,15 @@ pub mod rpc_full {
) -> Result<String> { ) -> Result<String> {
debug!("send_transaction rpc request received"); debug!("send_transaction rpc request received");
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58); let tx_encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58);
let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| {
Error::invalid_params(format!(
"unsupported encoding: {}. Supported encodings: base58, base64",
tx_encoding
))
})?;
let (wire_transaction, unsanitized_tx) = let (wire_transaction, unsanitized_tx) =
decode_and_deserialize::<VersionedTransaction>(data, encoding)?; decode_and_deserialize::<VersionedTransaction>(data, binary_encoding)?;
let preflight_commitment = config let preflight_commitment = config
.preflight_commitment .preflight_commitment
@ -3568,9 +3574,15 @@ pub mod rpc_full {
) -> Result<RpcResponse<RpcSimulateTransactionResult>> { ) -> Result<RpcResponse<RpcSimulateTransactionResult>> {
debug!("simulate_transaction rpc request received"); debug!("simulate_transaction rpc request received");
let config = config.unwrap_or_default(); let config = config.unwrap_or_default();
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58); let tx_encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58);
let binary_encoding = tx_encoding.into_binary_encoding().ok_or_else(|| {
Error::invalid_params(format!(
"unsupported encoding: {}. Supported encodings: base58, base64",
tx_encoding
))
})?;
let (_, mut unsanitized_tx) = let (_, mut unsanitized_tx) =
decode_and_deserialize::<VersionedTransaction>(data, encoding)?; decode_and_deserialize::<VersionedTransaction>(data, binary_encoding)?;
let bank = &*meta.bank(config.commitment); let bank = &*meta.bank(config.commitment);
if config.replace_recent_blockhash { if config.replace_recent_blockhash {
@ -3804,7 +3816,7 @@ pub mod rpc_full {
) -> Result<RpcResponse<Option<u64>>> { ) -> Result<RpcResponse<Option<u64>>> {
debug!("get_fee_for_message rpc request received"); debug!("get_fee_for_message rpc request received");
let (_, message) = let (_, message) =
decode_and_deserialize::<Message>(data, UiTransactionEncoding::Base64)?; decode_and_deserialize::<Message>(data, TransactionBinaryEncoding::Base64)?;
let sanitized_message = SanitizedMessage::try_from(message).map_err(|err| { let sanitized_message = SanitizedMessage::try_from(message).map_err(|err| {
Error::invalid_params(format!("invalid transaction message: {}", err)) Error::invalid_params(format!("invalid transaction message: {}", err))
})?; })?;
@ -4206,13 +4218,13 @@ const MAX_BASE58_SIZE: usize = 1683; // Golden, bump if PACKET_DATA_SIZE changes
const MAX_BASE64_SIZE: usize = 1644; // Golden, bump if PACKET_DATA_SIZE changes const MAX_BASE64_SIZE: usize = 1644; // Golden, bump if PACKET_DATA_SIZE changes
fn decode_and_deserialize<T>( fn decode_and_deserialize<T>(
encoded: String, encoded: String,
encoding: UiTransactionEncoding, encoding: TransactionBinaryEncoding,
) -> Result<(Vec<u8>, T)> ) -> Result<(Vec<u8>, T)>
where where
T: serde::de::DeserializeOwned, T: serde::de::DeserializeOwned,
{ {
let wire_output = match encoding { let wire_output = match encoding {
UiTransactionEncoding::Base58 => { TransactionBinaryEncoding::Base58 => {
inc_new_counter_info!("rpc-base58_encoded_tx", 1); inc_new_counter_info!("rpc-base58_encoded_tx", 1);
if encoded.len() > MAX_BASE58_SIZE { if encoded.len() > MAX_BASE58_SIZE {
return Err(Error::invalid_params(format!( return Err(Error::invalid_params(format!(
@ -4227,7 +4239,7 @@ where
.into_vec() .into_vec()
.map_err(|e| Error::invalid_params(format!("{:?}", e)))? .map_err(|e| Error::invalid_params(format!("{:?}", e)))?
} }
UiTransactionEncoding::Base64 => { TransactionBinaryEncoding::Base64 => {
inc_new_counter_info!("rpc-base64_encoded_tx", 1); inc_new_counter_info!("rpc-base64_encoded_tx", 1);
if encoded.len() > MAX_BASE64_SIZE { if encoded.len() > MAX_BASE64_SIZE {
return Err(Error::invalid_params(format!( return Err(Error::invalid_params(format!(
@ -4240,12 +4252,6 @@ where
} }
base64::decode(encoded).map_err(|e| Error::invalid_params(format!("{:?}", e)))? base64::decode(encoded).map_err(|e| Error::invalid_params(format!("{:?}", e)))?
} }
_ => {
return Err(Error::invalid_params(format!(
"unsupported encoding: {}. Supported encodings: base58, base64",
encoding
)))
}
}; };
if wire_output.len() > PACKET_DATA_SIZE { if wire_output.len() > PACKET_DATA_SIZE {
let err = format!( let err = format!(
@ -7763,7 +7769,8 @@ pub mod tests {
tx58_len, MAX_BASE58_SIZE, PACKET_DATA_SIZE, tx58_len, MAX_BASE58_SIZE, PACKET_DATA_SIZE,
)); ));
assert_eq!( assert_eq!(
decode_and_deserialize::<Transaction>(tx58, UiTransactionEncoding::Base58).unwrap_err(), decode_and_deserialize::<Transaction>(tx58, TransactionBinaryEncoding::Base58)
.unwrap_err(),
expect58 expect58
); );
let tx64 = base64::encode(&tx_ser); let tx64 = base64::encode(&tx_ser);
@ -7773,7 +7780,8 @@ pub mod tests {
tx64_len, MAX_BASE64_SIZE, PACKET_DATA_SIZE, tx64_len, MAX_BASE64_SIZE, PACKET_DATA_SIZE,
)); ));
assert_eq!( assert_eq!(
decode_and_deserialize::<Transaction>(tx64, UiTransactionEncoding::Base64).unwrap_err(), decode_and_deserialize::<Transaction>(tx64, TransactionBinaryEncoding::Base64)
.unwrap_err(),
expect64 expect64
); );
let too_big = PACKET_DATA_SIZE + 1; let too_big = PACKET_DATA_SIZE + 1;
@ -7784,12 +7792,14 @@ pub mod tests {
too_big, PACKET_DATA_SIZE too_big, PACKET_DATA_SIZE
)); ));
assert_eq!( assert_eq!(
decode_and_deserialize::<Transaction>(tx58, UiTransactionEncoding::Base58).unwrap_err(), decode_and_deserialize::<Transaction>(tx58, TransactionBinaryEncoding::Base58)
.unwrap_err(),
expect expect
); );
let tx64 = base64::encode(&tx_ser); let tx64 = base64::encode(&tx_ser);
assert_eq!( assert_eq!(
decode_and_deserialize::<Transaction>(tx64, UiTransactionEncoding::Base64).unwrap_err(), decode_and_deserialize::<Transaction>(tx64, TransactionBinaryEncoding::Base64)
.unwrap_err(),
expect expect
); );
} }
@ -7804,7 +7814,7 @@ pub mod tests {
let unsanitary_versioned_tx = decode_and_deserialize::<VersionedTransaction>( let unsanitary_versioned_tx = decode_and_deserialize::<VersionedTransaction>(
unsanitary_tx58, unsanitary_tx58,
UiTransactionEncoding::Base58, TransactionBinaryEncoding::Base58,
) )
.unwrap() .unwrap()
.1; .1;

View File

@ -70,6 +70,13 @@ pub trait EncodableWithMeta {
) -> Self::Encoded; ) -> Self::Encoded;
} }
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum TransactionBinaryEncoding {
Base58,
Base64,
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum UiTransactionEncoding { pub enum UiTransactionEncoding {
@ -80,6 +87,16 @@ pub enum UiTransactionEncoding {
JsonParsed, JsonParsed,
} }
impl UiTransactionEncoding {
pub fn into_binary_encoding(&self) -> Option<TransactionBinaryEncoding> {
match self {
Self::Binary | Self::Base58 => Some(TransactionBinaryEncoding::Base58),
Self::Base64 => Some(TransactionBinaryEncoding::Base64),
_ => None,
}
}
}
impl fmt::Display for UiTransactionEncoding { impl fmt::Display for UiTransactionEncoding {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let v = serde_json::to_value(self).map_err(|_| fmt::Error)?; let v = serde_json::to_value(self).map_err(|_| fmt::Error)?;
@ -801,7 +818,7 @@ pub struct EncodedConfirmedTransactionWithStatusMeta {
#[serde(rename_all = "camelCase", untagged)] #[serde(rename_all = "camelCase", untagged)]
pub enum EncodedTransaction { pub enum EncodedTransaction {
LegacyBinary(String), // Old way of expressing base-58, retained for RPC backwards compatibility LegacyBinary(String), // Old way of expressing base-58, retained for RPC backwards compatibility
Binary(String, UiTransactionEncoding), Binary(String, TransactionBinaryEncoding),
Json(UiTransaction), Json(UiTransaction),
} }
@ -818,11 +835,11 @@ impl EncodableWithMeta for VersionedTransaction {
), ),
UiTransactionEncoding::Base58 => EncodedTransaction::Binary( UiTransactionEncoding::Base58 => EncodedTransaction::Binary(
bs58::encode(bincode::serialize(self).unwrap()).into_string(), bs58::encode(bincode::serialize(self).unwrap()).into_string(),
encoding, TransactionBinaryEncoding::Base58,
), ),
UiTransactionEncoding::Base64 => EncodedTransaction::Binary( UiTransactionEncoding::Base64 => EncodedTransaction::Binary(
base64::encode(bincode::serialize(self).unwrap()), base64::encode(bincode::serialize(self).unwrap()),
encoding, TransactionBinaryEncoding::Base64,
), ),
UiTransactionEncoding::Json | UiTransactionEncoding::JsonParsed => { UiTransactionEncoding::Json | UiTransactionEncoding::JsonParsed => {
EncodedTransaction::Json(UiTransaction { EncodedTransaction::Json(UiTransaction {
@ -846,11 +863,11 @@ impl Encodable for Transaction {
), ),
UiTransactionEncoding::Base58 => EncodedTransaction::Binary( UiTransactionEncoding::Base58 => EncodedTransaction::Binary(
bs58::encode(bincode::serialize(self).unwrap()).into_string(), bs58::encode(bincode::serialize(self).unwrap()).into_string(),
encoding, TransactionBinaryEncoding::Base58,
), ),
UiTransactionEncoding::Base64 => EncodedTransaction::Binary( UiTransactionEncoding::Base64 => EncodedTransaction::Binary(
base64::encode(bincode::serialize(self).unwrap()), base64::encode(bincode::serialize(self).unwrap()),
encoding, TransactionBinaryEncoding::Base64,
), ),
UiTransactionEncoding::Json | UiTransactionEncoding::JsonParsed => { UiTransactionEncoding::Json | UiTransactionEncoding::JsonParsed => {
EncodedTransaction::Json(UiTransaction { EncodedTransaction::Json(UiTransaction {
@ -871,16 +888,13 @@ impl EncodedTransaction {
.ok() .ok()
.and_then(|bytes| bincode::deserialize(&bytes).ok()), .and_then(|bytes| bincode::deserialize(&bytes).ok()),
EncodedTransaction::Binary(blob, encoding) => match *encoding { EncodedTransaction::Binary(blob, encoding) => match *encoding {
UiTransactionEncoding::Base58 => bs58::decode(blob) TransactionBinaryEncoding::Base58 => bs58::decode(blob)
.into_vec() .into_vec()
.ok() .ok()
.and_then(|bytes| bincode::deserialize(&bytes).ok()), .and_then(|bytes| bincode::deserialize(&bytes).ok()),
UiTransactionEncoding::Base64 => base64::decode(blob) TransactionBinaryEncoding::Base64 => base64::decode(blob)
.ok() .ok()
.and_then(|bytes| bincode::deserialize(&bytes).ok()), .and_then(|bytes| bincode::deserialize(&bytes).ok()),
UiTransactionEncoding::Binary
| UiTransactionEncoding::Json
| UiTransactionEncoding::JsonParsed => None,
}, },
}; };
transaction.filter(|transaction| transaction.sanitize().is_ok()) transaction.filter(|transaction| transaction.sanitize().is_ok())
@ -1030,7 +1044,7 @@ mod test {
pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\ pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\
hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK" hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK"
.to_string(), .to_string(),
UiTransactionEncoding::Base58, TransactionBinaryEncoding::Base58,
); );
assert!(unsanitary_transaction.decode().is_none()); assert!(unsanitary_transaction.decode().is_none());
} }