Feature gate builtin consumes static units during processing instruction (#30702)
* add feature gate * builtins consume statically defined units at beginning of process_instruction() * Add new instructionError; return error if builtin did not consume units to enforce builtin to consume units; * updated related tests * updated ProgramTest with deactivated native_programs_consume_cu feature to continue support existing mock/test programs that do not consume units
This commit is contained in:
parent
5a05e9bacf
commit
3e500d9e92
|
@ -15,7 +15,10 @@ use {
|
|||
solana_sdk::{
|
||||
account::{AccountSharedData, ReadableAccount},
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
feature_set::{enable_early_verification_of_account_modifications, FeatureSet},
|
||||
feature_set::{
|
||||
enable_early_verification_of_account_modifications, native_programs_consume_cu,
|
||||
FeatureSet,
|
||||
},
|
||||
hash::Hash,
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
native_loader,
|
||||
|
@ -751,8 +754,9 @@ impl<'a> InvokeContext<'a> {
|
|||
self.transaction_context
|
||||
.set_return_data(program_id, Vec::new())?;
|
||||
|
||||
let is_builtin_program = builtin_id == program_id;
|
||||
let pre_remaining_units = self.get_remaining();
|
||||
let result = if builtin_id == program_id {
|
||||
let result = if is_builtin_program {
|
||||
let logger = self.get_log_collector();
|
||||
stable_log::program_invoke(&logger, &program_id, self.get_stack_height());
|
||||
(entry.process_instruction)(self)
|
||||
|
@ -769,6 +773,15 @@ impl<'a> InvokeContext<'a> {
|
|||
let post_remaining_units = self.get_remaining();
|
||||
*compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
|
||||
|
||||
if is_builtin_program
|
||||
&& *compute_units_consumed == 0
|
||||
&& self
|
||||
.feature_set
|
||||
.is_active(&native_programs_consume_cu::id())
|
||||
{
|
||||
return Err(InstructionError::BuiltinProgramsMustConsumeComputeUnits);
|
||||
}
|
||||
|
||||
process_executable_chain_time.stop();
|
||||
saturating_add_assign!(
|
||||
timings
|
||||
|
@ -1082,6 +1095,8 @@ mod tests {
|
|||
assert!(!format!("{builtin_programs:?}").is_empty());
|
||||
}
|
||||
|
||||
const MOCK_BUILTIN_COMPUTE_UNIT_COST: u64 = 1;
|
||||
|
||||
#[allow(clippy::integer_arithmetic)]
|
||||
fn mock_process_instruction(
|
||||
invoke_context: &mut InvokeContext,
|
||||
|
@ -1090,6 +1105,8 @@ mod tests {
|
|||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(MOCK_BUILTIN_COMPUTE_UNIT_COST)?;
|
||||
let instruction_accounts = (0..4)
|
||||
.map(|instruction_account_index| InstructionAccount {
|
||||
index_in_transaction: instruction_account_index,
|
||||
|
@ -1372,7 +1389,10 @@ mod tests {
|
|||
// the number of compute units consumed should be a non-default which is something greater
|
||||
// than zero.
|
||||
assert!(compute_units_consumed > 0);
|
||||
assert_eq!(compute_units_consumed, compute_units_to_consume);
|
||||
assert_eq!(
|
||||
compute_units_consumed,
|
||||
compute_units_to_consume + MOCK_BUILTIN_COMPUTE_UNIT_COST
|
||||
);
|
||||
assert_eq!(result, expected_result);
|
||||
|
||||
invoke_context.pop().unwrap();
|
||||
|
|
|
@ -463,13 +463,18 @@ impl Default for ProgramTest {
|
|||
let prefer_bpf =
|
||||
std::env::var("BPF_OUT_DIR").is_ok() || std::env::var("SBF_OUT_DIR").is_ok();
|
||||
|
||||
// deactivate feature `native_program_consume_cu` to continue support existing mock/test
|
||||
// programs that do not consume units.
|
||||
let deactivate_feature_set =
|
||||
HashSet::from([solana_sdk::feature_set::native_programs_consume_cu::id()]);
|
||||
|
||||
Self {
|
||||
accounts: vec![],
|
||||
builtins: vec![],
|
||||
compute_max_units: None,
|
||||
prefer_bpf,
|
||||
use_bpf_jit: false,
|
||||
deactivate_feature_set: HashSet::default(),
|
||||
deactivate_feature_set,
|
||||
transaction_account_lock_limit: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ use {
|
|||
};
|
||||
|
||||
pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(750)?;
|
||||
}
|
||||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
|
|
|
@ -45,8 +45,8 @@ use {
|
|||
check_slice_translation_size, delay_visibility_of_program_deployment,
|
||||
disable_deploy_of_alloc_free_syscall, enable_bpf_loader_extend_program_ix,
|
||||
enable_bpf_loader_set_authority_checked_ix, enable_program_redeployment_cooldown,
|
||||
limit_max_instruction_trace_length, remove_bpf_loader_incorrect_program_id,
|
||||
round_up_heap_size, FeatureSet,
|
||||
limit_max_instruction_trace_length, native_programs_consume_cu,
|
||||
remove_bpf_loader_incorrect_program_id, round_up_heap_size, FeatureSet,
|
||||
},
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
loader_instruction::LoaderInstruction,
|
||||
|
@ -520,15 +520,29 @@ fn process_instruction_common(
|
|||
let program_account =
|
||||
instruction_context.try_borrow_last_program_account(transaction_context)?;
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated
|
||||
let native_programs_consume_cu = invoke_context
|
||||
.feature_set
|
||||
.is_active(&native_programs_consume_cu::id());
|
||||
|
||||
// Program Management Instruction
|
||||
if native_loader::check_id(program_account.get_owner()) {
|
||||
drop(program_account);
|
||||
let program_id = instruction_context.get_last_program_key(transaction_context)?;
|
||||
return if bpf_loader_upgradeable::check_id(program_id) {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(2_370)?;
|
||||
}
|
||||
process_loader_upgradeable_instruction(invoke_context, use_jit)
|
||||
} else if bpf_loader::check_id(program_id) {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(570)?;
|
||||
}
|
||||
process_loader_instruction(invoke_context, use_jit)
|
||||
} else if bpf_loader_deprecated::check_id(program_id) {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(1_140)?;
|
||||
}
|
||||
ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
|
||||
Err(InstructionError::UnsupportedProgramId)
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
use {
|
||||
solana_program_runtime::invoke_context::InvokeContext,
|
||||
solana_sdk::instruction::InstructionError,
|
||||
solana_sdk::{feature_set, instruction::InstructionError},
|
||||
};
|
||||
|
||||
pub fn process_instruction(_invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
|
||||
pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), InstructionError> {
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(150)?;
|
||||
}
|
||||
|
||||
// Do nothing, compute budget instructions handled by the runtime
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -16,6 +16,14 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let data = instruction_context.get_instruction_data();
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(450)?;
|
||||
}
|
||||
|
||||
let key_list: ConfigKeys = limited_deserialize(data)?;
|
||||
let config_account_key = transaction_context.get_key_of_account_at_index(
|
||||
instruction_context.get_index_of_instruction_account_in_transaction(0)?,
|
||||
|
|
|
@ -1470,7 +1470,7 @@ fn test_program_sbf_compute_budget() {
|
|||
);
|
||||
let message = Message::new(
|
||||
&[
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(150),
|
||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||
],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
|
@ -2185,7 +2185,13 @@ fn test_program_sbf_disguised_as_sbf_loader() {
|
|||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(50);
|
||||
|
||||
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||
// disable native_programs_consume_cu feature to allow test program
|
||||
// not consume units.
|
||||
let mut feature_set = FeatureSet::all_enabled();
|
||||
feature_set.deactivate(&solana_sdk::feature_set::native_programs_consume_cu::id());
|
||||
bank.feature_set = Arc::new(feature_set);
|
||||
let (name, id, entrypoint) = solana_bpf_loader_program!();
|
||||
bank.add_builtin(&name, &id, entrypoint);
|
||||
bank.deactivate_feature(
|
||||
|
|
|
@ -58,6 +58,14 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
|
||||
trace!("process_instruction: {:?}", data);
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(750)?;
|
||||
}
|
||||
|
||||
let get_stake_account = || {
|
||||
let me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
||||
if *me.get_owner() != id() {
|
||||
|
|
|
@ -62,6 +62,16 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
|
||||
trace!("process_instruction: {:?}", data);
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
// Citing `runtime/src/block_cost_limit.rs`, vote has statically defined 2_100
|
||||
// units; can consume based on instructions in the future like `bpf_loader` does.
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(2_100)?;
|
||||
}
|
||||
|
||||
let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
||||
if *me.get_owner() != id() {
|
||||
return Err(InstructionError::InvalidAccountOwner);
|
||||
|
|
|
@ -4,6 +4,7 @@ use {
|
|||
bytemuck::Pod,
|
||||
solana_program_runtime::{ic_msg, invoke_context::InvokeContext},
|
||||
solana_sdk::{
|
||||
feature_set,
|
||||
instruction::{InstructionError, TRANSACTION_LEVEL_STACK_HEIGHT},
|
||||
system_program,
|
||||
},
|
||||
|
@ -119,12 +120,10 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
return Err(InstructionError::UnsupportedProgramId);
|
||||
}
|
||||
|
||||
// Consume compute units since proof verification is an expensive operation
|
||||
{
|
||||
// TODO: Tune the number of units consumed. The current value is just a rough estimate
|
||||
invoke_context.consume_checked(100_000)?;
|
||||
}
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated
|
||||
let native_programs_consume_cu = invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id());
|
||||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
|
@ -137,28 +136,46 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
process_close_proof_context(invoke_context)
|
||||
}
|
||||
ProofInstruction::VerifyCloseAccount => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(6_012)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyCloseAccount");
|
||||
process_verify_proof::<CloseAccountData, CloseAccountProofContext>(invoke_context)
|
||||
}
|
||||
ProofInstruction::VerifyWithdraw => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(112_454)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyWithdraw");
|
||||
process_verify_proof::<WithdrawData, WithdrawProofContext>(invoke_context)
|
||||
}
|
||||
ProofInstruction::VerifyWithdrawWithheldTokens => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(7_943)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyWithdrawWithheldTokens");
|
||||
process_verify_proof::<WithdrawWithheldTokensData, WithdrawWithheldTokensProofContext>(
|
||||
invoke_context,
|
||||
)
|
||||
}
|
||||
ProofInstruction::VerifyTransfer => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(219_290)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyTransfer");
|
||||
process_verify_proof::<TransferData, TransferProofContext>(invoke_context)
|
||||
}
|
||||
ProofInstruction::VerifyTransferWithFee => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(407_121)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyTransferWithFee");
|
||||
process_verify_proof::<TransferWithFeeData, TransferWithFeeProofContext>(invoke_context)
|
||||
}
|
||||
ProofInstruction::VerifyPubkeyValidity => {
|
||||
if native_programs_consume_cu {
|
||||
invoke_context.consume_checked(2_619)?;
|
||||
}
|
||||
ic_msg!(invoke_context, "VerifyPubkeyValidity");
|
||||
process_verify_proof::<PubkeyValidityData, PubkeyValidityProofContext>(invoke_context)
|
||||
}
|
||||
|
|
|
@ -5890,7 +5890,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
"unitsConsumed":150,
|
||||
}
|
||||
},
|
||||
"id": 1,
|
||||
|
@ -5974,7 +5974,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
"unitsConsumed":150,
|
||||
}
|
||||
},
|
||||
"id": 1,
|
||||
|
@ -6002,7 +6002,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
"unitsConsumed":150,
|
||||
}
|
||||
},
|
||||
"id": 1,
|
||||
|
@ -6051,7 +6051,7 @@ pub mod tests {
|
|||
"accounts":null,
|
||||
"logs":[],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
"unitsConsumed":0,
|
||||
}
|
||||
},
|
||||
"id":1
|
||||
|
@ -6080,7 +6080,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
"unitsConsumed":150,
|
||||
}
|
||||
},
|
||||
"id": 1,
|
||||
|
|
|
@ -283,7 +283,7 @@ impl RentDebits {
|
|||
}
|
||||
|
||||
pub type BankStatusCache = StatusCache<Result<()>>;
|
||||
#[frozen_abi(digest = "3qia1Zm8X66bzFaBuC8ahz3hADRRATyUPRV36ZzrSois")]
|
||||
#[frozen_abi(digest = "GBTLfFjModD9ykS9LV4pGi4S8eCrUj2JjWSDQLf8tMwV")]
|
||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||
|
||||
// Eager rent collection repeats in cyclic manner.
|
||||
|
|
|
@ -5129,8 +5129,10 @@ fn test_add_duplicate_static_program() {
|
|||
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||
|
||||
fn mock_vote_processor(
|
||||
_invoke_context: &mut InvokeContext,
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> std::result::Result<(), InstructionError> {
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
Err(InstructionError::Custom(42))
|
||||
}
|
||||
|
||||
|
@ -5177,8 +5179,10 @@ fn test_add_instruction_processor_for_existing_unrelated_accounts() {
|
|||
let mut bank = create_simple_test_bank(500);
|
||||
|
||||
fn mock_ix_processor(
|
||||
_invoke_context: &mut InvokeContext,
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> std::result::Result<(), InstructionError> {
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
Err(InstructionError::Custom(42))
|
||||
}
|
||||
|
||||
|
@ -6473,6 +6477,8 @@ fn test_transaction_with_duplicate_accounts_in_instruction() {
|
|||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
let lamports = u64::from_le_bytes(instruction_data.try_into().unwrap());
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, 2)?
|
||||
.checked_sub_lamports(lamports)?;
|
||||
|
@ -6526,8 +6532,10 @@ fn test_transaction_with_program_ids_passed_to_programs() {
|
|||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn mock_process_instruction(
|
||||
_invoke_context: &mut InvokeContext,
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> result::Result<(), InstructionError> {
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -6744,8 +6752,10 @@ fn test_program_id_as_payer() {
|
|||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn mock_ok_vote_processor(
|
||||
_invoke_context: &mut InvokeContext,
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> std::result::Result<(), InstructionError> {
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -10193,6 +10203,8 @@ fn test_transfer_sysvar() {
|
|||
) -> std::result::Result<(), InstructionError> {
|
||||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
instruction_context
|
||||
.try_borrow_instruction_account(transaction_context, 1)?
|
||||
.set_data(vec![0; 40])?;
|
||||
|
@ -10406,11 +10418,13 @@ fn test_compute_budget_program_noop() {
|
|||
assert_eq!(
|
||||
*compute_budget,
|
||||
ComputeBudget {
|
||||
compute_unit_limit: 1,
|
||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
||||
heap_size: Some(48 * 1024),
|
||||
..ComputeBudget::default()
|
||||
}
|
||||
);
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
Ok(())
|
||||
}
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
@ -10418,7 +10432,9 @@ fn test_compute_budget_program_noop() {
|
|||
|
||||
let message = Message::new(
|
||||
&[
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||
),
|
||||
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||
],
|
||||
|
@ -10449,11 +10465,13 @@ fn test_compute_request_instruction() {
|
|||
assert_eq!(
|
||||
*compute_budget,
|
||||
ComputeBudget {
|
||||
compute_unit_limit: 1,
|
||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
||||
heap_size: Some(48 * 1024),
|
||||
..ComputeBudget::default()
|
||||
}
|
||||
);
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
Ok(())
|
||||
}
|
||||
let program_id = solana_sdk::pubkey::new_rand();
|
||||
|
@ -10461,7 +10479,9 @@ fn test_compute_request_instruction() {
|
|||
|
||||
let message = Message::new(
|
||||
&[
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(1),
|
||||
ComputeBudgetInstruction::set_compute_unit_limit(
|
||||
compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT,
|
||||
),
|
||||
ComputeBudgetInstruction::request_heap_frame(48 * 1024),
|
||||
Instruction::new_with_bincode(program_id, &0, vec![]),
|
||||
],
|
||||
|
@ -10499,7 +10519,7 @@ fn test_failed_compute_request_instruction() {
|
|||
assert_eq!(
|
||||
*compute_budget,
|
||||
ComputeBudget {
|
||||
compute_unit_limit: 1,
|
||||
compute_unit_limit: compute_budget::DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT as u64,
|
||||
heap_size: Some(48 * 1024),
|
||||
..ComputeBudget::default()
|
||||
}
|
||||
|
@ -11063,6 +11083,8 @@ fn mock_transfer_process_instruction(
|
|||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
if let Ok(instruction) = bincode::deserialize(instruction_data) {
|
||||
match instruction {
|
||||
MockTransferInstruction::Transfer(amount) => {
|
||||
|
@ -11871,6 +11893,8 @@ fn mock_realloc_process_instruction(
|
|||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
// mock builtin must consume units
|
||||
invoke_context.consume_checked(1)?;
|
||||
if let Ok(instruction) = bincode::deserialize(instruction_data) {
|
||||
match instruction {
|
||||
MockReallocInstruction::Realloc(new_size, new_balance, _) => {
|
||||
|
|
|
@ -223,6 +223,8 @@ mod tests {
|
|||
let transaction_context = &invoke_context.transaction_context;
|
||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||
let instruction_data = instruction_context.get_instruction_data();
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
if let Ok(instruction) = bincode::deserialize(instruction_data) {
|
||||
match instruction {
|
||||
MockSystemInstruction::Correct => Ok(()),
|
||||
|
@ -436,6 +438,8 @@ mod tests {
|
|||
let instruction_data = instruction_context.get_instruction_data();
|
||||
let mut to_account =
|
||||
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
if let Ok(instruction) = bincode::deserialize(instruction_data) {
|
||||
match instruction {
|
||||
MockSystemInstruction::BorrowFail => {
|
||||
|
@ -642,8 +646,10 @@ mod tests {
|
|||
fn test_precompile() {
|
||||
let mock_program_id = Pubkey::new_unique();
|
||||
fn mock_process_instruction(
|
||||
_invoke_context: &mut InvokeContext,
|
||||
invoke_context: &mut InvokeContext,
|
||||
) -> Result<(), InstructionError> {
|
||||
// mock builtin should consume units
|
||||
let _ = invoke_context.consume_checked(1);
|
||||
Err(InstructionError::Custom(0xbabb1e))
|
||||
}
|
||||
let builtin_programs = &[BuiltinProgram {
|
||||
|
|
|
@ -323,6 +323,14 @@ pub fn process_instruction(invoke_context: &mut InvokeContext) -> Result<(), Ins
|
|||
|
||||
trace!("process_instruction: {:?}", instruction);
|
||||
|
||||
// Consume compute units if feature `native_programs_consume_cu` is activated,
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::native_programs_consume_cu::id())
|
||||
{
|
||||
invoke_context.consume_checked(150)?;
|
||||
}
|
||||
|
||||
let signers = instruction_context.get_signers(transaction_context)?;
|
||||
match instruction {
|
||||
SystemInstruction::CreateAccount {
|
||||
|
|
|
@ -260,6 +260,10 @@ pub enum InstructionError {
|
|||
/// Max instruction trace length exceeded
|
||||
#[error("Max instruction trace length exceeded")]
|
||||
MaxInstructionTraceLengthExceeded,
|
||||
|
||||
/// Builtin programs must consume compute units
|
||||
#[error("Builtin programs must consume compute units")]
|
||||
BuiltinProgramsMustConsumeComputeUnits,
|
||||
// Note: For any new error added here an equivalent ProgramError and its
|
||||
// conversions must also be added
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ pub enum ProgramError {
|
|||
InvalidRealloc,
|
||||
#[error("Instruction trace length exceeded the maximum allowed per transaction")]
|
||||
MaxInstructionTraceLengthExceeded,
|
||||
#[error("Builtin programs must consume compute units")]
|
||||
BuiltinProgramsMustConsumeComputeUnits,
|
||||
}
|
||||
|
||||
pub trait PrintProgramError {
|
||||
|
@ -102,6 +104,9 @@ impl PrintProgramError for ProgramError {
|
|||
Self::MaxInstructionTraceLengthExceeded => {
|
||||
msg!("Error: MaxInstructionTraceLengthExceeded")
|
||||
}
|
||||
Self::BuiltinProgramsMustConsumeComputeUnits => {
|
||||
msg!("Error: BuiltinProgramsMustConsumeComputeUnits")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +140,7 @@ pub const ILLEGAL_OWNER: u64 = to_builtin!(18);
|
|||
pub const MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED: u64 = to_builtin!(19);
|
||||
pub const INVALID_ACCOUNT_DATA_REALLOC: u64 = to_builtin!(20);
|
||||
pub const MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED: u64 = to_builtin!(21);
|
||||
pub const BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS: u64 = to_builtin!(22);
|
||||
// Warning: Any new program errors added here must also be:
|
||||
// - Added to the below conversions
|
||||
// - Added as an equivalent to InstructionError
|
||||
|
@ -168,6 +174,9 @@ impl From<ProgramError> for u64 {
|
|||
ProgramError::MaxInstructionTraceLengthExceeded => {
|
||||
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED
|
||||
}
|
||||
ProgramError::BuiltinProgramsMustConsumeComputeUnits => {
|
||||
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS
|
||||
}
|
||||
ProgramError::Custom(error) => {
|
||||
if error == 0 {
|
||||
CUSTOM_ZERO
|
||||
|
@ -203,6 +212,9 @@ impl From<u64> for ProgramError {
|
|||
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
|
||||
INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
|
||||
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
|
||||
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
|
||||
Self::BuiltinProgramsMustConsumeComputeUnits
|
||||
}
|
||||
_ => Self::Custom(error as u32),
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +250,9 @@ impl TryFrom<InstructionError> for ProgramError {
|
|||
Self::Error::MaxInstructionTraceLengthExceeded => {
|
||||
Ok(Self::MaxInstructionTraceLengthExceeded)
|
||||
}
|
||||
Self::Error::BuiltinProgramsMustConsumeComputeUnits => {
|
||||
Ok(Self::BuiltinProgramsMustConsumeComputeUnits)
|
||||
}
|
||||
_ => Err(error),
|
||||
}
|
||||
}
|
||||
|
@ -271,6 +286,9 @@ where
|
|||
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED => Self::MaxAccountsDataAllocationsExceeded,
|
||||
INVALID_ACCOUNT_DATA_REALLOC => Self::InvalidRealloc,
|
||||
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED => Self::MaxInstructionTraceLengthExceeded,
|
||||
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS => {
|
||||
Self::BuiltinProgramsMustConsumeComputeUnits
|
||||
}
|
||||
_ => {
|
||||
// A valid custom error has no bits set in the upper 32
|
||||
if error >> BUILTIN_BIT_SHIFT == 0 {
|
||||
|
|
|
@ -638,6 +638,10 @@ pub mod include_loaded_accounts_data_size_in_fee_calculation {
|
|||
solana_sdk::declare_id!("EaQpmC6GtRssaZ3PCUM5YksGqUdMLeZ46BQXYtHYakDS");
|
||||
}
|
||||
|
||||
pub mod native_programs_consume_cu {
|
||||
solana_sdk::declare_id!("8pgXCMNXC8qyEFypuwpXyRxLXZdpM4Qo72gJ6k87A6wL");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -792,6 +796,7 @@ lazy_static! {
|
|||
(round_up_heap_size::id(), "round up heap size when calculating heap cost #30679"),
|
||||
(remove_bpf_loader_incorrect_program_id::id(), "stop incorrectly throwing IncorrectProgramId in bpf_loader #30747"),
|
||||
(include_loaded_accounts_data_size_in_fee_calculation::id(), "include transaction loaded accounts data size in base fee calculation #30657"),
|
||||
(native_programs_consume_cu::id(), "Native program should consume compute units #30620"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
|
@ -125,6 +125,7 @@ enum InstructionErrorType {
|
|||
MAX_ACCOUNTS_DATA_ALLOCATIONS_EXCEEDED = 50;
|
||||
MAX_ACCOUNTS_EXCEEDED = 51;
|
||||
MAX_INSTRUCTION_TRACE_LENGTH_EXCEEDED = 52;
|
||||
BUILTIN_PROGRAMS_MUST_CONSUME_COMPUTE_UNITS = 53;
|
||||
}
|
||||
|
||||
message UnixTimestamp {
|
||||
|
|
|
@ -745,6 +745,7 @@ impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
|
|||
50 => InstructionError::MaxAccountsDataAllocationsExceeded,
|
||||
51 => InstructionError::MaxAccountsExceeded,
|
||||
52 => InstructionError::MaxInstructionTraceLengthExceeded,
|
||||
53 => InstructionError::BuiltinProgramsMustConsumeComputeUnits,
|
||||
_ => return Err("Invalid InstructionError"),
|
||||
};
|
||||
|
||||
|
@ -1075,6 +1076,9 @@ impl From<TransactionError> for tx_by_addr::TransactionError {
|
|||
InstructionError::MaxInstructionTraceLengthExceeded => {
|
||||
tx_by_addr::InstructionErrorType::MaxInstructionTraceLengthExceeded
|
||||
}
|
||||
InstructionError::BuiltinProgramsMustConsumeComputeUnits => {
|
||||
tx_by_addr::InstructionErrorType::BuiltinProgramsMustConsumeComputeUnits
|
||||
}
|
||||
} as i32,
|
||||
custom: match instruction_error {
|
||||
InstructionError::Custom(custom) => {
|
||||
|
|
Loading…
Reference in New Issue