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:
parent
e587df1285
commit
10995f66c0
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 } => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue