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

View File

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

View File

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

View File

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