solana/programs/stake_api/src/stake_instruction.rs

256 lines
8.8 KiB
Rust
Raw Normal View History

use crate::id;
use crate::stake_state::{StakeAccount, StakeState};
use bincode::deserialize;
use log::*;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError};
use solana_sdk::pubkey::Pubkey;
2019-06-17 19:34:21 -07:00
use solana_sdk::syscall;
use solana_sdk::system_instruction;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StakeInstruction {
/// `Delegate` a stake to a particular node
///
/// Expects 3 Accounts:
2019-06-17 19:34:21 -07:00
/// 0 - Uninitialized StakeAccount to be delegated <= must have this signature
/// 1 - VoteAccount to which this Stake will be delegated
/// 2 - Current syscall Account that carries current bank epoch
///
/// The u64 is the portion of the Stake account balance to be activated,
/// must be less than StakeAccount.lamports
///
DelegateStake(u64),
/// Redeem credits in the stake account
///
/// Expects 4 Accounts:
/// 0 - Delegate 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 syscall Account that carries points values
RedeemVoteCredits,
}
pub fn create_stake_account(
from_pubkey: &Pubkey,
staker_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
2019-06-17 19:34:21 -07:00
vec![system_instruction::create_account(
from_pubkey,
staker_pubkey,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
)]
}
pub fn create_stake_account_and_delegate_stake(
from_pubkey: &Pubkey,
staker_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));
instructions
}
pub fn redeem_vote_credits(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, false),
AccountMeta::new(*vote_pubkey, false),
AccountMeta::new(crate::rewards_pools::random_id(), false),
AccountMeta::new(syscall::rewards::id(), false),
];
Instruction::new(id(), &StakeInstruction::RedeemVoteCredits, account_metas)
}
pub fn delegate_stake(stake_pubkey: &Pubkey, vote_pubkey: &Pubkey, stake: u64) -> Instruction {
let account_metas = vec![
AccountMeta::new(*stake_pubkey, true),
AccountMeta::new(*vote_pubkey, false),
2019-06-17 19:34:21 -07:00
AccountMeta::new(syscall::current::id(), false),
];
Instruction::new(id(), &StakeInstruction::DelegateStake(stake), account_metas)
}
pub fn process_instruction(
_program_id: &Pubkey,
keyed_accounts: &mut [KeyedAccount],
data: &[u8],
) -> Result<(), InstructionError> {
solana_logger::setup();
trace!("process_instruction: {:?}", data);
trace!("keyed_accounts: {:?}", keyed_accounts);
if keyed_accounts.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
let (me, rest) = &mut keyed_accounts.split_at_mut(1);
let me = &mut me[0];
// TODO: data-driven unpack and dispatch of KeyedAccounts
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
StakeInstruction::DelegateStake(stake) => {
2019-06-17 19:34:21 -07:00
if rest.len() != 2 {
Err(InstructionError::InvalidInstructionData)?;
}
let vote = &rest[0];
2019-06-17 19:34:21 -07:00
me.delegate_stake(
vote,
stake,
&syscall::current::from_keyed_account(&rest[1])?,
)
}
StakeInstruction::RedeemVoteCredits => {
if rest.len() != 3 {
Err(InstructionError::InvalidInstructionData)?;
}
let (vote, rest) = rest.split_at_mut(1);
let vote = &mut vote[0];
let (rewards_pool, rest) = rest.split_at_mut(1);
let rewards_pool = &mut rewards_pool[0];
me.redeem_vote_credits(
vote,
rewards_pool,
&syscall::rewards::from_keyed_account(&rest[0])?,
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bincode::serialize;
use solana_sdk::account::Account;
fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> {
2019-06-17 19:34:21 -07:00
let mut accounts: Vec<_> = instruction
.accounts
.iter()
.map(|meta| {
if syscall::current::check_id(&meta.pubkey) {
syscall::current::create_account(1, 0, 0, 0)
} else if syscall::rewards::check_id(&meta.pubkey) {
syscall::rewards::create_account(1, 0.0, 0.0)
2019-06-17 19:34:21 -07:00
} else {
Account::default()
}
})
.collect();
{
let mut keyed_accounts: Vec<_> = instruction
.accounts
.iter()
.zip(accounts.iter_mut())
.map(|(meta, account)| KeyedAccount::new(&meta.pubkey, meta.is_signer, account))
.collect();
super::process_instruction(&Pubkey::default(), &mut keyed_accounts, &instruction.data)
}
}
#[test]
fn test_stake_process_instruction() {
assert_eq!(
process_instruction(&redeem_vote_credits(&Pubkey::default(), &Pubkey::default(),)),
Err(InstructionError::InvalidAccountData),
);
assert_eq!(
process_instruction(&delegate_stake(&Pubkey::default(), &Pubkey::default(), 0)),
Err(InstructionError::InvalidAccountData),
);
}
#[test]
fn test_stake_process_instruction_decode_bail() {
// these will not call stake_state, have bogus contents
// gets the first check
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [KeyedAccount::new(
&Pubkey::default(),
false,
&mut Account::default(),
)],
&serialize(&StakeInstruction::DelegateStake(0)).unwrap(),
),
Err(InstructionError::InvalidInstructionData),
);
// gets the sub-check for number of args
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [KeyedAccount::new(
&Pubkey::default(),
false,
&mut Account::default()
),],
&serialize(&StakeInstruction::DelegateStake(0)).unwrap(),
),
Err(InstructionError::InvalidInstructionData),
);
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
],
&serialize(&StakeInstruction::RedeemVoteCredits).unwrap(),
),
Err(InstructionError::InvalidInstructionData),
);
2019-06-17 19:34:21 -07:00
// gets the check non-deserialize-able account in delegate_stake
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), true, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
2019-06-17 19:34:21 -07:00
KeyedAccount::new(
&syscall::current::id(),
false,
&mut syscall::current::create_account(1, 0, 0, 0)
),
],
&serialize(&StakeInstruction::DelegateStake(0)).unwrap(),
),
Err(InstructionError::InvalidAccountData),
);
// gets the deserialization checks in redeem_vote_credits
assert_eq!(
super::process_instruction(
&Pubkey::default(),
&mut [
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(&Pubkey::default(), false, &mut Account::default()),
KeyedAccount::new(
&syscall::rewards::id(),
false,
&mut syscall::rewards::create_account(1, 0.0, 0.0)
),
],
&serialize(&StakeInstruction::RedeemVoteCredits).unwrap(),
),
Err(InstructionError::InvalidAccountData),
);
}
}