Refactor stake program into solana_program (#17906)
* Move stake state / instructions into solana_program * Update account-decoder * Update cli and runtime * Update all other parts * Commit Cargo.lock changes in programs/bpf * Update cli stake instruction import * Allow integer arithmetic * Update ABI digest * Bump rust mem instruction count * Remove useless structs * Move stake::id() -> stake::program::id() * Re-export from solana_sdk and mark deprecated * Address feedback * Run cargo fmt
This commit is contained in:
3
programs/bpf/Cargo.lock
generated
3
programs/bpf/Cargo.lock
generated
@ -2730,7 +2730,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
"solana-config-program",
|
||||
"solana-sdk",
|
||||
"solana-stake-program",
|
||||
"solana-vote-program",
|
||||
"spl-token",
|
||||
"thiserror",
|
||||
@ -3143,7 +3142,6 @@ dependencies = [
|
||||
"solana-clap-utils",
|
||||
"solana-client",
|
||||
"solana-sdk",
|
||||
"solana-stake-program",
|
||||
"solana-transaction-status",
|
||||
"solana-vote-program",
|
||||
"spl-memo",
|
||||
@ -3643,7 +3641,6 @@ dependencies = [
|
||||
"solana-account-decoder",
|
||||
"solana-runtime",
|
||||
"solana-sdk",
|
||||
"solana-stake-program",
|
||||
"solana-vote-program",
|
||||
"spl-associated-token-account",
|
||||
"spl-memo",
|
||||
|
@ -1297,7 +1297,7 @@ fn assert_instruction_count() {
|
||||
("solana_bpf_rust_external_spend", 504),
|
||||
("solana_bpf_rust_iter", 724),
|
||||
("solana_bpf_rust_many_args", 233),
|
||||
("solana_bpf_rust_mem", 3096),
|
||||
("solana_bpf_rust_mem", 3117),
|
||||
("solana_bpf_rust_membuiltins", 4065),
|
||||
("solana_bpf_rust_noop", 478),
|
||||
("solana_bpf_rust_param_passing", 46),
|
||||
|
@ -9,6 +9,7 @@ use solana_sdk::{
|
||||
account::{Account, AccountSharedData},
|
||||
pubkey::Pubkey,
|
||||
short_vec,
|
||||
stake::config::Config as StakeConfig,
|
||||
};
|
||||
|
||||
solana_sdk::declare_id!("Config1111111111111111111111111111111111111");
|
||||
@ -18,6 +19,13 @@ pub trait ConfigState: serde::Serialize + Default {
|
||||
fn max_space() -> u64;
|
||||
}
|
||||
|
||||
// TODO move ConfigState into `solana_program` to implement trait locally
|
||||
impl ConfigState for StakeConfig {
|
||||
fn max_space() -> u64 {
|
||||
serialized_size(&StakeConfig::default()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of keys to be stored in Config account data.
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct ConfigKeys {
|
||||
|
@ -1,52 +1,36 @@
|
||||
//! config for staking
|
||||
//! carries variables that the stake program cares about
|
||||
use bincode::{deserialize, serialized_size};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use solana_config_program::{create_config_account, get_config_data, ConfigState};
|
||||
use bincode::deserialize;
|
||||
use solana_config_program::{create_config_account, get_config_data};
|
||||
use solana_sdk::{
|
||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||
genesis_config::GenesisConfig,
|
||||
instruction::InstructionError,
|
||||
keyed_account::KeyedAccount,
|
||||
stake::config::{self, Config},
|
||||
};
|
||||
|
||||
// stake config ID
|
||||
solana_sdk::declare_id!("StakeConfig11111111111111111111111111111111");
|
||||
#[deprecated(
|
||||
since = "1.8.0",
|
||||
note = "Please use `solana_sdk::stake::config` or `solana_program::stake::config` instead"
|
||||
)]
|
||||
pub use solana_sdk::stake::config::*;
|
||||
|
||||
// means that no more than RATE of current effective stake may be added or subtracted per
|
||||
// epoch
|
||||
pub const DEFAULT_WARMUP_COOLDOWN_RATE: f64 = 0.25;
|
||||
pub const DEFAULT_SLASH_PENALTY: u8 = ((5 * std::u8::MAX as usize) / 100) as u8;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||
pub struct Config {
|
||||
/// how much stake we can activate/deactivate per-epoch as a fraction of currently effective stake
|
||||
pub warmup_cooldown_rate: f64,
|
||||
/// percentage of stake lost when slash, expressed as a portion of std::u8::MAX
|
||||
pub slash_penalty: u8,
|
||||
pub fn from<T: ReadableAccount>(account: &T) -> Option<Config> {
|
||||
get_config_data(&account.data())
|
||||
.ok()
|
||||
.and_then(|data| deserialize(data).ok())
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from<T: ReadableAccount>(account: &T) -> Option<Self> {
|
||||
get_config_data(&account.data())
|
||||
.ok()
|
||||
.and_then(|data| deserialize(data).ok())
|
||||
pub fn from_keyed_account(account: &KeyedAccount) -> Result<Config, InstructionError> {
|
||||
if !config::check_id(account.unsigned_key()) {
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
from(&*account.try_account_ref()?).ok_or(InstructionError::InvalidArgument)
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
warmup_cooldown_rate: DEFAULT_WARMUP_COOLDOWN_RATE,
|
||||
slash_penalty: DEFAULT_SLASH_PENALTY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigState for Config {
|
||||
fn max_space() -> u64 {
|
||||
serialized_size(&Config::default()).unwrap()
|
||||
}
|
||||
pub fn create_account(lamports: u64, config: &Config) -> AccountSharedData {
|
||||
create_config_account(vec![], config, lamports)
|
||||
}
|
||||
|
||||
pub fn add_genesis_account(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
@ -55,22 +39,11 @@ pub fn add_genesis_account(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
|
||||
account.set_lamports(lamports.max(1));
|
||||
|
||||
genesis_config.add_account(id(), account);
|
||||
genesis_config.add_account(config::id(), account);
|
||||
|
||||
lamports
|
||||
}
|
||||
|
||||
pub fn create_account(lamports: u64, config: &Config) -> AccountSharedData {
|
||||
create_config_account(vec![], config, lamports)
|
||||
}
|
||||
|
||||
pub fn from_keyed_account(account: &KeyedAccount) -> Result<Config, InstructionError> {
|
||||
if !check_id(account.unsigned_key()) {
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
Config::from(&*account.try_account_ref()?).ok_or(InstructionError::InvalidArgument)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -80,7 +53,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test() {
|
||||
let account = RefCell::new(create_account(0, &Config::default()));
|
||||
assert_eq!(Config::from(&account.borrow()), Some(Config::default()));
|
||||
assert_eq!(from(&account.borrow()), Some(Config::default()));
|
||||
assert_eq!(
|
||||
from_keyed_account(&KeyedAccount::new(&Pubkey::default(), false, &account)),
|
||||
Err(InstructionError::InvalidArgument)
|
||||
|
@ -2,15 +2,16 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
use solana_sdk::genesis_config::GenesisConfig;
|
||||
|
||||
#[deprecated(
|
||||
since = "1.8.0",
|
||||
note = "Please use `solana_sdk::stake::program::id` or `solana_program::stake::program::id` instead"
|
||||
)]
|
||||
pub use solana_sdk::stake::program::{check_id, id};
|
||||
|
||||
pub mod config;
|
||||
pub mod stake_instruction;
|
||||
pub mod stake_state;
|
||||
|
||||
solana_sdk::declare_id!("Stake11111111111111111111111111111111111111");
|
||||
|
||||
pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 {
|
||||
config::add_genesis_account(genesis_config)
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_frozen_abi_macro;
|
||||
|
@ -1,481 +1,23 @@
|
||||
use crate::{
|
||||
config, id,
|
||||
stake_state::{Authorized, Lockup, StakeAccount, StakeAuthorize, StakeState},
|
||||
use {
|
||||
crate::{config, stake_state::StakeAccount},
|
||||
log::*,
|
||||
solana_sdk::{
|
||||
feature_set,
|
||||
instruction::InstructionError,
|
||||
keyed_account::{from_keyed_account, get_signers, keyed_account_at_index},
|
||||
process_instruction::{get_sysvar, InvokeContext},
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
stake::{instruction::StakeInstruction, program::id},
|
||||
sysvar::{self, clock::Clock, rent::Rent, stake_history::StakeHistory},
|
||||
},
|
||||
};
|
||||
use log::*;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, UnixTimestamp},
|
||||
decode_error::DecodeError,
|
||||
feature_set,
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
keyed_account::{from_keyed_account, get_signers, keyed_account_at_index},
|
||||
process_instruction::{get_sysvar, InvokeContext},
|
||||
program_utils::limited_deserialize,
|
||||
pubkey::Pubkey,
|
||||
system_instruction,
|
||||
sysvar::{self, clock::Clock, rent::Rent, stake_history::StakeHistory},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Reasons the stake might have had an error
|
||||
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
|
||||
pub enum StakeError {
|
||||
#[error("not enough credits to redeem")]
|
||||
NoCreditsToRedeem,
|
||||
|
||||
#[error("lockup has not yet expired")]
|
||||
LockupInForce,
|
||||
|
||||
#[error("stake already deactivated")]
|
||||
AlreadyDeactivated,
|
||||
|
||||
#[error("one re-delegation permitted per epoch")]
|
||||
TooSoonToRedelegate,
|
||||
|
||||
#[error("split amount is more than is staked")]
|
||||
InsufficientStake,
|
||||
|
||||
#[error("stake account with transient stake cannot be merged")]
|
||||
MergeTransientStake,
|
||||
|
||||
#[error("stake account merge failed due to different authority, lockups or state")]
|
||||
MergeMismatch,
|
||||
|
||||
#[error("custodian address not present")]
|
||||
CustodianMissing,
|
||||
|
||||
#[error("custodian signature not present")]
|
||||
CustodianSignatureMissing,
|
||||
}
|
||||
|
||||
impl<E> DecodeError<E> for StakeError {
|
||||
fn type_of() -> &'static str {
|
||||
"StakeError"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
pub enum StakeInstruction {
|
||||
/// Initialize a stake with lockup and authorization information
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Uninitialized stake account
|
||||
/// 1. [] Rent sysvar
|
||||
///
|
||||
/// Authorized carries pubkeys that must sign staker transactions
|
||||
/// and withdrawer transactions.
|
||||
/// Lockup carries information about withdrawal restrictions
|
||||
Initialize(Authorized, Lockup),
|
||||
|
||||
/// Authorize a key to manage stake or withdrawal
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Stake account to be updated
|
||||
/// 1. [] Clock sysvar
|
||||
/// 2. [SIGNER] The stake or withdraw authority
|
||||
/// 3. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
|
||||
/// lockup expiration
|
||||
Authorize(Pubkey, StakeAuthorize),
|
||||
|
||||
/// Delegate a stake to a particular vote account
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Initialized stake account to be delegated
|
||||
/// 1. [] Vote account to which this stake will be delegated
|
||||
/// 2. [] Clock sysvar
|
||||
/// 3. [] Stake history sysvar that carries stake warmup/cooldown history
|
||||
/// 4. [] Address of config account that carries stake config
|
||||
/// 5. [SIGNER] Stake authority
|
||||
///
|
||||
/// The entire balance of the staking account is staked. DelegateStake
|
||||
/// can be called multiple times, but re-delegation is delayed
|
||||
/// by one epoch
|
||||
DelegateStake,
|
||||
|
||||
/// Split u64 tokens and stake off a stake account into another stake account.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Stake account to be split; must be in the Initialized or Stake state
|
||||
/// 1. [WRITE] Uninitialized stake account that will take the split-off amount
|
||||
/// 2. [SIGNER] Stake authority
|
||||
Split(u64),
|
||||
|
||||
/// Withdraw unstaked lamports from the stake account
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Stake account from which to withdraw
|
||||
/// 1. [WRITE] Recipient account
|
||||
/// 2. [] Clock sysvar
|
||||
/// 3. [] Stake history sysvar that carries stake warmup/cooldown history
|
||||
/// 4. [SIGNER] Withdraw authority
|
||||
/// 5. Optional: [SIGNER] Lockup authority, if before lockup expiration
|
||||
///
|
||||
/// The u64 is the portion of the stake account balance to be withdrawn,
|
||||
/// must be `<= StakeAccount.lamports - staked_lamports`.
|
||||
Withdraw(u64),
|
||||
|
||||
/// Deactivates the stake in the account
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Delegated stake account
|
||||
/// 1. [] Clock sysvar
|
||||
/// 2. [SIGNER] Stake authority
|
||||
Deactivate,
|
||||
|
||||
/// Set stake lockup
|
||||
///
|
||||
/// 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
|
||||
SetLockup(LockupArgs),
|
||||
|
||||
/// Merge two stake accounts.
|
||||
///
|
||||
/// Both accounts must have identical lockup and authority keys. A merge
|
||||
/// is possible between two stakes in the following states with no additional
|
||||
/// conditions:
|
||||
///
|
||||
/// * two deactivated stakes
|
||||
/// * an inactive stake into an activating stake during its activation epoch
|
||||
///
|
||||
/// For the following cases, the voter pubkey and vote credits observed must match:
|
||||
///
|
||||
/// * two activated stakes
|
||||
/// * two activating accounts that share an activation epoch, during the activation epoch
|
||||
///
|
||||
/// All other combinations of stake states will fail to merge, including all
|
||||
/// "transient" states, where a stake is activating or deactivating with a
|
||||
/// non-zero effective stake.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Destination stake account for the merge
|
||||
/// 1. [WRITE] Source stake account for to merge. This account will be drained
|
||||
/// 2. [] Clock sysvar
|
||||
/// 3. [] Stake history sysvar that carries stake warmup/cooldown history
|
||||
/// 4. [SIGNER] Stake authority
|
||||
Merge,
|
||||
|
||||
/// Authorize a key to manage stake or withdrawal with a derived key
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [WRITE] Stake account to be updated
|
||||
/// 1. [SIGNER] Base key of stake or withdraw authority
|
||||
/// 2. [] Clock sysvar
|
||||
/// 3. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
|
||||
/// lockup expiration
|
||||
AuthorizeWithSeed(AuthorizeWithSeedArgs),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct LockupArgs {
|
||||
pub unix_timestamp: Option<UnixTimestamp>,
|
||||
pub epoch: Option<Epoch>,
|
||||
pub custodian: Option<Pubkey>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub struct AuthorizeWithSeedArgs {
|
||||
pub new_authorized_pubkey: Pubkey,
|
||||
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(),
|
||||
&StakeInstruction::Initialize(*authorized, *lockup),
|
||||
vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_account_with_seed(
|
||||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
base: &Pubkey,
|
||||
seed: &str,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
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(stake_pubkey, authorized, lockup),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn create_account(
|
||||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
from_pubkey,
|
||||
stake_pubkey,
|
||||
lamports,
|
||||
std::mem::size_of::<StakeState>() as u64,
|
||||
&id(),
|
||||
),
|
||||
initialize(stake_pubkey, authorized, lockup),
|
||||
]
|
||||
}
|
||||
|
||||
fn _split(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
split_stake_pubkey: &Pubkey,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new(*split_stake_pubkey, false),
|
||||
AccountMeta::new_readonly(*authorized_pubkey, true),
|
||||
];
|
||||
|
||||
Instruction::new_with_bincode(id(), &StakeInstruction::Split(lamports), account_metas)
|
||||
}
|
||||
|
||||
pub fn split(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
split_stake_pubkey: &Pubkey,
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::allocate(split_stake_pubkey, std::mem::size_of::<StakeState>() as u64),
|
||||
system_instruction::assign(split_stake_pubkey, &id()),
|
||||
_split(
|
||||
stake_pubkey,
|
||||
authorized_pubkey,
|
||||
lamports,
|
||||
split_stake_pubkey,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn split_with_seed(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
split_stake_pubkey: &Pubkey, // derived using create_with_seed()
|
||||
base: &Pubkey, // base
|
||||
seed: &str, // seed
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::allocate_with_seed(
|
||||
split_stake_pubkey,
|
||||
base,
|
||||
seed,
|
||||
std::mem::size_of::<StakeState>() as u64,
|
||||
&id(),
|
||||
),
|
||||
_split(
|
||||
stake_pubkey,
|
||||
authorized_pubkey,
|
||||
lamports,
|
||||
split_stake_pubkey,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn merge(
|
||||
destination_stake_pubkey: &Pubkey,
|
||||
source_stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
) -> Vec<Instruction> {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*destination_stake_pubkey, false),
|
||||
AccountMeta::new(*source_stake_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
AccountMeta::new_readonly(*authorized_pubkey, true),
|
||||
];
|
||||
|
||||
vec![Instruction::new_with_bincode(
|
||||
id(),
|
||||
&StakeInstruction::Merge,
|
||||
account_metas,
|
||||
)]
|
||||
}
|
||||
|
||||
pub fn create_account_and_delegate_stake(
|
||||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
vote_pubkey: &Pubkey,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lockup, lamports);
|
||||
instructions.push(delegate_stake(
|
||||
stake_pubkey,
|
||||
&authorized.staker,
|
||||
vote_pubkey,
|
||||
));
|
||||
instructions
|
||||
}
|
||||
|
||||
pub fn create_account_with_seed_and_delegate_stake(
|
||||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
base: &Pubkey,
|
||||
seed: &str,
|
||||
vote_pubkey: &Pubkey,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
let mut instructions = create_account_with_seed(
|
||||
from_pubkey,
|
||||
stake_pubkey,
|
||||
base,
|
||||
seed,
|
||||
authorized,
|
||||
lockup,
|
||||
lamports,
|
||||
);
|
||||
instructions.push(delegate_stake(
|
||||
stake_pubkey,
|
||||
&authorized.staker,
|
||||
vote_pubkey,
|
||||
));
|
||||
instructions
|
||||
}
|
||||
|
||||
pub fn authorize(
|
||||
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),
|
||||
];
|
||||
|
||||
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
|
||||
}
|
||||
|
||||
Instruction::new_with_bincode(
|
||||
id(),
|
||||
&StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
|
||||
account_metas,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn authorize_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),
|
||||
];
|
||||
|
||||
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
|
||||
}
|
||||
|
||||
let args = AuthorizeWithSeedArgs {
|
||||
new_authorized_pubkey: *new_authorized_pubkey,
|
||||
stake_authorize,
|
||||
authority_seed,
|
||||
authority_owner: *authority_owner,
|
||||
};
|
||||
|
||||
Instruction::new_with_bincode(
|
||||
id(),
|
||||
&StakeInstruction::AuthorizeWithSeed(args),
|
||||
account_metas,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn delegate_stake(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
vote_pubkey: &Pubkey,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new_readonly(*vote_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
AccountMeta::new_readonly(crate::config::id(), false),
|
||||
AccountMeta::new_readonly(*authorized_pubkey, true),
|
||||
];
|
||||
Instruction::new_with_bincode(id(), &StakeInstruction::DelegateStake, account_metas)
|
||||
}
|
||||
|
||||
pub fn withdraw(
|
||||
stake_pubkey: &Pubkey,
|
||||
withdrawer_pubkey: &Pubkey,
|
||||
to_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
custodian_pubkey: Option<&Pubkey>,
|
||||
) -> Instruction {
|
||||
let mut account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new(*to_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
AccountMeta::new_readonly(*withdrawer_pubkey, true),
|
||||
];
|
||||
|
||||
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
|
||||
}
|
||||
|
||||
Instruction::new_with_bincode(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
||||
}
|
||||
|
||||
pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(*authorized_pubkey, true),
|
||||
];
|
||||
Instruction::new_with_bincode(id(), &StakeInstruction::Deactivate, account_metas)
|
||||
}
|
||||
|
||||
pub fn set_lockup(
|
||||
stake_pubkey: &Pubkey,
|
||||
lockup: &LockupArgs,
|
||||
custodian_pubkey: &Pubkey,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new_readonly(*custodian_pubkey, true),
|
||||
];
|
||||
Instruction::new_with_bincode(id(), &StakeInstruction::SetLockup(*lockup), account_metas)
|
||||
}
|
||||
#[deprecated(
|
||||
since = "1.8.0",
|
||||
note = "Please use `solana_sdk::stake::instruction` or `solana_program::stake::instruction` instead"
|
||||
)]
|
||||
pub use solana_sdk::stake::instruction::*;
|
||||
|
||||
pub fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
@ -637,9 +179,15 @@ mod tests {
|
||||
use bincode::serialize;
|
||||
use solana_sdk::{
|
||||
account::{self, Account, AccountSharedData, WritableAccount},
|
||||
instruction::Instruction,
|
||||
keyed_account::KeyedAccount,
|
||||
process_instruction::{mock_set_sysvar, MockInvokeContext},
|
||||
rent::Rent,
|
||||
stake::{
|
||||
config as stake_config,
|
||||
instruction::{self, LockupArgs},
|
||||
state::{Authorized, Lockup, StakeAuthorize},
|
||||
},
|
||||
sysvar::stake_history::StakeHistory,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
@ -685,8 +233,8 @@ mod tests {
|
||||
))
|
||||
} else if sysvar::stake_history::check_id(&meta.pubkey) {
|
||||
account::create_account_shared_data_for_test(&StakeHistory::default())
|
||||
} else if config::check_id(&meta.pubkey) {
|
||||
config::create_account(0, &config::Config::default())
|
||||
} else if stake_config::check_id(&meta.pubkey) {
|
||||
config::create_account(0, &stake_config::Config::default())
|
||||
} else if sysvar::rent::check_id(&meta.pubkey) {
|
||||
account::create_account_shared_data_for_test(&Rent::default())
|
||||
} else if meta.pubkey == invalid_stake_state_pubkey() {
|
||||
@ -735,7 +283,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_stake_process_instruction() {
|
||||
assert_eq!(
|
||||
process_instruction(&initialize(
|
||||
process_instruction(&instruction::initialize(
|
||||
&Pubkey::default(),
|
||||
&Authorized::default(),
|
||||
&Lockup::default()
|
||||
@ -743,7 +291,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&authorize(
|
||||
process_instruction(&instruction::authorize(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
@ -754,7 +302,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&split(
|
||||
&instruction::split(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
100,
|
||||
@ -765,7 +313,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&merge(
|
||||
&instruction::merge(
|
||||
&Pubkey::default(),
|
||||
&invalid_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
@ -775,7 +323,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&split_with_seed(
|
||||
&instruction::split_with_seed(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
100,
|
||||
@ -787,7 +335,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&delegate_stake(
|
||||
process_instruction(&instruction::delegate_stake(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
&invalid_vote_state_pubkey(),
|
||||
@ -795,7 +343,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&withdraw(
|
||||
process_instruction(&instruction::withdraw(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
@ -805,11 +353,14 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&deactivate_stake(&Pubkey::default(), &Pubkey::default())),
|
||||
process_instruction(&instruction::deactivate_stake(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default()
|
||||
)),
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&set_lockup(
|
||||
process_instruction(&instruction::set_lockup(
|
||||
&Pubkey::default(),
|
||||
&LockupArgs::default(),
|
||||
&Pubkey::default()
|
||||
@ -821,7 +372,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_spoofed_stake_accounts() {
|
||||
assert_eq!(
|
||||
process_instruction(&initialize(
|
||||
process_instruction(&instruction::initialize(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Authorized::default(),
|
||||
&Lockup::default()
|
||||
@ -829,7 +380,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&authorize(
|
||||
process_instruction(&instruction::authorize(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
@ -840,7 +391,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&split(
|
||||
&instruction::split(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
100,
|
||||
@ -851,7 +402,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&split(
|
||||
&instruction::split(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
100,
|
||||
@ -862,7 +413,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&merge(
|
||||
&instruction::merge(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
@ -872,7 +423,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&merge(
|
||||
&instruction::merge(
|
||||
&Pubkey::default(),
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
@ -882,7 +433,7 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(
|
||||
&split_with_seed(
|
||||
&instruction::split_with_seed(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
100,
|
||||
@ -894,7 +445,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&delegate_stake(
|
||||
process_instruction(&instruction::delegate_stake(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
@ -902,7 +453,7 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&withdraw(
|
||||
process_instruction(&instruction::withdraw(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
&solana_sdk::pubkey::new_rand(),
|
||||
@ -912,14 +463,14 @@ mod tests {
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&deactivate_stake(
|
||||
process_instruction(&instruction::deactivate_stake(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&Pubkey::default()
|
||||
)),
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
assert_eq!(
|
||||
process_instruction(&set_lockup(
|
||||
process_instruction(&instruction::set_lockup(
|
||||
&spoofed_stake_state_pubkey(),
|
||||
&LockupArgs::default(),
|
||||
&Pubkey::default()
|
||||
@ -1051,8 +602,9 @@ mod tests {
|
||||
let stake_history_account = RefCell::new(account::create_account_shared_data_for_test(
|
||||
&sysvar::stake_history::StakeHistory::default(),
|
||||
));
|
||||
let config_address = config::id();
|
||||
let config_account = RefCell::new(config::create_account(0, &config::Config::default()));
|
||||
let config_address = stake_config::id();
|
||||
let config_account =
|
||||
RefCell::new(config::create_account(0, &stake_config::Config::default()));
|
||||
let keyed_accounts = vec![
|
||||
KeyedAccount::new(&stake_address, true, &stake_account),
|
||||
KeyedAccount::new(&vote_address, false, &bad_vote_account),
|
||||
@ -1140,30 +692,4 @@ mod tests {
|
||||
Err(InstructionError::NotEnoughAccountKeys),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_error_decode() {
|
||||
use num_traits::FromPrimitive;
|
||||
fn pretty_err<T>(err: InstructionError) -> String
|
||||
where
|
||||
T: 'static + std::error::Error + DecodeError<T> + FromPrimitive,
|
||||
{
|
||||
if let InstructionError::Custom(code) = err {
|
||||
let specific_error: T = T::decode_custom_error_to_enum(code).unwrap();
|
||||
format!(
|
||||
"{:?}: {}::{:?} - {}",
|
||||
err,
|
||||
T::type_of(),
|
||||
specific_error,
|
||||
specific_error,
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
"Custom(0): StakeError::NoCreditsToRedeem - not enough credits to redeem",
|
||||
pretty_err::<StakeError>(StakeError::NoCreditsToRedeem.into())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user