RPC: Support versioned txs in getFeeForMessage API (#28217)
* RPC: Support versioned txs in getFeeForMessage API * Update sdk/program/src/message/sanitized.rs Co-authored-by: Tyera Eulberg <teulberg@gmail.com> Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
This commit is contained in:
parent
39b37e2c6f
commit
ddf95c181c
|
@ -112,7 +112,7 @@ pub fn get_fee_for_messages(
|
|||
) -> Result<u64, CliError> {
|
||||
Ok(messages
|
||||
.iter()
|
||||
.map(|message| rpc_client.get_fee_for_message(message))
|
||||
.map(|message| rpc_client.get_fee_for_message(*message))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.iter()
|
||||
.sum())
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#[cfg(not(target_os = "solana"))]
|
||||
use solana_sdk::transaction::TransactionError;
|
||||
use solana_program::message::AddressLoaderError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||
|
@ -22,13 +22,13 @@ pub enum AddressLookupError {
|
|||
}
|
||||
|
||||
#[cfg(not(target_os = "solana"))]
|
||||
impl From<AddressLookupError> for TransactionError {
|
||||
impl From<AddressLookupError> for AddressLoaderError {
|
||||
fn from(err: AddressLookupError) -> Self {
|
||||
match err {
|
||||
AddressLookupError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
|
||||
AddressLookupError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
|
||||
AddressLookupError::InvalidAccountData => Self::InvalidAddressLookupTableData,
|
||||
AddressLookupError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
|
||||
AddressLookupError::LookupTableAccountNotFound => Self::LookupTableAccountNotFound,
|
||||
AddressLookupError::InvalidAccountOwner => Self::InvalidAccountOwner,
|
||||
AddressLookupError::InvalidAccountData => Self::InvalidAccountData,
|
||||
AddressLookupError::InvalidLookupIndex => Self::InvalidLookupIndex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ use {
|
|||
http_sender::HttpSender,
|
||||
mock_sender::MockSender,
|
||||
rpc_client::{
|
||||
GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableTransaction,
|
||||
GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableMessage,
|
||||
SerializableTransaction,
|
||||
},
|
||||
rpc_sender::*,
|
||||
},
|
||||
|
@ -47,10 +48,9 @@ use {
|
|||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self},
|
||||
transaction,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
||||
|
@ -5276,28 +5276,20 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub async fn get_fee_for_message(&self, message: &Message) -> ClientResult<u64> {
|
||||
if self.get_node_version().await? < semver::Version::new(1, 9, 0) {
|
||||
let fee_calculator = self
|
||||
.get_fee_calculator_for_blockhash(&message.recent_blockhash)
|
||||
.await?
|
||||
.ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()))?;
|
||||
Ok(fee_calculator
|
||||
.lamports_per_signature
|
||||
.saturating_mul(message.header.num_required_signatures as u64))
|
||||
} else {
|
||||
let serialized_encoded =
|
||||
serialize_and_encode::<Message>(message, UiTransactionEncoding::Base64)?;
|
||||
let result = self
|
||||
.send::<Response<Option<u64>>>(
|
||||
RpcRequest::GetFeeForMessage,
|
||||
json!([serialized_encoded, self.commitment()]),
|
||||
)
|
||||
.await?;
|
||||
result
|
||||
.value
|
||||
.ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
|
||||
}
|
||||
pub async fn get_fee_for_message(
|
||||
&self,
|
||||
message: &impl SerializableMessage,
|
||||
) -> ClientResult<u64> {
|
||||
let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?;
|
||||
let result = self
|
||||
.send::<Response<Option<u64>>>(
|
||||
RpcRequest::GetFeeForMessage,
|
||||
json!([serialized_encoded, self.commitment()]),
|
||||
)
|
||||
.await?;
|
||||
result
|
||||
.value
|
||||
.ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
|
||||
}
|
||||
|
||||
pub async fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult<Hash> {
|
||||
|
|
|
@ -41,7 +41,7 @@ use {
|
|||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
hash::Hash,
|
||||
message::Message,
|
||||
message::{v0, Message as LegacyMessage},
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction},
|
||||
|
@ -68,6 +68,12 @@ impl RpcClientConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Trait used to add support for versioned messages to RPC APIs while
|
||||
/// retaining backwards compatibility
|
||||
pub trait SerializableMessage: Serialize {}
|
||||
impl SerializableMessage for LegacyMessage {}
|
||||
impl SerializableMessage for v0::Message {}
|
||||
|
||||
/// Trait used to add support for versioned transactions to RPC APIs while
|
||||
/// retaining backwards compatibility
|
||||
pub trait SerializableTransaction: Serialize {
|
||||
|
@ -3975,7 +3981,7 @@ impl RpcClient {
|
|||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub fn get_fee_for_message(&self, message: &Message) -> ClientResult<u64> {
|
||||
pub fn get_fee_for_message(&self, message: &impl SerializableMessage) -> ClientResult<u64> {
|
||||
self.invoke((self.rpc_client.as_ref()).get_fee_for_message(message))
|
||||
}
|
||||
|
||||
|
|
123
rpc/src/rpc.rs
123
rpc/src/rpc.rs
|
@ -63,7 +63,7 @@ use {
|
|||
feature_set,
|
||||
fee_calculator::FeeCalculator,
|
||||
hash::Hash,
|
||||
message::{Message, SanitizedMessage},
|
||||
message::SanitizedMessage,
|
||||
pubkey::{Pubkey, PUBKEY_BYTES},
|
||||
signature::{Keypair, Signature, Signer},
|
||||
stake::state::{StakeActivationStatus, StakeState},
|
||||
|
@ -2164,16 +2164,6 @@ impl JsonRpcRequestProcessor {
|
|||
Ok(new_response(&bank, is_valid))
|
||||
}
|
||||
|
||||
fn get_fee_for_message(
|
||||
&self,
|
||||
message: &SanitizedMessage,
|
||||
config: RpcContextConfig,
|
||||
) -> Result<RpcResponse<Option<u64>>> {
|
||||
let bank = self.get_bank_with_config(config)?;
|
||||
let fee = bank.get_fee_for_message(message);
|
||||
Ok(new_response(&bank, fee))
|
||||
}
|
||||
|
||||
fn get_stake_minimum_delegation(&self, config: RpcContextConfig) -> Result<RpcResponse<u64>> {
|
||||
let bank = self.get_bank_with_config(config)?;
|
||||
let stake_minimum_delegation =
|
||||
|
@ -3288,7 +3278,10 @@ pub mod rpc_accounts {
|
|||
// Full RPC interface that an API node is expected to provide
|
||||
// (rpc_minimal should also be provided by an API node)
|
||||
pub mod rpc_full {
|
||||
use super::*;
|
||||
use {
|
||||
super::*,
|
||||
solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
|
||||
};
|
||||
#[rpc]
|
||||
pub trait Full {
|
||||
type Metadata;
|
||||
|
@ -4001,12 +3994,21 @@ pub mod rpc_full {
|
|||
config: Option<RpcContextConfig>,
|
||||
) -> Result<RpcResponse<Option<u64>>> {
|
||||
debug!("get_fee_for_message rpc request received");
|
||||
let (_, message) =
|
||||
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))
|
||||
})?;
|
||||
meta.get_fee_for_message(&sanitized_message, config.unwrap_or_default())
|
||||
let (_, message) = decode_and_deserialize::<VersionedMessage>(
|
||||
data,
|
||||
TransactionBinaryEncoding::Base64,
|
||||
)?;
|
||||
let bank = &*meta.get_bank_with_config(config.unwrap_or_default())?;
|
||||
let sanitized_versioned_message = SanitizedVersionedMessage::try_from(message)
|
||||
.map_err(|err| {
|
||||
Error::invalid_params(format!("invalid transaction message: {}", err))
|
||||
})?;
|
||||
let sanitized_message = SanitizedMessage::try_new(sanitized_versioned_message, bank)
|
||||
.map_err(|err| {
|
||||
Error::invalid_params(format!("invalid transaction message: {}", err))
|
||||
})?;
|
||||
let fee = bank.get_fee_for_message(&sanitized_message);
|
||||
Ok(new_response(bank, fee))
|
||||
}
|
||||
|
||||
fn get_stake_minimum_delegation(
|
||||
|
@ -4649,10 +4651,13 @@ pub mod tests {
|
|||
account::{Account, WritableAccount},
|
||||
clock::MAX_RECENT_BLOCKHASHES,
|
||||
compute_budget::ComputeBudgetInstruction,
|
||||
fee_calculator::DEFAULT_BURN_PERCENT,
|
||||
fee_calculator::{FeeRateGovernor, DEFAULT_BURN_PERCENT},
|
||||
hash::{hash, Hash},
|
||||
instruction::InstructionError,
|
||||
message::{v0, v0::MessageAddressTableLookup, MessageHeader, VersionedMessage},
|
||||
message::{
|
||||
v0::{self, MessageAddressTableLookup},
|
||||
Message, MessageHeader, VersionedMessage,
|
||||
},
|
||||
nonce::{self, state::DurableNonce},
|
||||
rpc_port,
|
||||
signature::{Keypair, Signer},
|
||||
|
@ -4683,7 +4688,8 @@ pub mod tests {
|
|||
std::{borrow::Cow, collections::HashMap},
|
||||
};
|
||||
|
||||
const TEST_MINT_LAMPORTS: u64 = 1_000_000;
|
||||
const TEST_MINT_LAMPORTS: u64 = 1_000_000_000;
|
||||
const TEST_SIGNATURE_FEE: u64 = 5_000;
|
||||
const TEST_SLOTS_PER_EPOCH: u64 = DELINQUENT_VALIDATOR_SLOT_DISTANCE + 1;
|
||||
|
||||
fn create_test_request(method: &str, params: Option<serde_json::Value>) -> serde_json::Value {
|
||||
|
@ -4829,8 +4835,12 @@ pub mod tests {
|
|||
let keypair3 = Keypair::new();
|
||||
let bank = self.working_bank();
|
||||
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
|
||||
bank.transfer(rent_exempt_amount, mint_keypair, &keypair2.pubkey())
|
||||
.unwrap();
|
||||
bank.transfer(
|
||||
rent_exempt_amount + TEST_SIGNATURE_FEE,
|
||||
mint_keypair,
|
||||
&keypair2.pubkey(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (entries, signatures) = create_test_transaction_entries(
|
||||
vec![&self.mint_keypair, &keypair1, &keypair2, &keypair3],
|
||||
|
@ -5471,7 +5481,7 @@ pub mod tests {
|
|||
"context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
|
||||
"value":{
|
||||
"owner": "11111111111111111111111111111111",
|
||||
"lamports": 1_000_000,
|
||||
"lamports": TEST_MINT_LAMPORTS,
|
||||
"data": "",
|
||||
"executable": false,
|
||||
"rentEpoch": 0
|
||||
|
@ -5548,7 +5558,7 @@ pub mod tests {
|
|||
let expected = json!([
|
||||
{
|
||||
"owner": "11111111111111111111111111111111",
|
||||
"lamports": 1_000_000,
|
||||
"lamports": TEST_MINT_LAMPORTS,
|
||||
"data": ["", "base64"],
|
||||
"executable": false,
|
||||
"rentEpoch": 0
|
||||
|
@ -5580,7 +5590,7 @@ pub mod tests {
|
|||
let expected = json!([
|
||||
{
|
||||
"owner": "11111111111111111111111111111111",
|
||||
"lamports": 1_000_000,
|
||||
"lamports": TEST_MINT_LAMPORTS,
|
||||
"data": ["", "base58"],
|
||||
"executable": false,
|
||||
"rentEpoch": 0
|
||||
|
@ -6167,7 +6177,7 @@ pub mod tests {
|
|||
"value":{
|
||||
"blockhash": recent_blockhash.to_string(),
|
||||
"feeCalculator": {
|
||||
"lamportsPerSignature": 0,
|
||||
"lamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -6196,7 +6206,7 @@ pub mod tests {
|
|||
"value": {
|
||||
"blockhash": recent_blockhash.to_string(),
|
||||
"feeCalculator": {
|
||||
"lamportsPerSignature": 0,
|
||||
"lamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||
},
|
||||
"lastValidSlot": MAX_RECENT_BLOCKHASHES,
|
||||
"lastValidBlockHeight": MAX_RECENT_BLOCKHASHES,
|
||||
|
@ -6276,9 +6286,9 @@ pub mod tests {
|
|||
"value":{
|
||||
"feeRateGovernor": {
|
||||
"burnPercent": DEFAULT_BURN_PERCENT,
|
||||
"maxLamportsPerSignature": 0,
|
||||
"minLamportsPerSignature": 0,
|
||||
"targetLamportsPerSignature": 0,
|
||||
"maxLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||
"minLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||
"targetLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||
"targetSignaturesPerSlot": 0
|
||||
}
|
||||
},
|
||||
|
@ -6547,6 +6557,7 @@ pub mod tests {
|
|||
genesis_config.rent.exemption_threshold = 2.0;
|
||||
genesis_config.epoch_schedule =
|
||||
EpochSchedule::custom(TEST_SLOTS_PER_EPOCH, TEST_SLOTS_PER_EPOCH, false);
|
||||
genesis_config.fee_rate_governor = FeeRateGovernor::new(TEST_SIGNATURE_FEE, 0);
|
||||
|
||||
let bank = Bank::new_for_tests(&genesis_config);
|
||||
(
|
||||
|
@ -8457,7 +8468,7 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(),
|
||||
Error::invalid_params(
|
||||
"invalid transaction: Transaction loads an address table account that doesn't exist".to_string(),
|
||||
"invalid transaction: Transaction version is unsupported".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -8479,6 +8490,54 @@ pub mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_fee_for_message() {
|
||||
let rpc = RpcHandler::start();
|
||||
let bank = rpc.working_bank();
|
||||
// Slot hashes is necessary for processing versioned txs.
|
||||
bank.set_sysvar_for_tests(&SlotHashes::default());
|
||||
// Correct blockhash is needed because fees are specific to blockhashes
|
||||
let recent_blockhash = bank.last_blockhash();
|
||||
|
||||
{
|
||||
let legacy_msg = VersionedMessage::Legacy(Message {
|
||||
header: MessageHeader {
|
||||
num_required_signatures: 1,
|
||||
..MessageHeader::default()
|
||||
},
|
||||
recent_blockhash,
|
||||
account_keys: vec![Pubkey::new_unique()],
|
||||
..Message::default()
|
||||
});
|
||||
|
||||
let request = create_test_request(
|
||||
"getFeeForMessage",
|
||||
Some(json!([base64::encode(&serialize(&legacy_msg).unwrap())])),
|
||||
);
|
||||
let response: RpcResponse<u64> = parse_success_result(rpc.handle_request_sync(request));
|
||||
assert_eq!(response.value, TEST_SIGNATURE_FEE);
|
||||
}
|
||||
|
||||
{
|
||||
let v0_msg = VersionedMessage::V0(v0::Message {
|
||||
header: MessageHeader {
|
||||
num_required_signatures: 1,
|
||||
..MessageHeader::default()
|
||||
},
|
||||
recent_blockhash,
|
||||
account_keys: vec![Pubkey::new_unique()],
|
||||
..v0::Message::default()
|
||||
});
|
||||
|
||||
let request = create_test_request(
|
||||
"getFeeForMessage",
|
||||
Some(json!([base64::encode(&serialize(&v0_msg).unwrap())])),
|
||||
);
|
||||
let response: RpcResponse<u64> = parse_success_result(rpc.handle_request_sync(request));
|
||||
assert_eq!(response.value, TEST_SIGNATURE_FEE);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rpc_get_recent_prioritization_fees() {
|
||||
fn wait_for_cache_blocks(cache: &PrioritizationFeeCache, num_blocks: usize) {
|
||||
|
|
|
@ -2,8 +2,11 @@ use {
|
|||
super::Bank,
|
||||
solana_address_lookup_table_program::error::AddressLookupError,
|
||||
solana_sdk::{
|
||||
message::v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||
transaction::{AddressLoader, Result as TransactionResult, TransactionError},
|
||||
message::{
|
||||
v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||
AddressLoaderError,
|
||||
},
|
||||
transaction::AddressLoader,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -11,9 +14,9 @@ impl AddressLoader for &Bank {
|
|||
fn load_addresses(
|
||||
self,
|
||||
address_table_lookups: &[MessageAddressTableLookup],
|
||||
) -> TransactionResult<LoadedAddresses> {
|
||||
) -> Result<LoadedAddresses, AddressLoaderError> {
|
||||
if !self.versioned_tx_message_enabled() {
|
||||
return Err(TransactionError::UnsupportedVersion);
|
||||
return Err(AddressLoaderError::Disabled);
|
||||
}
|
||||
|
||||
let slot_hashes = self
|
||||
|
@ -21,7 +24,7 @@ impl AddressLoader for &Bank {
|
|||
.read()
|
||||
.unwrap()
|
||||
.get_slot_hashes()
|
||||
.map_err(|_| TransactionError::AccountNotFound)?;
|
||||
.map_err(|_| AddressLoaderError::SlotHashesSysvarNotFound)?;
|
||||
|
||||
Ok(address_table_lookups
|
||||
.iter()
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use {
|
||||
super::v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||
pub enum AddressLoaderError {
|
||||
/// Address loading from lookup tables is disabled
|
||||
#[error("Address loading from lookup tables is disabled")]
|
||||
Disabled,
|
||||
|
||||
/// Failed to load slot hashes sysvar
|
||||
#[error("Failed to load slot hashes sysvar")]
|
||||
SlotHashesSysvarNotFound,
|
||||
|
||||
/// Attempted to lookup addresses from a table that does not exist
|
||||
#[error("Attempted to lookup addresses from a table that does not exist")]
|
||||
LookupTableAccountNotFound,
|
||||
|
||||
/// Attempted to lookup addresses from an account owned by the wrong program
|
||||
#[error("Attempted to lookup addresses from an account owned by the wrong program")]
|
||||
InvalidAccountOwner,
|
||||
|
||||
/// Attempted to lookup addresses from an invalid account
|
||||
#[error("Attempted to lookup addresses from an invalid account")]
|
||||
InvalidAccountData,
|
||||
|
||||
/// Address lookup contains an invalid index
|
||||
#[error("Address lookup contains an invalid index")]
|
||||
InvalidLookupIndex,
|
||||
}
|
||||
|
||||
pub trait AddressLoader: Clone {
|
||||
fn load_addresses(
|
||||
self,
|
||||
lookups: &[MessageAddressTableLookup],
|
||||
) -> Result<LoadedAddresses, AddressLoaderError>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SimpleAddressLoader {
|
||||
Disabled,
|
||||
Enabled(LoadedAddresses),
|
||||
}
|
||||
|
||||
impl AddressLoader for SimpleAddressLoader {
|
||||
fn load_addresses(
|
||||
self,
|
||||
_lookups: &[MessageAddressTableLookup],
|
||||
) -> Result<LoadedAddresses, AddressLoaderError> {
|
||||
match self {
|
||||
Self::Disabled => Err(AddressLoaderError::Disabled),
|
||||
Self::Enabled(loaded_addresses) => Ok(loaded_addresses),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,10 +44,11 @@ pub mod legacy;
|
|||
#[path = ""]
|
||||
mod non_bpf_modules {
|
||||
mod account_keys;
|
||||
mod address_loader;
|
||||
mod sanitized;
|
||||
mod versions;
|
||||
|
||||
pub use {account_keys::*, sanitized::*, versions::*};
|
||||
pub use {account_keys::*, address_loader::*, sanitized::*, versions::*};
|
||||
}
|
||||
|
||||
use compiled_keys::*;
|
||||
|
|
|
@ -5,7 +5,8 @@ use {
|
|||
message::{
|
||||
legacy,
|
||||
v0::{self, LoadedAddresses},
|
||||
AccountKeys, MessageHeader,
|
||||
AccountKeys, AddressLoader, AddressLoaderError, MessageHeader,
|
||||
SanitizedVersionedMessage, VersionedMessage,
|
||||
},
|
||||
nonce::NONCED_TX_MARKER_IX_INDEX,
|
||||
program_utils::limited_deserialize,
|
||||
|
@ -81,6 +82,8 @@ pub enum SanitizeMessageError {
|
|||
ValueOutOfBounds,
|
||||
#[error("invalid value")]
|
||||
InvalidValue,
|
||||
#[error("{0}")]
|
||||
AddressLoaderError(#[from] AddressLoaderError),
|
||||
}
|
||||
|
||||
impl From<SanitizeError> for SanitizeMessageError {
|
||||
|
@ -102,6 +105,25 @@ impl TryFrom<legacy::Message> for SanitizedMessage {
|
|||
}
|
||||
|
||||
impl SanitizedMessage {
|
||||
/// Create a sanitized message from a sanitized versioned message.
|
||||
/// If the input message uses address tables, attempt to look up the
|
||||
/// address for each table index.
|
||||
pub fn try_new(
|
||||
sanitized_msg: SanitizedVersionedMessage,
|
||||
address_loader: impl AddressLoader,
|
||||
) -> Result<Self, SanitizeMessageError> {
|
||||
Ok(match sanitized_msg.message {
|
||||
VersionedMessage::Legacy(message) => {
|
||||
SanitizedMessage::Legacy(LegacyMessage::new(message))
|
||||
}
|
||||
VersionedMessage::V0(message) => {
|
||||
let loaded_addresses =
|
||||
address_loader.load_addresses(&message.address_table_lookups)?;
|
||||
SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Return true if this message contains duplicate account keys
|
||||
pub fn has_duplicates(&self) -> bool {
|
||||
match self {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use {
|
||||
crate::{
|
||||
instruction::InstructionError, message::SanitizeMessageError, sanitize::SanitizeError,
|
||||
instruction::InstructionError,
|
||||
message::{AddressLoaderError, SanitizeMessageError},
|
||||
sanitize::SanitizeError,
|
||||
},
|
||||
serde::Serialize,
|
||||
thiserror::Error,
|
||||
|
@ -156,7 +158,23 @@ impl From<SanitizeError> for TransactionError {
|
|||
}
|
||||
|
||||
impl From<SanitizeMessageError> for TransactionError {
|
||||
fn from(_err: SanitizeMessageError) -> Self {
|
||||
Self::SanitizeFailure
|
||||
fn from(err: SanitizeMessageError) -> Self {
|
||||
match err {
|
||||
SanitizeMessageError::AddressLoaderError(err) => Self::from(err),
|
||||
_ => Self::SanitizeFailure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AddressLoaderError> for TransactionError {
|
||||
fn from(err: AddressLoaderError) -> Self {
|
||||
match err {
|
||||
AddressLoaderError::Disabled => Self::UnsupportedVersion,
|
||||
AddressLoaderError::SlotHashesSysvarNotFound => Self::AccountNotFound,
|
||||
AddressLoaderError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
|
||||
AddressLoaderError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
|
||||
AddressLoaderError::InvalidAccountData => Self::InvalidAddressLookupTableData,
|
||||
AddressLoaderError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#![cfg(feature = "full")]
|
||||
|
||||
pub use crate::message::{AddressLoader, SimpleAddressLoader};
|
||||
use {
|
||||
super::SanitizedVersionedTransaction,
|
||||
crate::{
|
||||
hash::Hash,
|
||||
message::{
|
||||
legacy,
|
||||
v0::{self, LoadedAddresses, MessageAddressTableLookup},
|
||||
v0::{self, LoadedAddresses},
|
||||
LegacyMessage, SanitizedMessage, VersionedMessage,
|
||||
},
|
||||
precompiles::verify_if_precompile,
|
||||
|
@ -43,25 +44,6 @@ pub struct TransactionAccountLocks<'a> {
|
|||
pub writable: Vec<&'a Pubkey>,
|
||||
}
|
||||
|
||||
pub trait AddressLoader: Clone {
|
||||
fn load_addresses(self, lookups: &[MessageAddressTableLookup]) -> Result<LoadedAddresses>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SimpleAddressLoader {
|
||||
Disabled,
|
||||
Enabled(LoadedAddresses),
|
||||
}
|
||||
|
||||
impl AddressLoader for SimpleAddressLoader {
|
||||
fn load_addresses(self, _lookups: &[MessageAddressTableLookup]) -> Result<LoadedAddresses> {
|
||||
match self {
|
||||
Self::Disabled => Err(TransactionError::AddressLookupTableNotFound),
|
||||
Self::Enabled(loaded_addresses) => Ok(loaded_addresses),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type that represents whether the transaction message has been precomputed or
|
||||
/// not.
|
||||
pub enum MessageHash {
|
||||
|
|
Loading…
Reference in New Issue