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:
Tao Zhu 2023-03-24 11:31:01 -05:00 committed by GitHub
parent 5a05e9bacf
commit 3e500d9e92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 204 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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, _) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) => {