Add fees to tx-wide caps (#22081)
This commit is contained in:
parent
12dffc105a
commit
3d9874b95a
|
@ -238,6 +238,7 @@ fn full_battery_tests(
|
||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::redundant_closure)]
|
#[allow(clippy::redundant_closure)]
|
||||||
fn test_create_account_with_seed() {
|
fn test_create_account_with_seed() {
|
||||||
|
const ONE_SIG_FEE: f64 = 0.000005;
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
|
@ -310,7 +311,7 @@ fn test_create_account_with_seed() {
|
||||||
&offline_nonce_authority_signer.pubkey(),
|
&offline_nonce_authority_signer.pubkey(),
|
||||||
);
|
);
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(4000.999999999),
|
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&online_nonce_creator_signer.pubkey(),
|
&online_nonce_creator_signer.pubkey(),
|
||||||
);
|
);
|
||||||
|
@ -381,12 +382,12 @@ fn test_create_account_with_seed() {
|
||||||
process_command(&submit_config).unwrap();
|
process_command(&submit_config).unwrap();
|
||||||
check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
|
check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(31.999999999),
|
sol_to_lamports(32.0 - ONE_SIG_FEE),
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&offline_nonce_authority_signer.pubkey(),
|
&offline_nonce_authority_signer.pubkey(),
|
||||||
);
|
);
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(4000.999999999),
|
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&online_nonce_creator_signer.pubkey(),
|
&online_nonce_creator_signer.pubkey(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,6 +18,7 @@ use {
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
|
fee::FeeStructure,
|
||||||
nonce::State as NonceState,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{keypair_from_seed, Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
|
@ -876,14 +877,15 @@ fn test_stake_authorize() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stake_authorize_with_fee_payer() {
|
fn test_stake_authorize_with_fee_payer() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
const SIG_FEE: u64 = 42;
|
let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
|
||||||
|
let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
|
||||||
|
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||||
let test_validator = TestValidator::with_custom_fees(
|
let test_validator = TestValidator::with_custom_fees(
|
||||||
mint_pubkey,
|
mint_pubkey,
|
||||||
SIG_FEE,
|
1,
|
||||||
Some(faucet_addr),
|
Some(faucet_addr),
|
||||||
SocketAddrSpace::Unspecified,
|
SocketAddrSpace::Unspecified,
|
||||||
);
|
);
|
||||||
|
@ -912,14 +914,14 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
config_offline.command = CliCommand::ClusterVersion;
|
config_offline.command = CliCommand::ClusterVersion;
|
||||||
process_command(&config_offline).unwrap_err();
|
process_command(&config_offline).unwrap_err();
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 5_000_000).unwrap();
|
||||||
check_balance!(100_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 100_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 5_000_000).unwrap();
|
||||||
check_balance!(100_000, &rpc_client, &payer_pubkey);
|
check_balance!(5_000_000, &rpc_client, &payer_pubkey);
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 5_000_000).unwrap();
|
||||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
check_balance!(5_000_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
check_ready(&rpc_client);
|
||||||
|
|
||||||
|
@ -934,7 +936,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
withdrawer: None,
|
withdrawer: None,
|
||||||
withdrawer_signer: None,
|
withdrawer_signer: None,
|
||||||
lockup: Lockup::default(),
|
lockup: Lockup::default(),
|
||||||
amount: SpendAmount::Some(50_000),
|
amount: SpendAmount::Some(1_000_000),
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
dump_transaction_message: false,
|
dump_transaction_message: false,
|
||||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||||
|
@ -945,8 +947,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
from: 0,
|
from: 0,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
|
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
|
||||||
|
|
||||||
// Assign authority with separate fee payer
|
// Assign authority with separate fee payer
|
||||||
config.signers = vec![&default_signer, &payer_keypair];
|
config.signers = vec![&default_signer, &payer_keypair];
|
||||||
|
@ -970,10 +971,10 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config` balance has not changed, despite submitting the TX
|
// `config` balance has not changed, despite submitting the TX
|
||||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||||
// `config_payer` however has paid `config`'s authority sig
|
// `config_payer` however has paid `config`'s authority sig
|
||||||
// and `config_payer`'s fee sig
|
// and `config_payer`'s fee sig
|
||||||
check_balance!(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey);
|
check_balance!(5_000_000 - fee_two_sig, &rpc_client, &payer_pubkey);
|
||||||
|
|
||||||
// Assign authority with offline fee payer
|
// Assign authority with offline fee payer
|
||||||
let blockhash = rpc_client.get_latest_blockhash().unwrap();
|
let blockhash = rpc_client.get_latest_blockhash().unwrap();
|
||||||
|
@ -1021,10 +1022,10 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config`'s balance again has not changed
|
// `config`'s balance again has not changed
|
||||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||||
// `config_offline` however has paid 1 sig due to being both authority
|
// `config_offline` however has paid 1 sig due to being both authority
|
||||||
// and fee payer
|
// and fee payer
|
||||||
check_balance!(100_000 - SIG_FEE, &rpc_client, &offline_pubkey);
|
check_balance!(5_000_000 - fee_one_sig, &rpc_client, &offline_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1058,12 +1059,17 @@ fn test_stake_split() {
|
||||||
config_offline.command = CliCommand::ClusterVersion;
|
config_offline.command = CliCommand::ClusterVersion;
|
||||||
process_command(&config_offline).unwrap_err();
|
process_command(&config_offline).unwrap_err();
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
request_and_confirm_airdrop(
|
||||||
.unwrap();
|
&rpc_client,
|
||||||
check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
|
&config,
|
||||||
|
&config.signers[0].pubkey(),
|
||||||
|
50_000_000,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
check_balance!(50_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Create stake account, identity is authority
|
// Create stake account, identity is authority
|
||||||
let minimum_stake_balance = rpc_client
|
let minimum_stake_balance = rpc_client
|
||||||
|
@ -1207,12 +1213,12 @@ fn test_stake_set_lockup() {
|
||||||
config_offline.command = CliCommand::ClusterVersion;
|
config_offline.command = CliCommand::ClusterVersion;
|
||||||
process_command(&config_offline).unwrap_err();
|
process_command(&config_offline).unwrap_err();
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 5_000_000)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
|
check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
// Create stake account, identity is authority
|
// Create stake account, identity is authority
|
||||||
let minimum_stake_balance = rpc_client
|
let minimum_stake_balance = rpc_client
|
||||||
|
|
|
@ -16,6 +16,7 @@ use {
|
||||||
solana_faucet::faucet::run_local_faucet,
|
solana_faucet::faucet::run_local_faucet,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
commitment_config::CommitmentConfig,
|
commitment_config::CommitmentConfig,
|
||||||
|
fee::FeeStructure,
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
nonce::State as NonceState,
|
nonce::State as NonceState,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -29,6 +30,8 @@ use {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer() {
|
fn test_transfer() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
|
||||||
|
let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||||
|
@ -77,7 +80,11 @@ fn test_transfer() {
|
||||||
derived_address_program_id: None,
|
derived_address_program_id: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
|
check_balance!(
|
||||||
|
sol_to_lamports(4.0) - fee_one_sig,
|
||||||
|
&rpc_client,
|
||||||
|
&sender_pubkey
|
||||||
|
);
|
||||||
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
||||||
|
@ -98,7 +105,11 @@ fn test_transfer() {
|
||||||
derived_address_program_id: None,
|
derived_address_program_id: None,
|
||||||
};
|
};
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
|
check_balance!(
|
||||||
|
sol_to_lamports(4.0) - fee_one_sig,
|
||||||
|
&rpc_client,
|
||||||
|
&sender_pubkey
|
||||||
|
);
|
||||||
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
let mut offline = CliConfig::recent_for_tests();
|
let mut offline = CliConfig::recent_for_tests();
|
||||||
|
@ -154,7 +165,11 @@ fn test_transfer() {
|
||||||
derived_address_program_id: None,
|
derived_address_program_id: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(sol_to_lamports(0.5) - 1, &rpc_client, &offline_pubkey);
|
check_balance!(
|
||||||
|
sol_to_lamports(0.5) - fee_one_sig,
|
||||||
|
&rpc_client,
|
||||||
|
&offline_pubkey
|
||||||
|
);
|
||||||
check_balance!(sol_to_lamports(1.5), &rpc_client, &recipient_pubkey);
|
check_balance!(sol_to_lamports(1.5), &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
// Create nonce account
|
// Create nonce account
|
||||||
|
@ -172,7 +187,7 @@ fn test_transfer() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(4.0) - 3 - minimum_nonce_balance,
|
sol_to_lamports(4.0) - fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&sender_pubkey,
|
&sender_pubkey,
|
||||||
);
|
);
|
||||||
|
@ -210,7 +225,7 @@ fn test_transfer() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(3.0) - 4 - minimum_nonce_balance,
|
sol_to_lamports(3.0) - 2 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&sender_pubkey,
|
&sender_pubkey,
|
||||||
);
|
);
|
||||||
|
@ -235,7 +250,7 @@ fn test_transfer() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(3.0) - 5 - minimum_nonce_balance,
|
sol_to_lamports(3.0) - 3 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&sender_pubkey,
|
&sender_pubkey,
|
||||||
);
|
);
|
||||||
|
@ -293,13 +308,18 @@ fn test_transfer() {
|
||||||
derived_address_program_id: None,
|
derived_address_program_id: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(sol_to_lamports(0.1) - 2, &rpc_client, &offline_pubkey);
|
check_balance!(
|
||||||
|
sol_to_lamports(0.1) - 2 * fee_one_sig,
|
||||||
|
&rpc_client,
|
||||||
|
&offline_pubkey
|
||||||
|
);
|
||||||
check_balance!(sol_to_lamports(2.9), &rpc_client, &recipient_pubkey);
|
check_balance!(sol_to_lamports(2.9), &rpc_client, &recipient_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_multisession_signing() {
|
fn test_transfer_multisession_signing() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let fee = FeeStructure::default().get_max_fee(2, 0);
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||||
|
@ -329,7 +349,7 @@ fn test_transfer_multisession_signing() {
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&CliConfig::recent_for_tests(),
|
&CliConfig::recent_for_tests(),
|
||||||
&offline_fee_payer_signer.pubkey(),
|
&offline_fee_payer_signer.pubkey(),
|
||||||
sol_to_lamports(1.0) + 3,
|
sol_to_lamports(1.0) + 2 * fee,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_balance!(
|
check_balance!(
|
||||||
|
@ -338,7 +358,7 @@ fn test_transfer_multisession_signing() {
|
||||||
&offline_from_signer.pubkey(),
|
&offline_from_signer.pubkey(),
|
||||||
);
|
);
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(1.0) + 3,
|
sol_to_lamports(1.0) + 2 * fee,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&offline_fee_payer_signer.pubkey(),
|
&offline_fee_payer_signer.pubkey(),
|
||||||
);
|
);
|
||||||
|
@ -438,7 +458,7 @@ fn test_transfer_multisession_signing() {
|
||||||
&offline_from_signer.pubkey(),
|
&offline_from_signer.pubkey(),
|
||||||
);
|
);
|
||||||
check_balance!(
|
check_balance!(
|
||||||
sol_to_lamports(1.0) + 1,
|
sol_to_lamports(1.0) + fee,
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
&offline_fee_payer_signer.pubkey(),
|
&offline_fee_payer_signer.pubkey(),
|
||||||
);
|
);
|
||||||
|
@ -448,6 +468,7 @@ fn test_transfer_multisession_signing() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_all() {
|
fn test_transfer_all() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||||
|
@ -470,8 +491,8 @@ fn test_transfer_all() {
|
||||||
let sender_pubkey = config.signers[0].pubkey();
|
let sender_pubkey = config.signers[0].pubkey();
|
||||||
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
let recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||||
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000).unwrap();
|
||||||
check_balance!(50_000, &rpc_client, &sender_pubkey);
|
check_balance!(500_000, &rpc_client, &sender_pubkey);
|
||||||
check_balance!(0, &rpc_client, &recipient_pubkey);
|
check_balance!(0, &rpc_client, &recipient_pubkey);
|
||||||
|
|
||||||
check_ready(&rpc_client);
|
check_ready(&rpc_client);
|
||||||
|
@ -495,7 +516,7 @@ fn test_transfer_all() {
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(0, &rpc_client, &sender_pubkey);
|
check_balance!(0, &rpc_client, &sender_pubkey);
|
||||||
check_balance!(49_999, &rpc_client, &recipient_pubkey);
|
check_balance!(500_000 - fee, &rpc_client, &recipient_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -554,6 +575,7 @@ fn test_transfer_unfunded_recipient() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_with_seed() {
|
fn test_transfer_with_seed() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||||
let mint_keypair = Keypair::new();
|
let mint_keypair = Keypair::new();
|
||||||
let mint_pubkey = mint_keypair.pubkey();
|
let mint_pubkey = mint_keypair.pubkey();
|
||||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||||
|
@ -612,7 +634,7 @@ fn test_transfer_with_seed() {
|
||||||
derived_address_program_id: Some(derived_address_program_id),
|
derived_address_program_id: Some(derived_address_program_id),
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
check_balance!(sol_to_lamports(1.0) - 1, &rpc_client, &sender_pubkey);
|
check_balance!(sol_to_lamports(1.0) - fee, &rpc_client, &sender_pubkey);
|
||||||
check_balance!(sol_to_lamports(5.0), &rpc_client, &recipient_pubkey);
|
check_balance!(sol_to_lamports(5.0), &rpc_client, &recipient_pubkey);
|
||||||
check_balance!(0, &rpc_client, &derived_address);
|
check_balance!(0, &rpc_client, &derived_address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,12 @@ The policy is as follows:
|
||||||
To prevent a program from abusing computation resources each instruction in a
|
To prevent a program from abusing computation resources each instruction in a
|
||||||
transaction is given a compute budget. The budget consists of computation units
|
transaction is given a compute budget. The budget consists of computation units
|
||||||
that are consumed as the program performs various operations and bounds that the
|
that are consumed as the program performs various operations and bounds that the
|
||||||
program may not exceed. When the program consumes its entire budget or exceeds
|
program may not exceed. When the program consumes its entire budget or exceeds a
|
||||||
a bound then the runtime halts the program and returns an error.
|
bound then the runtime halts the program and returns an error.
|
||||||
|
|
||||||
|
Note: The compute budget currently applies per-instruction but is moving toward
|
||||||
|
a per-transaction model. For more information see [Transaction-wide Compute
|
||||||
|
Budget](#transaction-wide-compute-buget).
|
||||||
|
|
||||||
The following operations incur a compute cost:
|
The following operations incur a compute cost:
|
||||||
|
|
||||||
|
@ -60,12 +64,12 @@ The following operations incur a compute cost:
|
||||||
- cross-program invocations
|
- cross-program invocations
|
||||||
- ...
|
- ...
|
||||||
|
|
||||||
For cross-program invocations the programs invoked inherit the budget of their
|
For cross-program invocations, the programs invoked inherit the budget of their
|
||||||
parent. If an invoked program consume the budget or exceeds a bound the entire
|
parent. If an invoked program consume the budget or exceeds a bound the entire
|
||||||
invocation chain and the parent are halted.
|
invocation chain is halted.
|
||||||
|
|
||||||
The current [compute
|
The current [compute
|
||||||
budget](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L65)
|
budget](https://github.com/solana-labs/solana/blob/0224a8b127ace4c6453dd6492a38c66cb999abd2/sdk/src/compute_budget.rs#L102)
|
||||||
can be found in the Solana SDK.
|
can be found in the Solana SDK.
|
||||||
|
|
||||||
For example, if the current budget is:
|
For example, if the current budget is:
|
||||||
|
@ -80,6 +84,7 @@ max_invoke_depth: 4,
|
||||||
max_call_depth: 64,
|
max_call_depth: 64,
|
||||||
stack_frame_size: 4096,
|
stack_frame_size: 4096,
|
||||||
log_pubkey_units: 100,
|
log_pubkey_units: 100,
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then the program
|
Then the program
|
||||||
|
@ -90,7 +95,7 @@ Then the program
|
||||||
- Can not exceed a BPF call depth of 64
|
- Can not exceed a BPF call depth of 64
|
||||||
- Cannot exceed 4 levels of cross-program invocations.
|
- Cannot exceed 4 levels of cross-program invocations.
|
||||||
|
|
||||||
Since the compute budget is consumed incrementally as the program executes the
|
Since the compute budget is consumed incrementally as the program executes, the
|
||||||
total budget consumption will be a combination of the various costs of the
|
total budget consumption will be a combination of the various costs of the
|
||||||
operations it performs.
|
operations it performs.
|
||||||
|
|
||||||
|
@ -98,12 +103,38 @@ At runtime a program may log how much of the compute budget remains. See
|
||||||
[debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption)
|
[debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
The budget values are conditional on feature enablement, take a look at the
|
## Transaction-wide Compute Budget
|
||||||
compute budget's
|
|
||||||
[new](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L97)
|
Transactions are processed as a single entity and are the primary unit of block
|
||||||
function to find out how the budget is constructed. An understanding of how
|
scheduling. In order to facilitate better block scheduling and account for the
|
||||||
[features](runtime.md#features) work and what features are enabled on the
|
computational cost of each transaction, the compute budget is moving to a
|
||||||
cluster being used are required to determine the current budget's values.
|
transaction-wide budget rather than per-instruction.
|
||||||
|
|
||||||
|
For information on what the compute budget is and how it is applied see [Compute
|
||||||
|
Budget](#compute-budget).
|
||||||
|
|
||||||
|
With a transaction-wide compute budget the `max_units` cap is applied to the
|
||||||
|
entire transaction rather than to each instruction within the transaction. The
|
||||||
|
default number of maximum units remains at 200k which means the sum of the
|
||||||
|
compute units used by each instruction in the transaction must not exceed that
|
||||||
|
value. The number of maximum units allows is intentionally kept small to
|
||||||
|
facilitate optimized programs and form the bases for a minimum fee level.
|
||||||
|
|
||||||
|
There are a lot of uses cases that require more than 200k units
|
||||||
|
transaction-wide. To enable these uses cases transactions can include a
|
||||||
|
[``ComputeBudgetInstruction`](https://github.com/solana-labs/solana/blob/0224a8b127ace4c6453dd6492a38c66cb999abd2/sdk/src/compute_budget.rs#L44)
|
||||||
|
requesting a higher compute unit cap. Higher compute caps will be charged
|
||||||
|
higher fees.
|
||||||
|
|
||||||
|
Compute Budget instructions don't require any accounts and must lie in the first
|
||||||
|
3 instructions of a transaction otherwise they will be ignored.
|
||||||
|
|
||||||
|
The `ComputeBudgetInstruction::request_units` function can be used to crate
|
||||||
|
these instructions:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let instruction = ComputeBudgetInstruction::request_units(300_000);
|
||||||
|
```
|
||||||
|
|
||||||
## New Features
|
## New Features
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use {
|
use solana_sdk::{
|
||||||
solana_sdk::{
|
borsh::try_from_slice_unchecked,
|
||||||
borsh::try_from_slice_unchecked,
|
compute_budget::{self, ComputeBudgetInstruction},
|
||||||
compute_budget::{self, ComputeBudgetInstruction},
|
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
instruction::InstructionError,
|
||||||
feature_set::{requestable_heap_size, FeatureSet},
|
message::SanitizedMessage,
|
||||||
instruction::InstructionError,
|
transaction::TransactionError,
|
||||||
transaction::{SanitizedTransaction, TransactionError},
|
|
||||||
},
|
|
||||||
std::sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_UNITS: u32 = 1_000_000;
|
const MAX_UNITS: u32 = 1_400_000;
|
||||||
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
||||||
|
|
||||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
|
@ -68,14 +65,19 @@ pub struct ComputeBudget {
|
||||||
|
|
||||||
impl Default for ComputeBudget {
|
impl Default for ComputeBudget {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputeBudget {
|
impl ComputeBudget {
|
||||||
pub fn new() -> Self {
|
pub fn new(use_max_units_default: bool) -> Self {
|
||||||
|
let max_units = if use_max_units_default {
|
||||||
|
MAX_UNITS
|
||||||
|
} else {
|
||||||
|
200_000
|
||||||
|
} as u64;
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
max_units: 200_000,
|
max_units,
|
||||||
log_64_units: 100,
|
log_64_units: 100,
|
||||||
create_program_address_units: 1500,
|
create_program_address_units: 1500,
|
||||||
invoke_units: 1000,
|
invoke_units: 1000,
|
||||||
|
@ -97,25 +99,27 @@ impl ComputeBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_transaction(
|
pub fn process_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
tx: &SanitizedTransaction,
|
message: &SanitizedMessage,
|
||||||
feature_set: Arc<FeatureSet>,
|
requestable_heap_size: bool,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<u64, TransactionError> {
|
||||||
|
let mut requested_additional_fee = 0;
|
||||||
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
|
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
|
||||||
// Compute budget instruction must be in the 1st 3 instructions (avoid
|
// Compute budget instruction must be in the 1st 3 instructions (avoid
|
||||||
// nonce marker), otherwise ignored
|
// nonce marker), otherwise ignored
|
||||||
for (program_id, instruction) in tx.message().program_instructions_iter().take(3) {
|
for (program_id, instruction) in message.program_instructions_iter().take(3) {
|
||||||
if compute_budget::check_id(program_id) {
|
if compute_budget::check_id(program_id) {
|
||||||
match try_from_slice_unchecked(&instruction.data) {
|
match try_from_slice_unchecked(&instruction.data) {
|
||||||
Ok(ComputeBudgetInstruction::RequestUnits(units)) => {
|
Ok(ComputeBudgetInstruction::RequestUnits {
|
||||||
if units > MAX_UNITS {
|
units,
|
||||||
return Err(error);
|
additional_fee,
|
||||||
}
|
}) => {
|
||||||
self.max_units = units as u64;
|
self.max_units = units.min(MAX_UNITS) as u64;
|
||||||
|
requested_additional_fee = additional_fee as u64;
|
||||||
}
|
}
|
||||||
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
||||||
if !feature_set.is_active(&requestable_heap_size::id())
|
if !requestable_heap_size
|
||||||
|| bytes > MAX_HEAP_FRAME_BYTES
|
|| bytes > MAX_HEAP_FRAME_BYTES
|
||||||
|| bytes < MIN_HEAP_FRAME_BYTES as u32
|
|| bytes < MIN_HEAP_FRAME_BYTES as u32
|
||||||
|| bytes % 1024 != 0
|
|| bytes % 1024 != 0
|
||||||
|
@ -128,7 +132,7 @@ impl ComputeBudget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(requested_additional_fee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,8 +141,13 @@ mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
hash::Hash, instruction::Instruction, message::Message, pubkey::Pubkey,
|
hash::Hash,
|
||||||
signature::Keypair, signer::Signer, transaction::Transaction,
|
instruction::Instruction,
|
||||||
|
message::Message,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::Keypair,
|
||||||
|
signer::Signer,
|
||||||
|
transaction::{SanitizedTransaction, Transaction},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -150,24 +159,23 @@ mod tests {
|
||||||
Message::new($instructions, Some(&payer_keypair.pubkey())),
|
Message::new($instructions, Some(&payer_keypair.pubkey())),
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
));
|
));
|
||||||
let feature_set = Arc::new(FeatureSet::all_enabled());
|
|
||||||
let mut compute_budget = ComputeBudget::default();
|
let mut compute_budget = ComputeBudget::default();
|
||||||
let result = compute_budget.process_transaction(&tx, feature_set);
|
let result = compute_budget.process_message(&tx.message(), true);
|
||||||
assert_eq!($expected_error as Result<(), TransactionError>, result);
|
assert_eq!($expected_error, result);
|
||||||
assert_eq!(compute_budget, $expected_budget);
|
assert_eq!(compute_budget, $expected_budget);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_transaction() {
|
fn test_process_mesage() {
|
||||||
// Units
|
// Units
|
||||||
test!(&[], Ok(()), ComputeBudget::default());
|
test!(&[], Ok(0), ComputeBudget::default());
|
||||||
test!(
|
test!(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::request_units(1),
|
ComputeBudgetInstruction::request_units(1, 0),
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
max_units: 1,
|
max_units: 1,
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
|
@ -175,21 +183,18 @@ mod tests {
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::request_units(MAX_UNITS + 1),
|
ComputeBudgetInstruction::request_units(MAX_UNITS + 1, 0),
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
],
|
],
|
||||||
Err(TransactionError::InstructionError(
|
Ok(0),
|
||||||
0,
|
|
||||||
InstructionError::InvalidInstructionData,
|
|
||||||
)),
|
|
||||||
ComputeBudget::default()
|
ComputeBudget::default()
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
&[
|
&[
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
max_units: MAX_UNITS as u64,
|
max_units: MAX_UNITS as u64,
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
|
@ -200,20 +205,20 @@ mod tests {
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
ComputeBudgetInstruction::request_units(1),
|
ComputeBudgetInstruction::request_units(1, 0),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget::default()
|
ComputeBudget::default()
|
||||||
);
|
);
|
||||||
|
|
||||||
// HeapFrame
|
// HeapFrame
|
||||||
test!(&[], Ok(()), ComputeBudget::default());
|
test!(&[], Ok(0), ComputeBudget::default());
|
||||||
test!(
|
test!(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
heap_size: Some(40 * 1024),
|
heap_size: Some(40 * 1024),
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
|
@ -257,7 +262,7 @@ mod tests {
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::default()
|
||||||
|
@ -270,7 +275,7 @@ mod tests {
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
ComputeBudgetInstruction::request_heap_frame(1), // ignored
|
ComputeBudgetInstruction::request_heap_frame(1), // ignored
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget::default()
|
ComputeBudget::default()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -279,9 +284,9 @@ mod tests {
|
||||||
&[
|
&[
|
||||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||||
],
|
],
|
||||||
Ok(()),
|
Ok(0),
|
||||||
ComputeBudget {
|
ComputeBudget {
|
||||||
max_units: MAX_UNITS as u64,
|
max_units: MAX_UNITS as u64,
|
||||||
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
||||||
|
|
|
@ -1610,11 +1610,12 @@ mod tests {
|
||||||
let mut transaction_context = TransactionContext::new(accounts, 1, 3);
|
let mut transaction_context = TransactionContext::new(accounts, 1, 3);
|
||||||
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||||
invoke_context.feature_set = Arc::new(feature_set);
|
invoke_context.feature_set = Arc::new(feature_set);
|
||||||
|
invoke_context.compute_budget = ComputeBudget::new(false);
|
||||||
|
|
||||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
ComputeBudget::default()
|
ComputeBudget::new(false)
|
||||||
);
|
);
|
||||||
invoke_context.pop().unwrap();
|
invoke_context.pop().unwrap();
|
||||||
|
|
||||||
|
@ -1622,7 +1623,7 @@ mod tests {
|
||||||
let expected_compute_budget = ComputeBudget {
|
let expected_compute_budget = ComputeBudget {
|
||||||
max_units: 500_000,
|
max_units: 500_000,
|
||||||
heap_size: Some(256_usize.saturating_mul(1024)),
|
heap_size: Some(256_usize.saturating_mul(1024)),
|
||||||
..ComputeBudget::default()
|
..ComputeBudget::new(false)
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
|
@ -1633,7 +1634,7 @@ mod tests {
|
||||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
ComputeBudget::default()
|
ComputeBudget::new(false)
|
||||||
);
|
);
|
||||||
invoke_context.pop().unwrap();
|
invoke_context.pop().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ use solana_sdk::{
|
||||||
clock::MAX_PROCESSING_AGE,
|
clock::MAX_PROCESSING_AGE,
|
||||||
compute_budget::ComputeBudgetInstruction,
|
compute_budget::ComputeBudgetInstruction,
|
||||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||||
|
feature_set::FeatureSet,
|
||||||
|
fee::FeeStructure,
|
||||||
|
fee_calculator::FeeRateGovernor,
|
||||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||||
loader_instruction,
|
loader_instruction,
|
||||||
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
||||||
|
@ -397,7 +400,7 @@ fn execute_transactions(
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
.expect("lamports_per_signature must be available");
|
.expect("lamports_per_signature must be available");
|
||||||
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
let fee = bank.get_fee_for_message_with_lamports_per_signature(
|
||||||
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
);
|
);
|
||||||
|
@ -1383,7 +1386,7 @@ fn test_program_bpf_compute_budget() {
|
||||||
);
|
);
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&[
|
&[
|
||||||
ComputeBudgetInstruction::request_units(1),
|
ComputeBudgetInstruction::request_units(1, 0),
|
||||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||||
],
|
],
|
||||||
Some(&mint_keypair.pubkey()),
|
Some(&mint_keypair.pubkey()),
|
||||||
|
@ -2886,8 +2889,8 @@ fn test_program_bpf_realloc() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bpf_rust")]
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(feature = "bpf_rust")]
|
||||||
fn test_program_bpf_realloc_invoke() {
|
fn test_program_bpf_realloc_invoke() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
|
@ -3418,3 +3421,76 @@ fn test_program_bpf_processed_inner_instruction() {
|
||||||
.send_and_confirm_message(&[&mint_keypair], message)
|
.send_and_confirm_message(&[&mint_keypair], message)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "bpf_rust")]
|
||||||
|
fn test_program_fees() {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
let congestion_multiplier = 1;
|
||||||
|
|
||||||
|
let GenesisConfigInfo {
|
||||||
|
mut genesis_config,
|
||||||
|
mint_keypair,
|
||||||
|
..
|
||||||
|
} = create_genesis_config(500_000_000);
|
||||||
|
genesis_config.fee_rate_governor = FeeRateGovernor::new(congestion_multiplier, 0);
|
||||||
|
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
let fee_structure =
|
||||||
|
FeeStructure::new(0.000005, 0.0, vec![(200, 0.0000005), (1400000, 0.000005)]);
|
||||||
|
bank.fee_structure = fee_structure.clone();
|
||||||
|
bank.feature_set = Arc::new(FeatureSet::all_enabled());
|
||||||
|
|
||||||
|
let (name, id, entrypoint) = solana_bpf_loader_program!();
|
||||||
|
bank.add_builtin(&name, &id, entrypoint);
|
||||||
|
let bank_client = BankClient::new(bank);
|
||||||
|
|
||||||
|
let program_id = load_bpf_program(
|
||||||
|
&bank_client,
|
||||||
|
&bpf_loader::id(),
|
||||||
|
&mint_keypair,
|
||||||
|
"solana_bpf_rust_noop",
|
||||||
|
);
|
||||||
|
|
||||||
|
let pre_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
|
||||||
|
let message = Message::new(
|
||||||
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
|
Some(&mint_keypair.pubkey()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap();
|
||||||
|
let expected_max_fee = Bank::calculate_fee(
|
||||||
|
&sanitized_message,
|
||||||
|
congestion_multiplier,
|
||||||
|
&fee_structure,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(&[&mint_keypair], message)
|
||||||
|
.unwrap();
|
||||||
|
let post_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
|
||||||
|
assert_eq!(pre_balance - post_balance, expected_max_fee);
|
||||||
|
|
||||||
|
let pre_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
|
||||||
|
let message = Message::new(
|
||||||
|
&[
|
||||||
|
ComputeBudgetInstruction::request_units(100, 42),
|
||||||
|
Instruction::new_with_bytes(program_id, &[], vec![]),
|
||||||
|
],
|
||||||
|
Some(&mint_keypair.pubkey()),
|
||||||
|
);
|
||||||
|
let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap();
|
||||||
|
let expected_min_fee = Bank::calculate_fee(
|
||||||
|
&sanitized_message,
|
||||||
|
congestion_multiplier,
|
||||||
|
&fee_structure,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
assert!(expected_min_fee < expected_max_fee);
|
||||||
|
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_message(&[&mint_keypair], message)
|
||||||
|
.unwrap();
|
||||||
|
let post_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
|
||||||
|
assert_eq!(pre_balance - post_balance, expected_min_fee);
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use {
|
||||||
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
|
||||||
},
|
},
|
||||||
solana_runtime::bank::{
|
solana_runtime::bank::{
|
||||||
Bank, DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
|
DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
|
||||||
},
|
},
|
||||||
solana_transaction_status::{
|
solana_transaction_status::{
|
||||||
extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
|
extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
|
||||||
|
@ -109,7 +109,7 @@ impl TransactionStatusService {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
.expect("lamports_per_signature must be available");
|
.expect("lamports_per_signature must be available");
|
||||||
let fee = Bank::get_fee_for_message_with_lamports_per_signature(
|
let fee = bank.get_fee_for_message_with_lamports_per_signature(
|
||||||
transaction.message(),
|
transaction.message(),
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
);
|
);
|
||||||
|
@ -204,7 +204,7 @@ pub(crate) mod tests {
|
||||||
dashmap::DashMap,
|
dashmap::DashMap,
|
||||||
solana_account_decoder::parse_token::token_amount_to_ui_amount,
|
solana_account_decoder::parse_token::token_amount_to_ui_amount,
|
||||||
solana_ledger::{genesis_utils::create_genesis_config, get_tmp_ledger_path},
|
solana_ledger::{genesis_utils::create_genesis_config, get_tmp_ledger_path},
|
||||||
solana_runtime::bank::{NonceFull, NoncePartial, RentDebits, TransactionBalancesSet},
|
solana_runtime::bank::{Bank, NonceFull, NoncePartial, RentDebits, TransactionBalancesSet},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
clock::Slot,
|
clock::Slot,
|
||||||
|
|
|
@ -28,7 +28,8 @@ use {
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
clock::{BankId, Slot, INITIAL_RENT_EPOCH},
|
clock::{BankId, Slot, INITIAL_RENT_EPOCH},
|
||||||
feature_set::{self, FeatureSet},
|
feature_set::{self, tx_wide_compute_cap, FeatureSet},
|
||||||
|
fee::FeeStructure,
|
||||||
genesis_config::ClusterType,
|
genesis_config::ClusterType,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{
|
message::{
|
||||||
|
@ -472,6 +473,7 @@ impl Accounts {
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
rent_collector: &RentCollector,
|
rent_collector: &RentCollector,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
|
fee_structure: &FeeStructure,
|
||||||
) -> Vec<TransactionLoadResult> {
|
) -> Vec<TransactionLoadResult> {
|
||||||
txs.iter()
|
txs.iter()
|
||||||
.zip(lock_results)
|
.zip(lock_results)
|
||||||
|
@ -484,7 +486,12 @@ impl Accounts {
|
||||||
hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
|
hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
|
||||||
});
|
});
|
||||||
let fee = if let Some(lamports_per_signature) = lamports_per_signature {
|
let fee = if let Some(lamports_per_signature) = lamports_per_signature {
|
||||||
Bank::calculate_fee(tx.message(), lamports_per_signature)
|
Bank::calculate_fee(
|
||||||
|
tx.message(),
|
||||||
|
lamports_per_signature,
|
||||||
|
fee_structure,
|
||||||
|
feature_set.is_active(&tx_wide_compute_cap::id()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
return (Err(TransactionError::BlockhashNotFound), None);
|
return (Err(TransactionError::BlockhashNotFound), None);
|
||||||
};
|
};
|
||||||
|
@ -1359,6 +1366,8 @@ mod tests {
|
||||||
lamports_per_signature: u64,
|
lamports_per_signature: u64,
|
||||||
rent_collector: &RentCollector,
|
rent_collector: &RentCollector,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
|
feature_set: &FeatureSet,
|
||||||
|
fee_structure: &FeeStructure,
|
||||||
) -> Vec<TransactionLoadResult> {
|
) -> Vec<TransactionLoadResult> {
|
||||||
let mut hash_queue = BlockhashQueue::new(100);
|
let mut hash_queue = BlockhashQueue::new(100);
|
||||||
hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
|
hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
|
||||||
|
@ -1382,7 +1391,8 @@ mod tests {
|
||||||
&hash_queue,
|
&hash_queue,
|
||||||
error_counters,
|
error_counters,
|
||||||
rent_collector,
|
rent_collector,
|
||||||
&FeatureSet::all_enabled(),
|
feature_set,
|
||||||
|
fee_structure,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1398,6 +1408,8 @@ mod tests {
|
||||||
lamports_per_signature,
|
lamports_per_signature,
|
||||||
&RentCollector::default(),
|
&RentCollector::default(),
|
||||||
error_counters,
|
error_counters,
|
||||||
|
&FeatureSet::all_enabled(),
|
||||||
|
&FeeStructure::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,6 +1561,8 @@ mod tests {
|
||||||
let fee = Bank::calculate_fee(
|
let fee = Bank::calculate_fee(
|
||||||
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
||||||
10,
|
10,
|
||||||
|
&FeeStructure::default(),
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
assert_eq!(fee, 10);
|
assert_eq!(fee, 10);
|
||||||
|
|
||||||
|
@ -1595,6 +1609,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_accounts_fee_payer_is_nonce() {
|
fn test_load_accounts_fee_payer_is_nonce() {
|
||||||
let mut error_counters = ErrorCounters::default();
|
let mut error_counters = ErrorCounters::default();
|
||||||
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
|
feature_set.deactivate(&tx_wide_compute_cap::id());
|
||||||
let rent_collector = RentCollector::new(
|
let rent_collector = RentCollector::new(
|
||||||
0,
|
0,
|
||||||
&EpochSchedule::default(),
|
&EpochSchedule::default(),
|
||||||
|
@ -1631,6 +1647,8 @@ mod tests {
|
||||||
min_balance,
|
min_balance,
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&mut error_counters,
|
&mut error_counters,
|
||||||
|
&feature_set,
|
||||||
|
&FeeStructure::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
let (load_res, _nonce) = &loaded_accounts[0];
|
let (load_res, _nonce) = &loaded_accounts[0];
|
||||||
|
@ -1645,6 +1663,8 @@ mod tests {
|
||||||
min_balance,
|
min_balance,
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&mut error_counters,
|
&mut error_counters,
|
||||||
|
&feature_set,
|
||||||
|
&FeeStructure::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
let (load_res, _nonce) = &loaded_accounts[0];
|
let (load_res, _nonce) = &loaded_accounts[0];
|
||||||
|
@ -1658,6 +1678,8 @@ mod tests {
|
||||||
min_balance,
|
min_balance,
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&mut error_counters,
|
&mut error_counters,
|
||||||
|
&feature_set,
|
||||||
|
&FeeStructure::default(),
|
||||||
);
|
);
|
||||||
assert_eq!(loaded_accounts.len(), 1);
|
assert_eq!(loaded_accounts.len(), 1);
|
||||||
let (load_res, _nonce) = &loaded_accounts[0];
|
let (load_res, _nonce) = &loaded_accounts[0];
|
||||||
|
@ -2982,6 +3004,7 @@ mod tests {
|
||||||
&mut error_counters,
|
&mut error_counters,
|
||||||
&rent_collector,
|
&rent_collector,
|
||||||
&FeatureSet::all_enabled(),
|
&FeatureSet::all_enabled(),
|
||||||
|
&FeeStructure::default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -272,7 +272,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors.clone(),
|
executors.clone(),
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -314,7 +314,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors.clone(),
|
executors.clone(),
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -346,7 +346,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors,
|
executors,
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -481,7 +481,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors.clone(),
|
executors.clone(),
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -514,7 +514,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors.clone(),
|
executors.clone(),
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -544,7 +544,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
executors,
|
executors,
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
@ -623,7 +623,7 @@ mod tests {
|
||||||
None,
|
None,
|
||||||
Rc::new(RefCell::new(Executors::default())),
|
Rc::new(RefCell::new(Executors::default())),
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
ComputeBudget::new(),
|
ComputeBudget::default(),
|
||||||
&mut ExecuteTimings::default(),
|
&mut ExecuteTimings::default(),
|
||||||
&sysvar_cache,
|
&sysvar_cache,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
|
|
|
@ -2,38 +2,49 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::instruction::Instruction,
|
crate::instruction::Instruction,
|
||||||
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
borsh::{BorshDeserialize, BorshSerialize},
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::declare_id!("ComputeBudget111111111111111111111111111111");
|
crate::declare_id!("ComputeBudget111111111111111111111111111111");
|
||||||
|
|
||||||
/// Compute Budget Instructions
|
/// Compute Budget Instructions
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
BorshSerialize,
|
|
||||||
BorshDeserialize,
|
|
||||||
BorshSchema,
|
|
||||||
Debug,
|
|
||||||
Clone,
|
|
||||||
PartialEq,
|
|
||||||
AbiExample,
|
AbiExample,
|
||||||
AbiEnumVisitor,
|
AbiEnumVisitor,
|
||||||
|
BorshDeserialize,
|
||||||
|
BorshSerialize,
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Deserialize,
|
||||||
|
PartialEq,
|
||||||
|
Serialize,
|
||||||
)]
|
)]
|
||||||
pub enum ComputeBudgetInstruction {
|
pub enum ComputeBudgetInstruction {
|
||||||
/// Request a specific maximum number of compute units the transaction is
|
/// Request a specific maximum number of compute units the transaction is
|
||||||
/// allowed to consume.
|
/// allowed to consume and an additional fee to pay.
|
||||||
RequestUnits(u32),
|
RequestUnits {
|
||||||
/// Request a specific transaction-wide program heap frame size in bytes.
|
/// Units to request
|
||||||
/// The value requested must be a multiple of 1024. This new heap frame size
|
units: u32,
|
||||||
/// applies to each program executed, including all calls to CPIs.
|
/// Additional fee to add
|
||||||
|
additional_fee: u32,
|
||||||
|
},
|
||||||
|
/// Request a specific transaction-wide program heap region size in bytes.
|
||||||
|
/// The value requested must be a multiple of 1024. This new heap region
|
||||||
|
/// size applies to each program executed, including all calls to CPIs.
|
||||||
RequestHeapFrame(u32),
|
RequestHeapFrame(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputeBudgetInstruction {
|
impl ComputeBudgetInstruction {
|
||||||
/// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
|
/// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
|
||||||
pub fn request_units(units: u32) -> Instruction {
|
pub fn request_units(units: u32, additional_fee: u32) -> Instruction {
|
||||||
Instruction::new_with_borsh(id(), &ComputeBudgetInstruction::RequestUnits(units), vec![])
|
Instruction::new_with_borsh(
|
||||||
|
id(),
|
||||||
|
&ComputeBudgetInstruction::RequestUnits {
|
||||||
|
units,
|
||||||
|
additional_fee,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `ComputeBudgetInstruction::RequestHeapFrame` `Instruction`
|
/// Create a `ComputeBudgetInstruction::RequestHeapFrame` `Instruction`
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
use crate::native_token::sol_to_lamports;
|
||||||
|
|
||||||
|
/// A fee and its associated compute unit limit
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct FeeBin {
|
||||||
|
/// maximum compute units for which this fee will be charged
|
||||||
|
pub limit: u64,
|
||||||
|
/// fee in lamports
|
||||||
|
pub fee: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information used to calculate fees
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FeeStructure {
|
||||||
|
/// lamports per signature
|
||||||
|
pub lamports_per_signature: u64,
|
||||||
|
/// lamports_per_write_lock
|
||||||
|
pub lamports_per_write_lock: u64,
|
||||||
|
/// Compute unit fee bins
|
||||||
|
pub compute_fee_bins: Vec<FeeBin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FeeStructure {
|
||||||
|
pub fn new(
|
||||||
|
sol_per_signature: f64,
|
||||||
|
sol_per_write_lock: f64,
|
||||||
|
compute_fee_bins: Vec<(u64, f64)>,
|
||||||
|
) -> Self {
|
||||||
|
let compute_fee_bins = compute_fee_bins
|
||||||
|
.iter()
|
||||||
|
.map(|(limit, sol)| FeeBin {
|
||||||
|
limit: *limit,
|
||||||
|
fee: sol_to_lamports(*sol),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
FeeStructure {
|
||||||
|
lamports_per_signature: sol_to_lamports(sol_per_signature),
|
||||||
|
lamports_per_write_lock: sol_to_lamports(sol_per_write_lock),
|
||||||
|
compute_fee_bins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_max_fee(&self, num_signatures: u64, num_write_locks: u64) -> u64 {
|
||||||
|
num_signatures
|
||||||
|
.saturating_mul(self.lamports_per_signature)
|
||||||
|
.saturating_add(num_write_locks.saturating_mul(self.lamports_per_write_lock))
|
||||||
|
.saturating_add(
|
||||||
|
self.compute_fee_bins
|
||||||
|
.last()
|
||||||
|
.map(|bin| bin.fee)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FeeStructure {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(0.000005, 0.0, vec![(1_400_000, 0.0)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
|
impl ::solana_frozen_abi::abi_example::AbiExample for FeeStructure {
|
||||||
|
fn example() -> Self {
|
||||||
|
FeeStructure::default()
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ pub mod example_mocks;
|
||||||
pub mod exit;
|
pub mod exit;
|
||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod feature_set;
|
pub mod feature_set;
|
||||||
|
pub mod fee;
|
||||||
pub mod genesis_config;
|
pub mod genesis_config;
|
||||||
pub mod hard_forks;
|
pub mod hard_forks;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
|
|
|
@ -1585,13 +1585,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_payer_balances_distribute_tokens_single_payer() {
|
fn test_check_payer_balances_distribute_tokens_single_payer() {
|
||||||
let fees = 10_000;
|
|
||||||
let fees_in_sol = lamports_to_sol(fees);
|
|
||||||
|
|
||||||
let alice = Keypair::new();
|
let alice = Keypair::new();
|
||||||
let test_validator = TestValidator::with_custom_fees(
|
let test_validator = TestValidator::with_custom_fees(
|
||||||
alice.pubkey(),
|
alice.pubkey(),
|
||||||
fees,
|
10_000,
|
||||||
None,
|
None,
|
||||||
SocketAddrSpace::Unspecified,
|
SocketAddrSpace::Unspecified,
|
||||||
);
|
);
|
||||||
|
@ -1601,6 +1598,11 @@ mod tests {
|
||||||
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
||||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||||
|
|
||||||
|
let fees = client
|
||||||
|
.get_fee_for_message(&one_signer_message(&client))
|
||||||
|
.unwrap();
|
||||||
|
let fees_in_sol = lamports_to_sol(fees);
|
||||||
|
|
||||||
let allocation_amount = 1000.0;
|
let allocation_amount = 1000.0;
|
||||||
|
|
||||||
// Fully funded payer
|
// Fully funded payer
|
||||||
|
@ -1678,12 +1680,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_payer_balances_distribute_tokens_separate_payers() {
|
fn test_check_payer_balances_distribute_tokens_separate_payers() {
|
||||||
let fees = 10_000;
|
|
||||||
let fees_in_sol = lamports_to_sol(fees);
|
|
||||||
let alice = Keypair::new();
|
let alice = Keypair::new();
|
||||||
let test_validator = TestValidator::with_custom_fees(
|
let test_validator = TestValidator::with_custom_fees(
|
||||||
alice.pubkey(),
|
alice.pubkey(),
|
||||||
fees,
|
10_000,
|
||||||
None,
|
None,
|
||||||
SocketAddrSpace::Unspecified,
|
SocketAddrSpace::Unspecified,
|
||||||
);
|
);
|
||||||
|
@ -1691,6 +1691,11 @@ mod tests {
|
||||||
|
|
||||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
||||||
|
|
||||||
|
let fees = client
|
||||||
|
.get_fee_for_message(&one_signer_message(&client))
|
||||||
|
.unwrap();
|
||||||
|
let fees_in_sol = lamports_to_sol(fees);
|
||||||
|
|
||||||
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
||||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||||
|
|
||||||
|
@ -1802,18 +1807,21 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_payer_balances_distribute_stakes_single_payer() {
|
fn test_check_payer_balances_distribute_stakes_single_payer() {
|
||||||
let fees = 10_000;
|
|
||||||
let fees_in_sol = lamports_to_sol(fees);
|
|
||||||
let alice = Keypair::new();
|
let alice = Keypair::new();
|
||||||
let test_validator = TestValidator::with_custom_fees(
|
let test_validator = TestValidator::with_custom_fees(
|
||||||
alice.pubkey(),
|
alice.pubkey(),
|
||||||
fees,
|
10_000,
|
||||||
None,
|
None,
|
||||||
SocketAddrSpace::Unspecified,
|
SocketAddrSpace::Unspecified,
|
||||||
);
|
);
|
||||||
let url = test_validator.rpc_url();
|
let url = test_validator.rpc_url();
|
||||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
||||||
|
|
||||||
|
let fees = client
|
||||||
|
.get_fee_for_message(&one_signer_message(&client))
|
||||||
|
.unwrap();
|
||||||
|
let fees_in_sol = lamports_to_sol(fees);
|
||||||
|
|
||||||
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
||||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||||
|
|
||||||
|
@ -1925,12 +1933,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_payer_balances_distribute_stakes_separate_payers() {
|
fn test_check_payer_balances_distribute_stakes_separate_payers() {
|
||||||
let fees = 10_000;
|
|
||||||
let fees_in_sol = lamports_to_sol(fees);
|
|
||||||
let alice = Keypair::new();
|
let alice = Keypair::new();
|
||||||
let test_validator = TestValidator::with_custom_fees(
|
let test_validator = TestValidator::with_custom_fees(
|
||||||
alice.pubkey(),
|
alice.pubkey(),
|
||||||
fees,
|
10_000,
|
||||||
None,
|
None,
|
||||||
SocketAddrSpace::Unspecified,
|
SocketAddrSpace::Unspecified,
|
||||||
);
|
);
|
||||||
|
@ -1938,6 +1944,11 @@ mod tests {
|
||||||
|
|
||||||
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
|
||||||
|
|
||||||
|
let fees = client
|
||||||
|
.get_fee_for_message(&one_signer_message(&client))
|
||||||
|
.unwrap();
|
||||||
|
let fees_in_sol = lamports_to_sol(fees);
|
||||||
|
|
||||||
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
||||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue