use one definition of builtin program cost in runtime and banking stage (#31930)

* use one definition of builtin program cost in runtime and banking stage
* add loader_v4
This commit is contained in:
Tao Zhu 2023-06-02 10:22:22 -05:00 committed by GitHub
parent e587df1285
commit 10995f66c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1016 additions and 950 deletions

View File

@ -18,25 +18,31 @@ use {
std::convert::TryFrom, std::convert::TryFrom,
}; };
declare_process_instruction!(process_instruction, 750, |invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 750;
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?; declare_process_instruction!(
let instruction_data = instruction_context.get_instruction_data(); process_instruction,
match limited_deserialize(instruction_data)? { DEFAULT_COMPUTE_UNITS,
ProgramInstruction::CreateLookupTable { |invoke_context| {
recent_slot, let transaction_context = &invoke_context.transaction_context;
bump_seed, let instruction_context = transaction_context.get_current_instruction_context()?;
} => Processor::create_lookup_table(invoke_context, recent_slot, bump_seed), let instruction_data = instruction_context.get_instruction_data();
ProgramInstruction::FreezeLookupTable => Processor::freeze_lookup_table(invoke_context), match limited_deserialize(instruction_data)? {
ProgramInstruction::ExtendLookupTable { new_addresses } => { ProgramInstruction::CreateLookupTable {
Processor::extend_lookup_table(invoke_context, new_addresses) 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<usize, InstructionError> { fn checked_add(a: usize, b: usize) -> Result<usize, InstructionError> {
a.checked_add(b).ok_or(InstructionError::ArithmeticOverflow) a.checked_add(b).ok_or(InstructionError::ArithmeticOverflow)

View File

@ -62,6 +62,10 @@ use {
syscalls::create_program_runtime_environment, 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)] #[allow(clippy::too_many_arguments)]
pub fn load_program_from_bytes( pub fn load_program_from_bytes(
feature_set: &FeatureSet, feature_set: &FeatureSet,
@ -515,17 +519,17 @@ fn process_instruction_inner(
let program_id = instruction_context.get_last_program_key(transaction_context)?; let program_id = instruction_context.get_last_program_key(transaction_context)?;
return if bpf_loader_upgradeable::check_id(program_id) { return if bpf_loader_upgradeable::check_id(program_id) {
if native_programs_consume_cu { 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) process_loader_upgradeable_instruction(invoke_context)
} else if bpf_loader::check_id(program_id) { } else if bpf_loader::check_id(program_id) {
if native_programs_consume_cu { if native_programs_consume_cu {
invoke_context.consume_checked(570)?; invoke_context.consume_checked(DEFAULT_LOADER_COMPUTE_UNITS)?;
} }
process_loader_instruction(invoke_context) process_loader_instruction(invoke_context)
} else if bpf_loader_deprecated::check_id(program_id) { } else if bpf_loader_deprecated::check_id(program_id) {
if native_programs_consume_cu { 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"); ic_logger_msg!(log_collector, "Deprecated loader is no longer supported");
Err(InstructionError::UnsupportedProgramId) Err(InstructionError::UnsupportedProgramId)

View File

@ -1,6 +1,12 @@
use solana_program_runtime::declare_process_instruction; use solana_program_runtime::declare_process_instruction;
declare_process_instruction!(process_instruction, 150, |_invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 150;
// Do nothing, compute budget instructions handled by the runtime
Ok(()) declare_process_instruction!(
}); process_instruction,
DEFAULT_COMPUTE_UNITS,
|_invoke_context| {
// Do nothing, compute budget instructions handled by the runtime
Ok(())
}
);

View File

@ -11,127 +11,133 @@ use {
std::collections::BTreeSet, std::collections::BTreeSet,
}; };
declare_process_instruction!(process_instruction, 450, |invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 450;
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)?; declare_process_instruction!(
let config_account_key = transaction_context.get_key_of_account_at_index( process_instruction,
instruction_context.get_index_of_instruction_account_in_transaction(0)?, DEFAULT_COMPUTE_UNITS,
)?; |invoke_context| {
let config_account = let transaction_context = &invoke_context.transaction_context;
instruction_context.try_borrow_instruction_account(transaction_context, 0)?; let instruction_context = transaction_context.get_current_instruction_context()?;
let is_config_account_signer = config_account.is_signer(); let data = instruction_context.get_instruction_data();
let current_data: ConfigKeys = {
if config_account.get_owner() != &crate::id() { let key_list: ConfigKeys = limited_deserialize(data)?;
return Err(InstructionError::InvalidAccountOwner); 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<Pubkey> = 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| { let mut counter = 0;
ic_msg!( for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) {
invoke_context, counter += 1;
"Unable to deserialize config account: {}", if signer != config_account_key {
err let signer_account = instruction_context
); .try_borrow_instruction_account(transaction_context, counter as IndexOfAccount)
InstructionError::InvalidAccountData .map_err(|_| {
})? ic_msg!(
}; invoke_context,
drop(config_account); "account {:?} is not in account list",
signer,
let current_signer_keys: Vec<Pubkey> = current_data );
.keys InstructionError::MissingRequiredSignature
.iter() })?;
.filter(|(_, is_signer)| *is_signer) if !signer_account.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(|_| {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"account {:?} is not in account list", "account {:?} signer_key().is_none()",
signer, signer
); );
InstructionError::MissingRequiredSignature return Err(InstructionError::MissingRequiredSignature);
})?; }
if !signer_account.is_signer() { if signer_account.get_key() != signer {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"account {:?} signer_key().is_none()", "account[{:?}].signer_key() does not match Config data)",
signer 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); return Err(InstructionError::MissingRequiredSignature);
} }
if signer_account.get_key() != signer { }
ic_msg!(
invoke_context, if invoke_context
"account[{:?}].signer_key() does not match Config data)", .feature_set
counter + 1 .is_active(&feature_set::dedupe_config_program_signers::id())
); {
return Err(InstructionError::MissingRequiredSignature); let total_new_keys = key_list.keys.len();
let unique_new_keys = key_list.keys.into_iter().collect::<BTreeSet<_>>();
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) // Check for Config data signers not present in incoming account update
{ if current_signer_keys.len() > counter {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"account {:?} is not in stored signer list", "too few signers: {:?}; expected: {:?}",
signer counter,
); current_signer_keys.len()
return Err(InstructionError::MissingRequiredSignature); );
}
} else if !is_config_account_signer {
ic_msg!(invoke_context, "account[0].signer_key().is_none()");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
}
if invoke_context let mut config_account =
.feature_set instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
.is_active(&feature_set::dedupe_config_program_signers::id()) if config_account.get_data().len() < data.len() {
{ ic_msg!(invoke_context, "instruction data too large");
let total_new_keys = key_list.keys.len(); return Err(InstructionError::InvalidInstructionData);
let unique_new_keys = key_list.keys.into_iter().collect::<BTreeSet<_>>();
if unique_new_keys.len() != total_new_keys {
ic_msg!(invoke_context, "new config contains duplicate keys");
return Err(InstructionError::InvalidArgument);
} }
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)] #[cfg(test)]
mod tests { mod tests {

View File

@ -38,6 +38,8 @@ use {
}, },
}; };
pub const DEFAULT_COMPUTE_UNITS: u64 = 2_000;
pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> { pub fn get_state(data: &[u8]) -> Result<&LoaderV4State, InstructionError> {
unsafe { unsafe {
let data = data let data = data
@ -560,7 +562,7 @@ pub fn process_instruction_inner(
.feature_set .feature_set
.is_active(&feature_set::native_programs_consume_cu::id()) .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)? { match limited_deserialize(instruction_data)? {
LoaderV4Instruction::Write { offset, bytes } => { LoaderV4Instruction::Write { offset, bytes } => {

View File

@ -51,431 +51,454 @@ fn get_optional_pubkey<'a>(
) )
} }
declare_process_instruction!(process_instruction, 750, |invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 750;
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let data = instruction_context.get_instruction_data();
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 = || { trace!("process_instruction: {:?}", data);
let me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
if *me.get_owner() != id() {
return Err(InstructionError::InvalidAccountOwner);
}
Ok(me)
};
let signers = instruction_context.get_signers(transaction_context)?; let get_stake_account = || {
match limited_deserialize(data) { let me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
Ok(StakeInstruction::Initialize(authorized, lockup)) => { if *me.get_owner() != id() {
let mut me = get_stake_account()?; return Err(InstructionError::InvalidAccountOwner);
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,
)
} }
} Ok(me)
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,
};
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 = let rent =
get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
initialize( initialize(
&mut me, &mut me,
&authorized, &authorized,
&Lockup::default(), &lockup,
&rent, &rent,
&invoke_context.feature_set, &invoke_context.feature_set,
) )
} else {
Err(InstructionError::InvalidInstructionData)
} }
} Ok(StakeInstruction::Authorize(authorized_pubkey, stake_authorize)) => {
Ok(StakeInstruction::AuthorizeChecked(stake_authorize)) => { let mut me = get_stake_account()?;
let mut me = get_stake_account()?; let require_custodian_for_locked_stake_authorize = invoke_context
if invoke_context .feature_set
.feature_set .is_active(&feature_set::require_custodian_for_locked_stake_authorize::id());
.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( if require_custodian_for_locked_stake_authorize {
&mut me, let clock = get_sysvar_with_account_check::clock(
&signers, invoke_context,
authorized_pubkey, instruction_context,
stake_authorize, 1,
true, )?;
&clock, instruction_context.check_number_of_instruction_accounts(3)?;
custodian_pubkey, let custodian_pubkey =
) get_optional_pubkey(transaction_context, instruction_context, 3, false)?;
} else {
Err(InstructionError::InvalidInstructionData) 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::AuthorizeWithSeed(args)) => {
Ok(StakeInstruction::AuthorizeCheckedWithSeed(args)) => { let mut me = get_stake_account()?;
let mut me = get_stake_account()?; instruction_context.check_number_of_instruction_accounts(2)?;
if invoke_context let require_custodian_for_locked_stake_authorize = invoke_context
.feature_set .feature_set
.is_active(&feature_set::vote_stake_checked_instructions::id()) .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)?; instruction_context.check_number_of_instruction_accounts(2)?;
let clock = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
instruction_context.check_number_of_instruction_accounts(4)?; let stake_history = get_sysvar_with_account_check::stake_history(
let authorized_pubkey = transaction_context.get_key_of_account_at_index( invoke_context,
instruction_context.get_index_of_instruction_account_in_transaction(3)?, instruction_context,
3,
)?; )?;
if !instruction_context.is_instruction_account_signer(3)? { instruction_context.check_number_of_instruction_accounts(5)?;
return Err(InstructionError::MissingRequiredSignature); drop(me);
}
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 = 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()) { if !config::check_id(config_account.get_key()) {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let config = let config =
config::from(&config_account).ok_or(InstructionError::InvalidArgument)?; config::from(&config_account).ok_or(InstructionError::InvalidArgument)?;
drop(config_account); drop(config_account);
delegate(
redelegate(
invoke_context, invoke_context,
transaction_context, transaction_context,
instruction_context, instruction_context,
&mut me, 0,
1, 1,
2, &clock,
&stake_history,
&config, &config,
&signers, &signers,
&invoke_context.feature_set,
) )
} else {
Err(InstructionError::InvalidInstructionData)
} }
} Ok(StakeInstruction::Split(lamports)) => {
// In order to prevent consensus issues, any new StakeInstruction variant added before the let me = get_stake_account()?;
// `add_get_minimum_delegation_instruction_to_stake_program` is activated needs to check instruction_context.check_number_of_instruction_accounts(2)?;
// the validity of the stake account by calling the `get_stake_account()` method outside drop(me);
// its own feature gate, as per the following pattern: split(
// ``` invoke_context,
// Ok(StakeInstruction::Variant) -> { transaction_context,
// let mut me = get_stake_account()?; instruction_context,
// if invoke_context 0,
// .feature_set lamports,
// .is_active(&feature_set::stake_variant_feature::id()) { .. } 1,
// } &signers,
// ``` )
// TODO: Remove this comment when `add_get_minimum_delegation_instruction_to_stake_program` }
// is cleaned up Ok(StakeInstruction::Merge) => {
Err(err) => { let me = get_stake_account()?;
if !invoke_context.feature_set.is_active( instruction_context.check_number_of_instruction_accounts(2)?;
&feature_set::add_get_minimum_delegation_instruction_to_stake_program::id(), let clock =
) { get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
let _ = get_stake_account()?; 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)] #[cfg(test)]
mod tests { mod tests {

View File

@ -312,246 +312,254 @@ fn transfer_with_seed(
) )
} }
declare_process_instruction!(process_instruction, 150, |invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 150;
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)?;
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)?; trace!("process_instruction: {:?}", instruction);
match instruction {
SystemInstruction::CreateAccount { let signers = instruction_context.get_signers(transaction_context)?;
lamports, match instruction {
space, SystemInstruction::CreateAccount {
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,
lamports, lamports,
space, space,
&owner, owner,
&signers, } => {
invoke_context, instruction_context.check_number_of_instruction_accounts(2)?;
transaction_context, let to_address = Address::create(
instruction_context, transaction_context.get_key_of_account_at_index(
) instruction_context.get_index_of_instruction_account_in_transaction(1)?,
} )?,
SystemInstruction::CreateAccountWithSeed { None,
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!(
invoke_context, invoke_context,
"Advance nonce account: recent blockhash list is empty", )?;
); create_account(
return Err(SystemError::NonceNoRecentBlockhashes.into()); 0,
} 1,
advance_nonce_account(&mut me, &signers, invoke_context) &to_address,
} lamports,
SystemInstruction::WithdrawNonceAccount(lamports) => { space,
instruction_context.check_number_of_instruction_accounts(2)?; &owner,
#[allow(deprecated)] &signers,
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, invoke_context,
"Initialize nonce account: recent blockhash list is empty", transaction_context,
); instruction_context,
return Err(SystemError::NonceNoRecentBlockhashes.into()); )
} }
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 2)?; SystemInstruction::CreateAccountWithSeed {
initialize_nonce_account(&mut me, &authorized, &rent, invoke_context) base,
} seed,
SystemInstruction::AuthorizeNonceAccount(nonce_authority) => { lamports,
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, space,
&owner, owner,
&signers, } => {
invoke_context, instruction_context.check_number_of_instruction_accounts(2)?;
) let to_address = Address::create(
} transaction_context.get_key_of_account_at_index(
SystemInstruction::AssignWithSeed { base, seed, owner } => { instruction_context.get_index_of_instruction_account_in_transaction(1)?,
instruction_context.check_number_of_instruction_accounts(1)?; )?,
let mut account = Some((&base, &seed, &owner)),
instruction_context.try_borrow_instruction_account(transaction_context, 0)?; invoke_context,
let address = Address::create( )?;
transaction_context.get_key_of_account_at_index( create_account(
instruction_context.get_index_of_instruction_account_in_transaction(0)?, 0,
)?, 1,
Some((&base, &seed, &owner)), &to_address,
invoke_context, lamports,
)?; space,
assign(&mut account, &address, &owner, &signers, invoke_context) &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)] #[cfg(test)]
mod tests { mod tests {

View File

@ -52,215 +52,228 @@ fn process_authorize_with_seed_instruction(
// Citing `runtime/src/block_cost_limit.rs`, vote has statically defined 2100 // 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. // units; can consume based on instructions in the future like `bpf_loader` does.
declare_process_instruction!(process_instruction, 2100, |invoke_context| { pub const DEFAULT_COMPUTE_UNITS: u64 = 2_100;
let transaction_context = &invoke_context.transaction_context;
let instruction_context = transaction_context.get_current_instruction_context()?;
let data = instruction_context.get_instruction_data();
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)?; trace!("process_instruction: {:?}", data);
if *me.get_owner() != id() {
return Err(InstructionError::InvalidAccountOwner);
}
let signers = instruction_context.get_signers(transaction_context)?; let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
match limited_deserialize(data)? { if *me.get_owner() != id() {
VoteInstruction::InitializeAccount(vote_init) => { return Err(InstructionError::InvalidAccountOwner);
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,
)
} }
VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
let clock = let signers = instruction_context.get_signers(transaction_context)?;
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; match limited_deserialize(data)? {
vote_state::authorize( VoteInstruction::InitializeAccount(vote_init) => {
&mut me, let rent =
&voter_pubkey, get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
vote_authorize, if !rent.is_exempt(me.get_lamports(), me.get_data().len()) {
&signers, return Err(InstructionError::InsufficientFunds);
&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 clock =
vote_state::update_commission( get_sysvar_with_account_check::clock(invoke_context, instruction_context, 2)?;
&mut me, vote_state::initialize_account(
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, &mut me,
slot_hashes.slot_hashes(), &vote_init,
&clock,
vote_state_update,
&signers, &signers,
&clock,
&invoke_context.feature_set, &invoke_context.feature_set,
) )
} else {
Err(InstructionError::InvalidInstructionData)
} }
} VoteInstruction::Authorize(voter_pubkey, vote_authorize) => {
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 = let clock =
get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?; get_sysvar_with_account_check::clock(invoke_context, instruction_context, 1)?;
vote_state::authorize( vote_state::authorize(
&mut me, &mut me,
voter_pubkey, &voter_pubkey,
vote_authorize, vote_authorize,
&signers, &signers,
&clock, &clock,
&invoke_context.feature_set, &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)] #[cfg(test)]
mod tests { mod tests {

View File

@ -4,7 +4,7 @@ use {
lazy_static::lazy_static, lazy_static::lazy_static,
solana_sdk::{ solana_sdk::{
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, compute_budget, ed25519_program, 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, std::collections::HashMap,
}; };
@ -32,21 +32,19 @@ pub const INSTRUCTION_DATA_BYTES_COST: u64 = 140 /*bytes per us*/ / COMPUTE_UNIT
lazy_static! { lazy_static! {
/// Number of compute units for each built-in programs /// Number of compute units for each built-in programs
pub static ref BUILT_IN_INSTRUCTION_COSTS: HashMap<Pubkey, u64> = [ pub static ref BUILT_IN_INSTRUCTION_COSTS: HashMap<Pubkey, u64> = [
(feature::id(), COMPUTE_UNIT_TO_US_RATIO * 2), (solana_stake_program::id(), solana_stake_program::stake_instruction::DEFAULT_COMPUTE_UNITS),
(incinerator::id(), COMPUTE_UNIT_TO_US_RATIO * 2), (solana_config_program::id(), solana_config_program::config_processor::DEFAULT_COMPUTE_UNITS),
(native_loader::id(), COMPUTE_UNIT_TO_US_RATIO * 2), (solana_vote_program::id(), solana_vote_program::vote_processor::DEFAULT_COMPUTE_UNITS),
(solana_sdk::stake::config::id(), COMPUTE_UNIT_TO_US_RATIO * 2), (solana_system_program::id(), solana_system_program::system_processor::DEFAULT_COMPUTE_UNITS),
(solana_sdk::stake::program::id(), COMPUTE_UNIT_TO_US_RATIO * 25), (compute_budget::id(), solana_compute_budget_program::DEFAULT_COMPUTE_UNITS),
(solana_config_program::id(), COMPUTE_UNIT_TO_US_RATIO * 15), (solana_address_lookup_table_program::id(), solana_address_lookup_table_program::processor::DEFAULT_COMPUTE_UNITS),
(solana_vote_program::id(), COMPUTE_UNIT_TO_US_RATIO * 70), (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), (secp256k1_program::id(), COMPUTE_UNIT_TO_US_RATIO * 24),
(ed25519_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() .iter()
.cloned() .cloned()