Prechádzať zdrojové kódy

Add fees to tx-wide caps (#22081)

Jack May 3 rokov pred
rodič
commit
3d9874b95a

+ 4 - 3
cli/tests/nonce.rs

@@ -238,6 +238,7 @@ fn full_battery_tests(
 #[test]
 #[test]
 #[allow(clippy::redundant_closure)]
 #[allow(clippy::redundant_closure)]
 fn test_create_account_with_seed() {
 fn test_create_account_with_seed() {
+    const ONE_SIG_FEE: f64 = 0.000005;
     solana_logger::setup();
     solana_logger::setup();
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
@@ -310,7 +311,7 @@ fn test_create_account_with_seed() {
         &offline_nonce_authority_signer.pubkey(),
         &offline_nonce_authority_signer.pubkey(),
     );
     );
     check_balance!(
     check_balance!(
-        sol_to_lamports(4000.999999999),
+        sol_to_lamports(4001.0 - ONE_SIG_FEE),
         &rpc_client,
         &rpc_client,
         &online_nonce_creator_signer.pubkey(),
         &online_nonce_creator_signer.pubkey(),
     );
     );
@@ -381,12 +382,12 @@ fn test_create_account_with_seed() {
     process_command(&submit_config).unwrap();
     process_command(&submit_config).unwrap();
     check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
     check_balance!(sol_to_lamports(241.0), &rpc_client, &nonce_address);
     check_balance!(
     check_balance!(
-        sol_to_lamports(31.999999999),
+        sol_to_lamports(32.0 - ONE_SIG_FEE),
         &rpc_client,
         &rpc_client,
         &offline_nonce_authority_signer.pubkey(),
         &offline_nonce_authority_signer.pubkey(),
     );
     );
     check_balance!(
     check_balance!(
-        sol_to_lamports(4000.999999999),
+        sol_to_lamports(4001.0 - ONE_SIG_FEE),
         &rpc_client,
         &rpc_client,
         &online_nonce_creator_signer.pubkey(),
         &online_nonce_creator_signer.pubkey(),
     );
     );

+ 30 - 24
cli/tests/stake.rs

@@ -18,6 +18,7 @@ use {
     solana_sdk::{
     solana_sdk::{
         account_utils::StateMut,
         account_utils::StateMut,
         commitment_config::CommitmentConfig,
         commitment_config::CommitmentConfig,
+        fee::FeeStructure,
         nonce::State as NonceState,
         nonce::State as NonceState,
         pubkey::Pubkey,
         pubkey::Pubkey,
         signature::{keypair_from_seed, Keypair, Signer},
         signature::{keypair_from_seed, Keypair, Signer},
@@ -876,14 +877,15 @@ fn test_stake_authorize() {
 #[test]
 #[test]
 fn test_stake_authorize_with_fee_payer() {
 fn test_stake_authorize_with_fee_payer() {
     solana_logger::setup();
     solana_logger::setup();
-    const SIG_FEE: u64 = 42;
+    let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
+    let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
 
 
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let test_validator = TestValidator::with_custom_fees(
     let test_validator = TestValidator::with_custom_fees(
         mint_pubkey,
         mint_pubkey,
-        SIG_FEE,
+        1,
         Some(faucet_addr),
         Some(faucet_addr),
         SocketAddrSpace::Unspecified,
         SocketAddrSpace::Unspecified,
     );
     );
@@ -912,14 +914,14 @@ fn test_stake_authorize_with_fee_payer() {
     config_offline.command = CliCommand::ClusterVersion;
     config_offline.command = CliCommand::ClusterVersion;
     process_command(&config_offline).unwrap_err();
     process_command(&config_offline).unwrap_err();
 
 
-    request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 100_000).unwrap();
-    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);
     check_ready(&rpc_client);
 
 
@@ -934,7 +936,7 @@ fn test_stake_authorize_with_fee_payer() {
         withdrawer: None,
         withdrawer: None,
         withdrawer_signer: None,
         withdrawer_signer: None,
         lockup: Lockup::default(),
         lockup: Lockup::default(),
-        amount: SpendAmount::Some(50_000),
+        amount: SpendAmount::Some(1_000_000),
         sign_only: false,
         sign_only: false,
         dump_transaction_message: false,
         dump_transaction_message: false,
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
         blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
@@ -945,8 +947,7 @@ fn test_stake_authorize_with_fee_payer() {
         from: 0,
         from: 0,
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
-    // `config` balance should be 50,000 - 1 stake account sig - 1 fee sig
-    check_balance!(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
     // Assign authority with separate fee payer
     config.signers = vec![&default_signer, &payer_keypair];
     config.signers = vec![&default_signer, &payer_keypair];
@@ -970,10 +971,10 @@ fn test_stake_authorize_with_fee_payer() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     // `config` balance has not changed, despite submitting the TX
     // `config` balance has not changed, despite submitting the TX
-    check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
+    check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
     // `config_payer` however has paid `config`'s authority sig
     // `config_payer` however has paid `config`'s authority sig
     // and `config_payer`'s fee sig
     // and `config_payer`'s fee sig
-    check_balance!(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey);
+    check_balance!(5_000_000 - fee_two_sig, &rpc_client, &payer_pubkey);
 
 
     // Assign authority with offline fee payer
     // Assign authority with offline fee payer
     let blockhash = rpc_client.get_latest_blockhash().unwrap();
     let blockhash = rpc_client.get_latest_blockhash().unwrap();
@@ -1021,10 +1022,10 @@ fn test_stake_authorize_with_fee_payer() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     // `config`'s balance again has not changed
     // `config`'s balance again has not changed
-    check_balance!(50_000 - SIG_FEE - SIG_FEE, &rpc_client, &default_pubkey);
+    check_balance!(4_000_000 - fee_two_sig, &rpc_client, &default_pubkey);
     // `config_offline` however has paid 1 sig due to being both authority
     // `config_offline` however has paid 1 sig due to being both authority
     // and fee payer
     // and fee payer
-    check_balance!(100_000 - SIG_FEE, &rpc_client, &offline_pubkey);
+    check_balance!(5_000_000 - fee_one_sig, &rpc_client, &offline_pubkey);
 }
 }
 
 
 #[test]
 #[test]
@@ -1058,12 +1059,17 @@ fn test_stake_split() {
     config_offline.command = CliCommand::ClusterVersion;
     config_offline.command = CliCommand::ClusterVersion;
     process_command(&config_offline).unwrap_err();
     process_command(&config_offline).unwrap_err();
 
 
-    request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
-        .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
     // Create stake account, identity is authority
     let minimum_stake_balance = rpc_client
     let minimum_stake_balance = rpc_client
@@ -1207,12 +1213,12 @@ fn test_stake_set_lockup() {
     config_offline.command = CliCommand::ClusterVersion;
     config_offline.command = CliCommand::ClusterVersion;
     process_command(&config_offline).unwrap_err();
     process_command(&config_offline).unwrap_err();
 
 
-    request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 500_000)
+    request_and_confirm_airdrop(&rpc_client, &config, &config.signers[0].pubkey(), 5_000_000)
         .unwrap();
         .unwrap();
-    check_balance!(500_000, &rpc_client, &config.signers[0].pubkey());
+    check_balance!(5_000_000, &rpc_client, &config.signers[0].pubkey());
 
 
-    request_and_confirm_airdrop(&rpc_client, &config_offline, &offline_pubkey, 100_000).unwrap();
-    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
     // Create stake account, identity is authority
     let minimum_stake_balance = rpc_client
     let minimum_stake_balance = rpc_client

+ 36 - 14
cli/tests/transfer.rs

@@ -16,6 +16,7 @@ use {
     solana_faucet::faucet::run_local_faucet,
     solana_faucet::faucet::run_local_faucet,
     solana_sdk::{
     solana_sdk::{
         commitment_config::CommitmentConfig,
         commitment_config::CommitmentConfig,
+        fee::FeeStructure,
         native_token::sol_to_lamports,
         native_token::sol_to_lamports,
         nonce::State as NonceState,
         nonce::State as NonceState,
         pubkey::Pubkey,
         pubkey::Pubkey,
@@ -29,6 +30,8 @@ use {
 #[test]
 #[test]
 fn test_transfer() {
 fn test_transfer() {
     solana_logger::setup();
     solana_logger::setup();
+    let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
+    let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let faucet_addr = run_local_faucet(mint_keypair, None);
@@ -77,7 +80,11 @@ fn test_transfer() {
         derived_address_program_id: None,
         derived_address_program_id: None,
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
-    check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
+    check_balance!(
+        sol_to_lamports(4.0) - fee_one_sig,
+        &rpc_client,
+        &sender_pubkey
+    );
     check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
     check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
 
 
     // Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
     // Plain ole transfer, failure due to InsufficientFundsForSpendAndFee
@@ -98,7 +105,11 @@ fn test_transfer() {
         derived_address_program_id: None,
         derived_address_program_id: None,
     };
     };
     assert!(process_command(&config).is_err());
     assert!(process_command(&config).is_err());
-    check_balance!(sol_to_lamports(4.0) - 1, &rpc_client, &sender_pubkey);
+    check_balance!(
+        sol_to_lamports(4.0) - fee_one_sig,
+        &rpc_client,
+        &sender_pubkey
+    );
     check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
     check_balance!(sol_to_lamports(1.0), &rpc_client, &recipient_pubkey);
 
 
     let mut offline = CliConfig::recent_for_tests();
     let mut offline = CliConfig::recent_for_tests();
@@ -154,7 +165,11 @@ fn test_transfer() {
         derived_address_program_id: None,
         derived_address_program_id: None,
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
-    check_balance!(sol_to_lamports(0.5) - 1, &rpc_client, &offline_pubkey);
+    check_balance!(
+        sol_to_lamports(0.5) - fee_one_sig,
+        &rpc_client,
+        &offline_pubkey
+    );
     check_balance!(sol_to_lamports(1.5), &rpc_client, &recipient_pubkey);
     check_balance!(sol_to_lamports(1.5), &rpc_client, &recipient_pubkey);
 
 
     // Create nonce account
     // Create nonce account
@@ -172,7 +187,7 @@ fn test_transfer() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     check_balance!(
     check_balance!(
-        sol_to_lamports(4.0) - 3 - minimum_nonce_balance,
+        sol_to_lamports(4.0) - fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
         &rpc_client,
         &sender_pubkey,
         &sender_pubkey,
     );
     );
@@ -210,7 +225,7 @@ fn test_transfer() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     check_balance!(
     check_balance!(
-        sol_to_lamports(3.0) - 4 - minimum_nonce_balance,
+        sol_to_lamports(3.0) - 2 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
         &rpc_client,
         &sender_pubkey,
         &sender_pubkey,
     );
     );
@@ -235,7 +250,7 @@ fn test_transfer() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     check_balance!(
     check_balance!(
-        sol_to_lamports(3.0) - 5 - minimum_nonce_balance,
+        sol_to_lamports(3.0) - 3 * fee_one_sig - fee_two_sig - minimum_nonce_balance,
         &rpc_client,
         &rpc_client,
         &sender_pubkey,
         &sender_pubkey,
     );
     );
@@ -293,13 +308,18 @@ fn test_transfer() {
         derived_address_program_id: None,
         derived_address_program_id: None,
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
-    check_balance!(sol_to_lamports(0.1) - 2, &rpc_client, &offline_pubkey);
+    check_balance!(
+        sol_to_lamports(0.1) - 2 * fee_one_sig,
+        &rpc_client,
+        &offline_pubkey
+    );
     check_balance!(sol_to_lamports(2.9), &rpc_client, &recipient_pubkey);
     check_balance!(sol_to_lamports(2.9), &rpc_client, &recipient_pubkey);
 }
 }
 
 
 #[test]
 #[test]
 fn test_transfer_multisession_signing() {
 fn test_transfer_multisession_signing() {
     solana_logger::setup();
     solana_logger::setup();
+    let fee = FeeStructure::default().get_max_fee(2, 0);
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let faucet_addr = run_local_faucet(mint_keypair, None);
@@ -329,7 +349,7 @@ fn test_transfer_multisession_signing() {
         &rpc_client,
         &rpc_client,
         &CliConfig::recent_for_tests(),
         &CliConfig::recent_for_tests(),
         &offline_fee_payer_signer.pubkey(),
         &offline_fee_payer_signer.pubkey(),
-        sol_to_lamports(1.0) + 3,
+        sol_to_lamports(1.0) + 2 * fee,
     )
     )
     .unwrap();
     .unwrap();
     check_balance!(
     check_balance!(
@@ -338,7 +358,7 @@ fn test_transfer_multisession_signing() {
         &offline_from_signer.pubkey(),
         &offline_from_signer.pubkey(),
     );
     );
     check_balance!(
     check_balance!(
-        sol_to_lamports(1.0) + 3,
+        sol_to_lamports(1.0) + 2 * fee,
         &rpc_client,
         &rpc_client,
         &offline_fee_payer_signer.pubkey(),
         &offline_fee_payer_signer.pubkey(),
     );
     );
@@ -438,7 +458,7 @@ fn test_transfer_multisession_signing() {
         &offline_from_signer.pubkey(),
         &offline_from_signer.pubkey(),
     );
     );
     check_balance!(
     check_balance!(
-        sol_to_lamports(1.0) + 1,
+        sol_to_lamports(1.0) + fee,
         &rpc_client,
         &rpc_client,
         &offline_fee_payer_signer.pubkey(),
         &offline_fee_payer_signer.pubkey(),
     );
     );
@@ -448,6 +468,7 @@ fn test_transfer_multisession_signing() {
 #[test]
 #[test]
 fn test_transfer_all() {
 fn test_transfer_all() {
     solana_logger::setup();
     solana_logger::setup();
+    let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let faucet_addr = run_local_faucet(mint_keypair, None);
@@ -470,8 +491,8 @@ fn test_transfer_all() {
     let sender_pubkey = config.signers[0].pubkey();
     let sender_pubkey = config.signers[0].pubkey();
     let recipient_pubkey = Pubkey::new(&[1u8; 32]);
     let recipient_pubkey = Pubkey::new(&[1u8; 32]);
 
 
-    request_and_confirm_airdrop(&rpc_client, &config, &sender_pubkey, 50_000).unwrap();
-    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_balance!(0, &rpc_client, &recipient_pubkey);
 
 
     check_ready(&rpc_client);
     check_ready(&rpc_client);
@@ -495,7 +516,7 @@ fn test_transfer_all() {
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
     check_balance!(0, &rpc_client, &sender_pubkey);
     check_balance!(0, &rpc_client, &sender_pubkey);
-    check_balance!(49_999, &rpc_client, &recipient_pubkey);
+    check_balance!(500_000 - fee, &rpc_client, &recipient_pubkey);
 }
 }
 
 
 #[test]
 #[test]
@@ -554,6 +575,7 @@ fn test_transfer_unfunded_recipient() {
 #[test]
 #[test]
 fn test_transfer_with_seed() {
 fn test_transfer_with_seed() {
     solana_logger::setup();
     solana_logger::setup();
+    let fee = FeeStructure::default().get_max_fee(1, 0);
     let mint_keypair = Keypair::new();
     let mint_keypair = Keypair::new();
     let mint_pubkey = mint_keypair.pubkey();
     let mint_pubkey = mint_keypair.pubkey();
     let faucet_addr = run_local_faucet(mint_keypair, None);
     let faucet_addr = run_local_faucet(mint_keypair, None);
@@ -612,7 +634,7 @@ fn test_transfer_with_seed() {
         derived_address_program_id: Some(derived_address_program_id),
         derived_address_program_id: Some(derived_address_program_id),
     };
     };
     process_command(&config).unwrap();
     process_command(&config).unwrap();
-    check_balance!(sol_to_lamports(1.0) - 1, &rpc_client, &sender_pubkey);
+    check_balance!(sol_to_lamports(1.0) - fee, &rpc_client, &sender_pubkey);
     check_balance!(sol_to_lamports(5.0), &rpc_client, &recipient_pubkey);
     check_balance!(sol_to_lamports(5.0), &rpc_client, &recipient_pubkey);
     check_balance!(0, &rpc_client, &derived_address);
     check_balance!(0, &rpc_client, &derived_address);
 }
 }

+ 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
 To prevent a program from abusing computation resources each instruction in a
 transaction is given a compute budget. The budget consists of computation units
 transaction is given a compute budget. The budget consists of computation units
 that are consumed as the program performs various operations and bounds that the
 that are consumed as the program performs various operations and bounds that the
-program may not exceed. When the program consumes its entire budget or exceeds
-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:
 The following operations incur a compute cost:
 
 
@@ -60,12 +64,12 @@ The following operations incur a compute cost:
   - cross-program invocations
   - cross-program invocations
   - ...
   - ...
 
 
-For cross-program invocations the programs invoked inherit the budget of their
+For cross-program invocations, the programs invoked inherit the budget of their
 parent. If an invoked program consume the budget or exceeds a bound the entire
 parent. If an invoked program consume the budget or exceeds a bound the entire
-invocation chain and the parent are halted.
+invocation chain is halted.
 
 
 The current [compute
 The current [compute
-budget](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L65)
+budget](https://github.com/solana-labs/solana/blob/0224a8b127ace4c6453dd6492a38c66cb999abd2/sdk/src/compute_budget.rs#L102)
 can be found in the Solana SDK.
 can be found in the Solana SDK.
 
 
 For example, if the current budget is:
 For example, if the current budget is:
@@ -80,6 +84,7 @@ max_invoke_depth: 4,
 max_call_depth: 64,
 max_call_depth: 64,
 stack_frame_size: 4096,
 stack_frame_size: 4096,
 log_pubkey_units: 100,
 log_pubkey_units: 100,
+...
 ```
 ```
 
 
 Then the program
 Then the program
@@ -90,7 +95,7 @@ Then the program
 - Can not exceed a BPF call depth of 64
 - Can not exceed a BPF call depth of 64
 - Cannot exceed 4 levels of cross-program invocations.
 - Cannot exceed 4 levels of cross-program invocations.
 
 
-Since the compute budget is consumed incrementally as the program executes the
+Since the compute budget is consumed incrementally as the program executes, the
 total budget consumption will be a combination of the various costs of the
 total budget consumption will be a combination of the various costs of the
 operations it performs.
 operations it performs.
 
 
@@ -98,12 +103,38 @@ At runtime a program may log how much of the compute budget remains. See
 [debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption)
 [debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption)
 for more information.
 for more information.
 
 
-The budget values are conditional on feature enablement, take a look at the
-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
 ## 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;
 const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1024;
 
 
 #[cfg(RUSTC_WITH_SPECIALIZATION)]
 #[cfg(RUSTC_WITH_SPECIALIZATION)]
@@ -68,14 +65,19 @@ pub struct ComputeBudget {
 
 
 impl Default for ComputeBudget {
 impl Default for ComputeBudget {
     fn default() -> Self {
     fn default() -> Self {
-        Self::new()
+        Self::new(true)
     }
     }
 }
 }
 
 
 impl ComputeBudget {
 impl ComputeBudget {
-    pub fn new() -> Self {
+    pub fn new(use_max_units_default: bool) -> Self {
+        let max_units = if use_max_units_default {
+            MAX_UNITS
+        } else {
+            200_000
+        } as u64;
         ComputeBudget {
         ComputeBudget {
-            max_units: 200_000,
+            max_units,
             log_64_units: 100,
             log_64_units: 100,
             create_program_address_units: 1500,
             create_program_address_units: 1500,
             invoke_units: 1000,
             invoke_units: 1000,
@@ -97,25 +99,27 @@ impl ComputeBudget {
         }
         }
     }
     }
 
 
-    pub fn process_transaction(
+    pub fn process_message(
         &mut self,
         &mut self,
-        tx: &SanitizedTransaction,
-        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);
         let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
         // Compute budget instruction must be in the 1st 3 instructions (avoid
         // Compute budget instruction must be in the 1st 3 instructions (avoid
         // nonce marker), otherwise ignored
         // nonce marker), otherwise ignored
-        for (program_id, instruction) in tx.message().program_instructions_iter().take(3) {
+        for (program_id, instruction) in message.program_instructions_iter().take(3) {
             if compute_budget::check_id(program_id) {
             if compute_budget::check_id(program_id) {
                 match try_from_slice_unchecked(&instruction.data) {
                 match try_from_slice_unchecked(&instruction.data) {
-                    Ok(ComputeBudgetInstruction::RequestUnits(units)) => {
-                        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)) => {
                     Ok(ComputeBudgetInstruction::RequestHeapFrame(bytes)) => {
-                        if !feature_set.is_active(&requestable_heap_size::id())
+                        if !requestable_heap_size
                             || bytes > MAX_HEAP_FRAME_BYTES
                             || bytes > MAX_HEAP_FRAME_BYTES
                             || bytes < MIN_HEAP_FRAME_BYTES as u32
                             || bytes < MIN_HEAP_FRAME_BYTES as u32
                             || bytes % 1024 != 0
                             || bytes % 1024 != 0
@@ -128,7 +132,7 @@ impl ComputeBudget {
                 }
                 }
             }
             }
         }
         }
-        Ok(())
+        Ok(requested_additional_fee)
     }
     }
 }
 }
 
 
@@ -137,8 +141,13 @@ mod tests {
     use {
     use {
         super::*,
         super::*,
         solana_sdk::{
         solana_sdk::{
-            hash::Hash, instruction::Instruction, message::Message, pubkey::Pubkey,
-            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())),
                 Message::new($instructions, Some(&payer_keypair.pubkey())),
                 Hash::default(),
                 Hash::default(),
             ));
             ));
-            let feature_set = Arc::new(FeatureSet::all_enabled());
             let mut compute_budget = ComputeBudget::default();
             let mut compute_budget = ComputeBudget::default();
-            let result = compute_budget.process_transaction(&tx, feature_set);
-            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);
             assert_eq!(compute_budget, $expected_budget);
         };
         };
     }
     }
 
 
     #[test]
     #[test]
-    fn test_process_transaction() {
+    fn test_process_mesage() {
         // Units
         // Units
-        test!(&[], Ok(()), ComputeBudget::default());
+        test!(&[], Ok(0), ComputeBudget::default());
         test!(
         test!(
             &[
             &[
-                ComputeBudgetInstruction::request_units(1),
+                ComputeBudgetInstruction::request_units(1, 0),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget {
             ComputeBudget {
                 max_units: 1,
                 max_units: 1,
                 ..ComputeBudget::default()
                 ..ComputeBudget::default()
@@ -175,21 +183,18 @@ mod tests {
         );
         );
         test!(
         test!(
             &[
             &[
-                ComputeBudgetInstruction::request_units(MAX_UNITS + 1),
+                ComputeBudgetInstruction::request_units(MAX_UNITS + 1, 0),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
             ],
             ],
-            Err(TransactionError::InstructionError(
-                0,
-                InstructionError::InvalidInstructionData,
-            )),
+            Ok(0),
             ComputeBudget::default()
             ComputeBudget::default()
         );
         );
         test!(
         test!(
             &[
             &[
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
-                ComputeBudgetInstruction::request_units(MAX_UNITS),
+                ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget {
             ComputeBudget {
                 max_units: MAX_UNITS as u64,
                 max_units: MAX_UNITS as u64,
                 ..ComputeBudget::default()
                 ..ComputeBudget::default()
@@ -200,20 +205,20 @@ mod tests {
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
-                ComputeBudgetInstruction::request_units(1),
+                ComputeBudgetInstruction::request_units(1, 0),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget::default()
             ComputeBudget::default()
         );
         );
 
 
         // HeapFrame
         // HeapFrame
-        test!(&[], Ok(()), ComputeBudget::default());
+        test!(&[], Ok(0), ComputeBudget::default());
         test!(
         test!(
             &[
             &[
                 ComputeBudgetInstruction::request_heap_frame(40 * 1024),
                 ComputeBudgetInstruction::request_heap_frame(40 * 1024),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget {
             ComputeBudget {
                 heap_size: Some(40 * 1024),
                 heap_size: Some(40 * 1024),
                 ..ComputeBudget::default()
                 ..ComputeBudget::default()
@@ -257,7 +262,7 @@ mod tests {
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
                 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget {
             ComputeBudget {
                 heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
                 heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
                 ..ComputeBudget::default()
                 ..ComputeBudget::default()
@@ -270,7 +275,7 @@ mod tests {
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 ComputeBudgetInstruction::request_heap_frame(1), // ignored
                 ComputeBudgetInstruction::request_heap_frame(1), // ignored
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget::default()
             ComputeBudget::default()
         );
         );
 
 
@@ -279,9 +284,9 @@ mod tests {
             &[
             &[
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
                 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
                 ComputeBudgetInstruction::request_heap_frame(MAX_HEAP_FRAME_BYTES),
-                ComputeBudgetInstruction::request_units(MAX_UNITS),
+                ComputeBudgetInstruction::request_units(MAX_UNITS, 0),
             ],
             ],
-            Ok(()),
+            Ok(0),
             ComputeBudget {
             ComputeBudget {
                 max_units: MAX_UNITS as u64,
                 max_units: MAX_UNITS as u64,
                 heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),
                 heap_size: Some(MAX_HEAP_FRAME_BYTES as usize),

+ 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 transaction_context = TransactionContext::new(accounts, 1, 3);
         let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
         let mut invoke_context = InvokeContext::new_mock(&mut transaction_context, &[]);
         invoke_context.feature_set = Arc::new(feature_set);
         invoke_context.feature_set = Arc::new(feature_set);
+        invoke_context.compute_budget = ComputeBudget::new(false);
 
 
         invoke_context.push(&[], &[0], &[]).unwrap();
         invoke_context.push(&[], &[0], &[]).unwrap();
         assert_eq!(
         assert_eq!(
             *invoke_context.get_compute_budget(),
             *invoke_context.get_compute_budget(),
-            ComputeBudget::default()
+            ComputeBudget::new(false)
         );
         );
         invoke_context.pop().unwrap();
         invoke_context.pop().unwrap();
 
 
@@ -1622,7 +1623,7 @@ mod tests {
         let expected_compute_budget = ComputeBudget {
         let expected_compute_budget = ComputeBudget {
             max_units: 500_000,
             max_units: 500_000,
             heap_size: Some(256_usize.saturating_mul(1024)),
             heap_size: Some(256_usize.saturating_mul(1024)),
-            ..ComputeBudget::default()
+            ..ComputeBudget::new(false)
         };
         };
         assert_eq!(
         assert_eq!(
             *invoke_context.get_compute_budget(),
             *invoke_context.get_compute_budget(),
@@ -1633,7 +1634,7 @@ mod tests {
         invoke_context.push(&[], &[0], &[]).unwrap();
         invoke_context.push(&[], &[0], &[]).unwrap();
         assert_eq!(
         assert_eq!(
             *invoke_context.get_compute_budget(),
             *invoke_context.get_compute_budget(),
-            ComputeBudget::default()
+            ComputeBudget::new(false)
         );
         );
         invoke_context.pop().unwrap();
         invoke_context.pop().unwrap();
     }
     }

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

@@ -46,6 +46,9 @@ use solana_sdk::{
     clock::MAX_PROCESSING_AGE,
     clock::MAX_PROCESSING_AGE,
     compute_budget::ComputeBudgetInstruction,
     compute_budget::ComputeBudgetInstruction,
     entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
     entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
+    feature_set::FeatureSet,
+    fee::FeeStructure,
+    fee_calculator::FeeRateGovernor,
     instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
     instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
     loader_instruction,
     loader_instruction,
     message::{v0::LoadedAddresses, Message, SanitizedMessage},
     message::{v0::LoadedAddresses, Message, SanitizedMessage},
@@ -397,7 +400,7 @@ fn execute_transactions(
                         ),
                         ),
                     }
                     }
                     .expect("lamports_per_signature must be available");
                     .expect("lamports_per_signature must be available");
-                    let fee = Bank::get_fee_for_message_with_lamports_per_signature(
+                    let fee = bank.get_fee_for_message_with_lamports_per_signature(
                         &SanitizedMessage::try_from(tx.message().clone()).unwrap(),
                         &SanitizedMessage::try_from(tx.message().clone()).unwrap(),
                         lamports_per_signature,
                         lamports_per_signature,
                     );
                     );
@@ -1383,7 +1386,7 @@ fn test_program_bpf_compute_budget() {
     );
     );
     let message = Message::new(
     let message = Message::new(
         &[
         &[
-            ComputeBudgetInstruction::request_units(1),
+            ComputeBudgetInstruction::request_units(1, 0),
             Instruction::new_with_bincode(program_id, &0, vec![]),
             Instruction::new_with_bincode(program_id, &0, vec![]),
         ],
         ],
         Some(&mint_keypair.pubkey()),
         Some(&mint_keypair.pubkey()),
@@ -2886,8 +2889,8 @@ fn test_program_bpf_realloc() {
         .unwrap();
         .unwrap();
 }
 }
 
 
-#[cfg(feature = "bpf_rust")]
 #[test]
 #[test]
+#[cfg(feature = "bpf_rust")]
 fn test_program_bpf_realloc_invoke() {
 fn test_program_bpf_realloc_invoke() {
     solana_logger::setup();
     solana_logger::setup();
 
 
@@ -3418,3 +3421,76 @@ fn test_program_bpf_processed_inner_instruction() {
         .send_and_confirm_message(&[&mint_keypair], message)
         .send_and_confirm_message(&[&mint_keypair], message)
         .is_ok());
         .is_ok());
 }
 }
+
+#[test]
+#[cfg(feature = "bpf_rust")]
+fn test_program_fees() {
+    solana_logger::setup();
+
+    let congestion_multiplier = 1;
+
+    let GenesisConfigInfo {
+        mut genesis_config,
+        mint_keypair,
+        ..
+    } = create_genesis_config(500_000_000);
+    genesis_config.fee_rate_governor = FeeRateGovernor::new(congestion_multiplier, 0);
+    let mut bank = Bank::new_for_tests(&genesis_config);
+    let fee_structure =
+        FeeStructure::new(0.000005, 0.0, vec![(200, 0.0000005), (1400000, 0.000005)]);
+    bank.fee_structure = fee_structure.clone();
+    bank.feature_set = Arc::new(FeatureSet::all_enabled());
+
+    let (name, id, entrypoint) = solana_bpf_loader_program!();
+    bank.add_builtin(&name, &id, entrypoint);
+    let bank_client = BankClient::new(bank);
+
+    let program_id = load_bpf_program(
+        &bank_client,
+        &bpf_loader::id(),
+        &mint_keypair,
+        "solana_bpf_rust_noop",
+    );
+
+    let pre_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
+    let message = Message::new(
+        &[Instruction::new_with_bytes(program_id, &[], vec![])],
+        Some(&mint_keypair.pubkey()),
+    );
+
+    let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap();
+    let expected_max_fee = Bank::calculate_fee(
+        &sanitized_message,
+        congestion_multiplier,
+        &fee_structure,
+        true,
+    );
+    bank_client
+        .send_and_confirm_message(&[&mint_keypair], message)
+        .unwrap();
+    let post_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
+    assert_eq!(pre_balance - post_balance, expected_max_fee);
+
+    let pre_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
+    let message = Message::new(
+        &[
+            ComputeBudgetInstruction::request_units(100, 42),
+            Instruction::new_with_bytes(program_id, &[], vec![]),
+        ],
+        Some(&mint_keypair.pubkey()),
+    );
+    let sanitized_message = SanitizedMessage::try_from(message.clone()).unwrap();
+    let expected_min_fee = Bank::calculate_fee(
+        &sanitized_message,
+        congestion_multiplier,
+        &fee_structure,
+        true,
+    );
+    assert!(expected_min_fee < expected_max_fee);
+
+    bank_client
+        .send_and_confirm_message(&[&mint_keypair], message)
+        .unwrap();
+    let post_balance = bank_client.get_balance(&mint_keypair.pubkey()).unwrap();
+    assert_eq!(pre_balance - post_balance, expected_min_fee);
+}

+ 3 - 3
rpc/src/transaction_status_service.rs

@@ -7,7 +7,7 @@ use {
         blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
         blockstore_processor::{TransactionStatusBatch, TransactionStatusMessage},
     },
     },
     solana_runtime::bank::{
     solana_runtime::bank::{
-        Bank, DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
+        DurableNonceFee, TransactionExecutionDetails, TransactionExecutionResult,
     },
     },
     solana_transaction_status::{
     solana_transaction_status::{
         extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
         extract_and_fmt_memos, InnerInstructions, Reward, TransactionStatusMeta,
@@ -109,7 +109,7 @@ impl TransactionStatusService {
                             ),
                             ),
                         }
                         }
                         .expect("lamports_per_signature must be available");
                         .expect("lamports_per_signature must be available");
-                        let fee = Bank::get_fee_for_message_with_lamports_per_signature(
+                        let fee = bank.get_fee_for_message_with_lamports_per_signature(
                             transaction.message(),
                             transaction.message(),
                             lamports_per_signature,
                             lamports_per_signature,
                         );
                         );
@@ -204,7 +204,7 @@ pub(crate) mod tests {
         dashmap::DashMap,
         dashmap::DashMap,
         solana_account_decoder::parse_token::token_amount_to_ui_amount,
         solana_account_decoder::parse_token::token_amount_to_ui_amount,
         solana_ledger::{genesis_utils::create_genesis_config, get_tmp_ledger_path},
         solana_ledger::{genesis_utils::create_genesis_config, get_tmp_ledger_path},
-        solana_runtime::bank::{NonceFull, NoncePartial, RentDebits, TransactionBalancesSet},
+        solana_runtime::bank::{Bank, NonceFull, NoncePartial, RentDebits, TransactionBalancesSet},
         solana_sdk::{
         solana_sdk::{
             account_utils::StateMut,
             account_utils::StateMut,
             clock::Slot,
             clock::Slot,

+ 26 - 3
runtime/src/accounts.rs

@@ -28,7 +28,8 @@ use {
         account_utils::StateMut,
         account_utils::StateMut,
         bpf_loader_upgradeable::{self, UpgradeableLoaderState},
         bpf_loader_upgradeable::{self, UpgradeableLoaderState},
         clock::{BankId, Slot, INITIAL_RENT_EPOCH},
         clock::{BankId, Slot, INITIAL_RENT_EPOCH},
-        feature_set::{self, FeatureSet},
+        feature_set::{self, tx_wide_compute_cap, FeatureSet},
+        fee::FeeStructure,
         genesis_config::ClusterType,
         genesis_config::ClusterType,
         hash::Hash,
         hash::Hash,
         message::{
         message::{
@@ -472,6 +473,7 @@ impl Accounts {
         error_counters: &mut ErrorCounters,
         error_counters: &mut ErrorCounters,
         rent_collector: &RentCollector,
         rent_collector: &RentCollector,
         feature_set: &FeatureSet,
         feature_set: &FeatureSet,
+        fee_structure: &FeeStructure,
     ) -> Vec<TransactionLoadResult> {
     ) -> Vec<TransactionLoadResult> {
         txs.iter()
         txs.iter()
             .zip(lock_results)
             .zip(lock_results)
@@ -484,7 +486,12 @@ impl Accounts {
                             hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
                             hash_queue.get_lamports_per_signature(tx.message().recent_blockhash())
                         });
                         });
                     let fee = if let Some(lamports_per_signature) = lamports_per_signature {
                     let fee = if let Some(lamports_per_signature) = lamports_per_signature {
-                        Bank::calculate_fee(tx.message(), lamports_per_signature)
+                        Bank::calculate_fee(
+                            tx.message(),
+                            lamports_per_signature,
+                            fee_structure,
+                            feature_set.is_active(&tx_wide_compute_cap::id()),
+                        )
                     } else {
                     } else {
                         return (Err(TransactionError::BlockhashNotFound), None);
                         return (Err(TransactionError::BlockhashNotFound), None);
                     };
                     };
@@ -1359,6 +1366,8 @@ mod tests {
         lamports_per_signature: u64,
         lamports_per_signature: u64,
         rent_collector: &RentCollector,
         rent_collector: &RentCollector,
         error_counters: &mut ErrorCounters,
         error_counters: &mut ErrorCounters,
+        feature_set: &FeatureSet,
+        fee_structure: &FeeStructure,
     ) -> Vec<TransactionLoadResult> {
     ) -> Vec<TransactionLoadResult> {
         let mut hash_queue = BlockhashQueue::new(100);
         let mut hash_queue = BlockhashQueue::new(100);
         hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
         hash_queue.register_hash(&tx.message().recent_blockhash, lamports_per_signature);
@@ -1382,7 +1391,8 @@ mod tests {
             &hash_queue,
             &hash_queue,
             error_counters,
             error_counters,
             rent_collector,
             rent_collector,
-            &FeatureSet::all_enabled(),
+            feature_set,
+            fee_structure,
         )
         )
     }
     }
 
 
@@ -1398,6 +1408,8 @@ mod tests {
             lamports_per_signature,
             lamports_per_signature,
             &RentCollector::default(),
             &RentCollector::default(),
             error_counters,
             error_counters,
+            &FeatureSet::all_enabled(),
+            &FeeStructure::default(),
         )
         )
     }
     }
 
 
@@ -1549,6 +1561,8 @@ mod tests {
         let fee = Bank::calculate_fee(
         let fee = Bank::calculate_fee(
             &SanitizedMessage::try_from(tx.message().clone()).unwrap(),
             &SanitizedMessage::try_from(tx.message().clone()).unwrap(),
             10,
             10,
+            &FeeStructure::default(),
+            false,
         );
         );
         assert_eq!(fee, 10);
         assert_eq!(fee, 10);
 
 
@@ -1595,6 +1609,8 @@ mod tests {
     #[test]
     #[test]
     fn test_load_accounts_fee_payer_is_nonce() {
     fn test_load_accounts_fee_payer_is_nonce() {
         let mut error_counters = ErrorCounters::default();
         let mut error_counters = ErrorCounters::default();
+        let mut feature_set = FeatureSet::all_enabled();
+        feature_set.deactivate(&tx_wide_compute_cap::id());
         let rent_collector = RentCollector::new(
         let rent_collector = RentCollector::new(
             0,
             0,
             &EpochSchedule::default(),
             &EpochSchedule::default(),
@@ -1631,6 +1647,8 @@ mod tests {
             min_balance,
             min_balance,
             &rent_collector,
             &rent_collector,
             &mut error_counters,
             &mut error_counters,
+            &feature_set,
+            &FeeStructure::default(),
         );
         );
         assert_eq!(loaded_accounts.len(), 1);
         assert_eq!(loaded_accounts.len(), 1);
         let (load_res, _nonce) = &loaded_accounts[0];
         let (load_res, _nonce) = &loaded_accounts[0];
@@ -1645,6 +1663,8 @@ mod tests {
             min_balance,
             min_balance,
             &rent_collector,
             &rent_collector,
             &mut error_counters,
             &mut error_counters,
+            &feature_set,
+            &FeeStructure::default(),
         );
         );
         assert_eq!(loaded_accounts.len(), 1);
         assert_eq!(loaded_accounts.len(), 1);
         let (load_res, _nonce) = &loaded_accounts[0];
         let (load_res, _nonce) = &loaded_accounts[0];
@@ -1658,6 +1678,8 @@ mod tests {
             min_balance,
             min_balance,
             &rent_collector,
             &rent_collector,
             &mut error_counters,
             &mut error_counters,
+            &feature_set,
+            &FeeStructure::default(),
         );
         );
         assert_eq!(loaded_accounts.len(), 1);
         assert_eq!(loaded_accounts.len(), 1);
         let (load_res, _nonce) = &loaded_accounts[0];
         let (load_res, _nonce) = &loaded_accounts[0];
@@ -2982,6 +3004,7 @@ mod tests {
             &mut error_counters,
             &mut error_counters,
             &rent_collector,
             &rent_collector,
             &FeatureSet::all_enabled(),
             &FeatureSet::all_enabled(),
+            &FeeStructure::default(),
         )
         )
     }
     }
 
 

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 739 - 40
runtime/src/bank.rs


+ 7 - 7
runtime/src/message_processor.rs

@@ -272,7 +272,7 @@ mod tests {
             None,
             None,
             executors.clone(),
             executors.clone(),
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -314,7 +314,7 @@ mod tests {
             None,
             None,
             executors.clone(),
             executors.clone(),
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -346,7 +346,7 @@ mod tests {
             None,
             None,
             executors,
             executors,
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -481,7 +481,7 @@ mod tests {
             None,
             None,
             executors.clone(),
             executors.clone(),
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -514,7 +514,7 @@ mod tests {
             None,
             None,
             executors.clone(),
             executors.clone(),
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -544,7 +544,7 @@ mod tests {
             None,
             None,
             executors,
             executors,
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),
@@ -623,7 +623,7 @@ mod tests {
             None,
             None,
             Rc::new(RefCell::new(Executors::default())),
             Rc::new(RefCell::new(Executors::default())),
             Arc::new(FeatureSet::all_enabled()),
             Arc::new(FeatureSet::all_enabled()),
-            ComputeBudget::new(),
+            ComputeBudget::default(),
             &mut ExecuteTimings::default(),
             &mut ExecuteTimings::default(),
             &sysvar_cache,
             &sysvar_cache,
             Hash::default(),
             Hash::default(),

+ 26 - 15
sdk/src/compute_budget.rs

@@ -2,38 +2,49 @@
 
 
 use {
 use {
     crate::instruction::Instruction,
     crate::instruction::Instruction,
-    borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
+    borsh::{BorshDeserialize, BorshSerialize},
 };
 };
 
 
 crate::declare_id!("ComputeBudget111111111111111111111111111111");
 crate::declare_id!("ComputeBudget111111111111111111111111111111");
 
 
 /// Compute Budget Instructions
 /// Compute Budget Instructions
 #[derive(
 #[derive(
-    Serialize,
-    Deserialize,
-    BorshSerialize,
+    AbiExample,
+    AbiEnumVisitor,
     BorshDeserialize,
     BorshDeserialize,
-    BorshSchema,
-    Debug,
+    BorshSerialize,
     Clone,
     Clone,
+    Debug,
+    Deserialize,
     PartialEq,
     PartialEq,
-    AbiExample,
-    AbiEnumVisitor,
+    Serialize,
 )]
 )]
 pub enum ComputeBudgetInstruction {
 pub enum ComputeBudgetInstruction {
     /// Request a specific maximum number of compute units the transaction is
     /// Request a specific maximum number of compute units the transaction is
-    /// allowed to consume.
-    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),
     RequestHeapFrame(u32),
 }
 }
 
 
 impl ComputeBudgetInstruction {
 impl ComputeBudgetInstruction {
     /// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
     /// 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`
     /// 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 exit;
 pub mod feature;
 pub mod feature;
 pub mod feature_set;
 pub mod feature_set;
+pub mod fee;
 pub mod genesis_config;
 pub mod genesis_config;
 pub mod hard_forks;
 pub mod hard_forks;
 pub mod hash;
 pub mod hash;

+ 24 - 13
tokens/src/commands.rs

@@ -1585,13 +1585,10 @@ mod tests {
 
 
     #[test]
     #[test]
     fn test_check_payer_balances_distribute_tokens_single_payer() {
     fn test_check_payer_balances_distribute_tokens_single_payer() {
-        let fees = 10_000;
-        let fees_in_sol = lamports_to_sol(fees);
-
         let alice = Keypair::new();
         let alice = Keypair::new();
         let test_validator = TestValidator::with_custom_fees(
         let test_validator = TestValidator::with_custom_fees(
             alice.pubkey(),
             alice.pubkey(),
-            fees,
+            10_000,
             None,
             None,
             SocketAddrSpace::Unspecified,
             SocketAddrSpace::Unspecified,
         );
         );
@@ -1601,6 +1598,11 @@ mod tests {
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
 
 
+        let fees = client
+            .get_fee_for_message(&one_signer_message(&client))
+            .unwrap();
+        let fees_in_sol = lamports_to_sol(fees);
+
         let allocation_amount = 1000.0;
         let allocation_amount = 1000.0;
 
 
         // Fully funded payer
         // Fully funded payer
@@ -1678,12 +1680,10 @@ mod tests {
 
 
     #[test]
     #[test]
     fn test_check_payer_balances_distribute_tokens_separate_payers() {
     fn test_check_payer_balances_distribute_tokens_separate_payers() {
-        let fees = 10_000;
-        let fees_in_sol = lamports_to_sol(fees);
         let alice = Keypair::new();
         let alice = Keypair::new();
         let test_validator = TestValidator::with_custom_fees(
         let test_validator = TestValidator::with_custom_fees(
             alice.pubkey(),
             alice.pubkey(),
-            fees,
+            10_000,
             None,
             None,
             SocketAddrSpace::Unspecified,
             SocketAddrSpace::Unspecified,
         );
         );
@@ -1691,6 +1691,11 @@ mod tests {
 
 
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
 
 
+        let fees = client
+            .get_fee_for_message(&one_signer_message(&client))
+            .unwrap();
+        let fees_in_sol = lamports_to_sol(fees);
+
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
 
 
@@ -1802,18 +1807,21 @@ mod tests {
 
 
     #[test]
     #[test]
     fn test_check_payer_balances_distribute_stakes_single_payer() {
     fn test_check_payer_balances_distribute_stakes_single_payer() {
-        let fees = 10_000;
-        let fees_in_sol = lamports_to_sol(fees);
         let alice = Keypair::new();
         let alice = Keypair::new();
         let test_validator = TestValidator::with_custom_fees(
         let test_validator = TestValidator::with_custom_fees(
             alice.pubkey(),
             alice.pubkey(),
-            fees,
+            10_000,
             None,
             None,
             SocketAddrSpace::Unspecified,
             SocketAddrSpace::Unspecified,
         );
         );
         let url = test_validator.rpc_url();
         let url = test_validator.rpc_url();
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
 
 
+        let fees = client
+            .get_fee_for_message(&one_signer_message(&client))
+            .unwrap();
+        let fees_in_sol = lamports_to_sol(fees);
+
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
 
 
@@ -1925,12 +1933,10 @@ mod tests {
 
 
     #[test]
     #[test]
     fn test_check_payer_balances_distribute_stakes_separate_payers() {
     fn test_check_payer_balances_distribute_stakes_separate_payers() {
-        let fees = 10_000;
-        let fees_in_sol = lamports_to_sol(fees);
         let alice = Keypair::new();
         let alice = Keypair::new();
         let test_validator = TestValidator::with_custom_fees(
         let test_validator = TestValidator::with_custom_fees(
             alice.pubkey(),
             alice.pubkey(),
-            fees,
+            10_000,
             None,
             None,
             SocketAddrSpace::Unspecified,
             SocketAddrSpace::Unspecified,
         );
         );
@@ -1938,6 +1944,11 @@ mod tests {
 
 
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
         let client = RpcClient::new_with_commitment(url, CommitmentConfig::processed());
 
 
+        let fees = client
+            .get_fee_for_message(&one_signer_message(&client))
+            .unwrap();
+        let fees_in_sol = lamports_to_sol(fees);
+
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         let sender_keypair_file = tmp_file_path("keypair_file", &alice.pubkey());
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
         write_keypair_file(&alice, &sender_keypair_file).unwrap();
 
 

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov