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]
|
||||
#[allow(clippy::redundant_closure)]
|
||||
fn test_create_account_with_seed() {
|
||||
const ONE_SIG_FEE: f64 = 0.000005;
|
||||
solana_logger::setup();
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
|
@ -310,7 +311,7 @@ fn test_create_account_with_seed() {
|
|||
&offline_nonce_authority_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(4000.999999999),
|
||||
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||
&rpc_client,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
);
|
||||
|
@ -381,12 +382,12 @@ fn test_create_account_with_seed() {
|
|||
process_command(&submit_config).unwrap();
|
||||
check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
|
||||
check_balance!(
|
||||
sol_to_lamports(31.999999999),
|
||||
sol_to_lamports(32.0 - ONE_SIG_FEE),
|
||||
&rpc_client,
|
||||
&offline_nonce_authority_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(4000.999999999),
|
||||
sol_to_lamports(4001.0 - ONE_SIG_FEE),
|
||||
&rpc_client,
|
||||
&online_nonce_creator_signer.pubkey(),
|
||||
);
|
||||
|
|
|
@ -18,6 +18,7 @@ use {
|
|||
solana_sdk::{
|
||||
account_utils::StateMut,
|
||||
commitment_config::CommitmentConfig,
|
||||
fee::FeeStructure,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
|
@ -876,14 +877,15 @@ fn test_stake_authorize() {
|
|||
#[test]
|
||||
fn test_stake_authorize_with_fee_payer() {
|
||||
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_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
let test_validator = TestValidator::with_custom_fees(
|
||||
mint_pubkey,
|
||||
SIG_FEE,
|
||||
1,
|
||||
Some(faucet_addr),
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
@ -912,14 +914,14 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &config.signers[0].pubkey());
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &payer_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &payer_pubkey);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 5_000_000).unwrap();
|
||||
check_balance!(5_000_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
||||
|
@ -934,7 +936,7 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
withdrawer: None,
|
||||
withdrawer_signer: None,
|
||||
lockup: Lockup::default(),
|
||||
amount: SpendAmount::Some(50_000),
|
||||
amount: SpendAmount::Some(1_000_000),
|
||||
sign_only: false,
|
||||
dump_transaction_message: false,
|
||||
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
||||
|
@ -945,8 +947,7 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
from: 0,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
// `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
|
||||
check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
|
||||
check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
|
||||
|
||||
// Assign authority with separate fee payer
|
||||
config.signers = vec![&default_signer, &payer_keypair];
|
||||
|
@ -970,10 +971,10 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
// `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
|
||||
// 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
|
||||
let blockhash = rpc_client.get_latest_blockhash().unwrap();
|
||||
|
@ -1021,10 +1022,10 @@ fn test_stake_authorize_with_fee_payer() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
// `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
|
||||
// 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]
|
||||
|
@ -1058,12 +1059,17 @@ fn test_stake_split() {
|
|||
config_offline.command = CliCommand::ClusterVersion;
|
||||
process_command(&config_offline).unwrap_err();
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
|
||||
.unwrap();
|
||||
check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
|
||||
request_and_confirm_airdrop(
|
||||
&rpc_client,
|
||||
&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();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
|
@ -1207,12 +1213,12 @@ fn test_stake_set_lockup() {
|
|||
config_offline.command = CliCommand::ClusterVersion;
|
||||
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();
|
||||
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();
|
||||
check_balance!(100_000, &rpc_client, &offline_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 1_000_000).unwrap();
|
||||
check_balance!(1_000_000, &rpc_client, &offline_pubkey);
|
||||
|
||||
// Create stake account, identity is authority
|
||||
let minimum_stake_balance = rpc_client
|
||||
|
|
|
@ -16,6 +16,7 @@ use {
|
|||
solana_faucet::faucet::run_local_faucet,
|
||||
solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
fee::FeeStructure,
|
||||
native_token::sol_to_lamports,
|
||||
nonce::State as NonceState,
|
||||
pubkey::Pubkey,
|
||||
|
@ -29,6 +30,8 @@ use {
|
|||
#[test]
|
||||
fn test_transfer() {
|
||||
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_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
@ -77,7 +80,11 @@ fn test_transfer() {
|
|||
derived_address_program_id: None,
|
||||
};
|
||||
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);
|
||||
|
||||
// Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
|
||||
|
@ -98,7 +105,11 @@ fn test_transfer() {
|
|||
derived_address_program_id: None,
|
||||
};
|
||||
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);
|
||||
|
||||
let mut offline = CliConfig::recent_for_tests();
|
||||
|
@ -154,7 +165,11 @@ fn test_transfer() {
|
|||
derived_address_program_id: None,
|
||||
};
|
||||
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);
|
||||
|
||||
// Create nonce account
|
||||
|
@ -172,7 +187,7 @@ fn test_transfer() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
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,
|
||||
&sender_pubkey,
|
||||
);
|
||||
|
@ -210,7 +225,7 @@ fn test_transfer() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
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,
|
||||
&sender_pubkey,
|
||||
);
|
||||
|
@ -235,7 +250,7 @@ fn test_transfer() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
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,
|
||||
&sender_pubkey,
|
||||
);
|
||||
|
@ -293,13 +308,18 @@ fn test_transfer() {
|
|||
derived_address_program_id: None,
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transfer_multisession_signing() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(2, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
let faucet_addr = run_local_faucet(mint_keypair, None);
|
||||
|
@ -329,7 +349,7 @@ fn test_transfer_multisession_signing() {
|
|||
&rpc_client,
|
||||
&CliConfig::recent_for_tests(),
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
sol_to_lamports(1.0) + 3,
|
||||
sol_to_lamports(1.0) + 2 * fee,
|
||||
)
|
||||
.unwrap();
|
||||
check_balance!(
|
||||
|
@ -338,7 +358,7 @@ fn test_transfer_multisession_signing() {
|
|||
&offline_from_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(1.0) + 3,
|
||||
sol_to_lamports(1.0) + 2 * fee,
|
||||
&rpc_client,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
);
|
||||
|
@ -438,7 +458,7 @@ fn test_transfer_multisession_signing() {
|
|||
&offline_from_signer.pubkey(),
|
||||
);
|
||||
check_balance!(
|
||||
sol_to_lamports(1.0) + 1,
|
||||
sol_to_lamports(1.0) + fee,
|
||||
&rpc_client,
|
||||
&offline_fee_payer_signer.pubkey(),
|
||||
);
|
||||
|
@ -448,6 +468,7 @@ fn test_transfer_multisession_signing() {
|
|||
#[test]
|
||||
fn test_transfer_all() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
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 recipient_pubkey = Pubkey::new(&[1u8; 32]);
|
||||
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
|
||||
check_balance!(50_000, &rpc_client, &sender_pubkey);
|
||||
request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 500_000).unwrap();
|
||||
check_balance!(500_000, &rpc_client, &sender_pubkey);
|
||||
check_balance!(0, &rpc_client, &recipient_pubkey);
|
||||
|
||||
check_ready(&rpc_client);
|
||||
|
@ -495,7 +516,7 @@ fn test_transfer_all() {
|
|||
};
|
||||
process_command(&config).unwrap();
|
||||
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]
|
||||
|
@ -554,6 +575,7 @@ fn test_transfer_unfunded_recipient() {
|
|||
#[test]
|
||||
fn test_transfer_with_seed() {
|
||||
solana_logger::setup();
|
||||
let fee = FeeStructure::default().get_max_fee(1, 0);
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_pubkey = mint_keypair.pubkey();
|
||||
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),
|
||||
};
|
||||
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!(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
|
||||
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
|
||||
program may not exceed. When the program consumes its entire budget or exceeds
|
||||
a bound then the runtime halts the program and returns an error.
|
||||
program may not exceed. When the program consumes its entire budget or exceeds a
|
||||
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:
|
||||
|
||||
|
@ -60,12 +64,12 @@ The following operations incur a compute cost:
|
|||
- 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
|
||||
invocation chain and the parent are halted.
|
||||
invocation chain is halted.
|
||||
|
||||
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.
|
||||
|
||||
For example, if the current budget is:
|
||||
|
@ -80,6 +84,7 @@ max_invoke_depth: 4,
|
|||
max_call_depth: 64,
|
||||
stack_frame_size: 4096,
|
||||
log_pubkey_units: 100,
|
||||
...
|
||||
```
|
||||
|
||||
Then the program
|
||||
|
@ -90,7 +95,7 @@ Then the program
|
|||
- Can not exceed a BPF call depth of 64
|
||||
- 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
|
||||
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)
|
||||
for more information.
|
||||
|
||||
The budget values are conditional on feature enablement, take a look at the
|
||||
compute budget's
|
||||
[new](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L97)
|
||||
function to find out how the budget is constructed. An understanding of how
|
||||
[features](runtime.md#features) work and what features are enabled on the
|
||||
cluster being used are required to determine the current budget's values.
|
||||
## Transaction-wide Compute Budget
|
||||
|
||||
Transactions are processed as a single entity and are the primary unit of block
|
||||
scheduling. In order to facilitate better block scheduling and account for the
|
||||
computational cost of each transaction, the compute budget is moving to a
|
||||
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
|
||||
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
use {
|
||||
solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||
feature_set::{requestable_heap_size, FeatureSet},
|
||||
instruction::InstructionError,
|
||||
transaction::{SanitizedTransaction, TransactionError},
|
||||
},
|
||||
std::sync::Arc,
|
||||
use solana_sdk::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
compute_budget::{self, ComputeBudgetInstruction},
|
||||
entrypoint::HEAP_LENGTH as MIN_HEAP_FRAME_BYTES,
|
||||
instruction::InstructionError,
|
||||
message::SanitizedMessage,
|
||||
transaction::TransactionError,
|
||||
};
|
||||
|
||||
const MAX_UNITS: u32 = 1_000_000;
|
||||
const MAX_UNITS: u32 = 1_400_000;
|
||||
const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
|
||||
|
||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||
|
@ -68,14 +65,19 @@ pub struct ComputeBudget {
|
|||
|
||||
impl Default for ComputeBudget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
Self::new(true)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
max_units: 200_000,
|
||||
max_units,
|
||||
log_64_units: 100,
|
||||
create_program_address_units: 1500,
|
||||
invoke_units: 1000,
|
||||
|
@ -97,25 +99,27 @@ impl ComputeBudget {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process_transaction(
|
||||
pub fn process_message(
|
||||
&mut self,
|
||||
tx: &SanitizedTransaction,
|
||||
feature_set: Arc<FeatureSet>,
|
||||
) -> Result<(), TransactionError> {
|
||||
message: &SanitizedMessage,
|
||||
requestable_heap_size: bool,
|
||||
) -> Result<u64, TransactionError> {
|
||||
let mut requested_additional_fee = 0;
|
||||
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
|
||||
// Compute budget instruction must be in the 1st 3 instructions (avoid
|
||||
// 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) {
|
||||
match try_from_slice_unchecked(&instruction.data) {
|
||||
Ok(ComputeBudgetInstruction::RequestUnits(units)) => {
|
||||
if units > MAX_UNITS {
|
||||
return Err(error);
|
||||
}
|
||||
self.max_units = units as u64;
|
||||
Ok(ComputeBudgetInstruction::RequestUnits {
|
||||
units,
|
||||
additional_fee,
|
||||
}) => {
|
||||
self.max_units = units.min(MAX_UNITS) as u64;
|
||||
requested_additional_fee = additional_fee as u64;
|
||||
}
|
||||
Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
|
||||
if !feature_set.is_active(&requestable_heap_size::id())
|
||||
if !requestable_heap_size
|
||||
|| bytes > MAX_HEAP_FRAME_BYTES
|
||||
|| bytes < MIN_HEAP_FRAME_BYTES as u32
|
||||
|| bytes % 1024 != 0
|
||||
|
@ -128,7 +132,7 @@ impl ComputeBudget {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(requested_additional_fee)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,8 +141,13 @@ mod tests {
|
|||
use {
|
||||
super::*,
|
||||
solana_sdk::{
|
||||
hash::Hash, instruction::Instruction, message::Message, pubkey::Pubkey,
|
||||
signature::Keypair, signer::Signer, transaction::Transaction,
|
||||
hash::Hash,
|
||||
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())),
|
||||
Hash::default(),
|
||||
));
|
||||
let feature_set = Arc::new(FeatureSet::all_enabled());
|
||||
let mut compute_budget = ComputeBudget::default();
|
||||
let result = compute_budget.process_transaction(&tx, feature_set);
|
||||
assert_eq!($expected_error as Result<(), TransactionError>, result);
|
||||
let result = compute_budget.process_message(&tx.message(), true);
|
||||
assert_eq!($expected_error, result);
|
||||
assert_eq!(compute_budget, $expected_budget);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_transaction() {
|
||||
fn test_process_mesage() {
|
||||
// Units
|
||||
test!(&[], Ok(()), ComputeBudget::default());
|
||||
test!(&[], Ok(0), ComputeBudget::default());
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_units(1),
|
||||
ComputeBudgetInstruction::request_units(1, 0),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: 1,
|
||||
..ComputeBudget::default()
|
||||
|
@ -175,21 +183,18 @@ mod tests {
|
|||
);
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS + 1),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS + 1, 0),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Err(TransactionError::InstructionError(
|
||||
0,
|
||||
InstructionError::InvalidInstructionData,
|
||||
)),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
test!(
|
||||
&[
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: MAX_UNITS as u64,
|
||||
..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![]),
|
||||
ComputeBudgetInstruction::request_units(1),
|
||||
ComputeBudgetInstruction::request_units(1, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
|
||||
// HeapFrame
|
||||
test!(&[], Ok(()), ComputeBudget::default());
|
||||
test!(&[], Ok(0), ComputeBudget::default());
|
||||
test!(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_heap_frame(40 * 1024),
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
heap_size: Some(40 * 1024),
|
||||
..ComputeBudget::default()
|
||||
|
@ -257,7 +262,7 @@ mod tests {
|
|||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
|
||||
..ComputeBudget::default()
|
||||
|
@ -270,7 +275,7 @@ mod tests {
|
|||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(1), // ignored
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget::default()
|
||||
);
|
||||
|
||||
|
@ -279,9 +284,9 @@ mod tests {
|
|||
&[
|
||||
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
|
||||
ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS),
|
||||
ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
|
||||
],
|
||||
Ok(()),
|
||||
Ok(0),
|
||||
ComputeBudget {
|
||||
max_units: MAX_UNITS as u64,
|
||||
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 invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
|
||||
invoke_context.feature_set = Arc::new(feature_set);
|
||||
invoke_context.compute_budget = ComputeBudget::new(false);
|
||||
|
||||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop().unwrap();
|
||||
|
||||
|
@ -1622,7 +1623,7 @@ mod tests {
|
|||
let expected_compute_budget = ComputeBudget {
|
||||
max_units: 500_000,
|
||||
heap_size: Some(256_usize.saturating_mul(1024)),
|
||||
..ComputeBudget::default()
|
||||
..ComputeBudget::new(false)
|
||||
};
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
|
@ -1633,7 +1634,7 @@ mod tests {
|
|||
invoke_context.push(&[], &[0], &[]).unwrap();
|
||||
assert_eq!(
|
||||
*invoke_context.get_compute_budget(),
|
||||
ComputeBudget::default()
|
||||
ComputeBudget::new(false)
|
||||
);
|
||||
invoke_context.pop().unwrap();
|
||||
}
|
||||
|
|
|
@ -46,6 +46,9 @@ use solana_sdk::{
|
|||
clock::MAX_PROCESSING_AGE,
|
||||
compute_budget::ComputeBudgetInstruction,
|
||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||
feature_set::FeatureSet,
|
||||
fee::FeeStructure,
|
||||
fee_calculator::FeeRateGovernor,
|
||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||
loader_instruction,
|
||||
message::{v0::LoadedAddresses, Message, SanitizedMessage},
|
||||
|
@ -397,7 +400,7 @@ fn execute_transactions(
|
|||
),
|
||||
}
|
||||
.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(),
|
||||
lamports_per_signature,
|
||||
);
|
||||
|
@ -1383,7 +1386,7 @@ fn test_program_bpf_compute_budget() {
|
|||
);
|
||||
let message = Message::new(
|
||||
&[
|
||||
ComputeBudgetInstruction::request_units(1),
|
||||
ComputeBudgetInstruction::request_units(1, 0),
|
||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||
],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
|
@ -2886,8 +2889,8 @@ fn test_program_bpf_realloc() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
#[test]
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
fn test_program_bpf_realloc_invoke() {
|
||||
solana_logger::setup();
|
||||
|
||||
|
@ -3418,3 +3421,76 @@ fn test_program_bpf_processed_inner_instruction() {
|
|||
.send_and_confirm_message(&[&mint_keypair], message)
|
||||
.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},
|
||||
},
|
||||
solana_runtime::bank::{
|
||||
Bank, DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
|
||||
DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
|
||||
|
@ -109,7 +109,7 @@ impl TransactionStatusService {
|
|||
),
|
||||
}
|
||||
.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(),
|
||||
lamports_per_signature,
|
||||
);
|
||||
|
@ -204,7 +204,7 @@ pub(crate) mod tests {
|
|||
dashmap::DashMap,
|
||||
solana_account_decoder::parse_token::token_amount_to_ui_amount,
|
||||
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::{
|
||||
account_utils::StateMut,
|
||||
clock::Slot,
|
||||
|
|
|
@ -28,7 +28,8 @@ use {
|
|||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
clock::{BankId, Slot, INITIAL_RENT_EPOCH},
|
||||
feature_set::{self, FeatureSet},
|
||||
feature_set::{self, tx_wide_compute_cap, FeatureSet},
|
||||
fee::FeeStructure,
|
||||
genesis_config::ClusterType,
|
||||
hash::Hash,
|
||||
message::{
|
||||
|
@ -472,6 +473,7 @@ impl Accounts {
|
|||
error_counters: &mut ErrorCounters,
|
||||
rent_collector: &RentCollector,
|
||||
feature_set: &FeatureSet,
|
||||
fee_structure: &FeeStructure,
|
||||
) -> Vec<TransactionLoadResult> {
|
||||
txs.iter()
|
||||
.zip(lock_results)
|
||||
|
@ -484,7 +486,12 @@ impl Accounts {
|
|||
hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
|
||||
});
|
||||
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 {
|
||||
return (Err(TransactionError::BlockhashNotFound), None);
|
||||
};
|
||||
|
@ -1359,6 +1366,8 @@ mod tests {
|
|||
lamports_per_signature: u64,
|
||||
rent_collector: &RentCollector,
|
||||
error_counters: &mut ErrorCounters,
|
||||
feature_set: &FeatureSet,
|
||||
fee_structure: &FeeStructure,
|
||||
) -> Vec<TransactionLoadResult> {
|
||||
let mut hash_queue = BlockhashQueue::new(100);
|
||||
hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
|
||||
|
@ -1382,7 +1391,8 @@ mod tests {
|
|||
&hash_queue,
|
||||
error_counters,
|
||||
rent_collector,
|
||||
&FeatureSet::all_enabled(),
|
||||
feature_set,
|
||||
fee_structure,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1398,6 +1408,8 @@ mod tests {
|
|||
lamports_per_signature,
|
||||
&RentCollector::default(),
|
||||
error_counters,
|
||||
&FeatureSet::all_enabled(),
|
||||
&FeeStructure::default(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1549,6 +1561,8 @@ mod tests {
|
|||
let fee = Bank::calculate_fee(
|
||||
&SanitizedMessage::try_from(tx.message().clone()).unwrap(),
|
||||
10,
|
||||
&FeeStructure::default(),
|
||||
false,
|
||||
);
|
||||
assert_eq!(fee, 10);
|
||||
|
||||
|
@ -1595,6 +1609,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_load_accounts_fee_payer_is_nonce() {
|
||||
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(
|
||||
0,
|
||||
&EpochSchedule::default(),
|
||||
|
@ -1631,6 +1647,8 @@ mod tests {
|
|||
min_balance,
|
||||
&rent_collector,
|
||||
&mut error_counters,
|
||||
&feature_set,
|
||||
&FeeStructure::default(),
|
||||
);
|
||||
assert_eq!(loaded_accounts.len(), 1);
|
||||
let (load_res, _nonce) = &loaded_accounts[0];
|
||||
|
@ -1645,6 +1663,8 @@ mod tests {
|
|||
min_balance,
|
||||
&rent_collector,
|
||||
&mut error_counters,
|
||||
&feature_set,
|
||||
&FeeStructure::default(),
|
||||
);
|
||||
assert_eq!(loaded_accounts.len(), 1);
|
||||
let (load_res, _nonce) = &loaded_accounts[0];
|
||||
|
@ -1658,6 +1678,8 @@ mod tests {
|
|||
min_balance,
|
||||
&rent_collector,
|
||||
&mut error_counters,
|
||||
&feature_set,
|
||||
&FeeStructure::default(),
|
||||
);
|
||||
assert_eq!(loaded_accounts.len(), 1);
|
||||
let (load_res, _nonce) = &loaded_accounts[0];
|
||||
|
@ -2982,6 +3004,7 @@ mod tests {
|
|||
&mut error_counters,
|
||||
&rent_collector,
|
||||
&FeatureSet::all_enabled(),
|
||||
&FeeStructure::default(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -272,7 +272,7 @@ mod tests {
|
|||
None,
|
||||
executors.clone(),
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -314,7 +314,7 @@ mod tests {
|
|||
None,
|
||||
executors.clone(),
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -346,7 +346,7 @@ mod tests {
|
|||
None,
|
||||
executors,
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -481,7 +481,7 @@ mod tests {
|
|||
None,
|
||||
executors.clone(),
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -514,7 +514,7 @@ mod tests {
|
|||
None,
|
||||
executors.clone(),
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -544,7 +544,7 @@ mod tests {
|
|||
None,
|
||||
executors,
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
@ -623,7 +623,7 @@ mod tests {
|
|||
None,
|
||||
Rc::new(RefCell::new(Executors::default())),
|
||||
Arc::new(FeatureSet::all_enabled()),
|
||||
ComputeBudget::new(),
|
||||
ComputeBudget::default(),
|
||||
&mut ExecuteTimings::default(),
|
||||
&sysvar_cache,
|
||||
Hash::default(),
|
||||
|
|
|
@ -2,38 +2,49 @@
|
|||
|
||||
use {
|
||||
crate::instruction::Instruction,
|
||||
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
|
||||
borsh::{BorshDeserialize, BorshSerialize},
|
||||
};
|
||||
|
||||
crate::declare_id!("ComputeBudget111111111111111111111111111111");
|
||||
|
||||
/// Compute Budget Instructions
|
||||
#[derive(
|
||||
Serialize,
|
||||
Deserialize,
|
||||
BorshSerialize,
|
||||
BorshDeserialize,
|
||||
BorshSchema,
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
AbiExample,
|
||||
AbiEnumVisitor,
|
||||
BorshDeserialize,
|
||||
BorshSerialize,
|
||||
Clone,
|
||||
Debug,
|
||||
Deserialize,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
)]
|
||||
pub enum ComputeBudgetInstruction {
|
||||
/// Request a specific maximum number of compute units the transaction is
|
||||
/// allowed to consume.
|
||||
RequestUnits(u32),
|
||||
/// Request a specific transaction-wide program heap frame size in bytes.
|
||||
/// The value requested must be a multiple of 1024. This new heap frame size
|
||||
/// applies to each program executed, including all calls to CPIs.
|
||||
/// allowed to consume and an additional fee to pay.
|
||||
RequestUnits {
|
||||
/// Units to request
|
||||
units: u32,
|
||||
/// 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),
|
||||
}
|
||||
|
||||
impl ComputeBudgetInstruction {
|
||||
/// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
|
||||
pub fn request_units(units: u32) -> Instruction {
|
||||
Instruction::new_with_borsh(id(), &ComputeBudgetInstruction::RequestUnits(units), vec![])
|
||||
pub fn request_units(units: u32, additional_fee: u32) -> Instruction {
|
||||
Instruction::new_with_borsh(
|
||||
id(),
|
||||
&ComputeBudgetInstruction::RequestUnits {
|
||||
units,
|
||||
additional_fee,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
/// 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 feature;
|
||||
pub mod feature_set;
|
||||
pub mod fee;
|
||||
pub mod genesis_config;
|
||||
pub mod hard_forks;
|
||||
pub mod hash;
|
||||
|
|
|
@ -1585,13 +1585,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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 test_validator = TestValidator::with_custom_fees(
|
||||
alice.pubkey(),
|
||||
fees,
|
||||
10_000,
|
||||
None,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
@ -1601,6 +1598,11 @@ mod tests {
|
|||
let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
|
||||
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;
|
||||
|
||||
// Fully funded payer
|
||||
|
@ -1678,12 +1680,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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 test_validator = TestValidator::with_custom_fees(
|
||||
alice.pubkey(),
|
||||
fees,
|
||||
10_000,
|
||||
None,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
@ -1691,6 +1691,11 @@ mod tests {
|
|||
|
||||
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());
|
||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||
|
||||
|
@ -1802,18 +1807,21 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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 test_validator = TestValidator::with_custom_fees(
|
||||
alice.pubkey(),
|
||||
fees,
|
||||
10_000,
|
||||
None,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
let url = test_validator.rpc_url();
|
||||
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());
|
||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||
|
||||
|
@ -1925,12 +1933,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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 test_validator = TestValidator::with_custom_fees(
|
||||
alice.pubkey(),
|
||||
fees,
|
||||
10_000,
|
||||
None,
|
||||
SocketAddrSpace::Unspecified,
|
||||
);
|
||||
|
@ -1938,6 +1944,11 @@ mod tests {
|
|||
|
||||
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());
|
||||
write_keypair_file(&alice, &sender_keypair_file).unwrap();
|
||||
|
||||
|
|
Loading…
Reference in New Issue