Add vote/stake checked instructions (backport #18345) (#18457)

* Add vote/stake checked instructions

(cherry picked from commit ee219ffa47)

* Fix set-lockup custodian index

(cherry picked from commit 544f62c92f)

* Add parsing for new stake instructions; clean up confusing test args

(cherry picked from commit 9b302ac0b5)

* Add parsing for new vote instructions

(cherry picked from commit 39bac256ab)

* Add VoteInstruction::AuthorizeChecked test

(cherry picked from commit b8ca2250fd)

* Add Stake checked tests

(cherry picked from commit 74e89a3e3e)

Co-authored-by: Michael Vines <mvines@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
This commit is contained in:
mergify[bot]
2021-07-07 03:17:21 +00:00
committed by GitHub
parent cda681a2f0
commit 0d0478c4a4
6 changed files with 1072 additions and 31 deletions

View File

@@ -167,6 +167,61 @@ pub enum StakeInstruction {
/// 3. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
/// lockup expiration
AuthorizeWithSeed(AuthorizeWithSeedArgs),
/// Initialize a stake with authorization information
///
/// This instruction is similar to `Initialize` except that the withdraw authority
/// must be a signer, and no lockup is applied to the account.
///
/// # Account references
/// 0. [WRITE] Uninitialized stake account
/// 1. [] Rent sysvar
/// 2. [] The stake authority
/// 3. [SIGNER] The withdraw authority
///
InitializeChecked,
/// Authorize a key to manage stake or withdrawal
///
/// This instruction behaves like `Authorize` with the additional requirement that the new
/// stake or withdraw authority must also be a signer.
///
/// # Account references
/// 0. [WRITE] Stake account to be updated
/// 1. [] Clock sysvar
/// 2. [SIGNER] The stake or withdraw authority
/// 3. [SIGNER] The new stake or withdraw authority
/// 4. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
/// lockup expiration
AuthorizeChecked(StakeAuthorize),
/// Authorize a key to manage stake or withdrawal with a derived key
///
/// This instruction behaves like `AuthorizeWithSeed` with the additional requirement that
/// the new stake or withdraw authority must also be a signer.
///
/// # Account references
/// 0. [WRITE] Stake account to be updated
/// 1. [SIGNER] Base key of stake or withdraw authority
/// 2. [] Clock sysvar
/// 3. [SIGNER] The new stake or withdraw authority
/// 4. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
/// lockup expiration
AuthorizeCheckedWithSeed(AuthorizeCheckedWithSeedArgs),
/// Set stake lockup
///
/// This instruction behaves like `SetLockup` with the additional requirement that
/// the new lockup authority also be a signer.
///
/// If a lockup is not active, the withdraw authority may set a new lockup
/// If a lockup is active, the lockup custodian may update the lockup parameters
///
/// # Account references
/// 0. [WRITE] Initialized stake account
/// 1. [SIGNER] Lockup authority or withdraw authority
/// 2. Optional: [SIGNER] New lockup authority
SetLockupChecked(LockupCheckedArgs),
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
@@ -176,6 +231,12 @@ pub struct LockupArgs {
pub custodian: Option<Pubkey>,
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct LockupCheckedArgs {
pub unix_timestamp: Option<UnixTimestamp>,
pub epoch: Option<Epoch>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct AuthorizeWithSeedArgs {
pub new_authorized_pubkey: Pubkey,
@@ -184,6 +245,13 @@ pub struct AuthorizeWithSeedArgs {
pub authority_owner: Pubkey,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct AuthorizeCheckedWithSeedArgs {
pub stake_authorize: StakeAuthorize,
pub authority_seed: String,
pub authority_owner: Pubkey,
}
pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
Instruction::new_with_bincode(
id(),
@@ -195,6 +263,19 @@ pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Locku
)
}
pub fn initialize_checked(stake_pubkey: &Pubkey, authorized: &Authorized) -> Instruction {
Instruction::new_with_bincode(
id(),
&StakeInstruction::InitializeChecked,
vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(authorized.staker, false),
AccountMeta::new_readonly(authorized.withdrawer, true),
],
)
}
pub fn create_account_with_seed(
from_pubkey: &Pubkey,
stake_pubkey: &Pubkey,
@@ -237,6 +318,46 @@ pub fn create_account(
]
}
pub fn create_account_with_seed_checked(
from_pubkey: &Pubkey,
stake_pubkey: &Pubkey,
base: &Pubkey,
seed: &str,
authorized: &Authorized,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account_with_seed(
from_pubkey,
stake_pubkey,
base,
seed,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
),
initialize_checked(stake_pubkey, authorized),
]
}
pub fn create_account_checked(
from_pubkey: &Pubkey,
stake_pubkey: &Pubkey,
authorized: &Authorized,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_pubkey,
stake_pubkey,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
),
initialize_checked(stake_pubkey, authorized),
]
}
fn _split(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
@@ -383,6 +504,31 @@ pub fn authorize(
)
}
pub fn authorize_checked(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
new_authorized_pubkey: &Pubkey,
stake_authorize: StakeAuthorize,
custodian_pubkey: Option<&Pubkey>,
) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(*authorized_pubkey, true),
AccountMeta::new_readonly(*new_authorized_pubkey, true),
];
if let Some(custodian_pubkey) = custodian_pubkey {
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
}
Instruction::new_with_bincode(
id(),
&StakeInstruction::AuthorizeChecked(stake_authorize),
account_metas,
)
}
pub fn authorize_with_seed(
stake_pubkey: &Pubkey,
authority_base: &Pubkey,
@@ -416,6 +562,39 @@ pub fn authorize_with_seed(
)
}
pub fn authorize_checked_with_seed(
stake_pubkey: &Pubkey,
authority_base: &Pubkey,
authority_seed: String,
authority_owner: &Pubkey,
new_authorized_pubkey: &Pubkey,
stake_authorize: StakeAuthorize,
custodian_pubkey: Option<&Pubkey>,
) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(*authority_base, true),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(*new_authorized_pubkey, true),
];
if let Some(custodian_pubkey) = custodian_pubkey {
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
}
let args = AuthorizeCheckedWithSeedArgs {
stake_authorize,
authority_seed,
authority_owner: *authority_owner,
};
Instruction::new_with_bincode(
id(),
&StakeInstruction::AuthorizeCheckedWithSeed(args),
account_metas,
)
}
pub fn delegate_stake(
stake_pubkey: &Pubkey,
authorized_pubkey: &Pubkey,
@@ -475,6 +654,30 @@ pub fn set_lockup(
Instruction::new_with_bincode(id(), &StakeInstruction::SetLockup(*lockup), account_metas)
}
pub fn set_lockup_checked(
stake_pubkey: &Pubkey,
lockup: &LockupArgs,
custodian_pubkey: &Pubkey,
) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new_readonly(*custodian_pubkey, true),
];
let lockup_checked = LockupCheckedArgs {
unix_timestamp: lockup.unix_timestamp,
epoch: lockup.epoch,
};
if let Some(new_custodian) = lockup.custodian {
account_metas.push(AccountMeta::new_readonly(new_custodian, true));
}
Instruction::new_with_bincode(
id(),
&StakeInstruction::SetLockupChecked(lockup_checked),
account_metas,
)
}
#[cfg(test)]
mod tests {
use super::*;