Add mining pool wallet commands (#4360)

automerge
This commit is contained in:
Michael Vines
2019-05-21 07:32:38 -07:00
committed by Grimes
parent ef0580bd3d
commit 6374e69a69
5 changed files with 256 additions and 35 deletions

View File

@ -10,26 +10,84 @@ use solana_sdk::system_instruction;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum StakeInstruction {
/// Initialize the stake account as a Delegate account.
///
/// Expects 2 Accounts:
/// 0 - payer (TODO unused/remove)
/// 1 - Delegate StakeAccount to be initialized
InitializeDelegate,
// Initialize the stake account as a MiningPool account
///
/// Expects 2 Accounts:
/// 0 - payer (TODO unused/remove)
/// 1 - MiningPool StakeAccount to be initialized
InitializeMiningPool,
/// `Delegate` or `Assign` a stake account to a particular node
/// expects 2 KeyedAccounts:
/// StakeAccount to be updated
/// VoteAccount to which this Stake will be delegated
///
/// Expects 3 Accounts:
/// 0 - payer (TODO unused/remove)
/// 1 - Delegate StakeAccount to be updated
/// 2 - VoteAccount to which this Stake will be delegated
DelegateStake,
/// Redeem credits in the stake account
/// expects 3 KeyedAccounts: the StakeAccount to be updated
/// and the VoteAccount to which this Stake will be delegated
///
/// Expects 4 Accounts:
/// 0 - payer (TODO unused/remove)
/// 1 - MiningPool Stake Account to redeem credits from
/// 2 - Delegate StakeAccount to be updated
/// 3 - VoteAccount to which the Stake is delegated
RedeemVoteCredits,
}
pub fn create_account(from_id: &Pubkey, staker_id: &Pubkey, lamports: u64) -> Instruction {
system_instruction::create_account(
from_id,
staker_id,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
)
pub fn create_delegate_account(
from_id: &Pubkey,
staker_id: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_id,
staker_id,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
),
Instruction::new(
id(),
&StakeInstruction::InitializeDelegate,
vec![
AccountMeta::new(*from_id, true),
AccountMeta::new(*staker_id, false),
],
),
]
}
pub fn create_mining_pool_account(
from_id: &Pubkey,
staker_id: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
vec![
system_instruction::create_account(
from_id,
staker_id,
lamports,
std::mem::size_of::<StakeState>() as u64,
&id(),
),
Instruction::new(
id(),
&StakeInstruction::InitializeMiningPool,
vec![
AccountMeta::new(*from_id, true),
AccountMeta::new(*staker_id, false),
],
),
]
}
pub fn redeem_vote_credits(
@ -67,17 +125,29 @@ pub fn process_instruction(
trace!("process_instruction: {:?}", data);
trace!("keyed_accounts: {:?}", keyed_accounts);
if keyed_accounts.len() < 3 {
if keyed_accounts.len() < 2 {
Err(InstructionError::InvalidInstructionData)?;
}
// 0th index is the guy who paid for the transaction
// 0th index is the account who paid for the transaction
// TODO: Remove the 0th index from the instruction. The stake program doesn't care who paid.
let (me, rest) = &mut keyed_accounts.split_at_mut(2);
let me = &mut me[1];
// TODO: data-driven unpack and dispatch of KeyedAccounts
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
StakeInstruction::InitializeMiningPool => {
if !rest.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
me.initialize_mining_pool()
}
StakeInstruction::InitializeDelegate => {
if !rest.is_empty() {
Err(InstructionError::InvalidInstructionData)?;
}
me.initialize_delegate()
}
StakeInstruction::DelegateStake => {
if rest.len() != 1 {
Err(InstructionError::InvalidInstructionData)?;
@ -127,10 +197,6 @@ mod tests {
#[test]
fn test_stake_process_instruction() {
assert_eq!(
process_instruction(&create_account(&Pubkey::default(), &Pubkey::default(), 0)),
Err(InstructionError::InvalidInstructionData) // won't even decode ;)
);
assert_eq!(
process_instruction(&redeem_vote_credits(
&Pubkey::default(),

View File

@ -13,6 +13,7 @@ use solana_vote_api::vote_state::VoteState;
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum StakeState {
Uninitialized,
Delegate {
voter_id: Pubkey,
credits_observed: u64,
@ -22,10 +23,7 @@ pub enum StakeState {
impl Default for StakeState {
fn default() -> Self {
StakeState::Delegate {
voter_id: Pubkey::default(),
credits_observed: 0,
}
StakeState::Uninitialized
}
}
// TODO: trusted values of network parameters come from where?
@ -90,6 +88,8 @@ impl StakeState {
}
pub trait StakeAccount {
fn initialize_mining_pool(&mut self) -> Result<(), InstructionError>;
fn initialize_delegate(&mut self) -> Result<(), InstructionError>;
fn delegate_stake(&mut self, vote_account: &KeyedAccount) -> Result<(), InstructionError>;
fn redeem_vote_credits(
&mut self,
@ -99,6 +99,23 @@ pub trait StakeAccount {
}
impl<'a> StakeAccount for KeyedAccount<'a> {
fn initialize_mining_pool(&mut self) -> Result<(), InstructionError> {
if let StakeState::Uninitialized = self.state()? {
self.set_state(&StakeState::MiningPool)
} else {
Err(InstructionError::InvalidAccountData)
}
}
fn initialize_delegate(&mut self) -> Result<(), InstructionError> {
if let StakeState::Uninitialized = self.state()? {
self.set_state(&StakeState::Delegate {
voter_id: Pubkey::default(),
credits_observed: 0,
})
} else {
Err(InstructionError::InvalidAccountData)
}
}
fn delegate_stake(&mut self, vote_account: &KeyedAccount) -> Result<(), InstructionError> {
if self.signer_key().is_none() {
return Err(InstructionError::MissingRequiredSignature);
@ -156,7 +173,7 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
})
} else {
// not worth collecting
Ok(())
Err(InstructionError::CustomError(1))
}
} else {
Err(InstructionError::InvalidAccountData)
@ -215,6 +232,7 @@ mod tests {
assert_eq!(stake_state, StakeState::default());
}
stake_keyed_account.initialize_delegate().unwrap();
assert_eq!(
stake_keyed_account.delegate_stake(&vote_keyed_account),
Err(InstructionError::MissingRequiredSignature)
@ -316,6 +334,7 @@ mod tests {
&id(),
);
let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account);
stake_keyed_account.initialize_delegate().unwrap();
// delegate the stake
assert!(stake_keyed_account
@ -338,9 +357,11 @@ mod tests {
.unwrap();
// no movement in vote account, so no redemption needed
assert!(mining_pool_keyed_account
.redeem_vote_credits(&mut stake_keyed_account, &mut vote_keyed_account)
.is_ok());
assert_eq!(
mining_pool_keyed_account
.redeem_vote_credits(&mut stake_keyed_account, &mut vote_keyed_account),
Err(InstructionError::CustomError(1))
);
// move the vote account forward
vote_state.process_vote(&Vote::new(1000));
@ -383,6 +404,7 @@ mod tests {
let pubkey = Pubkey::default();
let mut stake_account = Account::new(0, std::mem::size_of::<StakeState>(), &id());
let mut stake_keyed_account = KeyedAccount::new(&pubkey, true, &mut stake_account);
stake_keyed_account.initialize_delegate().unwrap();
// delegate the stake
assert!(stake_keyed_account