Forráskód Böngészése

Add fees to tx-wide caps (#22081)

Jack May 3 éve
szülő
commit
3d9874b95a

+ 4 - 3
cli/tests/nonce.rs

@@ -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(),
     );

+ 30 - 24
cli/tests/stake.rs

@@ -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

+ 36 - 14
cli/tests/transfer.rs

@@ -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);
 }

+ 43 - 12
docs/src/developing/programming-model/runtime.md

@@ -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
 

+ 55 - 50
program-runtime/src/compute_budget.rs

@@ -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),

+ 4 - 3
program-runtime/src/invoke_context.rs

@@ -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();
     }

+ 79 - 3
programs/bpf/tests/programs.rs

@@ -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);
+}

+ 3 - 3
rpc/src/transaction_status_service.rs

@@ -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,

+ 26 - 3
runtime/src/accounts.rs

@@ -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(),
         )
     }
 

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 739 - 40
runtime/src/bank.rs


+ 7 - 7
runtime/src/message_processor.rs

@@ -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(),

+ 26 - 15
sdk/src/compute_budget.rs

@@ -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,
+    AbiExample,
+    AbiEnumVisitor,
     BorshDeserialize,
-    BorshSchema,
-    Debug,
+    BorshSerialize,
     Clone,
+    Debug,
+    Deserialize,
     PartialEq,
-    AbiExample,
-    AbiEnumVisitor,
+    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`

+ 67 - 0
sdk/src/fee.rs

@@ -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()
+    }
+}

+ 1 - 0
sdk/src/lib.rs

@@ -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;

+ 24 - 13
tokens/src/commands.rs

@@ -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();
 

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott