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> {
|
) -> Result<u64, CliError> {
|
||||||
Ok(messages
|
Ok(messages
|
||||||
.iter()
|
.iter()
|
||||||
.map(|message| rpc_client.get_fee_for_message(message))
|
.map(|message| rpc_client.get_fee_for_message(*message))
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.iter()
|
.iter()
|
||||||
.sum())
|
.sum())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
use solana_sdk::transaction::TransactionError;
|
use solana_program::message::AddressLoaderError;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||||
|
@ -22,13 +22,13 @@ pub enum AddressLookupError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "solana"))]
|
#[cfg(not(target_os = "solana"))]
|
||||||
impl From<AddressLookupError> for TransactionError {
|
impl From<AddressLookupError> for AddressLoaderError {
|
||||||
fn from(err: AddressLookupError) -> Self {
|
fn from(err: AddressLookupError) -> Self {
|
||||||
match err {
|
match err {
|
||||||
AddressLookupError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
|
AddressLookupError::LookupTableAccountNotFound => Self::LookupTableAccountNotFound,
|
||||||
AddressLookupError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
|
AddressLookupError::InvalidAccountOwner => Self::InvalidAccountOwner,
|
||||||
AddressLookupError::InvalidAccountData => Self::InvalidAddressLookupTableData,
|
AddressLookupError::InvalidAccountData => Self::InvalidAccountData,
|
||||||
AddressLookupError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
|
AddressLookupError::InvalidLookupIndex => Self::InvalidLookupIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ use {
|
||||||
http_sender::HttpSender,
|
http_sender::HttpSender,
|
||||||
mock_sender::MockSender,
|
mock_sender::MockSender,
|
||||||
rpc_client::{
|
rpc_client::{
|
||||||
GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableTransaction,
|
GetConfirmedSignaturesForAddress2Config, RpcClientConfig, SerializableMessage,
|
||||||
|
SerializableTransaction,
|
||||||
},
|
},
|
||||||
rpc_sender::*,
|
rpc_sender::*,
|
||||||
},
|
},
|
||||||
|
@ -47,10 +48,9 @@ use {
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{self},
|
transaction,
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus,
|
||||||
|
@ -5276,18 +5276,11 @@ impl RpcClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
pub async fn get_fee_for_message(&self, message: &Message) -> ClientResult<u64> {
|
pub async fn get_fee_for_message(
|
||||||
if self.get_node_version().await? < semver::Version::new(1, 9, 0) {
|
&self,
|
||||||
let fee_calculator = self
|
message: &impl SerializableMessage,
|
||||||
.get_fee_calculator_for_blockhash(&message.recent_blockhash)
|
) -> ClientResult<u64> {
|
||||||
.await?
|
let serialized_encoded = serialize_and_encode(message, UiTransactionEncoding::Base64)?;
|
||||||
.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
|
let result = self
|
||||||
.send::<Response<Option<u64>>>(
|
.send::<Response<Option<u64>>>(
|
||||||
RpcRequest::GetFeeForMessage,
|
RpcRequest::GetFeeForMessage,
|
||||||
|
@ -5298,7 +5291,6 @@ impl RpcClient {
|
||||||
.value
|
.value
|
||||||
.ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
|
.ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult<Hash> {
|
pub async fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult<Hash> {
|
||||||
let mut num_retries = 0;
|
let mut num_retries = 0;
|
||||||
|
|
|
@ -41,7 +41,7 @@ use {
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::{v0, Message as LegacyMessage},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{self, uses_durable_nonce, Transaction, VersionedTransaction},
|
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
|
/// Trait used to add support for versioned transactions to RPC APIs while
|
||||||
/// retaining backwards compatibility
|
/// retaining backwards compatibility
|
||||||
pub trait SerializableTransaction: Serialize {
|
pub trait SerializableTransaction: Serialize {
|
||||||
|
@ -3975,7 +3981,7 @@ impl RpcClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[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))
|
self.invoke((self.rpc_client.as_ref()).get_fee_for_message(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
117
rpc/src/rpc.rs
117
rpc/src/rpc.rs
|
@ -63,7 +63,7 @@ use {
|
||||||
feature_set,
|
feature_set,
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{Message, SanitizedMessage},
|
message::SanitizedMessage,
|
||||||
pubkey::{Pubkey, PUBKEY_BYTES},
|
pubkey::{Pubkey, PUBKEY_BYTES},
|
||||||
signature::{Keypair, Signature, Signer},
|
signature::{Keypair, Signature, Signer},
|
||||||
stake::state::{StakeActivationStatus, StakeState},
|
stake::state::{StakeActivationStatus, StakeState},
|
||||||
|
@ -2164,16 +2164,6 @@ impl JsonRpcRequestProcessor {
|
||||||
Ok(new_response(&bank, is_valid))
|
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>> {
|
fn get_stake_minimum_delegation(&self, config: RpcContextConfig) -> Result<RpcResponse<u64>> {
|
||||||
let bank = self.get_bank_with_config(config)?;
|
let bank = self.get_bank_with_config(config)?;
|
||||||
let stake_minimum_delegation =
|
let stake_minimum_delegation =
|
||||||
|
@ -3288,7 +3278,10 @@ pub mod rpc_accounts {
|
||||||
// Full RPC interface that an API node is expected to provide
|
// Full RPC interface that an API node is expected to provide
|
||||||
// (rpc_minimal should also be provided by an API node)
|
// (rpc_minimal should also be provided by an API node)
|
||||||
pub mod rpc_full {
|
pub mod rpc_full {
|
||||||
use super::*;
|
use {
|
||||||
|
super::*,
|
||||||
|
solana_sdk::message::{SanitizedVersionedMessage, VersionedMessage},
|
||||||
|
};
|
||||||
#[rpc]
|
#[rpc]
|
||||||
pub trait Full {
|
pub trait Full {
|
||||||
type Metadata;
|
type Metadata;
|
||||||
|
@ -4001,12 +3994,21 @@ pub mod rpc_full {
|
||||||
config: Option<RpcContextConfig>,
|
config: Option<RpcContextConfig>,
|
||||||
) -> 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::<VersionedMessage>(
|
||||||
decode_and_deserialize::<Message>(data, TransactionBinaryEncoding::Base64)?;
|
data,
|
||||||
let sanitized_message = SanitizedMessage::try_from(message).map_err(|err| {
|
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))
|
Error::invalid_params(format!("invalid transaction message: {}", err))
|
||||||
})?;
|
})?;
|
||||||
meta.get_fee_for_message(&sanitized_message, config.unwrap_or_default())
|
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(
|
fn get_stake_minimum_delegation(
|
||||||
|
@ -4649,10 +4651,13 @@ pub mod tests {
|
||||||
account::{Account, WritableAccount},
|
account::{Account, WritableAccount},
|
||||||
clock::MAX_RECENT_BLOCKHASHES,
|
clock::MAX_RECENT_BLOCKHASHES,
|
||||||
compute_budget::ComputeBudgetInstruction,
|
compute_budget::ComputeBudgetInstruction,
|
||||||
fee_calculator::DEFAULT_BURN_PERCENT,
|
fee_calculator::{FeeRateGovernor, DEFAULT_BURN_PERCENT},
|
||||||
hash::{hash, Hash},
|
hash::{hash, Hash},
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
message::{v0, v0::MessageAddressTableLookup, MessageHeader, VersionedMessage},
|
message::{
|
||||||
|
v0::{self, MessageAddressTableLookup},
|
||||||
|
Message, MessageHeader, VersionedMessage,
|
||||||
|
},
|
||||||
nonce::{self, state::DurableNonce},
|
nonce::{self, state::DurableNonce},
|
||||||
rpc_port,
|
rpc_port,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
|
@ -4683,7 +4688,8 @@ pub mod tests {
|
||||||
std::{borrow::Cow, collections::HashMap},
|
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;
|
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 {
|
fn create_test_request(method: &str, params: Option<serde_json::Value>) -> serde_json::Value {
|
||||||
|
@ -4829,7 +4835,11 @@ pub mod tests {
|
||||||
let keypair3 = Keypair::new();
|
let keypair3 = Keypair::new();
|
||||||
let bank = self.working_bank();
|
let bank = self.working_bank();
|
||||||
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
|
let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0);
|
||||||
bank.transfer(rent_exempt_amount, mint_keypair, &keypair2.pubkey())
|
bank.transfer(
|
||||||
|
rent_exempt_amount + TEST_SIGNATURE_FEE,
|
||||||
|
mint_keypair,
|
||||||
|
&keypair2.pubkey(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (entries, signatures) = create_test_transaction_entries(
|
let (entries, signatures) = create_test_transaction_entries(
|
||||||
|
@ -5471,7 +5481,7 @@ pub mod tests {
|
||||||
"context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
|
"context": {"slot": 0, "apiVersion": RpcApiVersion::default()},
|
||||||
"value":{
|
"value":{
|
||||||
"owner": "11111111111111111111111111111111",
|
"owner": "11111111111111111111111111111111",
|
||||||
"lamports": 1_000_000,
|
"lamports": TEST_MINT_LAMPORTS,
|
||||||
"data": "",
|
"data": "",
|
||||||
"executable": false,
|
"executable": false,
|
||||||
"rentEpoch": 0
|
"rentEpoch": 0
|
||||||
|
@ -5548,7 +5558,7 @@ pub mod tests {
|
||||||
let expected = json!([
|
let expected = json!([
|
||||||
{
|
{
|
||||||
"owner": "11111111111111111111111111111111",
|
"owner": "11111111111111111111111111111111",
|
||||||
"lamports": 1_000_000,
|
"lamports": TEST_MINT_LAMPORTS,
|
||||||
"data": ["", "base64"],
|
"data": ["", "base64"],
|
||||||
"executable": false,
|
"executable": false,
|
||||||
"rentEpoch": 0
|
"rentEpoch": 0
|
||||||
|
@ -5580,7 +5590,7 @@ pub mod tests {
|
||||||
let expected = json!([
|
let expected = json!([
|
||||||
{
|
{
|
||||||
"owner": "11111111111111111111111111111111",
|
"owner": "11111111111111111111111111111111",
|
||||||
"lamports": 1_000_000,
|
"lamports": TEST_MINT_LAMPORTS,
|
||||||
"data": ["", "base58"],
|
"data": ["", "base58"],
|
||||||
"executable": false,
|
"executable": false,
|
||||||
"rentEpoch": 0
|
"rentEpoch": 0
|
||||||
|
@ -6167,7 +6177,7 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"blockhash": recent_blockhash.to_string(),
|
"blockhash": recent_blockhash.to_string(),
|
||||||
"feeCalculator": {
|
"feeCalculator": {
|
||||||
"lamportsPerSignature": 0,
|
"lamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6196,7 +6206,7 @@ pub mod tests {
|
||||||
"value": {
|
"value": {
|
||||||
"blockhash": recent_blockhash.to_string(),
|
"blockhash": recent_blockhash.to_string(),
|
||||||
"feeCalculator": {
|
"feeCalculator": {
|
||||||
"lamportsPerSignature": 0,
|
"lamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||||
},
|
},
|
||||||
"lastValidSlot": MAX_RECENT_BLOCKHASHES,
|
"lastValidSlot": MAX_RECENT_BLOCKHASHES,
|
||||||
"lastValidBlockHeight": MAX_RECENT_BLOCKHASHES,
|
"lastValidBlockHeight": MAX_RECENT_BLOCKHASHES,
|
||||||
|
@ -6276,9 +6286,9 @@ pub mod tests {
|
||||||
"value":{
|
"value":{
|
||||||
"feeRateGovernor": {
|
"feeRateGovernor": {
|
||||||
"burnPercent": DEFAULT_BURN_PERCENT,
|
"burnPercent": DEFAULT_BURN_PERCENT,
|
||||||
"maxLamportsPerSignature": 0,
|
"maxLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||||
"minLamportsPerSignature": 0,
|
"minLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||||
"targetLamportsPerSignature": 0,
|
"targetLamportsPerSignature": TEST_SIGNATURE_FEE,
|
||||||
"targetSignaturesPerSlot": 0
|
"targetSignaturesPerSlot": 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6547,6 +6557,7 @@ pub mod tests {
|
||||||
genesis_config.rent.exemption_threshold = 2.0;
|
genesis_config.rent.exemption_threshold = 2.0;
|
||||||
genesis_config.epoch_schedule =
|
genesis_config.epoch_schedule =
|
||||||
EpochSchedule::custom(TEST_SLOTS_PER_EPOCH, TEST_SLOTS_PER_EPOCH, false);
|
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);
|
let bank = Bank::new_for_tests(&genesis_config);
|
||||||
(
|
(
|
||||||
|
@ -8457,7 +8468,7 @@ pub mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(),
|
sanitize_transaction(versioned_tx, SimpleAddressLoader::Disabled).unwrap_err(),
|
||||||
Error::invalid_params(
|
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]
|
#[test]
|
||||||
fn test_rpc_get_recent_prioritization_fees() {
|
fn test_rpc_get_recent_prioritization_fees() {
|
||||||
fn wait_for_cache_blocks(cache: &PrioritizationFeeCache, num_blocks: usize) {
|
fn wait_for_cache_blocks(cache: &PrioritizationFeeCache, num_blocks: usize) {
|
||||||
|
|
|
@ -2,8 +2,11 @@ use {
|
||||||
super::Bank,
|
super::Bank,
|
||||||
solana_address_lookup_table_program::error::AddressLookupError,
|
solana_address_lookup_table_program::error::AddressLookupError,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
message::v0::{LoadedAddresses, MessageAddressTableLookup},
|
message::{
|
||||||
transaction::{AddressLoader, Result as TransactionResult, TransactionError},
|
v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||||
|
AddressLoaderError,
|
||||||
|
},
|
||||||
|
transaction::AddressLoader,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,9 +14,9 @@ impl AddressLoader for &Bank {
|
||||||
fn load_addresses(
|
fn load_addresses(
|
||||||
self,
|
self,
|
||||||
address_table_lookups: &[MessageAddressTableLookup],
|
address_table_lookups: &[MessageAddressTableLookup],
|
||||||
) -> TransactionResult<LoadedAddresses> {
|
) -> Result<LoadedAddresses, AddressLoaderError> {
|
||||||
if !self.versioned_tx_message_enabled() {
|
if !self.versioned_tx_message_enabled() {
|
||||||
return Err(TransactionError::UnsupportedVersion);
|
return Err(AddressLoaderError::Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
let slot_hashes = self
|
let slot_hashes = self
|
||||||
|
@ -21,7 +24,7 @@ impl AddressLoader for &Bank {
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_slot_hashes()
|
.get_slot_hashes()
|
||||||
.map_err(|_| TransactionError::AccountNotFound)?;
|
.map_err(|_| AddressLoaderError::SlotHashesSysvarNotFound)?;
|
||||||
|
|
||||||
Ok(address_table_lookups
|
Ok(address_table_lookups
|
||||||
.iter()
|
.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 = ""]
|
#[path = ""]
|
||||||
mod non_bpf_modules {
|
mod non_bpf_modules {
|
||||||
mod account_keys;
|
mod account_keys;
|
||||||
|
mod address_loader;
|
||||||
mod sanitized;
|
mod sanitized;
|
||||||
mod versions;
|
mod versions;
|
||||||
|
|
||||||
pub use {account_keys::*, sanitized::*, versions::*};
|
pub use {account_keys::*, address_loader::*, sanitized::*, versions::*};
|
||||||
}
|
}
|
||||||
|
|
||||||
use compiled_keys::*;
|
use compiled_keys::*;
|
||||||
|
|
|
@ -5,7 +5,8 @@ use {
|
||||||
message::{
|
message::{
|
||||||
legacy,
|
legacy,
|
||||||
v0::{self, LoadedAddresses},
|
v0::{self, LoadedAddresses},
|
||||||
AccountKeys, MessageHeader,
|
AccountKeys, AddressLoader, AddressLoaderError, MessageHeader,
|
||||||
|
SanitizedVersionedMessage, VersionedMessage,
|
||||||
},
|
},
|
||||||
nonce::NONCED_TX_MARKER_IX_INDEX,
|
nonce::NONCED_TX_MARKER_IX_INDEX,
|
||||||
program_utils::limited_deserialize,
|
program_utils::limited_deserialize,
|
||||||
|
@ -81,6 +82,8 @@ pub enum SanitizeMessageError {
|
||||||
ValueOutOfBounds,
|
ValueOutOfBounds,
|
||||||
#[error("invalid value")]
|
#[error("invalid value")]
|
||||||
InvalidValue,
|
InvalidValue,
|
||||||
|
#[error("{0}")]
|
||||||
|
AddressLoaderError(#[from] AddressLoaderError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SanitizeError> for SanitizeMessageError {
|
impl From<SanitizeError> for SanitizeMessageError {
|
||||||
|
@ -102,6 +105,25 @@ impl TryFrom<legacy::Message> for SanitizedMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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
|
/// Return true if this message contains duplicate account keys
|
||||||
pub fn has_duplicates(&self) -> bool {
|
pub fn has_duplicates(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
instruction::InstructionError, message::SanitizeMessageError, sanitize::SanitizeError,
|
instruction::InstructionError,
|
||||||
|
message::{AddressLoaderError, SanitizeMessageError},
|
||||||
|
sanitize::SanitizeError,
|
||||||
},
|
},
|
||||||
serde::Serialize,
|
serde::Serialize,
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
@ -156,7 +158,23 @@ impl From<SanitizeError> for TransactionError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SanitizeMessageError> for TransactionError {
|
impl From<SanitizeMessageError> for TransactionError {
|
||||||
fn from(_err: SanitizeMessageError) -> Self {
|
fn from(err: SanitizeMessageError) -> Self {
|
||||||
Self::SanitizeFailure
|
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")]
|
#![cfg(feature = "full")]
|
||||||
|
|
||||||
|
pub use crate::message::{AddressLoader, SimpleAddressLoader};
|
||||||
use {
|
use {
|
||||||
super::SanitizedVersionedTransaction,
|
super::SanitizedVersionedTransaction,
|
||||||
crate::{
|
crate::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{
|
message::{
|
||||||
legacy,
|
legacy,
|
||||||
v0::{self, LoadedAddresses, MessageAddressTableLookup},
|
v0::{self, LoadedAddresses},
|
||||||
LegacyMessage, SanitizedMessage, VersionedMessage,
|
LegacyMessage, SanitizedMessage, VersionedMessage,
|
||||||
},
|
},
|
||||||
precompiles::verify_if_precompile,
|
precompiles::verify_if_precompile,
|
||||||
|
@ -43,25 +44,6 @@ pub struct TransactionAccountLocks<'a> {
|
||||||
pub writable: Vec<&'a Pubkey>,
|
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
|
/// Type that represents whether the transaction message has been precomputed or
|
||||||
/// not.
|
/// not.
|
||||||
pub enum MessageHash {
|
pub enum MessageHash {
|
||||||
|
|
Loading…
Reference in New Issue