parent
94eb78d399
commit
933e835838
|
@ -19,7 +19,7 @@ source scripts/ulimit-n.sh
|
|||
# Clear cached json keypair files
|
||||
rm -rf "$HOME/.config/solana"
|
||||
|
||||
# Clear the C dependency files, if dependeny moves these files are not regenerated
|
||||
# Clear the C dependency files, if dependency moves these files are not regenerated
|
||||
test -d target/debug/bpf && find target/debug/bpf -name '*.d' -delete
|
||||
test -d target/release/bpf && find target/release/bpf -name '*.d' -delete
|
||||
|
||||
|
|
|
@ -895,7 +895,9 @@ fn process_show_stake_account(
|
|||
Ok("".to_string())
|
||||
}
|
||||
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
|
||||
Ok(StakeState::Uninitialized) => Ok("Stake account is uninitialized".to_string()),
|
||||
Ok(StakeState::Uninitialized) | Ok(StakeState::Lockup(_)) => {
|
||||
Ok("Stake account is uninitialized".to_string())
|
||||
}
|
||||
Err(err) => Err(WalletError::RpcRequestError(format!(
|
||||
"Account data could not be deserialized to stake state: {:?}",
|
||||
err
|
||||
|
|
|
@ -10,14 +10,25 @@ use solana_sdk::{
|
|||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
pubkey::Pubkey,
|
||||
system_instruction, sysvar,
|
||||
timing::Slot,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum StakeInstruction {
|
||||
/// `Lockup` a stake until the specified slot
|
||||
///
|
||||
/// Expects 1 Account:
|
||||
/// 0 - Uninitialized StakeAccount to be lockup'd
|
||||
///
|
||||
/// The u64 is the portion of the Stake account balance to be activated,
|
||||
/// must be less than StakeAccount.lamports
|
||||
///
|
||||
Lockup(Slot),
|
||||
|
||||
/// `Delegate` a stake to a particular node
|
||||
///
|
||||
/// Expects 3 Accounts:
|
||||
/// 0 - Uninitialized StakeAccount to be delegated <= must have this signature
|
||||
/// 0 - Lockup'd StakeAccount to be delegated <= must have this signature
|
||||
/// 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
|
||||
|
@ -58,28 +69,44 @@ pub enum StakeInstruction {
|
|||
Deactivate,
|
||||
}
|
||||
|
||||
pub fn create_stake_account_with_lockup(
|
||||
from_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
lockup: Slot,
|
||||
) -> Vec<Instruction> {
|
||||
vec![
|
||||
system_instruction::create_account(
|
||||
from_pubkey,
|
||||
stake_pubkey,
|
||||
lamports,
|
||||
std::mem::size_of::<StakeState>() as u64,
|
||||
&id(),
|
||||
),
|
||||
Instruction::new(
|
||||
id(),
|
||||
&StakeInstruction::Lockup(lockup),
|
||||
vec![AccountMeta::new(*stake_pubkey, false)],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn create_stake_account(
|
||||
from_pubkey: &Pubkey,
|
||||
staker_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
vec![system_instruction::create_account(
|
||||
from_pubkey,
|
||||
staker_pubkey,
|
||||
lamports,
|
||||
std::mem::size_of::<StakeState>() as u64,
|
||||
&id(),
|
||||
)]
|
||||
create_stake_account_with_lockup(from_pubkey, stake_pubkey, lamports, 0)
|
||||
}
|
||||
|
||||
pub fn create_stake_account_and_delegate_stake(
|
||||
from_pubkey: &Pubkey,
|
||||
staker_pubkey: &Pubkey,
|
||||
stake_pubkey: &Pubkey,
|
||||
vote_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Vec<Instruction> {
|
||||
let mut instructions = create_stake_account(from_pubkey, staker_pubkey, lamports);
|
||||
instructions.push(delegate_stake(staker_pubkey, vote_pubkey, lamports));
|
||||
let mut instructions = create_stake_account(from_pubkey, stake_pubkey, lamports);
|
||||
instructions.push(delegate_stake(stake_pubkey, vote_pubkey, lamports));
|
||||
instructions
|
||||
}
|
||||
|
||||
|
@ -142,6 +169,7 @@ pub fn process_instruction(
|
|||
|
||||
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||
StakeInstruction::Lockup(slot) => me.lockup(slot),
|
||||
StakeInstruction::DelegateStake(stake) => {
|
||||
if rest.len() != 3 {
|
||||
Err(InstructionError::InvalidInstructionData)?;
|
||||
|
|
|
@ -14,13 +14,14 @@ use solana_sdk::{
|
|||
self,
|
||||
stake_history::{StakeHistory, StakeHistoryEntry},
|
||||
},
|
||||
timing::Epoch,
|
||||
timing::{Epoch, Slot},
|
||||
};
|
||||
use solana_vote_api::vote_state::VoteState;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub enum StakeState {
|
||||
Uninitialized,
|
||||
Lockup(Slot),
|
||||
Stake(Stake),
|
||||
RewardsPool,
|
||||
}
|
||||
|
@ -57,6 +58,7 @@ pub struct Stake {
|
|||
pub activation_epoch: Epoch, // epoch the stake was activated, std::Epoch::MAX if is a bootstrap stake
|
||||
pub deactivation_epoch: Epoch, // epoch the stake was deactivated, std::Epoch::MAX if not deactivated
|
||||
pub config: Config,
|
||||
pub lockup: Slot,
|
||||
}
|
||||
|
||||
impl Default for Stake {
|
||||
|
@ -68,6 +70,7 @@ impl Default for Stake {
|
|||
activation_epoch: 0,
|
||||
deactivation_epoch: std::u64::MAX,
|
||||
config: Config::default(),
|
||||
lockup: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +259,7 @@ impl Stake {
|
|||
vote_state,
|
||||
std::u64::MAX,
|
||||
&Config::default(),
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -265,6 +269,7 @@ impl Stake {
|
|||
vote_state: &VoteState,
|
||||
activation_epoch: Epoch,
|
||||
config: &Config,
|
||||
lockup: Slot,
|
||||
) -> Self {
|
||||
Self {
|
||||
stake,
|
||||
|
@ -272,6 +277,7 @@ impl Stake {
|
|||
voter_pubkey: *voter_pubkey,
|
||||
credits_observed: vote_state.credits(),
|
||||
config: *config,
|
||||
lockup,
|
||||
..Stake::default()
|
||||
}
|
||||
}
|
||||
|
@ -282,6 +288,7 @@ impl Stake {
|
|||
}
|
||||
|
||||
pub trait StakeAccount {
|
||||
fn lockup(&mut self, slot: Slot) -> Result<(), InstructionError>;
|
||||
fn delegate_stake(
|
||||
&mut self,
|
||||
vote_account: &KeyedAccount,
|
||||
|
@ -311,6 +318,13 @@ pub trait StakeAccount {
|
|||
}
|
||||
|
||||
impl<'a> StakeAccount for KeyedAccount<'a> {
|
||||
fn lockup(&mut self, lockup: Slot) -> Result<(), InstructionError> {
|
||||
if let StakeState::Uninitialized = self.state()? {
|
||||
self.set_state(&StakeState::Lockup(lockup))
|
||||
} else {
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
}
|
||||
}
|
||||
fn delegate_stake(
|
||||
&mut self,
|
||||
vote_account: &KeyedAccount,
|
||||
|
@ -326,13 +340,14 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
|
||||
if let StakeState::Uninitialized = self.state()? {
|
||||
if let StakeState::Lockup(lockup) = self.state()? {
|
||||
let stake = Stake::new(
|
||||
new_stake,
|
||||
vote_account.unsigned_key(),
|
||||
&vote_account.state()?,
|
||||
clock.epoch,
|
||||
config,
|
||||
lockup,
|
||||
);
|
||||
|
||||
self.set_state(&StakeState::Stake(stake))
|
||||
|
@ -410,6 +425,19 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
return Err(InstructionError::MissingRequiredSignature);
|
||||
}
|
||||
|
||||
fn transfer(
|
||||
from: &mut Account,
|
||||
to: &mut Account,
|
||||
lamports: u64,
|
||||
) -> Result<(), InstructionError> {
|
||||
if lamports > from.lamports {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
from.lamports -= lamports;
|
||||
to.lamports += lamports;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
match self.state()? {
|
||||
StakeState::Stake(stake) => {
|
||||
// if we have a deactivation epoch and we're in cooldown
|
||||
|
@ -425,20 +453,16 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
if lamports > self.account.lamports.saturating_sub(staked) {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
self.account.lamports -= lamports;
|
||||
to.account.lamports += lamports;
|
||||
Ok(())
|
||||
}
|
||||
StakeState::Uninitialized => {
|
||||
if lamports > self.account.lamports {
|
||||
StakeState::Lockup(lockup) => {
|
||||
if lockup > clock.slot {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
self.account.lamports -= lamports;
|
||||
to.account.lamports += lamports;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InstructionError::InvalidAccountData),
|
||||
StakeState::Uninitialized => {}
|
||||
_ => return Err(InstructionError::InvalidAccountData),
|
||||
}
|
||||
transfer(&mut self.account, &mut to.account, lamports)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,15 +570,20 @@ mod tests {
|
|||
|
||||
let stake_pubkey = Pubkey::default();
|
||||
let stake_lamports = 42;
|
||||
let mut stake_account =
|
||||
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(0),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
// unsigned keyed account
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
|
||||
|
||||
{
|
||||
let stake_state: StakeState = stake_keyed_account.state().unwrap();
|
||||
assert_eq!(stake_state, StakeState::default());
|
||||
assert_eq!(stake_state, StakeState::Lockup(0));
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
@ -583,7 +612,8 @@ mod tests {
|
|||
stake: stake_lamports,
|
||||
activation_epoch: clock.epoch,
|
||||
deactivation_epoch: std::u64::MAX,
|
||||
config: Config::default()
|
||||
config: Config::default(),
|
||||
lockup: 0
|
||||
})
|
||||
);
|
||||
// verify that delegate_stake can't be called twice StakeState::default()
|
||||
|
@ -865,12 +895,41 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_deactivate_stake() {
|
||||
fn test_stake_lockup() {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
let stake_lamports = 42;
|
||||
let mut stake_account =
|
||||
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
|
||||
// unsigned keyed account
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, false, &mut stake_account);
|
||||
assert_eq!(stake_keyed_account.lockup(1), Ok(()));
|
||||
|
||||
// first time works, as is uninit
|
||||
assert_eq!(
|
||||
StakeState::from(&stake_keyed_account.account).unwrap(),
|
||||
StakeState::Lockup(1)
|
||||
);
|
||||
|
||||
// 2nd time fails, can't move it from anything other than uninit->lockup
|
||||
assert_eq!(
|
||||
stake_keyed_account.lockup(1),
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deactivate_stake() {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
let stake_lamports = 42;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(0),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
let clock = sysvar::clock::Clock {
|
||||
epoch: 1,
|
||||
..sysvar::clock::Clock::default()
|
||||
|
@ -923,8 +982,13 @@ mod tests {
|
|||
let stake_pubkey = Pubkey::new_rand();
|
||||
let total_lamports = 100;
|
||||
let stake_lamports = 42;
|
||||
let mut stake_account =
|
||||
Account::new(total_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Lockup(0),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
let mut clock = sysvar::clock::Clock::default();
|
||||
|
||||
|
@ -1051,8 +1115,13 @@ mod tests {
|
|||
let stake_pubkey = Pubkey::new_rand();
|
||||
let total_lamports = 100;
|
||||
let stake_lamports = 42;
|
||||
let mut stake_account =
|
||||
Account::new(total_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Lockup(0),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
let clock = sysvar::clock::Clock::default();
|
||||
let mut future = sysvar::clock::Clock::default();
|
||||
|
@ -1102,21 +1171,49 @@ mod tests {
|
|||
fn test_withdraw_stake_invalid_state() {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
let total_lamports = 100;
|
||||
let mut stake_account =
|
||||
Account::new(total_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::RewardsPool,
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");;
|
||||
|
||||
let clock = sysvar::clock::Clock::default();
|
||||
let mut future = sysvar::clock::Clock::default();
|
||||
future.epoch += 16;
|
||||
let to = Pubkey::new_rand();
|
||||
let mut to_account = Account::new(1, 0, &system_program::id());
|
||||
let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account);
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
&mut to_keyed_account,
|
||||
&sysvar::clock::Clock::default(),
|
||||
&StakeHistory::default()
|
||||
),
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_withdraw_lockout() {
|
||||
let stake_pubkey = Pubkey::new_rand();
|
||||
let total_lamports = 100;
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
total_lamports,
|
||||
&StakeState::Lockup(1),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
let to = Pubkey::new_rand();
|
||||
let mut to_account = Account::new(1, 0, &system_program::id());
|
||||
let mut to_keyed_account = KeyedAccount::new(&to, false, &mut to_account);
|
||||
|
||||
let mut stake_keyed_account = KeyedAccount::new(&stake_pubkey, true, &mut stake_account);
|
||||
let stake_state = StakeState::RewardsPool;
|
||||
stake_keyed_account.set_state(&stake_state).unwrap();
|
||||
|
||||
let mut clock = sysvar::clock::Clock::default();
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
|
@ -1124,7 +1221,18 @@ mod tests {
|
|||
&clock,
|
||||
&StakeHistory::default()
|
||||
),
|
||||
Err(InstructionError::InvalidAccountData)
|
||||
Err(InstructionError::InsufficientFunds)
|
||||
);
|
||||
|
||||
clock.slot += 1;
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
&mut to_keyed_account,
|
||||
&clock,
|
||||
&StakeHistory::default()
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1221,8 +1329,14 @@ mod tests {
|
|||
|
||||
let pubkey = Pubkey::default();
|
||||
let stake_lamports = 100;
|
||||
let mut stake_account =
|
||||
Account::new(stake_lamports, std::mem::size_of::<StakeState>(), &id());
|
||||
let mut stake_account = Account::new_data_with_space(
|
||||
stake_lamports,
|
||||
&StakeState::Lockup(0),
|
||||
std::mem::size_of::<StakeState>(),
|
||||
&id(),
|
||||
)
|
||||
.expect("stake_account");
|
||||
|
||||
let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account);
|
||||
|
||||
let vote_pubkey = Pubkey::new_rand();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use bincode::{deserialize_from, serialize_into, serialized_size};
|
||||
use memmap::MmapMut;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::{account::Account, pubkey::Pubkey, Epoch};
|
||||
use solana_sdk::{account::Account, pubkey::Pubkey, timing::Epoch};
|
||||
use std::fmt;
|
||||
use std::fs::{create_dir_all, remove_file, OpenOptions};
|
||||
use std::io;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::pubkey::Pubkey;
|
||||
use crate::Epoch;
|
||||
use crate::{pubkey::Pubkey, timing::Epoch};
|
||||
use std::{cmp, fmt};
|
||||
|
||||
/// An Account with data that is stored on chain
|
||||
|
@ -64,6 +63,19 @@ impl Account {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn new_data_with_space<T: serde::Serialize>(
|
||||
lamports: u64,
|
||||
state: &T,
|
||||
space: usize,
|
||||
owner: &Pubkey,
|
||||
) -> Result<Account, bincode::Error> {
|
||||
let mut account = Self::new(lamports, space, owner);
|
||||
|
||||
account.serialize_data(state)?;
|
||||
|
||||
Ok(account)
|
||||
}
|
||||
|
||||
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
||||
bincode::deserialize(&self.data)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,3 @@ pub mod transport;
|
|||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub type Epoch = u64;
|
||||
pub type Slot = u64;
|
||||
|
|
Loading…
Reference in New Issue