Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC: Support versioned txs in getFeeForMessage API (backport #28217) #28257

Merged
merged 3 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions programs/address-lookup-table/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#[cfg(not(target_arch = "bpf"))]
use solana_sdk::transaction::TransactionError;
use solana_program::message::AddressLoaderError;
use thiserror::Error;

#[derive(Debug, Error, PartialEq, Eq, Clone)]
Expand All @@ -22,13 +22,13 @@ pub enum AddressLookupError {
}

#[cfg(not(target_arch = "bpf"))]
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,
}
}
}
123 changes: 91 additions & 32 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use {
feature_set::{self, nonce_must_be_writable},
fee_calculator::FeeCalculator,
hash::Hash,
message::{Message, SanitizedMessage},
message::SanitizedMessage,
pubkey::{Pubkey, PUBKEY_BYTES},
signature::{Keypair, Signature, Signer},
stake::state::{StakeActivationStatus, StakeState},
Expand Down Expand Up @@ -2157,16 +2157,6 @@ impl JsonRpcRequestProcessor {
let is_valid = bank.is_blockhash_valid(blockhash);
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 optimize_filters(filters: &mut [RpcFilterType]) {
Expand Down Expand Up @@ -3255,7 +3245,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;
Expand Down Expand Up @@ -3956,12 +3949,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))
}
}
}
Expand Down Expand Up @@ -4565,10 +4567,13 @@ pub mod tests {
solana_sdk::{
account::{Account, WritableAccount},
clock::MAX_RECENT_BLOCKHASHES,
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},
Expand Down Expand Up @@ -4599,7 +4604,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 {
Expand Down Expand Up @@ -4744,8 +4750,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],
Expand Down Expand Up @@ -5373,7 +5383,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
Expand Down Expand Up @@ -5450,7 +5460,7 @@ pub mod tests {
let expected = json!([
{
"owner": "11111111111111111111111111111111",
"lamports": 1_000_000,
"lamports": TEST_MINT_LAMPORTS,
"data": ["", "base64"],
"executable": false,
"rentEpoch": 0
Expand Down Expand Up @@ -5482,7 +5492,7 @@ pub mod tests {
let expected = json!([
{
"owner": "11111111111111111111111111111111",
"lamports": 1_000_000,
"lamports": TEST_MINT_LAMPORTS,
"data": ["", "base58"],
"executable": false,
"rentEpoch": 0
Expand Down Expand Up @@ -6063,7 +6073,7 @@ pub mod tests {
"value":{
"blockhash": recent_blockhash.to_string(),
"feeCalculator": {
"lamportsPerSignature": 0,
"lamportsPerSignature": TEST_SIGNATURE_FEE,
}
},
},
Expand Down Expand Up @@ -6092,7 +6102,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,
Expand Down Expand Up @@ -6172,9 +6182,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
}
},
Expand Down Expand Up @@ -6440,6 +6450,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);
(
Expand Down Expand Up @@ -8430,8 +8441,56 @@ 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(),
)
);
}

#[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);
}
}
}
13 changes: 8 additions & 5 deletions runtime/src/bank/address_lookup_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ use {
solana_address_lookup_table_program::error::AddressLookupError,
solana_sdk::{
feature_set::return_none_for_zero_lamport_accounts,
message::v0::{LoadedAddresses, MessageAddressTableLookup},
transaction::{AddressLoader, Result as TransactionResult, TransactionError},
message::{
v0::{LoadedAddresses, MessageAddressTableLookup},
AddressLoaderError,
},
transaction::AddressLoader,
},
};

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 load_zero_lamports = if self
Expand All @@ -32,7 +35,7 @@ impl AddressLoader for &Bank {
.read()
.unwrap()
.get_slot_hashes()
.map_err(|_| TransactionError::AccountNotFound)?;
.map_err(|_| AddressLoaderError::SlotHashesSysvarNotFound)?;

Ok(address_table_lookups
.iter()
Expand Down
56 changes: 56 additions & 0 deletions sdk/program/src/message/address_loader.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
3 changes: 2 additions & 1 deletion sdk/program/src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down
Loading