add authorities to stake init (#6104)
* add authorities to stake init * fixups * code review
This commit is contained in:
parent
50bbe34b66
commit
a964570b1a
|
@ -29,7 +29,10 @@ use solana_sdk::{
|
|||
system_transaction,
|
||||
transaction::{Transaction, TransactionError},
|
||||
};
|
||||
use solana_stake_api::stake_instruction::{self, StakeError};
|
||||
use solana_stake_api::{
|
||||
stake_instruction::{self, StakeError},
|
||||
stake_state::{Authorized, Lockup},
|
||||
};
|
||||
use solana_storage_api::storage_instruction;
|
||||
use solana_vote_api::vote_state::{VoteAuthorize, VoteInit, VoteState};
|
||||
use std::{
|
||||
|
@ -80,7 +83,7 @@ pub enum WalletCommand {
|
|||
aggregate: bool,
|
||||
span: Option<u64>,
|
||||
},
|
||||
DelegateStake(Keypair, Pubkey, u64, bool),
|
||||
DelegateStake(Keypair, Pubkey, u64, Authorized, bool),
|
||||
WithdrawStake(Keypair, Pubkey, u64),
|
||||
DeactivateStake(Keypair, Pubkey),
|
||||
RedeemVoteCredits(Pubkey, Pubkey),
|
||||
|
@ -257,11 +260,13 @@ pub fn parse_command(
|
|||
matches.value_of("amount").unwrap(),
|
||||
matches.value_of("unit"),
|
||||
)?;
|
||||
let authorized = Authorized::auto(&stake_account_keypair.pubkey());
|
||||
let force = matches.is_present("force");
|
||||
Ok(WalletCommand::DelegateStake(
|
||||
stake_account_keypair,
|
||||
vote_account_pubkey,
|
||||
lamports,
|
||||
authorized,
|
||||
force,
|
||||
))
|
||||
}
|
||||
|
@ -607,6 +612,7 @@ fn process_delegate_stake(
|
|||
stake_account_keypair: &Keypair,
|
||||
vote_account_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
authorized: &Authorized,
|
||||
force: bool,
|
||||
) -> ProcessResult {
|
||||
check_unique_pubkeys(
|
||||
|
@ -623,6 +629,7 @@ fn process_delegate_stake(
|
|||
&stake_account_keypair.pubkey(),
|
||||
vote_account_pubkey,
|
||||
lamports,
|
||||
authorized,
|
||||
);
|
||||
|
||||
// Sanity check the vote account to ensure it is attached to a validator that has recently
|
||||
|
@ -740,8 +747,16 @@ fn process_show_stake_account(
|
|||
format!("{:?} is not a stake account", stake_account_pubkey).to_string(),
|
||||
))?;
|
||||
}
|
||||
fn show_authorized(authorized: &Authorized) {
|
||||
println!("authorized staker: {}", authorized.staker);
|
||||
println!("authorized withdrawer: {}", authorized.staker);
|
||||
}
|
||||
fn show_lockup(lockup: &Lockup) {
|
||||
println!("lockup slot: {}", lockup.slot);
|
||||
println!("lockup custodian: {}", lockup.custodian);
|
||||
}
|
||||
match stake_account.state() {
|
||||
Ok(StakeState::Stake(stake)) => {
|
||||
Ok(StakeState::Stake(authorized, lockup, stake)) => {
|
||||
println!(
|
||||
"total stake: {}",
|
||||
build_balance_message(stake_account.lamports, use_lamports_unit)
|
||||
|
@ -764,11 +779,17 @@ fn process_show_stake_account(
|
|||
stake.deactivation_epoch
|
||||
);
|
||||
}
|
||||
show_authorized(&authorized);
|
||||
show_lockup(&lockup);
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
|
||||
Ok(StakeState::Uninitialized) | Ok(StakeState::Lockup(_)) => {
|
||||
Ok("Stake account is uninitialized".to_string())
|
||||
Ok(StakeState::Uninitialized) => Ok("Stake account is uninitialized".to_string()),
|
||||
Ok(StakeState::Initialized(authorized, lockup)) => {
|
||||
println!("Stake account is undelegated");
|
||||
show_authorized(&authorized);
|
||||
show_lockup(&lockup);
|
||||
Ok("".to_string())
|
||||
}
|
||||
Err(err) => Err(WalletError::RpcRequestError(format!(
|
||||
"Account data could not be deserialized to stake state: {:?}",
|
||||
|
@ -1347,6 +1368,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||
stake_account_keypair,
|
||||
vote_account_pubkey,
|
||||
lamports,
|
||||
authorized,
|
||||
force,
|
||||
) => process_delegate_stake(
|
||||
&rpc_client,
|
||||
|
@ -1354,6 +1376,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
|||
&stake_account_keypair,
|
||||
&vote_account_pubkey,
|
||||
*lamports,
|
||||
&authorized,
|
||||
*force,
|
||||
),
|
||||
|
||||
|
@ -2477,9 +2500,16 @@ mod tests {
|
|||
"42",
|
||||
"lamports",
|
||||
]);
|
||||
let stake_pubkey = keypair.pubkey();
|
||||
assert_eq!(
|
||||
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
||||
WalletCommand::DelegateStake(keypair, pubkey, 42, false)
|
||||
WalletCommand::DelegateStake(
|
||||
keypair,
|
||||
pubkey,
|
||||
42,
|
||||
Authorized::auto(&stake_pubkey),
|
||||
false,
|
||||
)
|
||||
);
|
||||
|
||||
let keypair = read_keypair(&keypair_file).unwrap();
|
||||
|
@ -2492,9 +2522,16 @@ mod tests {
|
|||
"42",
|
||||
"lamports",
|
||||
]);
|
||||
let stake_pubkey = keypair.pubkey();
|
||||
assert_eq!(
|
||||
parse_command(&pubkey, &test_delegate_stake).unwrap(),
|
||||
WalletCommand::DelegateStake(keypair, pubkey, 42, true)
|
||||
WalletCommand::DelegateStake(
|
||||
keypair,
|
||||
pubkey,
|
||||
42,
|
||||
Authorized::auto(&stake_pubkey),
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
// Test WithdrawStake Subcommand
|
||||
|
|
|
@ -319,18 +319,20 @@ mod tests {
|
|||
mut genesis_block, ..
|
||||
} = create_genesis_block(10_000);
|
||||
|
||||
let sk1 = Pubkey::new_rand();
|
||||
let pk1 = Pubkey::new_rand();
|
||||
let mut vote_account1 = vote_state::create_account(&pk1, &Pubkey::new_rand(), 0, 100);
|
||||
let stake_account1 = stake_state::create_account(&pk1, &vote_account1, 100);
|
||||
let stake_account1 = stake_state::create_account(&sk1, &pk1, &vote_account1, 100);
|
||||
let sk2 = Pubkey::new_rand();
|
||||
let pk2 = Pubkey::new_rand();
|
||||
let mut vote_account2 = vote_state::create_account(&pk2, &Pubkey::new_rand(), 0, 50);
|
||||
let stake_account2 = stake_state::create_account(&pk2, &vote_account2, 50);
|
||||
let stake_account2 = stake_state::create_account(&sk2, &pk2, &vote_account2, 50);
|
||||
|
||||
genesis_block.accounts.extend(vec![
|
||||
(pk1, vote_account1.clone()),
|
||||
(Pubkey::new_rand(), stake_account1),
|
||||
(sk1, stake_account1),
|
||||
(pk2, vote_account2.clone()),
|
||||
(Pubkey::new_rand(), stake_account2),
|
||||
(sk2, stake_account2),
|
||||
]);
|
||||
|
||||
// Create bank
|
||||
|
|
|
@ -104,7 +104,10 @@ pub(crate) mod tests {
|
|||
sysvar::stake_history::{self, StakeHistory},
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_api::{stake_instruction, stake_state::Stake};
|
||||
use solana_stake_api::{
|
||||
stake_instruction,
|
||||
stake_state::{Authorized, Stake},
|
||||
};
|
||||
use solana_vote_api::{vote_instruction, vote_state::VoteInit};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -160,6 +163,7 @@ pub(crate) mod tests {
|
|||
&stake_account_pubkey,
|
||||
vote_pubkey,
|
||||
amount,
|
||||
&Authorized::auto(&stake_account_pubkey),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -315,6 +315,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||
1,
|
||||
);
|
||||
let stake_account = stake_state::create_account(
|
||||
&bootstrap_stake_keypair.pubkey(),
|
||||
&bootstrap_vote_keypair.pubkey(),
|
||||
&vote_account,
|
||||
bootstrap_leader_stake_lamports,
|
||||
|
|
|
@ -21,7 +21,10 @@ use solana_sdk::{
|
|||
system_transaction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solana_stake_api::{config as stake_config, stake_instruction, stake_state::StakeState};
|
||||
use solana_stake_api::{
|
||||
config as stake_config, stake_instruction,
|
||||
stake_state::{Authorized as StakeAuthorized, StakeState},
|
||||
};
|
||||
use solana_storage_api::{storage_contract, storage_instruction};
|
||||
use solana_vote_api::{
|
||||
vote_instruction,
|
||||
|
@ -462,6 +465,7 @@ impl LocalCluster {
|
|||
&stake_account_pubkey,
|
||||
&vote_account_pubkey,
|
||||
amount,
|
||||
&StakeAuthorized::auto(&stake_account_pubkey),
|
||||
),
|
||||
client.get_recent_blockhash().unwrap().0,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
config, id,
|
||||
stake_state::{StakeAccount, StakeState},
|
||||
stake_state::{Authorized, Lockup, StakeAccount, StakeAuthorize, StakeState},
|
||||
};
|
||||
use bincode::deserialize;
|
||||
use log::*;
|
||||
|
@ -8,7 +8,6 @@ use num_derive::{FromPrimitive, ToPrimitive};
|
|||
use serde_derive::{Deserialize, Serialize};
|
||||
use solana_sdk::{
|
||||
account::KeyedAccount,
|
||||
clock::Slot,
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
instruction_processor_utils::DecodeError,
|
||||
pubkey::Pubkey,
|
||||
|
@ -36,30 +35,33 @@ impl std::fmt::Display for StakeError {
|
|||
}
|
||||
impl std::error::Error for StakeError {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
pub enum StakeInstruction {
|
||||
/// `Lockup` a stake until the specified slot
|
||||
/// `Initialize` a stake with Lockup and Authorized information
|
||||
///
|
||||
/// Expects 1 Account:
|
||||
/// 0 - Uninitialized StakeAccount to be lockup'd
|
||||
/// 0 - Uninitialized StakeAccount
|
||||
///
|
||||
/// The Slot parameter denotes slot height at which this stake
|
||||
/// will allow withdrawal from the stake account.
|
||||
/// The Pubkey parameter denotes a "custodian" account, the only
|
||||
/// account to which this stake will honor a withdrawal *before*
|
||||
// lockup expires.
|
||||
/// Authorized carries pubkeys that must sign staker transactions
|
||||
/// and withdrawer transactions.
|
||||
/// Lockup carries information about withdrawal restrictions
|
||||
///
|
||||
Lockup((Slot, Pubkey)),
|
||||
Initialize(Authorized, Lockup),
|
||||
|
||||
/// Authorize a system account to manage stake
|
||||
/// Authorize a key to manage stake or withdrawal
|
||||
/// requires Authorized::staker or Authorized::withdrawer
|
||||
/// signature, depending on which key's being updated
|
||||
///
|
||||
/// Expects 1 Account:
|
||||
/// 0 - Locked-up or delegated StakeAccount to be updated with authorized staker
|
||||
Authorize(Pubkey),
|
||||
/// 0 - StakeAccount to be updated with the Pubkey for
|
||||
/// authorization
|
||||
Authorize(Pubkey, StakeAuthorize),
|
||||
|
||||
/// `Delegate` a stake to a particular vote account
|
||||
/// requires Authorized::staker signature
|
||||
///
|
||||
/// Expects 4 Accounts:
|
||||
/// 0 - Lockup'd StakeAccount to be delegated <= transaction must have this signature
|
||||
/// 0 - Initialized StakeAccount to be delegated
|
||||
/// 1 - VoteAccount to which this Stake will be delegated
|
||||
/// 2 - Clock sysvar Account that carries clock bank epoch
|
||||
/// 3 - Config Account that carries stake config
|
||||
|
@ -71,9 +73,10 @@ pub enum StakeInstruction {
|
|||
DelegateStake,
|
||||
|
||||
/// Redeem credits in the stake account
|
||||
/// requires Authorized::staker signature
|
||||
///
|
||||
/// Expects 5 Accounts:
|
||||
/// 0 - Delegate StakeAccount to be updated with rewards
|
||||
/// 0 - StakeAccount to be updated with rewards
|
||||
/// 1 - VoteAccount to which the Stake is delegated,
|
||||
/// 2 - RewardsPool Stake Account from which to redeem credits
|
||||
/// 3 - Rewards sysvar Account that carries points values
|
||||
|
@ -81,21 +84,23 @@ pub enum StakeInstruction {
|
|||
RedeemVoteCredits,
|
||||
|
||||
/// Withdraw unstaked lamports from the stake account
|
||||
/// requires Authorized::withdrawer signature
|
||||
///
|
||||
/// Expects 4 Accounts:
|
||||
/// 0 - Delegate StakeAccount <= transaction must have this signature
|
||||
/// 0 - StakeAccount from which to withdraw
|
||||
/// 1 - System account to which the lamports will be transferred,
|
||||
/// 2 - Syscall Account that carries epoch
|
||||
/// 3 - StakeHistory sysvar that carries stake warmup/cooldown history
|
||||
///
|
||||
/// The u64 is the portion of the Stake account balance to be withdrawn,
|
||||
/// must be <= StakeAccount.lamports - staked lamports
|
||||
/// must be <= StakeAccount.lamports - staked lamports.
|
||||
Withdraw(u64),
|
||||
|
||||
/// Deactivates the stake in the account
|
||||
/// requires Authorized::staker signature
|
||||
///
|
||||
/// Expects 3 Accounts:
|
||||
/// 0 - Delegate StakeAccount <= transaction must have this signature
|
||||
/// 0 - Delegate StakeAccount
|
||||
/// 1 - VoteAccount to which the Stake is delegated
|
||||
/// 2 - Syscall Account that carries epoch
|
||||
///
|
||||
|
@ -106,8 +111,8 @@ pub fn create_stake_account_with_lockup(
|
|||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
lockup: Slot,
|
||||
custodian: &Pubkey,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
|
@ -119,7 +124,7 @@ pub fn create_stake_account_with_lockup(
|
|||
),
|
||||
Instruction::new(
|
||||
id(),
|
||||
&StakeInstruction::Lockup((lockup, *custodian)),
|
||||
&StakeInstruction::Initialize(*authorized, *lockup),
|
||||
vec![AccountMeta::new(*stake_pubkey, false)],
|
||||
),
|
||||
]
|
||||
|
@ -129,8 +134,15 @@ pub fn create_stake_account(
|
|||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
authorized: &Authorized,
|
||||
) -> Vec<Instruction> {
|
||||
create_stake_account_with_lockup(from_pubkey, stake_pubkey, lamports, 0, &Pubkey::default())
|
||||
create_stake_account_with_lockup(
|
||||
from_pubkey,
|
||||
stake_pubkey,
|
||||
lamports,
|
||||
authorized,
|
||||
&Lockup::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_stake_account_and_delegate_stake(
|
||||
|
@ -138,21 +150,23 @@ pub fn create_stake_account_and_delegate_stake(
|
|||
stake_pubkey: &Pubkey,
|
||||
vote_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
authorized: &Authorized,
|
||||
) -> Vec<Instruction> {
|
||||
let mut instructions = create_stake_account(from_pubkey, stake_pubkey, lamports);
|
||||
let mut instructions = create_stake_account(from_pubkey, stake_pubkey, lamports, authorized);
|
||||
instructions.push(delegate_stake(stake_pubkey, vote_pubkey));
|
||||
instructions
|
||||
}
|
||||
|
||||
fn metas_for_authorized_staker(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey, // currently authorized
|
||||
// for instructions that whose authorized signer may differ from the account's pubkey
|
||||
fn metas_for_authorized_signer(
|
||||
account_pubkey: &Pubkey,
|
||||
authorized_signer: &Pubkey, // currently authorized
|
||||
other_params: &[AccountMeta],
|
||||
) -> Vec<AccountMeta> {
|
||||
let is_own_signer = authorized_pubkey == stake_pubkey;
|
||||
let is_own_signer = authorized_signer == account_pubkey;
|
||||
|
||||
// stake account
|
||||
let mut account_metas = vec![AccountMeta::new(*stake_pubkey, is_own_signer)];
|
||||
// vote account
|
||||
let mut account_metas = vec![AccountMeta::new(*account_pubkey, is_own_signer)];
|
||||
|
||||
for meta in other_params {
|
||||
account_metas.push(meta.clone());
|
||||
|
@ -160,7 +174,7 @@ fn metas_for_authorized_staker(
|
|||
|
||||
// append signer at the end
|
||||
if !is_own_signer {
|
||||
account_metas.push(AccountMeta::new_credit_only(*authorized_pubkey, true)) // signer
|
||||
account_metas.push(AccountMeta::new_credit_only(*authorized_signer, true)) // signer
|
||||
}
|
||||
|
||||
account_metas
|
||||
|
@ -170,12 +184,13 @@ pub fn authorize(
|
|||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
new_authorized_pubkey: &Pubkey,
|
||||
stake_authorize: StakeAuthorize,
|
||||
) -> Instruction {
|
||||
let account_metas = metas_for_authorized_staker(stake_pubkey, authorized_pubkey, &[]);
|
||||
let account_metas = metas_for_authorized_signer(stake_pubkey, authorized_pubkey, &[]);
|
||||
|
||||
Instruction::new(
|
||||
id(),
|
||||
&StakeInstruction::Authorize(*new_authorized_pubkey),
|
||||
&StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
|
||||
account_metas,
|
||||
)
|
||||
}
|
||||
|
@ -239,8 +254,10 @@ pub fn process_instruction(
|
|||
|
||||
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||
StakeInstruction::Lockup((lockup, custodian)) => me.lockup(lockup, &custodian),
|
||||
StakeInstruction::Authorize(authorized_pubkey) => me.authorize(&authorized_pubkey, &rest),
|
||||
StakeInstruction::Initialize(authorized, lockup) => me.initialize(&authorized, &lockup),
|
||||
StakeInstruction::Authorize(authorized_pubkey, stake_authorize) => {
|
||||
me.authorize(&authorized_pubkey, stake_authorize, &rest)
|
||||
}
|
||||
StakeInstruction::DelegateStake => {
|
||||
if rest.len() < 3 {
|
||||
Err(InstructionError::InvalidInstructionData)?;
|
||||
|
@ -366,7 +383,11 @@ mod tests {
|
|||
super::process_instruction(
|
||||
&Pubkey::default(),
|
||||
&mut [],
|
||||
&serialize(&StakeInstruction::Lockup((0, Pubkey::default()))).unwrap(),
|
||||
&serialize(&StakeInstruction::Initialize(
|
||||
Authorized::default(),
|
||||
Lockup::default()
|
||||
))
|
||||
.unwrap(),
|
||||
),
|
||||
Err(InstructionError::InvalidInstructionData),
|
||||
);
|
||||
|
|
|
@ -18,12 +18,12 @@ use solana_sdk::{
|
|||
};
|
||||
use solana_vote_api::vote_state::VoteState;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum StakeState {
|
||||
Uninitialized,
|
||||
Lockup(Lockup),
|
||||
Stake(Stake),
|
||||
Initialized(Authorized, Lockup),
|
||||
Stake(Authorized, Lockup, Stake),
|
||||
RewardsPool,
|
||||
}
|
||||
|
||||
|
@ -43,26 +43,48 @@ impl StakeState {
|
|||
Self::from(account).and_then(|state: Self| state.stake())
|
||||
}
|
||||
|
||||
pub fn authorized_from(account: &Account) -> Option<Authorized> {
|
||||
Self::from(account).and_then(|state: Self| state.authorized())
|
||||
}
|
||||
|
||||
pub fn stake(&self) -> Option<Stake> {
|
||||
match self {
|
||||
StakeState::Stake(stake) => Some(stake.clone()),
|
||||
StakeState::Stake(_authorized, _lockup, stake) => Some(*stake),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn authorized(&self) -> Option<Authorized> {
|
||||
match self {
|
||||
StakeState::Stake(authorized, _lockup, _stake) => Some(*authorized),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Lockup {
|
||||
/// slot height at which this stake will allow withdrawal, unless to the custodian
|
||||
pub slot: Slot,
|
||||
/// custodian account, the only account to which this stake will honor a
|
||||
/// withdrawal *before* lockup expires
|
||||
pub custodian: Pubkey,
|
||||
/// alternate signer that is enabled to act on the Stake account
|
||||
pub authority: Pubkey,
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub enum StakeAuthorize {
|
||||
Staker,
|
||||
Withdrawer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Lockup {
|
||||
/// slot height at which this stake will allow withdrawal, unless
|
||||
/// to the custodian
|
||||
pub slot: Slot,
|
||||
/// custodian account, the only account to which this stake will honor a
|
||||
/// withdrawal before lockup expires. After lockup expires, custodian
|
||||
/// is irrelevant
|
||||
pub custodian: Pubkey,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Authorized {
|
||||
pub staker: Pubkey,
|
||||
pub withdrawer: Pubkey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Stake {
|
||||
/// most recently delegated vote account pubkey
|
||||
pub voter_pubkey: Pubkey,
|
||||
|
@ -78,8 +100,6 @@ pub struct Stake {
|
|||
pub deactivation_epoch: Epoch,
|
||||
/// stake config (warmup, etc.)
|
||||
pub config: Config,
|
||||
/// the Lockup information, see above
|
||||
pub lockup: Lockup,
|
||||
/// history of prior delegates and the epoch ranges for which
|
||||
/// they were set, circular buffer
|
||||
pub prior_delegates: [(Pubkey, Epoch, Epoch); MAX_PRIOR_DELEGATES],
|
||||
|
@ -92,7 +112,6 @@ const MAX_PRIOR_DELEGATES: usize = 32; // this is how many epochs a stake is exp
|
|||
impl Default for Stake {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
lockup: Lockup::default(),
|
||||
voter_pubkey: Pubkey::default(),
|
||||
voter_pubkey_epoch: 0,
|
||||
credits_observed: 0,
|
||||
|
@ -106,20 +125,54 @@ impl Default for Stake {
|
|||
}
|
||||
}
|
||||
|
||||
impl Authorized {
|
||||
pub fn auto(authorized: &Pubkey) -> Self {
|
||||
Self {
|
||||
staker: *authorized,
|
||||
withdrawer: *authorized,
|
||||
}
|
||||
}
|
||||
pub fn check(
|
||||
&self,
|
||||
stake_signer: Option<&Pubkey>,
|
||||
other_signers: &[KeyedAccount],
|
||||
stake_authorize: StakeAuthorize,
|
||||
) -> Result<(), InstructionError> {
|
||||
let authorized = match stake_authorize {
|
||||
StakeAuthorize::Staker => Some(&self.staker),
|
||||
StakeAuthorize::Withdrawer => Some(&self.withdrawer),
|
||||
};
|
||||
if stake_signer != authorized
|
||||
&& other_signers
|
||||
.iter()
|
||||
.all(|account| account.signer_key() != authorized)
|
||||
{
|
||||
Err(InstructionError::MissingRequiredSignature)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub fn authorize(
|
||||
&mut self,
|
||||
stake_signer: Option<&Pubkey>,
|
||||
other_signers: &[KeyedAccount],
|
||||
new_authorized: &Pubkey,
|
||||
stake_authorize: StakeAuthorize,
|
||||
) -> Result<(), InstructionError> {
|
||||
self.check(stake_signer, other_signers, stake_authorize)?;
|
||||
match stake_authorize {
|
||||
StakeAuthorize::Staker => self.staker = *new_authorized,
|
||||
StakeAuthorize::Withdrawer => self.withdrawer = *new_authorized,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Stake {
|
||||
fn is_bootstrap(&self) -> bool {
|
||||
self.activation_epoch == std::u64::MAX
|
||||
}
|
||||
|
||||
fn check_authorized(
|
||||
&self,
|
||||
stake_pubkey_signer: Option<&Pubkey>,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
self.lockup
|
||||
.check_authorized(stake_pubkey_signer, other_signers)
|
||||
}
|
||||
|
||||
pub fn stake(&self, epoch: Epoch, history: Option<&StakeHistory>) -> u64 {
|
||||
self.stake_activating_and_deactivating(epoch, history).0
|
||||
}
|
||||
|
@ -310,7 +363,6 @@ impl Stake {
|
|||
vote_state,
|
||||
std::u64::MAX,
|
||||
&Config::default(),
|
||||
&Lockup::default(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -340,7 +392,6 @@ impl Stake {
|
|||
vote_state: &VoteState,
|
||||
activation_epoch: Epoch,
|
||||
config: &Config,
|
||||
lockup: &Lockup,
|
||||
) -> Self {
|
||||
Self {
|
||||
stake,
|
||||
|
@ -349,7 +400,6 @@ impl Stake {
|
|||
voter_pubkey_epoch: activation_epoch,
|
||||
credits_observed: vote_state.credits(),
|
||||
config: *config,
|
||||
lockup: *lockup,
|
||||
..Stake::default()
|
||||
}
|
||||
}
|
||||
|
@ -359,29 +409,16 @@ impl Stake {
|
|||
}
|
||||
}
|
||||
|
||||
impl Lockup {
|
||||
fn check_authorized(
|
||||
&self,
|
||||
stake_pubkey_signer: Option<&Pubkey>,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
let authorized = Some(&self.authority);
|
||||
if stake_pubkey_signer != authorized
|
||||
&& other_signers
|
||||
.iter()
|
||||
.all(|account| account.signer_key() != authorized)
|
||||
{
|
||||
return Err(InstructionError::MissingRequiredSignature);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StakeAccount {
|
||||
fn lockup(&mut self, slot: Slot, custodian: &Pubkey) -> Result<(), InstructionError>;
|
||||
fn initialize(
|
||||
&mut self,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
) -> Result<(), InstructionError>;
|
||||
fn authorize(
|
||||
&mut self,
|
||||
authorized_pubkey: &Pubkey,
|
||||
authority: &Pubkey,
|
||||
stake_authorize: StakeAuthorize,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError>;
|
||||
fn delegate_stake(
|
||||
|
@ -415,13 +452,13 @@ pub trait StakeAccount {
|
|||
}
|
||||
|
||||
impl<'a> StakeAccount for KeyedAccount<'a> {
|
||||
fn lockup(&mut self, slot: Slot, custodian: &Pubkey) -> Result<(), InstructionError> {
|
||||
fn initialize(
|
||||
&mut self,
|
||||
authorized: &Authorized,
|
||||
lockup: &Lockup,
|
||||
) -> Result<(), InstructionError> {
|
||||
if let StakeState::Uninitialized = self.state()? {
|
||||
self.set_state(&StakeState::Lockup(Lockup {
|
||||
slot,
|
||||
custodian: *custodian,
|
||||
authority: *self.unsigned_key(),
|
||||
}))
|
||||
self.set_state(&StakeState::Initialized(*authorized, *lockup))
|
||||
} else {
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
}
|
||||
|
@ -432,17 +469,17 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
fn authorize(
|
||||
&mut self,
|
||||
authority: &Pubkey,
|
||||
stake_authorize: StakeAuthorize,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
let stake_state = self.state()?;
|
||||
if let StakeState::Stake(mut stake) = stake_state {
|
||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
||||
stake.lockup.authority = *authority;
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
} else if let StakeState::Lockup(mut lockup) = stake_state {
|
||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
||||
lockup.authority = *authority;
|
||||
self.set_state(&StakeState::Lockup(lockup))
|
||||
|
||||
if let StakeState::Stake(mut authorized, lockup, stake) = stake_state {
|
||||
authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?;
|
||||
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||
} else if let StakeState::Initialized(mut authorized, lockup) = stake_state {
|
||||
authorized.authorize(self.signer_key(), other_signers, authority, stake_authorize)?;
|
||||
self.set_state(&StakeState::Initialized(authorized, lockup))
|
||||
} else {
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
}
|
||||
|
@ -454,26 +491,25 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
config: &Config,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
if let StakeState::Lockup(lockup) = self.state()? {
|
||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
||||
if let StakeState::Initialized(authorized, lockup) = self.state()? {
|
||||
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||
let stake = Stake::new(
|
||||
self.account.lamports,
|
||||
vote_account.unsigned_key(),
|
||||
&vote_account.state()?,
|
||||
clock.epoch,
|
||||
config,
|
||||
&lockup,
|
||||
);
|
||||
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
} else if let StakeState::Stake(mut stake) = self.state()? {
|
||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
||||
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||
} else if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? {
|
||||
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||
stake.redelegate(
|
||||
vote_account.unsigned_key(),
|
||||
&vote_account.state()?,
|
||||
clock.epoch,
|
||||
)?;
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||
} else {
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
}
|
||||
|
@ -484,11 +520,11 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
clock: &sysvar::clock::Clock,
|
||||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
if let StakeState::Stake(mut stake) = self.state()? {
|
||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
||||
if let StakeState::Stake(authorized, lockup, mut stake) = self.state()? {
|
||||
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Staker)?;
|
||||
stake.deactivate(clock.epoch);
|
||||
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||
} else {
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
}
|
||||
|
@ -500,7 +536,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
rewards: &sysvar::rewards::Rewards,
|
||||
stake_history: &sysvar::stake_history::StakeHistory,
|
||||
) -> Result<(), InstructionError> {
|
||||
if let (StakeState::Stake(mut stake), StakeState::RewardsPool) =
|
||||
if let (StakeState::Stake(authorized, lockup, mut stake), StakeState::RewardsPool) =
|
||||
(self.state()?, rewards_account.state()?)
|
||||
{
|
||||
let vote_state: VoteState = vote_account.state()?;
|
||||
|
@ -528,7 +564,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
|
||||
stake.credits_observed = credits_observed;
|
||||
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
self.set_state(&StakeState::Stake(authorized, lockup, stake))
|
||||
} else {
|
||||
// not worth collecting
|
||||
Err(StakeError::NoCreditsToRedeem.into())
|
||||
|
@ -546,8 +582,8 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
other_signers: &[KeyedAccount],
|
||||
) -> Result<(), InstructionError> {
|
||||
let lockup = match self.state()? {
|
||||
StakeState::Stake(stake) => {
|
||||
stake.check_authorized(self.signer_key(), other_signers)?;
|
||||
StakeState::Stake(authorized, lockup, stake) => {
|
||||
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Withdrawer)?;
|
||||
// if we have a deactivation epoch and we're in cooldown
|
||||
let staked = if clock.epoch >= stake.deactivation_epoch {
|
||||
stake.stake(clock.epoch, Some(stake_history))
|
||||
|
@ -561,10 +597,10 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
if lamports > self.account.lamports.saturating_sub(staked) {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
stake.lockup
|
||||
lockup
|
||||
}
|
||||
StakeState::Lockup(lockup) => {
|
||||
lockup.check_authorized(self.signer_key(), other_signers)?;
|
||||
StakeState::Initialized(authorized, lockup) => {
|
||||
authorized.check(self.signer_key(), other_signers, StakeAuthorize::Withdrawer)?;
|
||||
lockup
|
||||
}
|
||||
StakeState::Uninitialized => {
|
||||
|
@ -615,17 +651,25 @@ where
|
|||
}
|
||||
|
||||
// utility function, used by Bank, tests, genesis
|
||||
pub fn create_account(voter_pubkey: &Pubkey, vote_account: &Account, lamports: u64) -> Account {
|
||||
pub fn create_account(
|
||||
authorized: &Pubkey,
|
||||
voter_pubkey: &Pubkey,
|
||||
vote_account: &Account,
|
||||
lamports: u64,
|
||||
) -> Account {
|
||||
let mut stake_account = Account::new(lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
|
||||
let vote_state = VoteState::from(vote_account).expect("vote_state");
|
||||
|
||||
stake_account
|
||||
.set_state(&StakeState::Stake(Stake::new_bootstrap(
|
||||
lamports,
|
||||
voter_pubkey,
|
||||
&vote_state,
|
||||
)))
|
||||
.set_state(&StakeState::Stake(
|
||||
Authorized {
|
||||
staker: *authorized,
|
||||
withdrawer: *authorized,
|
||||
},
|
||||
Lockup::default(),
|
||||
Stake::new_bootstrap(lamports, voter_pubkey, &vote_state),
|
||||
))
|
||||
.expect("set_state");
|
||||
|
||||
stake_account
|
||||
|
@ -691,10 +735,13 @@ mod tests {
|
|||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(
|
||||
Authorized {
|
||||
staker: stake_pubkey,
|
||||
withdrawer: stake_pubkey,
|
||||
},
|
||||
Lockup::default(),
|
||||
),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -707,10 +754,13 @@ mod tests {
|
|||
let stake_state: StakeState = stake_keyed_account.state().unwrap();
|
||||
assert_eq!(
|
||||
stake_state,
|
||||
StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
})
|
||||
StakeState::Initialized(
|
||||
Authorized {
|
||||
staker: stake_pubkey,
|
||||
withdrawer: stake_pubkey,
|
||||
},
|
||||
Lockup::default(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -741,10 +791,6 @@ mod tests {
|
|||
stake: stake_lamports,
|
||||
activation_epoch: clock.epoch,
|
||||
deactivation_epoch: std::u64::MAX,
|
||||
lockup: Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
},
|
||||
..Stake::default()
|
||||
}
|
||||
);
|
||||
|
@ -1062,21 +1108,32 @@ mod tests {
|
|||
// unsigned keyed account
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
|
||||
let custodian = Pubkey::new_rand();
|
||||
assert_eq!(stake_keyed_account.lockup(1, &custodian), Ok(()));
|
||||
assert_eq!(
|
||||
stake_keyed_account.initialize(
|
||||
&Authorized {
|
||||
staker: stake_pubkey,
|
||||
withdrawer: stake_pubkey
|
||||
},
|
||||
&Lockup { slot: 1, custodian }
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
// first time works, as is uninit
|
||||
assert_eq!(
|
||||
StakeState::from(&stake_keyed_account.account).unwrap(),
|
||||
StakeState::Lockup(Lockup {
|
||||
slot: 1,
|
||||
authority: stake_pubkey,
|
||||
custodian
|
||||
})
|
||||
StakeState::Initialized(
|
||||
Authorized {
|
||||
staker: stake_pubkey,
|
||||
withdrawer: stake_pubkey
|
||||
},
|
||||
Lockup { slot: 1, custodian }
|
||||
)
|
||||
);
|
||||
|
||||
// 2nd time fails, can't move it from anything other than uninit->lockup
|
||||
assert_eq!(
|
||||
stake_keyed_account.lockup(1, &Pubkey::default()),
|
||||
stake_keyed_account.initialize(&Authorized::default(), &Lockup::default()),
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
);
|
||||
}
|
||||
|
@ -1087,10 +1144,7 @@ mod tests {
|
|||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1195,7 +1249,12 @@ mod tests {
|
|||
// lockup
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||
let custodian = Pubkey::new_rand();
|
||||
stake_keyed_account.lockup(0, &custodian).unwrap();
|
||||
stake_keyed_account
|
||||
.initialize(
|
||||
&Authorized::auto(&stake_pubkey),
|
||||
&Lockup { slot: 0, custodian },
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// signed keyed account and locked up, more than available should fail
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||
|
@ -1297,10 +1356,7 @@ mod tests {
|
|||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1381,17 +1437,16 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_withdraw_lockout() {
|
||||
fn test_withdraw_lockup() {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
let custodian = Pubkey::new_rand();
|
||||
let total_lamports = 100;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
slot: 1,
|
||||
authority: stake_pubkey,
|
||||
custodian,
|
||||
}),
|
||||
&StakeState::Initialized(
|
||||
Authorized::auto(&stake_pubkey),
|
||||
Lockup { slot: 1, custodian },
|
||||
),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1542,10 +1597,7 @@ mod tests {
|
|||
let stake_lamports = 100;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1673,10 +1725,7 @@ mod tests {
|
|||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1690,16 +1739,27 @@ mod tests {
|
|||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||
|
||||
let stake_pubkey0 = Pubkey::new_rand();
|
||||
assert_eq!(stake_keyed_account.authorize(&stake_pubkey0, &[]), Ok(()));
|
||||
if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap()
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Staker, &[]),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(&stake_pubkey0, StakeAuthorize::Withdrawer, &[]),
|
||||
Ok(())
|
||||
);
|
||||
if let StakeState::Initialized(authorized, _lockup) =
|
||||
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||
{
|
||||
assert_eq!(lockup.authority, stake_pubkey0);
|
||||
assert_eq!(authorized.staker, stake_pubkey0);
|
||||
assert_eq!(authorized.withdrawer, stake_pubkey0);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
// A second authorization signed by the stake_keyed_account should fail
|
||||
let stake_pubkey1 = Pubkey::new_rand();
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(&stake_pubkey1, &[]),
|
||||
stake_keyed_account.authorize(&stake_pubkey1, StakeAuthorize::Staker, &[]),
|
||||
Err(InstructionError::MissingRequiredSignature)
|
||||
);
|
||||
|
||||
|
@ -1709,18 +1769,38 @@ mod tests {
|
|||
// Test a second authorization by the newly authorized pubkey
|
||||
let stake_pubkey2 = Pubkey::new_rand();
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(&stake_pubkey2, &[staker_keyed_account0]),
|
||||
stake_keyed_account.authorize(
|
||||
&stake_pubkey2,
|
||||
StakeAuthorize::Staker,
|
||||
&[staker_keyed_account0]
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
if let StakeState::Lockup(lockup) = StakeState::from(&stake_keyed_account.account).unwrap()
|
||||
if let StakeState::Initialized(authorized, _lockup) =
|
||||
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||
{
|
||||
assert_eq!(lockup.authority, stake_pubkey2);
|
||||
assert_eq!(authorized.staker, stake_pubkey2);
|
||||
}
|
||||
|
||||
let staker_keyed_account0 = KeyedAccount::new(&stake_pubkey0, true, &mut staker_account0);
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(
|
||||
&stake_pubkey2,
|
||||
StakeAuthorize::Withdrawer,
|
||||
&[staker_keyed_account0]
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
if let StakeState::Initialized(authorized, _lockup) =
|
||||
StakeState::from(&stake_keyed_account.account).unwrap()
|
||||
{
|
||||
assert_eq!(authorized.staker, stake_pubkey2);
|
||||
}
|
||||
|
||||
let mut staker_account2 = Account::new(1, 0, &system_program::id());
|
||||
let staker_keyed_account2 = KeyedAccount::new(&stake_pubkey2, true, &mut staker_account2);
|
||||
|
||||
// Test an action by the currently authorized pubkey
|
||||
// Test an action by the currently authorized withdrawer
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
stake_lamports,
|
||||
|
@ -1739,10 +1819,7 @@ mod tests {
|
|||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(Lockup {
|
||||
authority: stake_pubkey,
|
||||
..Lockup::default()
|
||||
}),
|
||||
&StakeState::Initialized(Authorized::auto(&stake_pubkey), Lockup::default()),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
|
@ -1762,11 +1839,11 @@ mod tests {
|
|||
|
||||
let new_staker_pubkey = Pubkey::new_rand();
|
||||
assert_eq!(
|
||||
stake_keyed_account.authorize(&new_staker_pubkey, &[]),
|
||||
stake_keyed_account.authorize(&new_staker_pubkey, StakeAuthorize::Staker, &[]),
|
||||
Ok(())
|
||||
);
|
||||
let stake = StakeState::stake_from(&stake_keyed_account.account).unwrap();
|
||||
assert_eq!(stake.lockup.authority, new_staker_pubkey);
|
||||
let authorized = StakeState::authorized_from(&stake_keyed_account.account).unwrap();
|
||||
assert_eq!(authorized.staker, new_staker_pubkey);
|
||||
|
||||
let other_pubkey = Pubkey::new_rand();
|
||||
let mut other_account = Account::new(1, 0, &system_program::id());
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
use assert_matches::assert_matches;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::bank_client::BankClient;
|
||||
use solana_runtime::genesis_utils::{create_genesis_block_with_leader, GenesisBlockInfo};
|
||||
use solana_sdk::account_utils::State;
|
||||
use solana_sdk::client::SyncClient;
|
||||
use solana_sdk::message::Message;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::sysvar;
|
||||
use solana_sdk::sysvar::rewards::Rewards;
|
||||
use solana_stake_api::id;
|
||||
use solana_stake_api::stake_instruction;
|
||||
use solana_stake_api::stake_instruction::process_instruction;
|
||||
use solana_stake_api::stake_state::StakeState;
|
||||
use solana_vote_api::vote_instruction;
|
||||
use solana_vote_api::vote_state::{Vote, VoteInit, VoteState};
|
||||
use solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_client::BankClient,
|
||||
genesis_utils::{create_genesis_block_with_leader, GenesisBlockInfo},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account_utils::State,
|
||||
client::SyncClient,
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, KeypairUtil},
|
||||
sysvar,
|
||||
sysvar::rewards::Rewards,
|
||||
};
|
||||
use solana_stake_api::{
|
||||
id,
|
||||
stake_instruction::{self, process_instruction},
|
||||
stake_state::{self, StakeState},
|
||||
};
|
||||
use solana_vote_api::{
|
||||
vote_instruction,
|
||||
vote_state::{Vote, VoteInit, VoteState},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn fill_epoch_with_votes(
|
||||
|
@ -88,12 +95,14 @@ fn test_stake_account_delegate() {
|
|||
.send_message(&[&mint_keypair], message)
|
||||
.expect("failed to create vote account");
|
||||
|
||||
let authorized = stake_state::Authorized::auto(&staker_pubkey);
|
||||
// Create stake account and delegate to vote account
|
||||
let message = Message::new(stake_instruction::create_stake_account_and_delegate_stake(
|
||||
&mint_pubkey,
|
||||
&staker_pubkey,
|
||||
&vote_pubkey,
|
||||
20000,
|
||||
&authorized,
|
||||
));
|
||||
bank_client
|
||||
.send_message(&[&mint_keypair, &staker_keypair], message)
|
||||
|
@ -102,7 +111,7 @@ fn test_stake_account_delegate() {
|
|||
// Test that correct lamports are staked
|
||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||
let stake_state = account.state().expect("couldn't unpack account data");
|
||||
if let StakeState::Stake(stake) = stake_state {
|
||||
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||
assert_eq!(stake.stake, 20000);
|
||||
} else {
|
||||
assert!(false, "wrong account type found")
|
||||
|
@ -124,7 +133,7 @@ fn test_stake_account_delegate() {
|
|||
// Test that lamports are still staked
|
||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||
let stake_state = account.state().expect("couldn't unpack account data");
|
||||
if let StakeState::Stake(stake) = stake_state {
|
||||
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||
assert_eq!(stake.stake, 20000);
|
||||
} else {
|
||||
assert!(false, "wrong account type found")
|
||||
|
@ -168,7 +177,7 @@ fn test_stake_account_delegate() {
|
|||
let rewards;
|
||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||
let stake_state = account.state().expect("couldn't unpack account data");
|
||||
if let StakeState::Stake(stake) = stake_state {
|
||||
if let StakeState::Stake(_authorized, _lockup, stake) = stake_state {
|
||||
assert!(account.lamports > 20000);
|
||||
assert_eq!(stake.stake, 20000);
|
||||
rewards = account.lamports - 20000;
|
||||
|
@ -251,7 +260,7 @@ fn test_stake_account_delegate() {
|
|||
// Test that balance and stake is updated correctly (we have withdrawn all lamports except rewards)
|
||||
let account = bank.get_account(&staker_pubkey).expect("account not found");
|
||||
let stake_state = account.state().expect("couldn't unpack account data");
|
||||
if let StakeState::Stake(_stake) = stake_state {
|
||||
if let StakeState::Stake(_, _, _stake) = stake_state {
|
||||
assert_eq!(account.lamports, rewards);
|
||||
} else {
|
||||
assert!(false, "wrong account type found")
|
||||
|
|
|
@ -41,6 +41,7 @@ pub fn create_genesis_block_with_leader(
|
|||
);
|
||||
|
||||
let stake_account = stake_state::create_account(
|
||||
&staking_keypair.pubkey(),
|
||||
&voting_keypair.pubkey(),
|
||||
&vote_account,
|
||||
bootstrap_leader_stake_lamports,
|
||||
|
|
|
@ -220,9 +220,11 @@ pub mod tests {
|
|||
|
||||
// add stake to a vote_pubkey ( stake )
|
||||
pub fn create_stake_account(stake: u64, vote_pubkey: &Pubkey) -> (Pubkey, Account) {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
(
|
||||
Pubkey::new_rand(),
|
||||
stake_pubkey,
|
||||
stake_state::create_account(
|
||||
&stake_pubkey,
|
||||
&vote_pubkey,
|
||||
&vote_state::create_account(&vote_pubkey, &Pubkey::new_rand(), 0, 1),
|
||||
stake,
|
||||
|
|
Loading…
Reference in New Issue