Remove `KeyedAccount` in builtin program "stake" (#24210)
* Inline keyed_account_at_index() in all instructions of stake which have more than one KeyedAccount parameter, because these could cause a borrow collision. * Uses transaction_context.get_key_of_account_at_index() in stake. * Refactors stake::config::from to use BorrowedAccount instead of ReadableAccount. * Replaces KeyedAccount by BorrowedAccount in stake.
This commit is contained in:
parent
1882434c69
commit
bf13fb4c4b
|
@ -12,11 +12,12 @@ use {
|
||||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
stake::config::{self, Config},
|
stake::config::{self, Config},
|
||||||
|
transaction_context::BorrowedAccount,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn from<T: ReadableAccount>(account: &T) -> Option<Config> {
|
pub fn from(account: &BorrowedAccount) -> Option<Config> {
|
||||||
get_config_data(account.data())
|
get_config_data(account.get_data())
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|data| deserialize(data).ok())
|
.and_then(|data| deserialize(data).ok())
|
||||||
}
|
}
|
||||||
|
@ -35,14 +36,3 @@ pub fn add_genesis_account(genesis_config: &mut GenesisConfig) -> u64 {
|
||||||
|
|
||||||
lamports
|
lamports
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use {super::*, std::cell::RefCell};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
let account = RefCell::new(create_account(0, &Config::default()));
|
|
||||||
assert_eq!(from(&account.borrow()), Some(Config::default()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,17 +13,38 @@ use {
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
feature_set,
|
feature_set,
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
keyed_account::keyed_account_at_index,
|
|
||||||
program_utils::limited_deserialize,
|
program_utils::limited_deserialize,
|
||||||
|
pubkey::Pubkey,
|
||||||
stake::{
|
stake::{
|
||||||
instruction::{LockupArgs, StakeInstruction},
|
instruction::{LockupArgs, StakeInstruction},
|
||||||
program::id,
|
program::id,
|
||||||
state::{Authorized, Lockup},
|
state::{Authorized, Lockup},
|
||||||
},
|
},
|
||||||
sysvar::clock::Clock,
|
sysvar::clock::Clock,
|
||||||
|
transaction_context::{InstructionContext, TransactionContext},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn get_optional_pubkey<'a>(
|
||||||
|
transaction_context: &'a TransactionContext,
|
||||||
|
instruction_context: &'a InstructionContext,
|
||||||
|
index_in_instruction: usize,
|
||||||
|
should_be_signer: bool,
|
||||||
|
) -> Result<Option<&'a Pubkey>, InstructionError> {
|
||||||
|
Ok(
|
||||||
|
if instruction_context.get_number_of_accounts() > index_in_instruction {
|
||||||
|
if should_be_signer && !instruction_context.is_signer(index_in_instruction)? {
|
||||||
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
|
}
|
||||||
|
Some(transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(index_in_instruction)?,
|
||||||
|
)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
first_instruction_account: usize,
|
first_instruction_account: usize,
|
||||||
invoke_context: &mut InvokeContext,
|
invoke_context: &mut InvokeContext,
|
||||||
|
@ -31,13 +52,11 @@ pub fn process_instruction(
|
||||||
let transaction_context = &invoke_context.transaction_context;
|
let transaction_context = &invoke_context.transaction_context;
|
||||||
let instruction_context = transaction_context.get_current_instruction_context()?;
|
let instruction_context = transaction_context.get_current_instruction_context()?;
|
||||||
let data = instruction_context.get_instruction_data();
|
let data = instruction_context.get_instruction_data();
|
||||||
let keyed_accounts = invoke_context.get_keyed_accounts()?;
|
|
||||||
|
|
||||||
trace!("process_instruction: {:?}", data);
|
trace!("process_instruction: {:?}", data);
|
||||||
trace!("keyed_accounts: {:?}", keyed_accounts);
|
|
||||||
|
|
||||||
let me = &keyed_account_at_index(keyed_accounts, first_instruction_account)?;
|
let mut me = instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
|
||||||
if me.owner()? != id() {
|
if *me.get_owner() != id() {
|
||||||
return Err(InstructionError::InvalidAccountOwner);
|
return Err(InstructionError::InvalidAccountOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +64,13 @@ pub fn process_instruction(
|
||||||
match limited_deserialize(data)? {
|
match limited_deserialize(data)? {
|
||||||
StakeInstruction::Initialize(authorized, lockup) => {
|
StakeInstruction::Initialize(authorized, lockup) => {
|
||||||
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
|
let rent = get_sysvar_with_account_check::rent(invoke_context, instruction_context, 1)?;
|
||||||
initialize(me, &authorized, &lockup, &rent, &invoke_context.feature_set)
|
initialize(
|
||||||
|
&mut me,
|
||||||
|
&authorized,
|
||||||
|
&lockup,
|
||||||
|
&rent,
|
||||||
|
&invoke_context.feature_set,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
|
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
|
||||||
let require_custodian_for_locked_stake_authorize = invoke_context
|
let require_custodian_for_locked_stake_authorize = invoke_context
|
||||||
|
@ -56,23 +81,25 @@ pub fn process_instruction(
|
||||||
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)?;
|
||||||
instruction_context.check_number_of_instruction_accounts(3)?;
|
instruction_context.check_number_of_instruction_accounts(3)?;
|
||||||
let custodian =
|
let custodian_pubkey = get_optional_pubkey(
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 3)
|
transaction_context,
|
||||||
.ok()
|
instruction_context,
|
||||||
.map(|ka| ka.unsigned_key());
|
first_instruction_account + 3,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
authorize(
|
authorize(
|
||||||
me,
|
&mut me,
|
||||||
&signers,
|
&signers,
|
||||||
&authorized_pubkey,
|
&authorized_pubkey,
|
||||||
stake_authorize,
|
stake_authorize,
|
||||||
require_custodian_for_locked_stake_authorize,
|
require_custodian_for_locked_stake_authorize,
|
||||||
&clock,
|
&clock,
|
||||||
custodian,
|
custodian_pubkey,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
authorize(
|
authorize(
|
||||||
me,
|
&mut me,
|
||||||
&signers,
|
&signers,
|
||||||
&authorized_pubkey,
|
&authorized_pubkey,
|
||||||
stake_authorize,
|
stake_authorize,
|
||||||
|
@ -84,35 +111,38 @@ pub fn process_instruction(
|
||||||
}
|
}
|
||||||
StakeInstruction::AuthorizeWithSeed(args) => {
|
StakeInstruction::AuthorizeWithSeed(args) => {
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let authority_base =
|
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
|
||||||
let require_custodian_for_locked_stake_authorize = invoke_context
|
let require_custodian_for_locked_stake_authorize = invoke_context
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&feature_set::require_custodian_for_locked_stake_authorize::id());
|
.is_active(&feature_set::require_custodian_for_locked_stake_authorize::id());
|
||||||
|
|
||||||
if require_custodian_for_locked_stake_authorize {
|
if require_custodian_for_locked_stake_authorize {
|
||||||
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)?;
|
||||||
let custodian =
|
let custodian_pubkey = get_optional_pubkey(
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 3)
|
transaction_context,
|
||||||
.ok()
|
instruction_context,
|
||||||
.map(|ka| ka.unsigned_key());
|
first_instruction_account + 3,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
authorize_with_seed(
|
authorize_with_seed(
|
||||||
me,
|
transaction_context,
|
||||||
authority_base,
|
instruction_context,
|
||||||
|
&mut me,
|
||||||
|
first_instruction_account + 1,
|
||||||
&args.authority_seed,
|
&args.authority_seed,
|
||||||
&args.authority_owner,
|
&args.authority_owner,
|
||||||
&args.new_authorized_pubkey,
|
&args.new_authorized_pubkey,
|
||||||
args.stake_authorize,
|
args.stake_authorize,
|
||||||
require_custodian_for_locked_stake_authorize,
|
require_custodian_for_locked_stake_authorize,
|
||||||
&clock,
|
&clock,
|
||||||
custodian,
|
custodian_pubkey,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
authorize_with_seed(
|
authorize_with_seed(
|
||||||
me,
|
transaction_context,
|
||||||
authority_base,
|
instruction_context,
|
||||||
|
&mut me,
|
||||||
|
first_instruction_account + 1,
|
||||||
&args.authority_seed,
|
&args.authority_seed,
|
||||||
&args.authority_owner,
|
&args.authority_owner,
|
||||||
&args.new_authorized_pubkey,
|
&args.new_authorized_pubkey,
|
||||||
|
@ -125,7 +155,6 @@ pub fn process_instruction(
|
||||||
}
|
}
|
||||||
StakeInstruction::DelegateStake => {
|
StakeInstruction::DelegateStake => {
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let vote = keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
|
||||||
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)?;
|
||||||
let stake_history = get_sysvar_with_account_check::stake_history(
|
let stake_history = get_sysvar_with_account_check::stake_history(
|
||||||
|
@ -134,25 +163,40 @@ pub fn process_instruction(
|
||||||
3,
|
3,
|
||||||
)?;
|
)?;
|
||||||
instruction_context.check_number_of_instruction_accounts(5)?;
|
instruction_context.check_number_of_instruction_accounts(5)?;
|
||||||
|
drop(me);
|
||||||
let config_account =
|
let config_account =
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 4)?;
|
instruction_context.try_borrow_instruction_account(transaction_context, 4)?;
|
||||||
if !config::check_id(config_account.unsigned_key()) {
|
if !config::check_id(config_account.get_key()) {
|
||||||
return Err(InstructionError::InvalidArgument);
|
return Err(InstructionError::InvalidArgument);
|
||||||
}
|
}
|
||||||
let config = config::from(&*config_account.try_account_ref()?)
|
let config = config::from(&config_account).ok_or(InstructionError::InvalidArgument)?;
|
||||||
.ok_or(InstructionError::InvalidArgument)?;
|
drop(config_account);
|
||||||
delegate(me, vote, &clock, &stake_history, &config, &signers)
|
delegate(
|
||||||
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
first_instruction_account,
|
||||||
|
first_instruction_account + 1,
|
||||||
|
&clock,
|
||||||
|
&stake_history,
|
||||||
|
&config,
|
||||||
|
&signers,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StakeInstruction::Split(lamports) => {
|
StakeInstruction::Split(lamports) => {
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let split_stake =
|
drop(me);
|
||||||
&keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
split(
|
||||||
split(me, invoke_context, lamports, split_stake, &signers)
|
invoke_context,
|
||||||
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
first_instruction_account,
|
||||||
|
lamports,
|
||||||
|
first_instruction_account + 1,
|
||||||
|
&signers,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
StakeInstruction::Merge => {
|
StakeInstruction::Merge => {
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let source_stake =
|
|
||||||
&keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
|
||||||
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)?;
|
||||||
let stake_history = get_sysvar_with_account_check::stake_history(
|
let stake_history = get_sysvar_with_account_check::stake_history(
|
||||||
|
@ -160,10 +204,13 @@ pub fn process_instruction(
|
||||||
instruction_context,
|
instruction_context,
|
||||||
3,
|
3,
|
||||||
)?;
|
)?;
|
||||||
|
drop(me);
|
||||||
merge(
|
merge(
|
||||||
me,
|
|
||||||
invoke_context,
|
invoke_context,
|
||||||
source_stake,
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
first_instruction_account,
|
||||||
|
first_instruction_account + 1,
|
||||||
&clock,
|
&clock,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
&signers,
|
&signers,
|
||||||
|
@ -171,7 +218,6 @@ pub fn process_instruction(
|
||||||
}
|
}
|
||||||
StakeInstruction::Withdraw(lamports) => {
|
StakeInstruction::Withdraw(lamports) => {
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let to = &keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
|
||||||
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)?;
|
||||||
let stake_history = get_sysvar_with_account_check::stake_history(
|
let stake_history = get_sysvar_with_account_check::stake_history(
|
||||||
|
@ -180,25 +226,32 @@ pub fn process_instruction(
|
||||||
3,
|
3,
|
||||||
)?;
|
)?;
|
||||||
instruction_context.check_number_of_instruction_accounts(5)?;
|
instruction_context.check_number_of_instruction_accounts(5)?;
|
||||||
|
drop(me);
|
||||||
withdraw(
|
withdraw(
|
||||||
me,
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
first_instruction_account,
|
||||||
lamports,
|
lamports,
|
||||||
to,
|
first_instruction_account + 1,
|
||||||
&clock,
|
&clock,
|
||||||
&stake_history,
|
&stake_history,
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 4)?,
|
first_instruction_account + 4,
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 5).ok(),
|
if instruction_context.get_number_of_instruction_accounts() >= 6 {
|
||||||
|
Some(first_instruction_account + 5)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
&invoke_context.feature_set,
|
&invoke_context.feature_set,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
StakeInstruction::Deactivate => {
|
StakeInstruction::Deactivate => {
|
||||||
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)?;
|
||||||
deactivate(me, &clock, &signers)
|
deactivate(&mut me, &clock, &signers)
|
||||||
}
|
}
|
||||||
StakeInstruction::SetLockup(lockup) => {
|
StakeInstruction::SetLockup(lockup) => {
|
||||||
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
||||||
set_lockup(me, &lockup, &signers, &clock)
|
set_lockup(&mut me, &lockup, &signers, &clock)
|
||||||
}
|
}
|
||||||
StakeInstruction::InitializeChecked => {
|
StakeInstruction::InitializeChecked => {
|
||||||
if invoke_context
|
if invoke_context
|
||||||
|
@ -206,21 +259,25 @@ pub fn process_instruction(
|
||||||
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
||||||
{
|
{
|
||||||
instruction_context.check_number_of_instruction_accounts(4)?;
|
instruction_context.check_number_of_instruction_accounts(4)?;
|
||||||
|
let staker_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(first_instruction_account + 2)?,
|
||||||
|
)?;
|
||||||
|
let withdrawer_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(first_instruction_account + 3)?,
|
||||||
|
)?;
|
||||||
|
if !instruction_context.is_signer(first_instruction_account + 3)? {
|
||||||
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
|
}
|
||||||
|
|
||||||
let authorized = Authorized {
|
let authorized = Authorized {
|
||||||
staker: *keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
|
staker: *staker_pubkey,
|
||||||
.unsigned_key(),
|
withdrawer: *withdrawer_pubkey,
|
||||||
withdrawer: *keyed_account_at_index(
|
|
||||||
keyed_accounts,
|
|
||||||
first_instruction_account + 3,
|
|
||||||
)?
|
|
||||||
.signer_key()
|
|
||||||
.ok_or(InstructionError::MissingRequiredSignature)?,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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(
|
||||||
me,
|
&mut me,
|
||||||
&authorized,
|
&authorized,
|
||||||
&Lockup::default(),
|
&Lockup::default(),
|
||||||
&rent,
|
&rent,
|
||||||
|
@ -238,25 +295,27 @@ pub fn process_instruction(
|
||||||
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)?;
|
||||||
instruction_context.check_number_of_instruction_accounts(4)?;
|
instruction_context.check_number_of_instruction_accounts(4)?;
|
||||||
let _current_authority =
|
let authorized_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?;
|
instruction_context.get_index_in_transaction(first_instruction_account + 3)?,
|
||||||
let authorized_pubkey =
|
)?;
|
||||||
&keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?
|
if !instruction_context.is_signer(first_instruction_account + 3)? {
|
||||||
.signer_key()
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
.ok_or(InstructionError::MissingRequiredSignature)?;
|
}
|
||||||
let custodian =
|
let custodian_pubkey = get_optional_pubkey(
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 4)
|
transaction_context,
|
||||||
.ok()
|
instruction_context,
|
||||||
.map(|ka| ka.unsigned_key());
|
first_instruction_account + 4,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
authorize(
|
authorize(
|
||||||
me,
|
&mut me,
|
||||||
&signers,
|
&signers,
|
||||||
authorized_pubkey,
|
authorized_pubkey,
|
||||||
stake_authorize,
|
stake_authorize,
|
||||||
true,
|
true,
|
||||||
&clock,
|
&clock,
|
||||||
custodian,
|
custodian_pubkey,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidInstructionData)
|
Err(InstructionError::InvalidInstructionData)
|
||||||
|
@ -268,30 +327,34 @@ pub fn process_instruction(
|
||||||
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
||||||
{
|
{
|
||||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||||
let authority_base =
|
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?;
|
|
||||||
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)?;
|
instruction_context.check_number_of_instruction_accounts(4)?;
|
||||||
let authorized_pubkey =
|
let authorized_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
&keyed_account_at_index(keyed_accounts, first_instruction_account + 3)?
|
instruction_context.get_index_in_transaction(first_instruction_account + 3)?,
|
||||||
.signer_key()
|
)?;
|
||||||
.ok_or(InstructionError::MissingRequiredSignature)?;
|
if !instruction_context.is_signer(first_instruction_account + 3)? {
|
||||||
let custodian =
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 4)
|
}
|
||||||
.ok()
|
let custodian_pubkey = get_optional_pubkey(
|
||||||
.map(|ka| ka.unsigned_key());
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
first_instruction_account + 4,
|
||||||
|
false,
|
||||||
|
)?;
|
||||||
|
|
||||||
authorize_with_seed(
|
authorize_with_seed(
|
||||||
me,
|
transaction_context,
|
||||||
authority_base,
|
instruction_context,
|
||||||
|
&mut me,
|
||||||
|
first_instruction_account + 1,
|
||||||
&args.authority_seed,
|
&args.authority_seed,
|
||||||
&args.authority_owner,
|
&args.authority_owner,
|
||||||
authorized_pubkey,
|
authorized_pubkey,
|
||||||
args.stake_authorize,
|
args.stake_authorize,
|
||||||
true,
|
true,
|
||||||
&clock,
|
&clock,
|
||||||
custodian,
|
custodian_pubkey,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidInstructionData)
|
Err(InstructionError::InvalidInstructionData)
|
||||||
|
@ -302,25 +365,20 @@ pub fn process_instruction(
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
.is_active(&feature_set::vote_stake_checked_instructions::id())
|
||||||
{
|
{
|
||||||
let custodian = if let Ok(custodian) =
|
let custodian_pubkey = get_optional_pubkey(
|
||||||
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)
|
transaction_context,
|
||||||
{
|
instruction_context,
|
||||||
Some(
|
first_instruction_account + 2,
|
||||||
*custodian
|
true,
|
||||||
.signer_key()
|
)?;
|
||||||
.ok_or(InstructionError::MissingRequiredSignature)?,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let lockup = LockupArgs {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: lockup_checked.unix_timestamp,
|
unix_timestamp: lockup_checked.unix_timestamp,
|
||||||
epoch: lockup_checked.epoch,
|
epoch: lockup_checked.epoch,
|
||||||
custodian,
|
custodian: custodian_pubkey.cloned(),
|
||||||
};
|
};
|
||||||
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
let clock = invoke_context.get_sysvar_cache().get_clock()?;
|
||||||
set_lockup(me, &lockup, &signers, &clock)
|
set_lockup(&mut me, &lockup, &signers, &clock)
|
||||||
} else {
|
} else {
|
||||||
Err(InstructionError::InvalidInstructionData)
|
Err(InstructionError::InvalidInstructionData)
|
||||||
}
|
}
|
||||||
|
@ -335,6 +393,7 @@ pub fn process_instruction(
|
||||||
|
|
||||||
let minimum_delegation = crate::get_minimum_delegation(feature_set);
|
let minimum_delegation = crate::get_minimum_delegation(feature_set);
|
||||||
let minimum_delegation = Vec::from(minimum_delegation.to_le_bytes());
|
let minimum_delegation = Vec::from(minimum_delegation.to_le_bytes());
|
||||||
|
drop(me);
|
||||||
invoke_context
|
invoke_context
|
||||||
.transaction_context
|
.transaction_context
|
||||||
.set_return_data(id(), minimum_delegation)
|
.set_return_data(id(), minimum_delegation)
|
||||||
|
|
|
@ -12,13 +12,12 @@ use {
|
||||||
solana_program_runtime::{ic_msg, invoke_context::InvokeContext},
|
solana_program_runtime::{ic_msg, invoke_context::InvokeContext},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||||
account_utils::{State, StateMut},
|
account_utils::StateMut,
|
||||||
clock::{Clock, Epoch},
|
clock::{Clock, Epoch},
|
||||||
feature_set::{
|
feature_set::{
|
||||||
stake_merge_with_unmatched_credits_observed, stake_split_uses_rent_sysvar, FeatureSet,
|
stake_merge_with_unmatched_credits_observed, stake_split_uses_rent_sysvar, FeatureSet,
|
||||||
},
|
},
|
||||||
instruction::{checked_add, InstructionError},
|
instruction::{checked_add, InstructionError},
|
||||||
keyed_account::KeyedAccount,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
|
rent::{Rent, ACCOUNT_STORAGE_OVERHEAD},
|
||||||
stake::{
|
stake::{
|
||||||
|
@ -27,6 +26,7 @@ use {
|
||||||
program::id,
|
program::id,
|
||||||
},
|
},
|
||||||
stake_history::{StakeHistory, StakeHistoryEntry},
|
stake_history::{StakeHistory, StakeHistoryEntry},
|
||||||
|
transaction_context::{BorrowedAccount, InstructionContext, TransactionContext},
|
||||||
},
|
},
|
||||||
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
||||||
std::{collections::HashSet, convert::TryFrom},
|
std::{collections::HashSet, convert::TryFrom},
|
||||||
|
@ -366,21 +366,21 @@ fn calculate_stake_rewards(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize(
|
pub fn initialize(
|
||||||
stake_account: &KeyedAccount,
|
stake_account: &mut BorrowedAccount,
|
||||||
authorized: &Authorized,
|
authorized: &Authorized,
|
||||||
lockup: &Lockup,
|
lockup: &Lockup,
|
||||||
rent: &Rent,
|
rent: &Rent,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if stake_account.data_len()? != std::mem::size_of::<StakeState>() {
|
if stake_account.get_data().len() != std::mem::size_of::<StakeState>() {
|
||||||
return Err(InstructionError::InvalidAccountData);
|
return Err(InstructionError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
if let StakeState::Uninitialized = stake_account.state()? {
|
if let StakeState::Uninitialized = stake_account.get_state()? {
|
||||||
let rent_exempt_reserve = rent.minimum_balance(stake_account.data_len()?);
|
let rent_exempt_reserve = rent.minimum_balance(stake_account.get_data().len());
|
||||||
let minimum_delegation = crate::get_minimum_delegation(feature_set);
|
let minimum_delegation = crate::get_minimum_delegation(feature_set);
|
||||||
let minimum_balance = rent_exempt_reserve + minimum_delegation;
|
let minimum_balance = rent_exempt_reserve + minimum_delegation;
|
||||||
|
|
||||||
if stake_account.lamports()? >= minimum_balance {
|
if stake_account.get_lamports() >= minimum_balance {
|
||||||
stake_account.set_state(&StakeState::Initialized(Meta {
|
stake_account.set_state(&StakeState::Initialized(Meta {
|
||||||
rent_exempt_reserve,
|
rent_exempt_reserve,
|
||||||
authorized: *authorized,
|
authorized: *authorized,
|
||||||
|
@ -398,7 +398,7 @@ pub fn initialize(
|
||||||
/// multiple times, but will implicitly withdraw authorization from the previously authorized
|
/// multiple times, but will implicitly withdraw authorization from the previously authorized
|
||||||
/// staker. The default staker is the owner of the stake account's pubkey.
|
/// staker. The default staker is the owner of the stake account's pubkey.
|
||||||
pub fn authorize(
|
pub fn authorize(
|
||||||
stake_account: &KeyedAccount,
|
stake_account: &mut BorrowedAccount,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
new_authority: &Pubkey,
|
new_authority: &Pubkey,
|
||||||
stake_authorize: StakeAuthorize,
|
stake_authorize: StakeAuthorize,
|
||||||
|
@ -406,7 +406,7 @@ pub fn authorize(
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
custodian: Option<&Pubkey>,
|
custodian: Option<&Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
match stake_account.state()? {
|
match stake_account.get_state()? {
|
||||||
StakeState::Stake(mut meta, stake) => {
|
StakeState::Stake(mut meta, stake) => {
|
||||||
meta.authorized.authorize(
|
meta.authorized.authorize(
|
||||||
signers,
|
signers,
|
||||||
|
@ -437,9 +437,12 @@ pub fn authorize(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn authorize_with_seed(
|
pub fn authorize_with_seed(
|
||||||
stake_account: &KeyedAccount,
|
transaction_context: &TransactionContext,
|
||||||
authority_base: &KeyedAccount,
|
instruction_context: &InstructionContext,
|
||||||
|
stake_account: &mut BorrowedAccount,
|
||||||
|
authority_base_index: usize,
|
||||||
authority_seed: &str,
|
authority_seed: &str,
|
||||||
authority_owner: &Pubkey,
|
authority_owner: &Pubkey,
|
||||||
new_authority: &Pubkey,
|
new_authority: &Pubkey,
|
||||||
|
@ -449,7 +452,10 @@ pub fn authorize_with_seed(
|
||||||
custodian: Option<&Pubkey>,
|
custodian: Option<&Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let mut signers = HashSet::default();
|
let mut signers = HashSet::default();
|
||||||
if let Some(base_pubkey) = authority_base.signer_key() {
|
if instruction_context.is_signer(authority_base_index)? {
|
||||||
|
let base_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(authority_base_index)?,
|
||||||
|
)?;
|
||||||
signers.insert(Pubkey::create_with_seed(
|
signers.insert(Pubkey::create_with_seed(
|
||||||
base_pubkey,
|
base_pubkey,
|
||||||
authority_seed,
|
authority_seed,
|
||||||
|
@ -468,26 +474,35 @@ pub fn authorize_with_seed(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delegate(
|
pub fn delegate(
|
||||||
stake_account: &KeyedAccount,
|
transaction_context: &TransactionContext,
|
||||||
vote_account: &KeyedAccount,
|
instruction_context: &InstructionContext,
|
||||||
|
stake_account_index: usize,
|
||||||
|
vote_account_index: usize,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if vote_account.owner()? != solana_vote_program::id() {
|
let vote_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, vote_account_index)?;
|
||||||
|
if *vote_account.get_owner() != solana_vote_program::id() {
|
||||||
return Err(InstructionError::IncorrectProgramId);
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
|
let vote_pubkey = *vote_account.get_key();
|
||||||
|
let vote_state = vote_account.get_state::<VoteStateVersions>();
|
||||||
|
drop(vote_account);
|
||||||
|
|
||||||
match stake_account.state()? {
|
let mut stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
|
match stake_account.get_state()? {
|
||||||
StakeState::Initialized(meta) => {
|
StakeState::Initialized(meta) => {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
let ValidatedDelegatedInfo { stake_amount } =
|
let ValidatedDelegatedInfo { stake_amount } =
|
||||||
validate_delegated_amount(stake_account, &meta)?;
|
validate_delegated_amount(&stake_account, &meta)?;
|
||||||
let stake = new_stake(
|
let stake = new_stake(
|
||||||
stake_amount,
|
stake_amount,
|
||||||
vote_account.unsigned_key(),
|
&vote_pubkey,
|
||||||
&State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
|
&vote_state?.convert_to_current(),
|
||||||
clock.epoch,
|
clock.epoch,
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
|
@ -496,12 +511,12 @@ pub fn delegate(
|
||||||
StakeState::Stake(meta, mut stake) => {
|
StakeState::Stake(meta, mut stake) => {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
let ValidatedDelegatedInfo { stake_amount } =
|
let ValidatedDelegatedInfo { stake_amount } =
|
||||||
validate_delegated_amount(stake_account, &meta)?;
|
validate_delegated_amount(&stake_account, &meta)?;
|
||||||
redelegate(
|
redelegate(
|
||||||
&mut stake,
|
&mut stake,
|
||||||
stake_amount,
|
stake_amount,
|
||||||
vote_account.unsigned_key(),
|
&vote_pubkey,
|
||||||
&State::<VoteStateVersions>::state(vote_account)?.convert_to_current(),
|
&vote_state?.convert_to_current(),
|
||||||
clock,
|
clock,
|
||||||
stake_history,
|
stake_history,
|
||||||
config,
|
config,
|
||||||
|
@ -513,11 +528,11 @@ pub fn delegate(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(
|
pub fn deactivate(
|
||||||
stake_account: &KeyedAccount,
|
stake_account: &mut BorrowedAccount,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.get_state()? {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
stake.deactivate(clock.epoch)?;
|
stake.deactivate(clock.epoch)?;
|
||||||
|
|
||||||
|
@ -528,12 +543,12 @@ pub fn deactivate(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_lockup(
|
pub fn set_lockup(
|
||||||
stake_account: &KeyedAccount,
|
stake_account: &mut BorrowedAccount,
|
||||||
lockup: &LockupArgs,
|
lockup: &LockupArgs,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
match stake_account.state()? {
|
match stake_account.get_state()? {
|
||||||
StakeState::Initialized(mut meta) => {
|
StakeState::Initialized(mut meta) => {
|
||||||
meta.set_lockup(lockup, signers, clock)?;
|
meta.set_lockup(lockup, signers, clock)?;
|
||||||
stake_account.set_state(&StakeState::Initialized(meta))
|
stake_account.set_state(&StakeState::Initialized(meta))
|
||||||
|
@ -547,32 +562,43 @@ pub fn set_lockup(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split(
|
pub fn split(
|
||||||
stake_account: &KeyedAccount,
|
|
||||||
invoke_context: &InvokeContext,
|
invoke_context: &InvokeContext,
|
||||||
|
transaction_context: &TransactionContext,
|
||||||
|
instruction_context: &InstructionContext,
|
||||||
|
stake_account_index: usize,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
split: &KeyedAccount,
|
split_index: usize,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if split.owner()? != id() {
|
let split = instruction_context.try_borrow_account(transaction_context, split_index)?;
|
||||||
|
if *split.get_owner() != id() {
|
||||||
return Err(InstructionError::IncorrectProgramId);
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
if split.data_len()? != std::mem::size_of::<StakeState>() {
|
if split.get_data().len() != std::mem::size_of::<StakeState>() {
|
||||||
return Err(InstructionError::InvalidAccountData);
|
return Err(InstructionError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
if !matches!(split.state()?, StakeState::Uninitialized) {
|
if !matches!(split.get_state()?, StakeState::Uninitialized) {
|
||||||
return Err(InstructionError::InvalidAccountData);
|
return Err(InstructionError::InvalidAccountData);
|
||||||
}
|
}
|
||||||
if lamports > stake_account.lamports()? {
|
let split_lamport_balance = split.get_lamports();
|
||||||
|
drop(split);
|
||||||
|
let stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
|
if lamports > stake_account.get_lamports() {
|
||||||
return Err(InstructionError::InsufficientFunds);
|
return Err(InstructionError::InsufficientFunds);
|
||||||
}
|
}
|
||||||
|
let stake_state = stake_account.get_state()?;
|
||||||
|
drop(stake_account);
|
||||||
|
|
||||||
match stake_account.state()? {
|
match stake_state {
|
||||||
StakeState::Stake(meta, mut stake) => {
|
StakeState::Stake(meta, mut stake) => {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
let validated_split_info = validate_split_amount(
|
let validated_split_info = validate_split_amount(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
stake_account,
|
transaction_context,
|
||||||
split,
|
instruction_context,
|
||||||
|
stake_account_index,
|
||||||
|
split_index,
|
||||||
lamports,
|
lamports,
|
||||||
&meta,
|
&meta,
|
||||||
Some(&stake),
|
Some(&stake),
|
||||||
|
@ -605,7 +631,7 @@ pub fn split(
|
||||||
lamports.saturating_sub(
|
lamports.saturating_sub(
|
||||||
validated_split_info
|
validated_split_info
|
||||||
.destination_rent_exempt_reserve
|
.destination_rent_exempt_reserve
|
||||||
.saturating_sub(split.lamports()?),
|
.saturating_sub(split_lamport_balance),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -613,19 +639,37 @@ pub fn split(
|
||||||
let mut split_meta = meta;
|
let mut split_meta = meta;
|
||||||
split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
|
split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
|
||||||
|
|
||||||
|
let mut stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
stake_account.set_state(&StakeState::Stake(meta, stake))?;
|
stake_account.set_state(&StakeState::Stake(meta, stake))?;
|
||||||
|
drop(stake_account);
|
||||||
|
let mut split =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, split_index)?;
|
||||||
split.set_state(&StakeState::Stake(split_meta, split_stake))?;
|
split.set_state(&StakeState::Stake(split_meta, split_stake))?;
|
||||||
}
|
}
|
||||||
StakeState::Initialized(meta) => {
|
StakeState::Initialized(meta) => {
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
||||||
let validated_split_info =
|
let validated_split_info = validate_split_amount(
|
||||||
validate_split_amount(invoke_context, stake_account, split, lamports, &meta, None)?;
|
invoke_context,
|
||||||
|
transaction_context,
|
||||||
|
instruction_context,
|
||||||
|
stake_account_index,
|
||||||
|
split_index,
|
||||||
|
lamports,
|
||||||
|
&meta,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
let mut split_meta = meta;
|
let mut split_meta = meta;
|
||||||
split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
|
split_meta.rent_exempt_reserve = validated_split_info.destination_rent_exempt_reserve;
|
||||||
|
let mut split =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, split_index)?;
|
||||||
split.set_state(&StakeState::Initialized(split_meta))?;
|
split.set_state(&StakeState::Initialized(split_meta))?;
|
||||||
}
|
}
|
||||||
StakeState::Uninitialized => {
|
StakeState::Uninitialized => {
|
||||||
if !signers.contains(stake_account.unsigned_key()) {
|
let stake_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(stake_account_index)?,
|
||||||
|
)?;
|
||||||
|
if !signers.contains(stake_pubkey) {
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -633,54 +677,67 @@ pub fn split(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deinitialize state upon zero balance
|
// Deinitialize state upon zero balance
|
||||||
if lamports == stake_account.lamports()? {
|
let mut stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
|
if lamports == stake_account.get_lamports() {
|
||||||
stake_account.set_state(&StakeState::Uninitialized)?;
|
stake_account.set_state(&StakeState::Uninitialized)?;
|
||||||
}
|
}
|
||||||
|
drop(stake_account);
|
||||||
|
|
||||||
split
|
let mut split = instruction_context.try_borrow_account(transaction_context, split_index)?;
|
||||||
.try_account_ref_mut()?
|
split.checked_add_lamports(lamports)?;
|
||||||
.checked_add_lamports(lamports)?;
|
drop(split);
|
||||||
stake_account
|
let mut stake_account =
|
||||||
.try_account_ref_mut()?
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
.checked_sub_lamports(lamports)?;
|
stake_account.checked_sub_lamports(lamports)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(
|
pub fn merge(
|
||||||
stake_account: &KeyedAccount,
|
|
||||||
invoke_context: &InvokeContext,
|
invoke_context: &InvokeContext,
|
||||||
source_account: &KeyedAccount,
|
transaction_context: &TransactionContext,
|
||||||
|
instruction_context: &InstructionContext,
|
||||||
|
stake_account_index: usize,
|
||||||
|
source_account_index: usize,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
signers: &HashSet<Pubkey>,
|
signers: &HashSet<Pubkey>,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
|
let mut source_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, source_account_index)?;
|
||||||
// Ensure source isn't spoofed
|
// Ensure source isn't spoofed
|
||||||
if source_account.owner()? != id() {
|
if *source_account.get_owner() != id() {
|
||||||
return Err(InstructionError::IncorrectProgramId);
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
}
|
}
|
||||||
// Close the stake_account-reference loophole
|
// Close the stake_account-reference loophole
|
||||||
if source_account.unsigned_key() == stake_account.unsigned_key() {
|
if instruction_context.get_index_in_transaction(stake_account_index)?
|
||||||
|
== instruction_context.get_index_in_transaction(source_account_index)?
|
||||||
|
{
|
||||||
return Err(InstructionError::InvalidArgument);
|
return Err(InstructionError::InvalidArgument);
|
||||||
}
|
}
|
||||||
|
let mut stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
|
|
||||||
ic_msg!(invoke_context, "Checking if destination stake is mergeable");
|
ic_msg!(invoke_context, "Checking if destination stake is mergeable");
|
||||||
let stake_merge_kind = MergeKind::get_if_mergeable(
|
let stake_merge_kind = MergeKind::get_if_mergeable(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
&stake_account.state()?,
|
&stake_account.get_state()?,
|
||||||
stake_account.lamports()?,
|
stake_account.get_lamports(),
|
||||||
clock,
|
clock,
|
||||||
stake_history,
|
stake_history,
|
||||||
)?;
|
)?;
|
||||||
let meta = stake_merge_kind.meta();
|
|
||||||
|
|
||||||
// Authorized staker is allowed to split/merge accounts
|
// Authorized staker is allowed to split/merge accounts
|
||||||
meta.authorized.check(signers, StakeAuthorize::Staker)?;
|
stake_merge_kind
|
||||||
|
.meta()
|
||||||
|
.authorized
|
||||||
|
.check(signers, StakeAuthorize::Staker)?;
|
||||||
|
|
||||||
ic_msg!(invoke_context, "Checking if source stake is mergeable");
|
ic_msg!(invoke_context, "Checking if source stake is mergeable");
|
||||||
let source_merge_kind = MergeKind::get_if_mergeable(
|
let source_merge_kind = MergeKind::get_if_mergeable(
|
||||||
invoke_context,
|
invoke_context,
|
||||||
&source_account.state()?,
|
&source_account.get_state()?,
|
||||||
source_account.lamports()?,
|
source_account.get_lamports(),
|
||||||
clock,
|
clock,
|
||||||
stake_history,
|
stake_history,
|
||||||
)?;
|
)?;
|
||||||
|
@ -694,33 +751,37 @@ pub fn merge(
|
||||||
source_account.set_state(&StakeState::Uninitialized)?;
|
source_account.set_state(&StakeState::Uninitialized)?;
|
||||||
|
|
||||||
// Drain the source stake account
|
// Drain the source stake account
|
||||||
let lamports = source_account.lamports()?;
|
let lamports = source_account.get_lamports();
|
||||||
source_account
|
source_account.checked_sub_lamports(lamports)?;
|
||||||
.try_account_ref_mut()?
|
stake_account.checked_add_lamports(lamports)?;
|
||||||
.checked_sub_lamports(lamports)?;
|
|
||||||
stake_account
|
|
||||||
.try_account_ref_mut()?
|
|
||||||
.checked_add_lamports(lamports)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn withdraw(
|
pub fn withdraw(
|
||||||
stake_account: &KeyedAccount,
|
transaction_context: &TransactionContext,
|
||||||
|
instruction_context: &InstructionContext,
|
||||||
|
stake_account_index: usize,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
to: &KeyedAccount,
|
to_index: usize,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
stake_history: &StakeHistory,
|
stake_history: &StakeHistory,
|
||||||
withdraw_authority: &KeyedAccount,
|
withdraw_authority_index: usize,
|
||||||
custodian: Option<&KeyedAccount>,
|
custodian_index: Option<usize>,
|
||||||
feature_set: &FeatureSet,
|
feature_set: &FeatureSet,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
|
let withdraw_authority_pubkey = transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(withdraw_authority_index)?,
|
||||||
|
)?;
|
||||||
|
if !instruction_context.is_signer(withdraw_authority_index)? {
|
||||||
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
|
}
|
||||||
let mut signers = HashSet::new();
|
let mut signers = HashSet::new();
|
||||||
let withdraw_authority_pubkey = withdraw_authority
|
|
||||||
.signer_key()
|
|
||||||
.ok_or(InstructionError::MissingRequiredSignature)?;
|
|
||||||
signers.insert(*withdraw_authority_pubkey);
|
signers.insert(*withdraw_authority_pubkey);
|
||||||
|
|
||||||
let (lockup, reserve, is_staked) = match stake_account.state()? {
|
let mut stake_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, stake_account_index)?;
|
||||||
|
let (lockup, reserve, is_staked) = match stake_account.get_state()? {
|
||||||
StakeState::Stake(meta, stake) => {
|
StakeState::Stake(meta, stake) => {
|
||||||
meta.authorized
|
meta.authorized
|
||||||
.check(&signers, StakeAuthorize::Withdrawer)?;
|
.check(&signers, StakeAuthorize::Withdrawer)?;
|
||||||
|
@ -749,7 +810,7 @@ pub fn withdraw(
|
||||||
(meta.lockup, reserve, false)
|
(meta.lockup, reserve, false)
|
||||||
}
|
}
|
||||||
StakeState::Uninitialized => {
|
StakeState::Uninitialized => {
|
||||||
if !signers.contains(stake_account.unsigned_key()) {
|
if !signers.contains(stake_account.get_key()) {
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
(Lockup::default(), 0, false) // no lockup, no restrictions
|
(Lockup::default(), 0, false) // no lockup, no restrictions
|
||||||
|
@ -759,7 +820,17 @@ pub fn withdraw(
|
||||||
|
|
||||||
// verify that lockup has expired or that the withdrawal is signed by
|
// verify that lockup has expired or that the withdrawal is signed by
|
||||||
// the custodian, both epoch and unix_timestamp must have passed
|
// the custodian, both epoch and unix_timestamp must have passed
|
||||||
let custodian_pubkey = custodian.and_then(|keyed_account| keyed_account.signer_key());
|
let custodian_pubkey = if let Some(custodian_index) = custodian_index {
|
||||||
|
if instruction_context.is_signer(custodian_index)? {
|
||||||
|
Some(transaction_context.get_key_of_account_at_index(
|
||||||
|
instruction_context.get_index_in_transaction(custodian_index)?,
|
||||||
|
)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
if lockup.is_in_force(clock, custodian_pubkey) {
|
if lockup.is_in_force(clock, custodian_pubkey) {
|
||||||
return Err(StakeError::LockupInForce.into());
|
return Err(StakeError::LockupInForce.into());
|
||||||
}
|
}
|
||||||
|
@ -767,27 +838,27 @@ pub fn withdraw(
|
||||||
let lamports_and_reserve = checked_add(lamports, reserve)?;
|
let lamports_and_reserve = checked_add(lamports, reserve)?;
|
||||||
// if the stake is active, we mustn't allow the account to go away
|
// if the stake is active, we mustn't allow the account to go away
|
||||||
if is_staked // line coverage for branch coverage
|
if is_staked // line coverage for branch coverage
|
||||||
&& lamports_and_reserve > stake_account.lamports()?
|
&& lamports_and_reserve > stake_account.get_lamports()
|
||||||
{
|
{
|
||||||
return Err(InstructionError::InsufficientFunds);
|
return Err(InstructionError::InsufficientFunds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if lamports != stake_account.lamports()? // not a full withdrawal
|
if lamports != stake_account.get_lamports() // not a full withdrawal
|
||||||
&& lamports_and_reserve > stake_account.lamports()?
|
&& lamports_and_reserve > stake_account.get_lamports()
|
||||||
{
|
{
|
||||||
assert!(!is_staked);
|
assert!(!is_staked);
|
||||||
return Err(InstructionError::InsufficientFunds);
|
return Err(InstructionError::InsufficientFunds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deinitialize state upon zero balance
|
// Deinitialize state upon zero balance
|
||||||
if lamports == stake_account.lamports()? {
|
if lamports == stake_account.get_lamports() {
|
||||||
stake_account.set_state(&StakeState::Uninitialized)?;
|
stake_account.set_state(&StakeState::Uninitialized)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
stake_account
|
stake_account.checked_sub_lamports(lamports)?;
|
||||||
.try_account_ref_mut()?
|
drop(stake_account);
|
||||||
.checked_sub_lamports(lamports)?;
|
let mut to = instruction_context.try_borrow_account(transaction_context, to_index)?;
|
||||||
to.try_account_ref_mut()?.checked_add_lamports(lamports)?;
|
to.checked_add_lamports(lamports)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -800,10 +871,12 @@ struct ValidatedDelegatedInfo {
|
||||||
/// Ensure the stake delegation amount is valid. This checks that the account meets the minimum
|
/// Ensure the stake delegation amount is valid. This checks that the account meets the minimum
|
||||||
/// balance requirements of delegated stake. If not, return an error.
|
/// balance requirements of delegated stake. If not, return an error.
|
||||||
fn validate_delegated_amount(
|
fn validate_delegated_amount(
|
||||||
account: &KeyedAccount,
|
account: &BorrowedAccount,
|
||||||
meta: &Meta,
|
meta: &Meta,
|
||||||
) -> Result<ValidatedDelegatedInfo, InstructionError> {
|
) -> Result<ValidatedDelegatedInfo, InstructionError> {
|
||||||
let stake_amount = account.lamports()?.saturating_sub(meta.rent_exempt_reserve); // can't stake the rent
|
let stake_amount = account
|
||||||
|
.get_lamports()
|
||||||
|
.saturating_sub(meta.rent_exempt_reserve); // can't stake the rent
|
||||||
Ok(ValidatedDelegatedInfo { stake_amount })
|
Ok(ValidatedDelegatedInfo { stake_amount })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,14 +894,24 @@ struct ValidatedSplitInfo {
|
||||||
/// not, return an error.
|
/// not, return an error.
|
||||||
fn validate_split_amount(
|
fn validate_split_amount(
|
||||||
invoke_context: &InvokeContext,
|
invoke_context: &InvokeContext,
|
||||||
source_account: &KeyedAccount,
|
transaction_context: &TransactionContext,
|
||||||
destination_account: &KeyedAccount,
|
instruction_context: &InstructionContext,
|
||||||
|
source_account_index: usize,
|
||||||
|
destination_account_index: usize,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
source_meta: &Meta,
|
source_meta: &Meta,
|
||||||
source_stake: Option<&Stake>,
|
source_stake: Option<&Stake>,
|
||||||
) -> Result<ValidatedSplitInfo, InstructionError> {
|
) -> Result<ValidatedSplitInfo, InstructionError> {
|
||||||
let source_lamports = source_account.lamports()?;
|
let source_account =
|
||||||
let destination_lamports = destination_account.lamports()?;
|
instruction_context.try_borrow_account(transaction_context, source_account_index)?;
|
||||||
|
let source_lamports = source_account.get_lamports();
|
||||||
|
let source_data_len = source_account.get_data().len();
|
||||||
|
drop(source_account);
|
||||||
|
let destination_account =
|
||||||
|
instruction_context.try_borrow_account(transaction_context, destination_account_index)?;
|
||||||
|
let destination_lamports = destination_account.get_lamports();
|
||||||
|
let destination_data_len = destination_account.get_data().len();
|
||||||
|
drop(destination_account);
|
||||||
|
|
||||||
// Split amount has to be something
|
// Split amount has to be something
|
||||||
if lamports == 0 {
|
if lamports == 0 {
|
||||||
|
@ -869,12 +952,12 @@ fn validate_split_amount(
|
||||||
.is_active(&stake_split_uses_rent_sysvar::ID)
|
.is_active(&stake_split_uses_rent_sysvar::ID)
|
||||||
{
|
{
|
||||||
let rent = invoke_context.get_sysvar_cache().get_rent()?;
|
let rent = invoke_context.get_sysvar_cache().get_rent()?;
|
||||||
rent.minimum_balance(destination_account.data_len()?)
|
rent.minimum_balance(destination_data_len)
|
||||||
} else {
|
} else {
|
||||||
calculate_split_rent_exempt_reserve(
|
calculate_split_rent_exempt_reserve(
|
||||||
source_meta.rent_exempt_reserve,
|
source_meta.rent_exempt_reserve,
|
||||||
source_account.data_len()? as u64,
|
source_data_len as u64,
|
||||||
destination_account.data_len()? as u64,
|
destination_data_len as u64,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let destination_minimum_balance =
|
let destination_minimum_balance =
|
||||||
|
|
Loading…
Reference in New Issue