diff --git a/programs/address-lookup-table/src/processor.rs b/programs/address-lookup-table/src/processor.rs index 6e8fd96a43..68416015dc 100644 --- a/programs/address-lookup-table/src/processor.rs +++ b/programs/address-lookup-table/src/processor.rs @@ -18,25 +18,31 @@ use { std::convert::TryFrom, }; -declare_process_instruction!(process_instruction, 750, |invoke_context| { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let instruction_data = instruction_context.get_instruction_data(); - match limited_deserialize(instruction_data)? { - ProgramInstruction::CreateLookupTable { - recent_slot, - bump_seed, - } => Processor::create_lookup_table(invoke_context, recent_slot, bump_seed), - ProgramInstruction::FreezeLookupTable => Processor::freeze_lookup_table(invoke_context), - ProgramInstruction::ExtendLookupTable { new_addresses } => { - Processor::extend_lookup_table(invoke_context, new_addresses) +pub const DEFAULT_COMPUTE_UNITS: u64 = 750; + +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |invoke_context| { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let instruction_data = instruction_context.get_instruction_data(); + match limited_deserialize(instruction_data)? { + ProgramInstruction::CreateLookupTable { + recent_slot, + bump_seed, + } => Processor::create_lookup_table(invoke_context, recent_slot, bump_seed), + ProgramInstruction::FreezeLookupTable => Processor::freeze_lookup_table(invoke_context), + ProgramInstruction::ExtendLookupTable { new_addresses } => { + Processor::extend_lookup_table(invoke_context, new_addresses) + } + ProgramInstruction::DeactivateLookupTable => { + Processor::deactivate_lookup_table(invoke_context) + } + ProgramInstruction::CloseLookupTable => Processor::close_lookup_table(invoke_context), } - ProgramInstruction::DeactivateLookupTable => { - Processor::deactivate_lookup_table(invoke_context) - } - ProgramInstruction::CloseLookupTable => Processor::close_lookup_table(invoke_context), } -}); +); fn checked_add(a: usize, b: usize) -> Result { a.checked_add(b).ok_or(InstructionError::ArithmeticOverflow) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 64a44d6eeb..06c6ce442b 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -62,6 +62,10 @@ use { syscalls::create_program_runtime_environment, }; +pub const DEFAULT_LOADER_COMPUTE_UNITS: u64 = 570; +pub const DEPRECATED_LOADER_COMPUTE_UNITS: u64 = 1_140; +pub const UPGRADEABLE_LOADER_COMPUTE_UNITS: u64 = 2_370; + #[allow(clippy::too_many_arguments)] pub fn load_program_from_bytes( feature_set: &FeatureSet, @@ -515,17 +519,17 @@ fn process_instruction_inner( 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)?; + invoke_context.consume_checked(UPGRADEABLE_LOADER_COMPUTE_UNITS)?; } process_loader_upgradeable_instruction(invoke_context) } else if bpf_loader::check_id(program_id) { if native_programs_consume_cu { - invoke_context.consume_checked(570)?; + invoke_context.consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?; } process_loader_instruction(invoke_context) } else if bpf_loader_deprecated::check_id(program_id) { if native_programs_consume_cu { - invoke_context.consume_checked(1_140)?; + invoke_context.consume_checked(DEPRECATED_LOADER_COMPUTE_UNITS)?; } ic_logger_msg!(log_collector, "Deprecated loader is no longer supported"); Err(InstructionError::UnsupportedProgramId) diff --git a/programs/compute-budget/src/lib.rs b/programs/compute-budget/src/lib.rs index 8fe6cbcd85..e296ca3a2f 100644 --- a/programs/compute-budget/src/lib.rs +++ b/programs/compute-budget/src/lib.rs @@ -1,6 +1,12 @@ use solana_program_runtime::declare_process_instruction; -declare_process_instruction!(process_instruction, 150, |_invoke_context| { - // Do nothing, compute budget instructions handled by the runtime - Ok(()) -}); +pub const DEFAULT_COMPUTE_UNITS: u64 = 150; + +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |_invoke_context| { + // Do nothing, compute budget instructions handled by the runtime + Ok(()) + } +); diff --git a/programs/config/src/config_processor.rs b/programs/config/src/config_processor.rs index 1431b314ad..3851d81524 100644 --- a/programs/config/src/config_processor.rs +++ b/programs/config/src/config_processor.rs @@ -11,127 +11,133 @@ use { std::collections::BTreeSet, }; -declare_process_instruction!(process_instruction, 450, |invoke_context| { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let data = instruction_context.get_instruction_data(); +pub const DEFAULT_COMPUTE_UNITS: u64 = 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)?, - )?; - let config_account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - let is_config_account_signer = config_account.is_signer(); - let current_data: ConfigKeys = { - if config_account.get_owner() != &crate::id() { - return Err(InstructionError::InvalidAccountOwner); +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |invoke_context| { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let data = instruction_context.get_instruction_data(); + + 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)?, + )?; + let config_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let is_config_account_signer = config_account.is_signer(); + let current_data: ConfigKeys = { + if config_account.get_owner() != &crate::id() { + return Err(InstructionError::InvalidAccountOwner); + } + + deserialize(config_account.get_data()).map_err(|err| { + ic_msg!( + invoke_context, + "Unable to deserialize config account: {}", + err + ); + InstructionError::InvalidAccountData + })? + }; + drop(config_account); + + let current_signer_keys: Vec = current_data + .keys + .iter() + .filter(|(_, is_signer)| *is_signer) + .map(|(pubkey, _)| *pubkey) + .collect(); + if current_signer_keys.is_empty() { + // Config account keypair must be a signer on account initialization, + // or when no signers specified in Config data + if !is_config_account_signer { + return Err(InstructionError::MissingRequiredSignature); + } } - deserialize(config_account.get_data()).map_err(|err| { - ic_msg!( - invoke_context, - "Unable to deserialize config account: {}", - err - ); - InstructionError::InvalidAccountData - })? - }; - drop(config_account); - - let current_signer_keys: Vec = current_data - .keys - .iter() - .filter(|(_, is_signer)| *is_signer) - .map(|(pubkey, _)| *pubkey) - .collect(); - if current_signer_keys.is_empty() { - // Config account keypair must be a signer on account initialization, - // or when no signers specified in Config data - if !is_config_account_signer { - return Err(InstructionError::MissingRequiredSignature); - } - } - - let mut counter = 0; - for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { - counter += 1; - if signer != config_account_key { - let signer_account = instruction_context - .try_borrow_instruction_account(transaction_context, counter as IndexOfAccount) - .map_err(|_| { + let mut counter = 0; + for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { + counter += 1; + if signer != config_account_key { + let signer_account = instruction_context + .try_borrow_instruction_account(transaction_context, counter as IndexOfAccount) + .map_err(|_| { + ic_msg!( + invoke_context, + "account {:?} is not in account list", + signer, + ); + InstructionError::MissingRequiredSignature + })?; + if !signer_account.is_signer() { ic_msg!( invoke_context, - "account {:?} is not in account list", - signer, + "account {:?} signer_key().is_none()", + signer ); - InstructionError::MissingRequiredSignature - })?; - if !signer_account.is_signer() { - ic_msg!( - invoke_context, - "account {:?} signer_key().is_none()", - signer - ); + return Err(InstructionError::MissingRequiredSignature); + } + if signer_account.get_key() != signer { + ic_msg!( + invoke_context, + "account[{:?}].signer_key() does not match Config data)", + counter + 1 + ); + return Err(InstructionError::MissingRequiredSignature); + } + // If Config account is already initialized, update signatures must match Config data + if !current_data.keys.is_empty() + && !current_signer_keys.iter().any(|pubkey| pubkey == signer) + { + ic_msg!( + invoke_context, + "account {:?} is not in stored signer list", + signer + ); + return Err(InstructionError::MissingRequiredSignature); + } + } else if !is_config_account_signer { + ic_msg!(invoke_context, "account[0].signer_key().is_none()"); return Err(InstructionError::MissingRequiredSignature); } - if signer_account.get_key() != signer { - ic_msg!( - invoke_context, - "account[{:?}].signer_key() does not match Config data)", - counter + 1 - ); - return Err(InstructionError::MissingRequiredSignature); + } + + if invoke_context + .feature_set + .is_active(&feature_set::dedupe_config_program_signers::id()) + { + let total_new_keys = key_list.keys.len(); + let unique_new_keys = key_list.keys.into_iter().collect::>(); + if unique_new_keys.len() != total_new_keys { + ic_msg!(invoke_context, "new config contains duplicate keys"); + return Err(InstructionError::InvalidArgument); } - // If Config account is already initialized, update signatures must match Config data - if !current_data.keys.is_empty() - && !current_signer_keys.iter().any(|pubkey| pubkey == signer) - { - ic_msg!( - invoke_context, - "account {:?} is not in stored signer list", - signer - ); - return Err(InstructionError::MissingRequiredSignature); - } - } else if !is_config_account_signer { - ic_msg!(invoke_context, "account[0].signer_key().is_none()"); + } + + // Check for Config data signers not present in incoming account update + if current_signer_keys.len() > counter { + ic_msg!( + invoke_context, + "too few signers: {:?}; expected: {:?}", + counter, + current_signer_keys.len() + ); return Err(InstructionError::MissingRequiredSignature); } - } - if invoke_context - .feature_set - .is_active(&feature_set::dedupe_config_program_signers::id()) - { - let total_new_keys = key_list.keys.len(); - let unique_new_keys = key_list.keys.into_iter().collect::>(); - if unique_new_keys.len() != total_new_keys { - ic_msg!(invoke_context, "new config contains duplicate keys"); - return Err(InstructionError::InvalidArgument); + let mut config_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + if config_account.get_data().len() < data.len() { + ic_msg!(invoke_context, "instruction data too large"); + return Err(InstructionError::InvalidInstructionData); } + config_account.get_data_mut()?[..data.len()].copy_from_slice(data); + Ok(()) } - - // Check for Config data signers not present in incoming account update - if current_signer_keys.len() > counter { - ic_msg!( - invoke_context, - "too few signers: {:?}; expected: {:?}", - counter, - current_signer_keys.len() - ); - return Err(InstructionError::MissingRequiredSignature); - } - - let mut config_account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - if config_account.get_data().len() < data.len() { - ic_msg!(invoke_context, "instruction data too large"); - return Err(InstructionError::InvalidInstructionData); - } - config_account.get_data_mut()?[..data.len()].copy_from_slice(data); - Ok(()) -}); +); #[cfg(test)] mod tests { diff --git a/programs/loader-v4/src/lib.rs b/programs/loader-v4/src/lib.rs index 2d92b485a8..fbf54b8b05 100644 --- a/programs/loader-v4/src/lib.rs +++ b/programs/loader-v4/src/lib.rs @@ -38,6 +38,8 @@ use { }, }; +pub const DEFAULT_COMPUTE_UNITS: u64 = 2_000; + pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> { unsafe { let data = data @@ -560,7 +562,7 @@ pub fn process_instruction_inner( .feature_set .is_active(&feature_set::native_programs_consume_cu::id()) { - invoke_context.consume_checked(2000)?; + invoke_context.consume_checked(DEFAULT_COMPUTE_UNITS)?; } match limited_deserialize(instruction_data)? { LoaderV4Instruction::Write { offset, bytes } => { diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index 22a93730e2..0f2012ecbb 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -51,431 +51,454 @@ fn get_optional_pubkey<'a>( ) } -declare_process_instruction!(process_instruction, 750, |invoke_context| { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let data = instruction_context.get_instruction_data(); +pub const DEFAULT_COMPUTE_UNITS: u64 = 750; - trace!("process_instruction: {:?}", data); +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |invoke_context| { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let data = instruction_context.get_instruction_data(); - let get_stake_account = || { - let me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - if *me.get_owner() != id() { - return Err(InstructionError::InvalidAccountOwner); - } - Ok(me) - }; + trace!("process_instruction: {:?}", data); - let signers = instruction_context.get_signers(transaction_context)?; - match limited_deserialize(data) { - Ok(StakeInstruction::Initialize(authorized, lockup)) => { - let mut me = get_stake_account()?; - let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; - initialize( - &mut me, - &authorized, - &lockup, - &rent, - &invoke_context.feature_set, - ) - } - Ok(StakeInstruction::Authorize(authorized_pubkey, stake_authorize)) => { - let mut me = get_stake_account()?; - let require_custodian_for_locked_stake_authorize = invoke_context - .feature_set - .is_active(&feature_set::require_custodian_for_locked_stake_authorize::id()); - - if require_custodian_for_locked_stake_authorize { - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; - instruction_context.check_number_of_instruction_accounts(3)?; - let custodian_pubkey = - get_optional_pubkey(transaction_context, instruction_context, 3, false)?; - - authorize( - &mut me, - &signers, - &authorized_pubkey, - stake_authorize, - require_custodian_for_locked_stake_authorize, - &clock, - custodian_pubkey, - ) - } else { - authorize( - &mut me, - &signers, - &authorized_pubkey, - stake_authorize, - require_custodian_for_locked_stake_authorize, - &Clock::default(), - None, - ) + let get_stake_account = || { + let me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + if *me.get_owner() != id() { + return Err(InstructionError::InvalidAccountOwner); } - } - Ok(StakeInstruction::AuthorizeWithSeed(args)) => { - let mut me = get_stake_account()?; - instruction_context.check_number_of_instruction_accounts(2)?; - let require_custodian_for_locked_stake_authorize = invoke_context - .feature_set - .is_active(&feature_set::require_custodian_for_locked_stake_authorize::id()); - if require_custodian_for_locked_stake_authorize { - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - let custodian_pubkey = - get_optional_pubkey(transaction_context, instruction_context, 3, false)?; - - authorize_with_seed( - transaction_context, - instruction_context, - &mut me, - 1, - &args.authority_seed, - &args.authority_owner, - &args.new_authorized_pubkey, - args.stake_authorize, - require_custodian_for_locked_stake_authorize, - &clock, - custodian_pubkey, - ) - } else { - authorize_with_seed( - transaction_context, - instruction_context, - &mut me, - 1, - &args.authority_seed, - &args.authority_owner, - &args.new_authorized_pubkey, - args.stake_authorize, - require_custodian_for_locked_stake_authorize, - &Clock::default(), - None, - ) - } - } - Ok(StakeInstruction::DelegateStake) => { - let me = get_stake_account()?; - instruction_context.check_number_of_instruction_accounts(2)?; - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - let stake_history = get_sysvar_with_account_check::stake_history( - invoke_context, - instruction_context, - 3, - )?; - instruction_context.check_number_of_instruction_accounts(5)?; - drop(me); - let config_account = - instruction_context.try_borrow_instruction_account(transaction_context, 4)?; - if !config::check_id(config_account.get_key()) { - return Err(InstructionError::InvalidArgument); - } - let config = config::from(&config_account).ok_or(InstructionError::InvalidArgument)?; - drop(config_account); - delegate( - invoke_context, - transaction_context, - instruction_context, - 0, - 1, - &clock, - &stake_history, - &config, - &signers, - &invoke_context.feature_set, - ) - } - Ok(StakeInstruction::Split(lamports)) => { - let me = get_stake_account()?; - instruction_context.check_number_of_instruction_accounts(2)?; - drop(me); - split( - invoke_context, - transaction_context, - instruction_context, - 0, - lamports, - 1, - &signers, - ) - } - Ok(StakeInstruction::Merge) => { - let me = get_stake_account()?; - instruction_context.check_number_of_instruction_accounts(2)?; - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - let stake_history = get_sysvar_with_account_check::stake_history( - invoke_context, - instruction_context, - 3, - )?; - drop(me); - merge( - invoke_context, - transaction_context, - instruction_context, - 0, - 1, - &clock, - &stake_history, - &signers, - ) - } - Ok(StakeInstruction::Withdraw(lamports)) => { - let me = get_stake_account()?; - instruction_context.check_number_of_instruction_accounts(2)?; - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - let stake_history = get_sysvar_with_account_check::stake_history( - invoke_context, - instruction_context, - 3, - )?; - instruction_context.check_number_of_instruction_accounts(5)?; - drop(me); - withdraw( - transaction_context, - instruction_context, - 0, - lamports, - 1, - &clock, - &stake_history, - 4, - if instruction_context.get_number_of_instruction_accounts() >= 6 { - Some(5) - } else { - None - }, - &invoke_context.feature_set, - ) - } - Ok(StakeInstruction::Deactivate) => { - let mut me = get_stake_account()?; - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; - deactivate(&mut me, &clock, &signers) - } - Ok(StakeInstruction::SetLockup(lockup)) => { - let mut me = get_stake_account()?; - let clock = invoke_context.get_sysvar_cache().get_clock()?; - set_lockup(&mut me, &lockup, &signers, &clock) - } - Ok(StakeInstruction::InitializeChecked) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::vote_stake_checked_instructions::id()) - { - instruction_context.check_number_of_instruction_accounts(4)?; - let staker_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(2)?, - )?; - let withdrawer_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(3)?, - )?; - if !instruction_context.is_instruction_account_signer(3)? { - return Err(InstructionError::MissingRequiredSignature); - } - - let authorized = Authorized { - staker: *staker_pubkey, - withdrawer: *withdrawer_pubkey, - }; + Ok(me) + }; + let signers = instruction_context.get_signers(transaction_context)?; + match limited_deserialize(data) { + Ok(StakeInstruction::Initialize(authorized, lockup)) => { + let mut me = get_stake_account()?; let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; initialize( &mut me, &authorized, - &Lockup::default(), + &lockup, &rent, &invoke_context.feature_set, ) - } else { - Err(InstructionError::InvalidInstructionData) } - } - Ok(StakeInstruction::AuthorizeChecked(stake_authorize)) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::vote_stake_checked_instructions::id()) - { - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; - instruction_context.check_number_of_instruction_accounts(4)?; - let authorized_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(3)?, - )?; - if !instruction_context.is_instruction_account_signer(3)? { - return Err(InstructionError::MissingRequiredSignature); - } - let custodian_pubkey = - get_optional_pubkey(transaction_context, instruction_context, 4, false)?; + Ok(StakeInstruction::Authorize(authorized_pubkey, stake_authorize)) => { + let mut me = get_stake_account()?; + let require_custodian_for_locked_stake_authorize = invoke_context + .feature_set + .is_active(&feature_set::require_custodian_for_locked_stake_authorize::id()); - authorize( - &mut me, - &signers, - authorized_pubkey, - stake_authorize, - true, - &clock, - custodian_pubkey, - ) - } else { - Err(InstructionError::InvalidInstructionData) + if require_custodian_for_locked_stake_authorize { + let clock = get_sysvar_with_account_check::clock( + invoke_context, + instruction_context, + 1, + )?; + instruction_context.check_number_of_instruction_accounts(3)?; + let custodian_pubkey = + get_optional_pubkey(transaction_context, instruction_context, 3, false)?; + + authorize( + &mut me, + &signers, + &authorized_pubkey, + stake_authorize, + require_custodian_for_locked_stake_authorize, + &clock, + custodian_pubkey, + ) + } else { + authorize( + &mut me, + &signers, + &authorized_pubkey, + stake_authorize, + require_custodian_for_locked_stake_authorize, + &Clock::default(), + None, + ) + } } - } - Ok(StakeInstruction::AuthorizeCheckedWithSeed(args)) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::vote_stake_checked_instructions::id()) - { + Ok(StakeInstruction::AuthorizeWithSeed(args)) => { + let mut me = get_stake_account()?; + instruction_context.check_number_of_instruction_accounts(2)?; + let require_custodian_for_locked_stake_authorize = invoke_context + .feature_set + .is_active(&feature_set::require_custodian_for_locked_stake_authorize::id()); + if require_custodian_for_locked_stake_authorize { + let clock = get_sysvar_with_account_check::clock( + invoke_context, + instruction_context, + 2, + )?; + let custodian_pubkey = + get_optional_pubkey(transaction_context, instruction_context, 3, false)?; + + authorize_with_seed( + transaction_context, + instruction_context, + &mut me, + 1, + &args.authority_seed, + &args.authority_owner, + &args.new_authorized_pubkey, + args.stake_authorize, + require_custodian_for_locked_stake_authorize, + &clock, + custodian_pubkey, + ) + } else { + authorize_with_seed( + transaction_context, + instruction_context, + &mut me, + 1, + &args.authority_seed, + &args.authority_owner, + &args.new_authorized_pubkey, + args.stake_authorize, + require_custodian_for_locked_stake_authorize, + &Clock::default(), + None, + ) + } + } + Ok(StakeInstruction::DelegateStake) => { + let me = get_stake_account()?; instruction_context.check_number_of_instruction_accounts(2)?; let clock = get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - instruction_context.check_number_of_instruction_accounts(4)?; - let authorized_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(3)?, + let stake_history = get_sysvar_with_account_check::stake_history( + invoke_context, + instruction_context, + 3, )?; - if !instruction_context.is_instruction_account_signer(3)? { - return Err(InstructionError::MissingRequiredSignature); - } - let custodian_pubkey = - get_optional_pubkey(transaction_context, instruction_context, 4, false)?; - - authorize_with_seed( - transaction_context, - instruction_context, - &mut me, - 1, - &args.authority_seed, - &args.authority_owner, - authorized_pubkey, - args.stake_authorize, - true, - &clock, - custodian_pubkey, - ) - } else { - Err(InstructionError::InvalidInstructionData) - } - } - Ok(StakeInstruction::SetLockupChecked(lockup_checked)) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::vote_stake_checked_instructions::id()) - { - let custodian_pubkey = - get_optional_pubkey(transaction_context, instruction_context, 2, true)?; - - let lockup = LockupArgs { - unix_timestamp: lockup_checked.unix_timestamp, - epoch: lockup_checked.epoch, - custodian: custodian_pubkey.cloned(), - }; - let clock = invoke_context.get_sysvar_cache().get_clock()?; - set_lockup(&mut me, &lockup, &signers, &clock) - } else { - Err(InstructionError::InvalidInstructionData) - } - } - Ok(StakeInstruction::GetMinimumDelegation) => { - let feature_set = invoke_context.feature_set.as_ref(); - if !feature_set.is_active( - &feature_set::add_get_minimum_delegation_instruction_to_stake_program::id(), - ) { - // Retain previous behavior of always checking that the first account - // is a stake account until the feature is activated - let _ = get_stake_account()?; - return Err(InstructionError::InvalidInstructionData); - } - - let minimum_delegation = crate::get_minimum_delegation(feature_set); - let minimum_delegation = Vec::from(minimum_delegation.to_le_bytes()); - invoke_context - .transaction_context - .set_return_data(id(), minimum_delegation) - } - Ok(StakeInstruction::DeactivateDelinquent) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::stake_deactivate_delinquent_instruction::id()) - { - instruction_context.check_number_of_instruction_accounts(3)?; - - let clock = invoke_context.get_sysvar_cache().get_clock()?; - deactivate_delinquent( - transaction_context, - instruction_context, - &mut me, - 1, - 2, - clock.epoch, - ) - } else { - Err(InstructionError::InvalidInstructionData) - } - } - Ok(StakeInstruction::Redelegate) => { - let mut me = get_stake_account()?; - if invoke_context - .feature_set - .is_active(&feature_set::stake_redelegate_instruction::id()) - { - instruction_context.check_number_of_instruction_accounts(3)?; + instruction_context.check_number_of_instruction_accounts(5)?; + drop(me); let config_account = - instruction_context.try_borrow_instruction_account(transaction_context, 3)?; + instruction_context.try_borrow_instruction_account(transaction_context, 4)?; if !config::check_id(config_account.get_key()) { return Err(InstructionError::InvalidArgument); } let config = config::from(&config_account).ok_or(InstructionError::InvalidArgument)?; drop(config_account); - - redelegate( + delegate( invoke_context, transaction_context, instruction_context, - &mut me, + 0, 1, - 2, + &clock, + &stake_history, &config, &signers, + &invoke_context.feature_set, ) - } else { - Err(InstructionError::InvalidInstructionData) } - } - // In order to prevent consensus issues, any new StakeInstruction variant added before the - // `add_get_minimum_delegation_instruction_to_stake_program` is activated needs to check - // the validity of the stake account by calling the `get_stake_account()` method outside - // its own feature gate, as per the following pattern: - // ``` - // Ok(StakeInstruction::Variant) -> { - // let mut me = get_stake_account()?; - // if invoke_context - // .feature_set - // .is_active(&feature_set::stake_variant_feature::id()) { .. } - // } - // ``` - // TODO: Remove this comment when `add_get_minimum_delegation_instruction_to_stake_program` - // is cleaned up - Err(err) => { - if !invoke_context.feature_set.is_active( - &feature_set::add_get_minimum_delegation_instruction_to_stake_program::id(), - ) { - let _ = get_stake_account()?; + Ok(StakeInstruction::Split(lamports)) => { + let me = get_stake_account()?; + instruction_context.check_number_of_instruction_accounts(2)?; + drop(me); + split( + invoke_context, + transaction_context, + instruction_context, + 0, + lamports, + 1, + &signers, + ) + } + Ok(StakeInstruction::Merge) => { + let me = get_stake_account()?; + instruction_context.check_number_of_instruction_accounts(2)?; + let clock = + get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; + let stake_history = get_sysvar_with_account_check::stake_history( + invoke_context, + instruction_context, + 3, + )?; + drop(me); + merge( + invoke_context, + transaction_context, + instruction_context, + 0, + 1, + &clock, + &stake_history, + &signers, + ) + } + Ok(StakeInstruction::Withdraw(lamports)) => { + let me = get_stake_account()?; + instruction_context.check_number_of_instruction_accounts(2)?; + let clock = + get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; + let stake_history = get_sysvar_with_account_check::stake_history( + invoke_context, + instruction_context, + 3, + )?; + instruction_context.check_number_of_instruction_accounts(5)?; + drop(me); + withdraw( + transaction_context, + instruction_context, + 0, + lamports, + 1, + &clock, + &stake_history, + 4, + if instruction_context.get_number_of_instruction_accounts() >= 6 { + Some(5) + } else { + None + }, + &invoke_context.feature_set, + ) + } + Ok(StakeInstruction::Deactivate) => { + let mut me = get_stake_account()?; + let clock = + get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; + deactivate(&mut me, &clock, &signers) + } + Ok(StakeInstruction::SetLockup(lockup)) => { + let mut me = get_stake_account()?; + let clock = invoke_context.get_sysvar_cache().get_clock()?; + set_lockup(&mut me, &lockup, &signers, &clock) + } + Ok(StakeInstruction::InitializeChecked) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::vote_stake_checked_instructions::id()) + { + instruction_context.check_number_of_instruction_accounts(4)?; + let staker_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(2)?, + )?; + let withdrawer_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(3)?, + )?; + if !instruction_context.is_instruction_account_signer(3)? { + return Err(InstructionError::MissingRequiredSignature); + } + + let authorized = Authorized { + staker: *staker_pubkey, + withdrawer: *withdrawer_pubkey, + }; + + let rent = get_sysvar_with_account_check::rent( + invoke_context, + instruction_context, + 1, + )?; + initialize( + &mut me, + &authorized, + &Lockup::default(), + &rent, + &invoke_context.feature_set, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + Ok(StakeInstruction::AuthorizeChecked(stake_authorize)) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::vote_stake_checked_instructions::id()) + { + let clock = get_sysvar_with_account_check::clock( + invoke_context, + instruction_context, + 1, + )?; + instruction_context.check_number_of_instruction_accounts(4)?; + let authorized_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(3)?, + )?; + if !instruction_context.is_instruction_account_signer(3)? { + return Err(InstructionError::MissingRequiredSignature); + } + let custodian_pubkey = + get_optional_pubkey(transaction_context, instruction_context, 4, false)?; + + authorize( + &mut me, + &signers, + authorized_pubkey, + stake_authorize, + true, + &clock, + custodian_pubkey, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + Ok(StakeInstruction::AuthorizeCheckedWithSeed(args)) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::vote_stake_checked_instructions::id()) + { + instruction_context.check_number_of_instruction_accounts(2)?; + let clock = get_sysvar_with_account_check::clock( + invoke_context, + instruction_context, + 2, + )?; + instruction_context.check_number_of_instruction_accounts(4)?; + let authorized_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(3)?, + )?; + if !instruction_context.is_instruction_account_signer(3)? { + return Err(InstructionError::MissingRequiredSignature); + } + let custodian_pubkey = + get_optional_pubkey(transaction_context, instruction_context, 4, false)?; + + authorize_with_seed( + transaction_context, + instruction_context, + &mut me, + 1, + &args.authority_seed, + &args.authority_owner, + authorized_pubkey, + args.stake_authorize, + true, + &clock, + custodian_pubkey, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + Ok(StakeInstruction::SetLockupChecked(lockup_checked)) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::vote_stake_checked_instructions::id()) + { + let custodian_pubkey = + get_optional_pubkey(transaction_context, instruction_context, 2, true)?; + + let lockup = LockupArgs { + unix_timestamp: lockup_checked.unix_timestamp, + epoch: lockup_checked.epoch, + custodian: custodian_pubkey.cloned(), + }; + let clock = invoke_context.get_sysvar_cache().get_clock()?; + set_lockup(&mut me, &lockup, &signers, &clock) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + Ok(StakeInstruction::GetMinimumDelegation) => { + let feature_set = invoke_context.feature_set.as_ref(); + if !feature_set.is_active( + &feature_set::add_get_minimum_delegation_instruction_to_stake_program::id(), + ) { + // Retain previous behavior of always checking that the first account + // is a stake account until the feature is activated + let _ = get_stake_account()?; + return Err(InstructionError::InvalidInstructionData); + } + + let minimum_delegation = crate::get_minimum_delegation(feature_set); + let minimum_delegation = Vec::from(minimum_delegation.to_le_bytes()); + invoke_context + .transaction_context + .set_return_data(id(), minimum_delegation) + } + Ok(StakeInstruction::DeactivateDelinquent) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::stake_deactivate_delinquent_instruction::id()) + { + instruction_context.check_number_of_instruction_accounts(3)?; + + let clock = invoke_context.get_sysvar_cache().get_clock()?; + deactivate_delinquent( + transaction_context, + instruction_context, + &mut me, + 1, + 2, + clock.epoch, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + Ok(StakeInstruction::Redelegate) => { + let mut me = get_stake_account()?; + if invoke_context + .feature_set + .is_active(&feature_set::stake_redelegate_instruction::id()) + { + instruction_context.check_number_of_instruction_accounts(3)?; + let config_account = instruction_context + .try_borrow_instruction_account(transaction_context, 3)?; + if !config::check_id(config_account.get_key()) { + return Err(InstructionError::InvalidArgument); + } + let config = + config::from(&config_account).ok_or(InstructionError::InvalidArgument)?; + drop(config_account); + + redelegate( + invoke_context, + transaction_context, + instruction_context, + &mut me, + 1, + 2, + &config, + &signers, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + // In order to prevent consensus issues, any new StakeInstruction variant added before the + // `add_get_minimum_delegation_instruction_to_stake_program` is activated needs to check + // the validity of the stake account by calling the `get_stake_account()` method outside + // its own feature gate, as per the following pattern: + // ``` + // Ok(StakeInstruction::Variant) -> { + // let mut me = get_stake_account()?; + // if invoke_context + // .feature_set + // .is_active(&feature_set::stake_variant_feature::id()) { .. } + // } + // ``` + // TODO: Remove this comment when `add_get_minimum_delegation_instruction_to_stake_program` + // is cleaned up + Err(err) => { + if !invoke_context.feature_set.is_active( + &feature_set::add_get_minimum_delegation_instruction_to_stake_program::id(), + ) { + let _ = get_stake_account()?; + } + Err(err) } - Err(err) } } -}); +); #[cfg(test)] mod tests { diff --git a/programs/system/src/system_processor.rs b/programs/system/src/system_processor.rs index 032968dcea..04962399c0 100644 --- a/programs/system/src/system_processor.rs +++ b/programs/system/src/system_processor.rs @@ -312,246 +312,254 @@ fn transfer_with_seed( ) } -declare_process_instruction!(process_instruction, 150, |invoke_context| { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let instruction_data = instruction_context.get_instruction_data(); - let instruction = limited_deserialize(instruction_data)?; +pub const DEFAULT_COMPUTE_UNITS: u64 = 150; - trace!("process_instruction: {:?}", instruction); +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |invoke_context| { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let instruction_data = instruction_context.get_instruction_data(); + let instruction = limited_deserialize(instruction_data)?; - let signers = instruction_context.get_signers(transaction_context)?; - match instruction { - SystemInstruction::CreateAccount { - lamports, - space, - owner, - } => { - instruction_context.check_number_of_instruction_accounts(2)?; - let to_address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(1)?, - )?, - None, - invoke_context, - )?; - create_account( - 0, - 1, - &to_address, + trace!("process_instruction: {:?}", instruction); + + let signers = instruction_context.get_signers(transaction_context)?; + match instruction { + SystemInstruction::CreateAccount { lamports, space, - &owner, - &signers, - invoke_context, - transaction_context, - instruction_context, - ) - } - SystemInstruction::CreateAccountWithSeed { - base, - seed, - lamports, - space, - owner, - } => { - instruction_context.check_number_of_instruction_accounts(2)?; - let to_address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(1)?, - )?, - Some((&base, &seed, &owner)), - invoke_context, - )?; - create_account( - 0, - 1, - &to_address, - lamports, - space, - &owner, - &signers, - invoke_context, - transaction_context, - instruction_context, - ) - } - SystemInstruction::Assign { owner } => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - let address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(0)?, - )?, - None, - invoke_context, - )?; - assign(&mut account, &address, &owner, &signers, invoke_context) - } - SystemInstruction::Transfer { lamports } => { - instruction_context.check_number_of_instruction_accounts(2)?; - transfer( - 0, - 1, - lamports, - invoke_context, - transaction_context, - instruction_context, - ) - } - SystemInstruction::TransferWithSeed { - lamports, - from_seed, - from_owner, - } => { - instruction_context.check_number_of_instruction_accounts(3)?; - transfer_with_seed( - 0, - 1, - &from_seed, - &from_owner, - 2, - lamports, - invoke_context, - transaction_context, - instruction_context, - ) - } - SystemInstruction::AdvanceNonceAccount => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut me = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - #[allow(deprecated)] - let recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( - invoke_context, - instruction_context, - 1, - )?; - if recent_blockhashes.is_empty() { - ic_msg!( + owner, + } => { + instruction_context.check_number_of_instruction_accounts(2)?; + let to_address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(1)?, + )?, + None, invoke_context, - "Advance nonce account: recent blockhash list is empty", - ); - return Err(SystemError::NonceNoRecentBlockhashes.into()); - } - advance_nonce_account(&mut me, &signers, invoke_context) - } - SystemInstruction::WithdrawNonceAccount(lamports) => { - instruction_context.check_number_of_instruction_accounts(2)?; - #[allow(deprecated)] - let _recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( - invoke_context, - instruction_context, - 2, - )?; - let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 3)?; - withdraw_nonce_account( - 0, - lamports, - 1, - &rent, - &signers, - invoke_context, - transaction_context, - instruction_context, - ) - } - SystemInstruction::InitializeNonceAccount(authorized) => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut me = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - #[allow(deprecated)] - let recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( - invoke_context, - instruction_context, - 1, - )?; - if recent_blockhashes.is_empty() { - ic_msg!( + )?; + create_account( + 0, + 1, + &to_address, + lamports, + space, + &owner, + &signers, invoke_context, - "Initialize nonce account: recent blockhash list is empty", - ); - return Err(SystemError::NonceNoRecentBlockhashes.into()); + transaction_context, + instruction_context, + ) } - let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 2)?; - initialize_nonce_account(&mut me, &authorized, &rent, invoke_context) - } - SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut me = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - authorize_nonce_account(&mut me, &nonce_authority, &signers, invoke_context) - } - SystemInstruction::UpgradeNonceAccount => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut nonce_account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - if !system_program::check_id(nonce_account.get_owner()) { - return Err(InstructionError::InvalidAccountOwner); - } - if !nonce_account.is_writable() { - return Err(InstructionError::InvalidArgument); - } - let nonce_versions: nonce::state::Versions = nonce_account.get_state()?; - match nonce_versions.upgrade() { - None => Err(InstructionError::InvalidArgument), - Some(nonce_versions) => nonce_account.set_state(&nonce_versions), - } - } - SystemInstruction::Allocate { space } => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - let address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(0)?, - )?, - None, - invoke_context, - )?; - allocate(&mut account, &address, space, &signers, invoke_context) - } - SystemInstruction::AllocateWithSeed { - base, - seed, - space, - owner, - } => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - let address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(0)?, - )?, - Some((&base, &seed, &owner)), - invoke_context, - )?; - allocate_and_assign( - &mut account, - &address, + SystemInstruction::CreateAccountWithSeed { + base, + seed, + lamports, space, - &owner, - &signers, - invoke_context, - ) - } - SystemInstruction::AssignWithSeed { base, seed, owner } => { - instruction_context.check_number_of_instruction_accounts(1)?; - let mut account = - instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - let address = Address::create( - transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(0)?, - )?, - Some((&base, &seed, &owner)), - invoke_context, - )?; - assign(&mut account, &address, &owner, &signers, invoke_context) + owner, + } => { + instruction_context.check_number_of_instruction_accounts(2)?; + let to_address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(1)?, + )?, + Some((&base, &seed, &owner)), + invoke_context, + )?; + create_account( + 0, + 1, + &to_address, + lamports, + space, + &owner, + &signers, + invoke_context, + transaction_context, + instruction_context, + ) + } + SystemInstruction::Assign { owner } => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(0)?, + )?, + None, + invoke_context, + )?; + assign(&mut account, &address, &owner, &signers, invoke_context) + } + SystemInstruction::Transfer { lamports } => { + instruction_context.check_number_of_instruction_accounts(2)?; + transfer( + 0, + 1, + lamports, + invoke_context, + transaction_context, + instruction_context, + ) + } + SystemInstruction::TransferWithSeed { + lamports, + from_seed, + from_owner, + } => { + instruction_context.check_number_of_instruction_accounts(3)?; + transfer_with_seed( + 0, + 1, + &from_seed, + &from_owner, + 2, + lamports, + invoke_context, + transaction_context, + instruction_context, + ) + } + SystemInstruction::AdvanceNonceAccount => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut me = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + #[allow(deprecated)] + let recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( + invoke_context, + instruction_context, + 1, + )?; + if recent_blockhashes.is_empty() { + ic_msg!( + invoke_context, + "Advance nonce account: recent blockhash list is empty", + ); + return Err(SystemError::NonceNoRecentBlockhashes.into()); + } + advance_nonce_account(&mut me, &signers, invoke_context) + } + SystemInstruction::WithdrawNonceAccount(lamports) => { + instruction_context.check_number_of_instruction_accounts(2)?; + #[allow(deprecated)] + let _recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( + invoke_context, + instruction_context, + 2, + )?; + let rent = + get_sysvar_with_account_check::rent(invoke_context, instruction_context, 3)?; + withdraw_nonce_account( + 0, + lamports, + 1, + &rent, + &signers, + invoke_context, + transaction_context, + instruction_context, + ) + } + SystemInstruction::InitializeNonceAccount(authorized) => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut me = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + #[allow(deprecated)] + let recent_blockhashes = get_sysvar_with_account_check::recent_blockhashes( + invoke_context, + instruction_context, + 1, + )?; + if recent_blockhashes.is_empty() { + ic_msg!( + invoke_context, + "Initialize nonce account: recent blockhash list is empty", + ); + return Err(SystemError::NonceNoRecentBlockhashes.into()); + } + let rent = + get_sysvar_with_account_check::rent(invoke_context, instruction_context, 2)?; + initialize_nonce_account(&mut me, &authorized, &rent, invoke_context) + } + SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut me = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + authorize_nonce_account(&mut me, &nonce_authority, &signers, invoke_context) + } + SystemInstruction::UpgradeNonceAccount => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut nonce_account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + if !system_program::check_id(nonce_account.get_owner()) { + return Err(InstructionError::InvalidAccountOwner); + } + if !nonce_account.is_writable() { + return Err(InstructionError::InvalidArgument); + } + let nonce_versions: nonce::state::Versions = nonce_account.get_state()?; + match nonce_versions.upgrade() { + None => Err(InstructionError::InvalidArgument), + Some(nonce_versions) => nonce_account.set_state(&nonce_versions), + } + } + SystemInstruction::Allocate { space } => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(0)?, + )?, + None, + invoke_context, + )?; + allocate(&mut account, &address, space, &signers, invoke_context) + } + SystemInstruction::AllocateWithSeed { + base, + seed, + space, + owner, + } => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(0)?, + )?, + Some((&base, &seed, &owner)), + invoke_context, + )?; + allocate_and_assign( + &mut account, + &address, + space, + &owner, + &signers, + invoke_context, + ) + } + SystemInstruction::AssignWithSeed { base, seed, owner } => { + instruction_context.check_number_of_instruction_accounts(1)?; + let mut account = + instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + let address = Address::create( + transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(0)?, + )?, + Some((&base, &seed, &owner)), + invoke_context, + )?; + assign(&mut account, &address, &owner, &signers, invoke_context) + } } } -}); +); #[cfg(test)] mod tests { diff --git a/programs/vote/src/vote_processor.rs b/programs/vote/src/vote_processor.rs index 88e258174a..671089b519 100644 --- a/programs/vote/src/vote_processor.rs +++ b/programs/vote/src/vote_processor.rs @@ -52,215 +52,228 @@ fn process_authorize_with_seed_instruction( // Citing `runtime/src/block_cost_limit.rs`, vote has statically defined 2100 // units; can consume based on instructions in the future like `bpf_loader` does. -declare_process_instruction!(process_instruction, 2100, |invoke_context| { - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let data = instruction_context.get_instruction_data(); +pub const DEFAULT_COMPUTE_UNITS: u64 = 2_100; - trace!("process_instruction: {:?}", data); +declare_process_instruction!( + process_instruction, + DEFAULT_COMPUTE_UNITS, + |invoke_context| { + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let data = instruction_context.get_instruction_data(); - let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; - if *me.get_owner() != id() { - return Err(InstructionError::InvalidAccountOwner); - } + trace!("process_instruction: {:?}", data); - let signers = instruction_context.get_signers(transaction_context)?; - match limited_deserialize(data)? { - VoteInstruction::InitializeAccount(vote_init) => { - let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; - if !rent.is_exempt(me.get_lamports(), me.get_data().len()) { - return Err(InstructionError::InsufficientFunds); - } - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - vote_state::initialize_account( - &mut me, - &vote_init, - &signers, - &clock, - &invoke_context.feature_set, - ) + let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?; + if *me.get_owner() != id() { + return Err(InstructionError::InvalidAccountOwner); } - VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; - vote_state::authorize( - &mut me, - &voter_pubkey, - vote_authorize, - &signers, - &clock, - &invoke_context.feature_set, - ) - } - VoteInstruction::AuthorizeWithSeed(args) => { - instruction_context.check_number_of_instruction_accounts(3)?; - process_authorize_with_seed_instruction( - invoke_context, - instruction_context, - transaction_context, - &mut me, - &args.new_authority, - args.authorization_type, - &args.current_authority_derived_key_owner, - args.current_authority_derived_key_seed.as_str(), - ) - } - VoteInstruction::AuthorizeCheckedWithSeed(args) => { - instruction_context.check_number_of_instruction_accounts(4)?; - let new_authority = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(3)?, - )?; - if !instruction_context.is_instruction_account_signer(3)? { - return Err(InstructionError::MissingRequiredSignature); - } - process_authorize_with_seed_instruction( - invoke_context, - instruction_context, - transaction_context, - &mut me, - new_authority, - args.authorization_type, - &args.current_authority_derived_key_owner, - args.current_authority_derived_key_seed.as_str(), - ) - } - VoteInstruction::UpdateValidatorIdentity => { - instruction_context.check_number_of_instruction_accounts(2)?; - let node_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(1)?, - )?; - vote_state::update_validator_identity( - &mut me, - node_pubkey, - &signers, - &invoke_context.feature_set, - ) - } - VoteInstruction::UpdateCommission(commission) => { - if invoke_context.feature_set.is_active( - &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(), - ) { - let sysvar_cache = invoke_context.get_sysvar_cache(); - let epoch_schedule = sysvar_cache.get_epoch_schedule()?; - let clock = sysvar_cache.get_clock()?; - if !vote_state::is_commission_update_allowed(clock.slot, &epoch_schedule) { - return Err(VoteError::CommissionUpdateTooLate.into()); + + let signers = instruction_context.get_signers(transaction_context)?; + match limited_deserialize(data)? { + VoteInstruction::InitializeAccount(vote_init) => { + let rent = + get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; + if !rent.is_exempt(me.get_lamports(), me.get_data().len()) { + return Err(InstructionError::InsufficientFunds); } - } - vote_state::update_commission( - &mut me, - commission, - &signers, - &invoke_context.feature_set, - ) - } - VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { - let slot_hashes = - get_sysvar_with_account_check::slot_hashes(invoke_context, instruction_context, 1)?; - let clock = - get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; - vote_state::process_vote_with_account( - &mut me, - &slot_hashes, - &clock, - &vote, - &signers, - &invoke_context.feature_set, - ) - } - VoteInstruction::UpdateVoteState(vote_state_update) - | VoteInstruction::UpdateVoteStateSwitch(vote_state_update, _) => { - if invoke_context - .feature_set - .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) - { - let sysvar_cache = invoke_context.get_sysvar_cache(); - let slot_hashes = sysvar_cache.get_slot_hashes()?; - let clock = sysvar_cache.get_clock()?; - vote_state::process_vote_state_update( + let clock = + get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; + vote_state::initialize_account( &mut me, - slot_hashes.slot_hashes(), - &clock, - vote_state_update, + &vote_init, &signers, + &clock, &invoke_context.feature_set, ) - } else { - Err(InstructionError::InvalidInstructionData) } - } - VoteInstruction::CompactUpdateVoteState(vote_state_update) - | VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => { - if invoke_context - .feature_set - .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) - && invoke_context - .feature_set - .is_active(&feature_set::compact_vote_state_updates::id()) - { - let sysvar_cache = invoke_context.get_sysvar_cache(); - let slot_hashes = sysvar_cache.get_slot_hashes()?; - let clock = sysvar_cache.get_clock()?; - vote_state::process_vote_state_update( - &mut me, - slot_hashes.slot_hashes(), - &clock, - vote_state_update, - &signers, - &invoke_context.feature_set, - ) - } else { - Err(InstructionError::InvalidInstructionData) - } - } - - VoteInstruction::Withdraw(lamports) => { - instruction_context.check_number_of_instruction_accounts(2)?; - let rent_sysvar = invoke_context.get_sysvar_cache().get_rent()?; - let clock_sysvar = invoke_context.get_sysvar_cache().get_clock()?; - - drop(me); - vote_state::withdraw( - transaction_context, - instruction_context, - 0, - lamports, - 1, - &signers, - &rent_sysvar, - &clock_sysvar, - &invoke_context.feature_set, - ) - } - VoteInstruction::AuthorizeChecked(vote_authorize) => { - if invoke_context - .feature_set - .is_active(&feature_set::vote_stake_checked_instructions::id()) - { - instruction_context.check_number_of_instruction_accounts(4)?; - let voter_pubkey = transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction(3)?, - )?; - if !instruction_context.is_instruction_account_signer(3)? { - return Err(InstructionError::MissingRequiredSignature); - } + VoteInstruction::Authorize(voter_pubkey, vote_authorize) => { let clock = get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; vote_state::authorize( &mut me, - voter_pubkey, + &voter_pubkey, vote_authorize, &signers, &clock, &invoke_context.feature_set, ) - } else { - Err(InstructionError::InvalidInstructionData) + } + VoteInstruction::AuthorizeWithSeed(args) => { + instruction_context.check_number_of_instruction_accounts(3)?; + process_authorize_with_seed_instruction( + invoke_context, + instruction_context, + transaction_context, + &mut me, + &args.new_authority, + args.authorization_type, + &args.current_authority_derived_key_owner, + args.current_authority_derived_key_seed.as_str(), + ) + } + VoteInstruction::AuthorizeCheckedWithSeed(args) => { + instruction_context.check_number_of_instruction_accounts(4)?; + let new_authority = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(3)?, + )?; + if !instruction_context.is_instruction_account_signer(3)? { + return Err(InstructionError::MissingRequiredSignature); + } + process_authorize_with_seed_instruction( + invoke_context, + instruction_context, + transaction_context, + &mut me, + new_authority, + args.authorization_type, + &args.current_authority_derived_key_owner, + args.current_authority_derived_key_seed.as_str(), + ) + } + VoteInstruction::UpdateValidatorIdentity => { + instruction_context.check_number_of_instruction_accounts(2)?; + let node_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(1)?, + )?; + vote_state::update_validator_identity( + &mut me, + node_pubkey, + &signers, + &invoke_context.feature_set, + ) + } + VoteInstruction::UpdateCommission(commission) => { + if invoke_context.feature_set.is_active( + &feature_set::commission_updates_only_allowed_in_first_half_of_epoch::id(), + ) { + let sysvar_cache = invoke_context.get_sysvar_cache(); + let epoch_schedule = sysvar_cache.get_epoch_schedule()?; + let clock = sysvar_cache.get_clock()?; + if !vote_state::is_commission_update_allowed(clock.slot, &epoch_schedule) { + return Err(VoteError::CommissionUpdateTooLate.into()); + } + } + vote_state::update_commission( + &mut me, + commission, + &signers, + &invoke_context.feature_set, + ) + } + VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => { + let slot_hashes = get_sysvar_with_account_check::slot_hashes( + invoke_context, + instruction_context, + 1, + )?; + let clock = + get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; + vote_state::process_vote_with_account( + &mut me, + &slot_hashes, + &clock, + &vote, + &signers, + &invoke_context.feature_set, + ) + } + VoteInstruction::UpdateVoteState(vote_state_update) + | VoteInstruction::UpdateVoteStateSwitch(vote_state_update, _) => { + if invoke_context + .feature_set + .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) + { + let sysvar_cache = invoke_context.get_sysvar_cache(); + let slot_hashes = sysvar_cache.get_slot_hashes()?; + let clock = sysvar_cache.get_clock()?; + vote_state::process_vote_state_update( + &mut me, + slot_hashes.slot_hashes(), + &clock, + vote_state_update, + &signers, + &invoke_context.feature_set, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + VoteInstruction::CompactUpdateVoteState(vote_state_update) + | VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => { + if invoke_context + .feature_set + .is_active(&feature_set::allow_votes_to_directly_update_vote_state::id()) + && invoke_context + .feature_set + .is_active(&feature_set::compact_vote_state_updates::id()) + { + let sysvar_cache = invoke_context.get_sysvar_cache(); + let slot_hashes = sysvar_cache.get_slot_hashes()?; + let clock = sysvar_cache.get_clock()?; + vote_state::process_vote_state_update( + &mut me, + slot_hashes.slot_hashes(), + &clock, + vote_state_update, + &signers, + &invoke_context.feature_set, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } + } + + VoteInstruction::Withdraw(lamports) => { + instruction_context.check_number_of_instruction_accounts(2)?; + let rent_sysvar = invoke_context.get_sysvar_cache().get_rent()?; + let clock_sysvar = invoke_context.get_sysvar_cache().get_clock()?; + + drop(me); + vote_state::withdraw( + transaction_context, + instruction_context, + 0, + lamports, + 1, + &signers, + &rent_sysvar, + &clock_sysvar, + &invoke_context.feature_set, + ) + } + VoteInstruction::AuthorizeChecked(vote_authorize) => { + if invoke_context + .feature_set + .is_active(&feature_set::vote_stake_checked_instructions::id()) + { + instruction_context.check_number_of_instruction_accounts(4)?; + let voter_pubkey = transaction_context.get_key_of_account_at_index( + instruction_context.get_index_of_instruction_account_in_transaction(3)?, + )?; + if !instruction_context.is_instruction_account_signer(3)? { + return Err(InstructionError::MissingRequiredSignature); + } + let clock = get_sysvar_with_account_check::clock( + invoke_context, + instruction_context, + 1, + )?; + vote_state::authorize( + &mut me, + voter_pubkey, + vote_authorize, + &signers, + &clock, + &invoke_context.feature_set, + ) + } else { + Err(InstructionError::InvalidInstructionData) + } } } } -}); +); #[cfg(test)] mod tests { diff --git a/runtime/src/block_cost_limits.rs b/runtime/src/block_cost_limits.rs index 543fd5dfa2..af751bdf01 100644 --- a/runtime/src/block_cost_limits.rs +++ b/runtime/src/block_cost_limits.rs @@ -4,7 +4,7 @@ use { lazy_static::lazy_static, solana_sdk::{ bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, compute_budget, ed25519_program, - feature, incinerator, native_loader, pubkey::Pubkey, secp256k1_program, system_program, + loader_v4, pubkey::Pubkey, secp256k1_program, }, std::collections::HashMap, }; @@ -32,21 +32,19 @@ pub const INSTRUCTION_DATA_BYTES_COST: u64 = 140 /*bytes per us*/ / COMPUTE_UNIT lazy_static! { /// Number of compute units for each built-in programs pub static ref BUILT_IN_INSTRUCTION_COSTS: HashMap = [ - (feature::id(), COMPUTE_UNIT_TO_US_RATIO * 2), - (incinerator::id(), COMPUTE_UNIT_TO_US_RATIO * 2), - (native_loader::id(), COMPUTE_UNIT_TO_US_RATIO * 2), - (solana_sdk::stake::config::id(), COMPUTE_UNIT_TO_US_RATIO * 2), - (solana_sdk::stake::program::id(), COMPUTE_UNIT_TO_US_RATIO * 25), - (solana_config_program::id(), COMPUTE_UNIT_TO_US_RATIO * 15), - (solana_vote_program::id(), COMPUTE_UNIT_TO_US_RATIO * 70), + (solana_stake_program::id(), solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS), + (solana_config_program::id(), solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS), + (solana_vote_program::id(), solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS), + (solana_system_program::id(), solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS), + (compute_budget::id(), solana_compute_budget_program::DEFAULT_COMPUTE_UNITS), + (solana_address_lookup_table_program::id(), solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS), + (bpf_loader_upgradeable::id(), solana_bpf_loader_program::UPGRADEABLE_LOADER_COMPUTE_UNITS), + (bpf_loader_deprecated::id(), solana_bpf_loader_program::DEPRECATED_LOADER_COMPUTE_UNITS), + (bpf_loader::id(), solana_bpf_loader_program::DEFAULT_LOADER_COMPUTE_UNITS), + (loader_v4::id(), solana_loader_v4_program::DEFAULT_COMPUTE_UNITS), + // Note: These are precompile, run directly in bank during sanitizing; (secp256k1_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24), (ed25519_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24), - (system_program::id(), COMPUTE_UNIT_TO_US_RATIO * 5), - (compute_budget::id(), COMPUTE_UNIT_TO_US_RATIO * 5), - (solana_address_lookup_table_program::id(), COMPUTE_UNIT_TO_US_RATIO * 25), - (bpf_loader_upgradeable::id(), COMPUTE_UNIT_TO_US_RATIO * 79), - (bpf_loader_deprecated::id(), COMPUTE_UNIT_TO_US_RATIO * 38), - (bpf_loader::id(), COMPUTE_UNIT_TO_US_RATIO * 19), ] .iter() .cloned()