stake-pool: Add depositor key on init, required on deposit (#1616)

* stake-pool: Add depositor key on init, required on deposit

Some stake pools need to be private, and not allow outside depositors.

Enhance the existing deposit authority in the stake pool be configurable
on initialization, and then require its signature on deposit.

The existing deposit authority is a program address, making deposits
permissionless. This allows a pool creator to set their own deposit_authority on
initialization. In a great turn of events, almost everything else works
the same way!

Here's the current workflow for deposit, where the user calls
stake_program::authorize and stake_pool::deposit in the same
transaction:

* stake_program::authorize assigns staker and withdraw authority to the
  stake pool deposit authority
* stake_pool::deposit
    - uses the deposit authority to assign authority on the deposited
  stake account to the stake pool withdraw authority
    - uses the withdraw authority to merge the deposited stake into the validator stake

The deposit authority must "sign" the transaction in order to reassign
authority to the withdraw authority. Currently, as a program address, it
can just do that. With this change, if the deposit authority is set
during initialization, then that deposit authority must sign the
instruction.

There's also a little update for ease-of-use to always do the
stake_program::authorize in the same transaction as stake_pool::deposit.
This way, in case someone tries to deposit into a forbidden stake pool, the
whole transaction will bail and their stake will stay as theirs.

* Address review feedback

* Fix rebase issues
This commit is contained in:
Jon Cinque 2021-04-22 21:34:41 +02:00 committed by GitHub
parent 804a61e558
commit c149b0a46e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 656 additions and 507 deletions

View File

@ -14,7 +14,7 @@ use {
input_validators::{is_amount, is_keypair, is_parsable, is_pubkey, is_url},
keypair::signer_from_path,
},
solana_client::{rpc_client::RpcClient, rpc_response::StakeActivationState},
solana_client::rpc_client::RpcClient,
solana_program::{
borsh::get_packed_len, instruction::Instruction, program_pack::Pack, pubkey::Pubkey,
},
@ -28,9 +28,9 @@ use {
spl_stake_pool::{
self,
borsh::get_instance_packed_len,
find_deposit_authority_program_address, find_stake_program_address,
find_transient_stake_program_address, find_withdraw_authority_program_address,
stake_program::{self, StakeAuthorize, StakeState},
find_stake_program_address, find_transient_stake_program_address,
find_withdraw_authority_program_address,
stake_program::{self, StakeState},
state::{Fee, StakePool, ValidatorList},
MAX_VALIDATORS_TO_UPDATE,
},
@ -42,6 +42,7 @@ struct Config {
verbose: bool,
manager: Box<dyn Signer>,
staker: Box<dyn Signer>,
depositor: Option<Box<dyn Signer>>,
token_owner: Box<dyn Signer>,
fee_payer: Box<dyn Signer>,
dry_run: bool,
@ -94,7 +95,12 @@ fn send_transaction(
Ok(())
}
fn command_create_pool(config: &Config, fee: Fee, max_validators: u32) -> CommandResult {
fn command_create_pool(
config: &Config,
deposit_authority: Option<Pubkey>,
fee: Fee,
max_validators: u32,
) -> CommandResult {
let reserve_stake = Keypair::new();
println!("Creating reserve stake {}", reserve_stake.pubkey());
@ -224,6 +230,7 @@ fn command_create_pool(config: &Config, fee: Fee, max_validators: u32) -> Comman
&mint_account.pubkey(),
&pool_fee_account.pubkey(),
&spl_token::id(),
deposit_authority,
fee,
max_validators,
)?,
@ -286,7 +293,17 @@ fn command_vsa_create(
}
fn command_vsa_add(config: &Config, stake_pool_address: &Pubkey, stake: &Pubkey) -> CommandResult {
if config.rpc_client.get_stake_activation(*stake, None)?.state != StakeActivationState::Active {
let stake_state = get_stake_state(&config.rpc_client, &stake)?;
if let stake_program::StakeState::Stake(meta, _stake) = stake_state {
if meta.authorized.withdrawer != config.staker.pubkey() {
let error = format!(
"Stake account withdraw authority must be the staker {}, actual {}",
config.staker.pubkey(),
meta.authorized.withdrawer
);
return Err(error.into());
}
} else {
return Err("Stake account is not active.".into());
}
@ -299,34 +316,16 @@ fn command_vsa_add(config: &Config, stake_pool_address: &Pubkey, stake: &Pubkey)
let mut instructions: Vec<Instruction> = vec![];
let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()];
// Calculate Deposit and Withdraw stake pool authorities
let pool_deposit_authority =
find_deposit_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
// Calculate Withdraw stake pool authorities
let pool_withdraw_authority =
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
instructions.extend(vec![
// Set Withdrawer on stake account to Deposit authority of the stake pool
stake_program::authorize(
&stake,
&config.staker.pubkey(),
&pool_deposit_authority,
StakeAuthorize::Withdrawer,
),
// Set Staker on stake account to Deposit authority of the stake pool
stake_program::authorize(
&stake,
&config.staker.pubkey(),
&pool_deposit_authority,
StakeAuthorize::Staker,
),
// Add validator stake account to the pool
spl_stake_pool::instruction::add_validator_to_pool(
&spl_stake_pool::id(),
&stake_pool_address,
&config.staker.pubkey(),
&pool_deposit_authority,
&pool_withdraw_authority,
&stake_pool.validator_list,
&stake,
@ -588,42 +587,49 @@ fn command_deposit(
},
)?;
// Calculate Deposit and Withdraw stake pool authorities
let pool_deposit_authority =
find_deposit_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
let pool_withdraw_authority =
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
instructions.extend(vec![
// Set Withdrawer on stake account to Deposit authority of the stake pool
stake_program::authorize(
&stake,
&config.staker.pubkey(),
&pool_deposit_authority,
StakeAuthorize::Withdrawer,
),
// Set Staker on stake account to Deposit authority of the stake pool
stake_program::authorize(
&stake,
&config.staker.pubkey(),
&pool_deposit_authority,
StakeAuthorize::Staker,
),
// Add stake account to the pool
spl_stake_pool::instruction::deposit(
let mut deposit_instructions = if let Some(deposit_authority) = config.depositor.as_ref() {
signers.push(deposit_authority.as_ref());
if deposit_authority.pubkey() != stake_pool.deposit_authority {
let error = format!(
"Invalid deposit authority specified, expected {}, received {}",
stake_pool.deposit_authority,
deposit_authority.pubkey()
);
return Err(error.into());
}
spl_stake_pool::instruction::deposit_with_authority(
&spl_stake_pool::id(),
&stake_pool_address,
&stake_pool.validator_list,
&pool_deposit_authority,
&deposit_authority.pubkey(),
&pool_withdraw_authority,
&stake,
&config.staker.pubkey(),
&validator_stake_account,
&token_receiver,
&stake_pool.pool_mint,
&spl_token::id(),
)?,
]);
)
} else {
spl_stake_pool::instruction::deposit(
&spl_stake_pool::id(),
&stake_pool_address,
&stake_pool.validator_list,
&pool_withdraw_authority,
&stake,
&config.staker.pubkey(),
&validator_stake_account,
&token_receiver,
&stake_pool.pool_mint,
&spl_token::id(),
)
};
instructions.append(&mut deposit_instructions);
let mut transaction =
Transaction::new_with_payer(&instructions, Some(&config.fee_payer.pubkey()));
@ -1130,6 +1136,17 @@ fn main() {
Defaults to the client keypair.",
),
)
.arg(
Arg::with_name("depositor")
.long("depositor")
.value_name("KEYPAIR")
.validator(is_keypair)
.takes_value(true)
.help(
"Specify the stake pool depositor. \
This may be a keypair file, the ASK keyword.",
),
)
.arg(
Arg::with_name("token_owner")
.long("token-owner")
@ -1186,6 +1203,15 @@ fn main() {
.required(true)
.help("Max number of validators included in the stake pool"),
)
.arg(
Arg::with_name("deposit_authority")
.long("deposit-authority")
.short("a")
.validator(is_pubkey)
.value_name("DEPOSIT_AUTHORITY_ADDRESS")
.takes_value(true)
.help("Deposit authority required to sign all deposits into the stake pool"),
)
)
.subcommand(SubCommand::with_name("create-validator-stake")
.about("Create a new stake account to use with the pool. Must be signed by the pool staker.")
@ -1515,6 +1541,22 @@ fn main() {
eprintln!("error: {}", e);
exit(1);
});
let depositor = if matches.is_present("depositor") {
Some(
signer_from_path(
&matches,
&cli_config.keypair_path,
"depositor",
&mut wallet_manager,
)
.unwrap_or_else(|e| {
eprintln!("error: {}", e);
exit(1);
}),
)
} else {
None
};
let manager = signer_from_path(
&matches,
&cli_config.keypair_path,
@ -1554,6 +1596,7 @@ fn main() {
verbose,
manager,
staker,
depositor,
token_owner,
fee_payer,
dry_run,
@ -1563,11 +1606,13 @@ fn main() {
let _ = match matches.subcommand() {
("create-pool", Some(arg_matches)) => {
let deposit_authority = pubkey_of(arg_matches, "deposit_authority");
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
command_create_pool(
&config,
deposit_authority,
Fee {
denominator,
numerator,

View File

@ -4,7 +4,8 @@
use {
crate::{
find_stake_program_address, find_transient_stake_program_address, stake_program, state::Fee,
find_deposit_authority_program_address, find_stake_program_address,
find_transient_stake_program_address, stake_program, state::Fee,
},
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
solana_program::{
@ -32,6 +33,9 @@ pub enum StakePoolInstruction {
/// 7. `[]` Clock sysvar
/// 8. `[]` Rent sysvar
/// 9. `[]` Token program id
/// 10. `[]` (Optional) Deposit authority that must sign all deposits.
/// Defaults to the program address generated using
/// `find_deposit_authority_program_address`, making deposits permissionless.
Initialize {
/// Fee assessed as percentage of perceived rewards
#[allow(dead_code)] // but it's not
@ -70,13 +74,13 @@ pub enum StakePoolInstruction {
///
/// 0. `[w]` Stake pool
/// 1. `[s]` Staker
/// 2. `[]` Stake pool deposit authority
/// 3. `[]` Stake pool withdraw authority
/// 4. `[w]` Validator stake list storage account
/// 5. `[w]` Stake account to add to the pool, its withdraw authority should be set to stake pool deposit
/// 6. `[]` Clock sysvar
/// 7. '[]' Sysvar stake history account
/// 8. `[]` Stake program
/// 2. `[]` Stake pool withdraw authority
/// 3. `[w]` Validator stake list storage account
/// 4. `[w]` Stake account to add to the pool, its withdraw authority must
/// be set to the staker
/// 5. `[]` Clock sysvar
/// 6. '[]' Sysvar stake history account
/// 7. `[]` Stake program
AddValidatorToPool,
/// (Staker only) Removes validator from the pool
@ -195,7 +199,7 @@ pub enum StakePoolInstruction {
/// 1. `[w]` Validator stake list storage account
/// 2. `[]` Stake pool deposit authority
/// 3. `[]` Stake pool withdraw authority
/// 4. `[w]` Stake account to join the pool (withdraw should be set to stake pool deposit)
/// 4. `[w]` Stake account to join the pool (withdraw authority for the stake account should be first set to the stake pool deposit authority)
/// 5. `[w]` Validator stake account for the stake account to be merged with
/// 6. `[w]` User account to receive pool tokens
/// 8. `[w]` Pool token mint account
@ -267,6 +271,7 @@ pub fn initialize(
pool_mint: &Pubkey,
manager_pool_account: &Pubkey,
token_program_id: &Pubkey,
deposit_authority: Option<Pubkey>,
fee: Fee,
max_validators: u32,
) -> Result<Instruction, ProgramError> {
@ -275,7 +280,7 @@ pub fn initialize(
max_validators,
};
let data = init_data.try_to_vec()?;
let accounts = vec![
let mut accounts = vec![
AccountMeta::new(*stake_pool, true),
AccountMeta::new_readonly(*manager, true),
AccountMeta::new_readonly(*staker, false),
@ -287,6 +292,9 @@ pub fn initialize(
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(*token_program_id, false),
];
if let Some(deposit_authority) = deposit_authority {
accounts.push(AccountMeta::new_readonly(deposit_authority, true));
}
Ok(Instruction {
program_id: *program_id,
accounts,
@ -328,7 +336,6 @@ pub fn add_validator_to_pool(
program_id: &Pubkey,
stake_pool: &Pubkey,
staker: &Pubkey,
stake_pool_deposit: &Pubkey,
stake_pool_withdraw: &Pubkey,
validator_list: &Pubkey,
stake_account: &Pubkey,
@ -336,7 +343,6 @@ pub fn add_validator_to_pool(
let accounts = vec![
AccountMeta::new(*stake_pool, false),
AccountMeta::new_readonly(*staker, true),
AccountMeta::new_readonly(*stake_pool_deposit, false),
AccountMeta::new_readonly(*stake_pool_withdraw, false),
AccountMeta::new(*validator_list, false),
AccountMeta::new(*stake_account, false),
@ -529,25 +535,28 @@ pub fn update_stake_pool_balance(
}
}
/// Creates a 'Deposit' instruction.
/// Creates instructions required to deposit into a stake pool, given a stake
/// account owned by the user.
pub fn deposit(
program_id: &Pubkey,
stake_pool: &Pubkey,
validator_list_storage: &Pubkey,
stake_pool_deposit: &Pubkey,
stake_pool_withdraw: &Pubkey,
stake_to_join: &Pubkey,
stake_pool_withdraw_authority: &Pubkey,
deposit_stake_address: &Pubkey,
deposit_stake_withdraw_authority: &Pubkey,
validator_stake_accont: &Pubkey,
pool_tokens_to: &Pubkey,
pool_mint: &Pubkey,
token_program_id: &Pubkey,
) -> Result<Instruction, ProgramError> {
) -> Vec<Instruction> {
let stake_pool_deposit_authority =
find_deposit_authority_program_address(program_id, stake_pool).0;
let accounts = vec![
AccountMeta::new(*stake_pool, false),
AccountMeta::new(*validator_list_storage, false),
AccountMeta::new_readonly(*stake_pool_deposit, false),
AccountMeta::new_readonly(*stake_pool_withdraw, false),
AccountMeta::new(*stake_to_join, false),
AccountMeta::new_readonly(stake_pool_deposit_authority, false),
AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
AccountMeta::new(*deposit_stake_address, false),
AccountMeta::new(*validator_stake_accont, false),
AccountMeta::new(*pool_tokens_to, false),
AccountMeta::new(*pool_mint, false),
@ -556,11 +565,76 @@ pub fn deposit(
AccountMeta::new_readonly(*token_program_id, false),
AccountMeta::new_readonly(stake_program::id(), false),
];
Ok(Instruction {
program_id: *program_id,
accounts,
data: StakePoolInstruction::Deposit.try_to_vec()?,
})
vec![
stake_program::authorize(
deposit_stake_address,
deposit_stake_withdraw_authority,
&stake_pool_deposit_authority,
stake_program::StakeAuthorize::Staker,
),
stake_program::authorize(
deposit_stake_address,
deposit_stake_withdraw_authority,
&stake_pool_deposit_authority,
stake_program::StakeAuthorize::Withdrawer,
),
Instruction {
program_id: *program_id,
accounts,
data: StakePoolInstruction::Deposit.try_to_vec().unwrap(),
},
]
}
/// Creates instructions required to deposit into a stake pool, given a stake
/// account owned by the user. The difference with `deposit()` is that a deposit
/// authority must sign this instruction, which is required for private pools.
pub fn deposit_with_authority(
program_id: &Pubkey,
stake_pool: &Pubkey,
validator_list_storage: &Pubkey,
stake_pool_deposit_authority: &Pubkey,
stake_pool_withdraw_authority: &Pubkey,
deposit_stake_address: &Pubkey,
deposit_stake_withdraw_authority: &Pubkey,
validator_stake_accont: &Pubkey,
pool_tokens_to: &Pubkey,
pool_mint: &Pubkey,
token_program_id: &Pubkey,
) -> Vec<Instruction> {
let accounts = vec![
AccountMeta::new(*stake_pool, false),
AccountMeta::new(*validator_list_storage, false),
AccountMeta::new_readonly(*stake_pool_deposit_authority, true),
AccountMeta::new_readonly(*stake_pool_withdraw_authority, false),
AccountMeta::new(*deposit_stake_address, false),
AccountMeta::new(*validator_stake_accont, false),
AccountMeta::new(*pool_tokens_to, false),
AccountMeta::new(*pool_mint, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
AccountMeta::new_readonly(*token_program_id, false),
AccountMeta::new_readonly(stake_program::id(), false),
];
vec![
stake_program::authorize(
deposit_stake_address,
deposit_stake_withdraw_authority,
stake_pool_deposit_authority,
stake_program::StakeAuthorize::Staker,
),
stake_program::authorize(
deposit_stake_address,
deposit_stake_withdraw_authority,
stake_pool_deposit_authority,
stake_program::StakeAuthorize::Withdrawer,
),
Instruction {
program_id: *program_id,
accounts,
data: StakePoolInstruction::Deposit.try_to_vec().unwrap(),
},
]
}
/// Creates a 'withdraw' instruction.

View File

@ -4,6 +4,7 @@ use {
crate::{
borsh::try_from_slice_unchecked,
error::StakePoolError,
find_deposit_authority_program_address,
instruction::StakePoolInstruction,
minimum_reserve_lamports, minimum_stake_lamports, stake_program,
state::{AccountType, Fee, StakePool, ValidatorList, ValidatorStakeInfo},
@ -203,10 +204,14 @@ impl Processor {
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
let signers = &[&authority_signature_seeds[..]];
let ix =
let split_instruction =
stake_program::split_only(stake_account.key, authority.key, amount, split_stake.key);
invoke_signed(&ix, &[stake_account, split_stake, authority], signers)
invoke_signed(
&split_instruction,
&[stake_account, split_stake, authority],
signers,
)
}
/// Issue a stake_merge instruction.
@ -226,10 +231,11 @@ impl Processor {
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
let signers = &[&authority_signature_seeds[..]];
let ix = stake_program::merge(destination_account.key, source_account.key, authority.key);
let merge_instruction =
stake_program::merge(destination_account.key, source_account.key, authority.key);
invoke_signed(
&ix,
&merge_instruction,
&[
destination_account,
source_account,
@ -242,16 +248,53 @@ impl Processor {
)
}
/// Issue a stake_set_manager instruction.
#[allow(clippy::too_many_arguments)]
/// Issue stake_program::authorize instructions to update both authorities
fn stake_authorize<'a>(
stake_account: AccountInfo<'a>,
stake_authority: AccountInfo<'a>,
new_stake_authority: &Pubkey,
clock: AccountInfo<'a>,
stake_program_info: AccountInfo<'a>,
) -> Result<(), ProgramError> {
let authorize_instruction = stake_program::authorize(
stake_account.key,
stake_authority.key,
new_stake_authority,
stake_program::StakeAuthorize::Staker,
);
invoke(
&authorize_instruction,
&[
stake_account.clone(),
clock.clone(),
stake_authority.clone(),
stake_program_info.clone(),
],
)?;
let authorize_instruction = stake_program::authorize(
stake_account.key,
stake_authority.key,
new_stake_authority,
stake_program::StakeAuthorize::Withdrawer,
);
invoke(
&authorize_instruction,
&[stake_account, clock, stake_authority, stake_program_info],
)
}
/// Issue stake_program::authorize instructions to update both authorities
#[allow(clippy::too_many_arguments)]
fn stake_authorize_signed<'a>(
stake_pool: &Pubkey,
stake_account: AccountInfo<'a>,
authority: AccountInfo<'a>,
stake_authority: AccountInfo<'a>,
authority_type: &[u8],
bump_seed: u8,
new_staker: &Pubkey,
staker_auth: stake_program::StakeAuthorize,
new_stake_authority: &Pubkey,
clock: AccountInfo<'a>,
stake_program_info: AccountInfo<'a>,
) -> Result<(), ProgramError> {
@ -259,12 +302,33 @@ impl Processor {
let authority_signature_seeds = [&me_bytes[..32], authority_type, &[bump_seed]];
let signers = &[&authority_signature_seeds[..]];
let ix =
stake_program::authorize(stake_account.key, authority.key, new_staker, staker_auth);
let authorize_instruction = stake_program::authorize(
stake_account.key,
stake_authority.key,
new_stake_authority,
stake_program::StakeAuthorize::Staker,
);
invoke_signed(
&ix,
&[stake_account, clock, authority, stake_program_info],
&authorize_instruction,
&[
stake_account.clone(),
clock.clone(),
stake_authority.clone(),
stake_program_info.clone(),
],
signers,
)?;
let authorize_instruction = stake_program::authorize(
stake_account.key,
stake_authority.key,
new_stake_authority,
stake_program::StakeAuthorize::Withdrawer,
);
invoke_signed(
&authorize_instruction,
&[stake_account, clock, stake_authority, stake_program_info],
signers,
)
}
@ -414,8 +478,10 @@ impl Processor {
return Err(StakePoolError::WrongAccountMint.into());
}
let (_, deposit_bump_seed) =
crate::find_deposit_authority_program_address(program_id, stake_pool_info.key);
let deposit_authority = match next_account_info(account_info_iter) {
Ok(deposit_authority_info) => *deposit_authority_info.key,
Err(_) => find_deposit_authority_program_address(program_id, stake_pool_info.key).0,
};
let (withdraw_authority_key, withdraw_bump_seed) =
crate::find_withdraw_authority_program_address(program_id, stake_pool_info.key);
@ -475,7 +541,7 @@ impl Processor {
stake_pool.manager = *manager_info.key;
stake_pool.staker = *staker_info.key;
stake_pool.reserve_stake = *reserve_stake_info.key;
stake_pool.deposit_bump_seed = deposit_bump_seed;
stake_pool.deposit_authority = deposit_authority;
stake_pool.withdraw_bump_seed = withdraw_bump_seed;
stake_pool.validator_list = *validator_list_info.key;
stake_pool.pool_mint = *pool_mint_info.key;
@ -594,8 +660,7 @@ impl Processor {
let account_info_iter = &mut accounts.iter();
let stake_pool_info = next_account_info(account_info_iter)?;
let staker_info = next_account_info(account_info_iter)?;
let deposit_info = next_account_info(account_info_iter)?;
let withdraw_info = next_account_info(account_info_iter)?;
let withdraw_authority_info = next_account_info(account_info_iter)?;
let validator_list_info = next_account_info(account_info_iter)?;
let stake_account_info = next_account_info(account_info_iter)?;
let clock_info = next_account_info(account_info_iter)?;
@ -614,8 +679,11 @@ impl Processor {
return Err(StakePoolError::InvalidState.into());
}
stake_pool.check_authority_withdraw(withdraw_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_authority_deposit(deposit_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_authority_withdraw(
withdraw_authority_info.key,
program_id,
stake_pool_info.key,
)?;
stake_pool.check_staker(staker_info)?;
@ -645,6 +713,11 @@ impl Processor {
&vote_account_address,
)?;
if meta.lockup != stake_program::Lockup::default() {
msg!("Validator stake account has a lockup");
return Err(StakePoolError::WrongStakeState.into());
}
if validator_list.contains(&vote_account_address) {
return Err(StakePoolError::ValidatorAlreadyAdded.into());
}
@ -665,22 +738,13 @@ impl Processor {
//Self::check_stake_activation(stake_account_info, clock, stake_history)?;
// Update Withdrawer and Staker authority to the program withdraw authority
for authority in &[
stake_program::StakeAuthorize::Withdrawer,
stake_program::StakeAuthorize::Staker,
] {
Self::stake_authorize(
stake_pool_info.key,
stake_account_info.clone(),
deposit_info.clone(),
AUTHORITY_DEPOSIT,
stake_pool.deposit_bump_seed,
withdraw_info.key,
*authority,
clock_info.clone(),
stake_program_info.clone(),
)?;
}
Self::stake_authorize(
stake_account_info.clone(),
staker_info.clone(),
withdraw_authority_info.key,
clock_info.clone(),
stake_program_info.clone(),
)?;
validator_list.validators.push(ValidatorStakeInfo {
vote_account_address,
@ -700,7 +764,7 @@ impl Processor {
let account_info_iter = &mut accounts.iter();
let stake_pool_info = next_account_info(account_info_iter)?;
let staker_info = next_account_info(account_info_iter)?;
let withdraw_info = next_account_info(account_info_iter)?;
let withdraw_authority_info = next_account_info(account_info_iter)?;
let new_stake_authority_info = next_account_info(account_info_iter)?;
let validator_list_info = next_account_info(account_info_iter)?;
let stake_account_info = next_account_info(account_info_iter)?;
@ -717,7 +781,11 @@ impl Processor {
return Err(StakePoolError::InvalidState.into());
}
stake_pool.check_authority_withdraw(withdraw_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_authority_withdraw(
withdraw_authority_info.key,
program_id,
stake_pool_info.key,
)?;
stake_pool.check_staker(staker_info)?;
if stake_pool.last_update_epoch < clock.epoch {
@ -772,22 +840,16 @@ impl Processor {
return Err(StakePoolError::StakeLamportsNotEqualToMinimum.into());
}
for authority in &[
stake_program::StakeAuthorize::Withdrawer,
stake_program::StakeAuthorize::Staker,
] {
Self::stake_authorize(
stake_pool_info.key,
stake_account_info.clone(),
withdraw_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
new_stake_authority_info.key,
*authority,
clock_info.clone(),
stake_program_info.clone(),
)?;
}
Self::stake_authorize_signed(
stake_pool_info.key,
stake_account_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
new_stake_authority_info.key,
clock_info.clone(),
stake_program_info.clone(),
)?;
validator_list
.validators
@ -1382,8 +1444,8 @@ impl Processor {
let account_info_iter = &mut accounts.iter();
let stake_pool_info = next_account_info(account_info_iter)?;
let validator_list_info = next_account_info(account_info_iter)?;
let deposit_info = next_account_info(account_info_iter)?;
let withdraw_info = next_account_info(account_info_iter)?;
let deposit_authority_info = next_account_info(account_info_iter)?;
let withdraw_authority_info = next_account_info(account_info_iter)?;
let stake_info = next_account_info(account_info_iter)?;
let validator_stake_account_info = next_account_info(account_info_iter)?;
let dest_user_info = next_account_info(account_info_iter)?;
@ -1406,8 +1468,12 @@ impl Processor {
//Self::check_stake_activation(stake_info, clock, stake_history)?;
stake_pool.check_authority_withdraw(withdraw_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_authority_deposit(deposit_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_authority_withdraw(
withdraw_authority_info.key,
program_id,
stake_pool_info.key,
)?;
stake_pool.check_deposit_authority(deposit_authority_info.key)?;
stake_pool.check_mint(pool_mint_info)?;
if stake_pool.token_program_id != *token_program_info.key {
@ -1451,34 +1517,33 @@ impl Processor {
validator_stake_account_info.lamports()
);
Self::stake_authorize(
stake_pool_info.key,
stake_info.clone(),
deposit_info.clone(),
AUTHORITY_DEPOSIT,
stake_pool.deposit_bump_seed,
withdraw_info.key,
stake_program::StakeAuthorize::Withdrawer,
clock_info.clone(),
stake_program_info.clone(),
)?;
Self::stake_authorize(
stake_pool_info.key,
stake_info.clone(),
deposit_info.clone(),
AUTHORITY_DEPOSIT,
stake_pool.deposit_bump_seed,
withdraw_info.key,
stake_program::StakeAuthorize::Staker,
clock_info.clone(),
stake_program_info.clone(),
)?;
let (deposit_authority_program_address, deposit_bump_seed) =
find_deposit_authority_program_address(program_id, stake_pool_info.key);
if *deposit_authority_info.key == deposit_authority_program_address {
Self::stake_authorize_signed(
stake_pool_info.key,
stake_info.clone(),
deposit_authority_info.clone(),
AUTHORITY_DEPOSIT,
deposit_bump_seed,
withdraw_authority_info.key,
clock_info.clone(),
stake_program_info.clone(),
)?;
} else {
Self::stake_authorize(
stake_info.clone(),
deposit_authority_info.clone(),
withdraw_authority_info.key,
clock_info.clone(),
stake_program_info.clone(),
)?;
}
Self::stake_merge(
stake_pool_info.key,
stake_info.clone(),
withdraw_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
validator_stake_account_info.clone(),
@ -1492,7 +1557,7 @@ impl Processor {
token_program_info.clone(),
pool_mint_info.clone(),
dest_user_info.clone(),
withdraw_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
new_pool_tokens,
@ -1530,7 +1595,7 @@ impl Processor {
let account_info_iter = &mut accounts.iter();
let stake_pool_info = next_account_info(account_info_iter)?;
let validator_list_info = next_account_info(account_info_iter)?;
let withdraw_info = next_account_info(account_info_iter)?;
let withdraw_authority_info = next_account_info(account_info_iter)?;
let stake_split_from = next_account_info(account_info_iter)?;
let stake_split_to = next_account_info(account_info_iter)?;
let user_stake_authority = next_account_info(account_info_iter)?;
@ -1550,8 +1615,12 @@ impl Processor {
return Err(StakePoolError::InvalidState.into());
}
stake_pool.check_authority_withdraw(withdraw_info.key, program_id, stake_pool_info.key)?;
stake_pool.check_mint(pool_mint_info)?;
stake_pool.check_authority_withdraw(
withdraw_authority_info.key,
program_id,
stake_pool_info.key,
)?;
if stake_pool.token_program_id != *token_program_info.key {
return Err(ProgramError::IncorrectProgramId);
@ -1601,7 +1670,7 @@ impl Processor {
token_program_info.clone(),
burn_from_info.clone(),
pool_mint_info.clone(),
withdraw_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
pool_tokens,
@ -1610,33 +1679,20 @@ impl Processor {
Self::stake_split(
stake_pool_info.key,
stake_split_from.clone(),
withdraw_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
withdraw_lamports,
stake_split_to.clone(),
)?;
Self::stake_authorize(
Self::stake_authorize_signed(
stake_pool_info.key,
stake_split_to.clone(),
withdraw_info.clone(),
withdraw_authority_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
user_stake_authority.key,
stake_program::StakeAuthorize::Withdrawer,
clock_info.clone(),
stake_program_info.clone(),
)?;
Self::stake_authorize(
stake_pool_info.key,
stake_split_to.clone(),
withdraw_info.clone(),
AUTHORITY_WITHDRAW,
stake_pool.withdraw_bump_seed,
user_stake_authority.key,
stake_program::StakeAuthorize::Staker,
clock_info.clone(),
stake_program_info.clone(),
)?;

View File

@ -39,9 +39,16 @@ pub struct StakePool {
/// distribution
pub staker: Pubkey,
/// Deposit authority bump seed
/// for `create_program_address(&[state::StakePool account, "deposit"])`
pub deposit_bump_seed: u8,
/// Deposit authority
///
/// If a depositor pubkey is specified on initialization, then deposits must be
/// signed by this authority. If no deposit authority is specified,
/// then the stake pool will default to the result of:
/// `Pubkey::find_program_address(
/// &[&stake_pool_address.to_bytes()[..32], b"deposit"],
/// program_id,
/// )`
pub deposit_authority: Pubkey,
/// Withdrawal authority bump seed
/// for `create_program_address(&[state::StakePool account, "withdrawal"])`
@ -165,19 +172,15 @@ impl StakePool {
)
}
/// Checks that the deposit authority is valid
pub(crate) fn check_authority_deposit(
pub(crate) fn check_deposit_authority(
&self,
deposit_authority: &Pubkey,
program_id: &Pubkey,
stake_pool_address: &Pubkey,
) -> Result<(), ProgramError> {
Self::check_authority(
deposit_authority,
program_id,
stake_pool_address,
crate::AUTHORITY_DEPOSIT,
self.deposit_bump_seed,
)
if self.deposit_authority == *deposit_authority {
Ok(())
} else {
Err(StakePoolError::InvalidProgramAddress.into())
}
}
/// Check staker validity and signature

View File

@ -233,10 +233,7 @@ async fn fail_with_unknown_validator() {
decrease_lamports,
) = setup().await;
let unknown_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let unknown_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
unknown_stake
.create_and_delegate(
&mut banks_client,

View File

@ -102,28 +102,6 @@ async fn test_stake_pool_deposit() {
)
.await;
// Change authority to the stake pool's deposit
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&stake_authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Withdrawer,
)
.await;
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&stake_authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Staker,
)
.await;
// make pool token account
let user_pool_account = Keypair::new();
create_token_account(
@ -163,6 +141,7 @@ async fn test_stake_pool_deposit() {
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&stake_authority,
)
.await
.unwrap();
@ -293,8 +272,8 @@ async fn test_stake_pool_deposit_with_wrong_token_program_id() {
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
create_independent_stake_account(
&mut banks_client,
@ -323,22 +302,21 @@ async fn test_stake_pool_deposit_with_wrong_token_program_id() {
let wrong_token_program = Keypair::new();
let mut transaction = Transaction::new_with_payer(
&[instruction::deposit(
&instruction::deposit(
&id(),
&stake_pool_accounts.stake_pool.pubkey(),
&stake_pool_accounts.validator_list.pubkey(),
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.withdraw_authority,
&user_stake.pubkey(),
&user.pubkey(),
&validator_stake_account.stake_account,
&user_pool_account.pubkey(),
&stake_pool_accounts.pool_mint.pubkey(),
&wrong_token_program.pubkey(),
)
.unwrap()],
),
Some(&payer.pubkey()),
);
transaction.sign(&[&payer], recent_blockhash);
transaction.sign(&[&payer, &user], recent_blockhash);
let transaction_error = banks_client
.process_transaction(transaction)
.await
@ -368,8 +346,8 @@ async fn test_stake_pool_deposit_with_wrong_validator_list_account() {
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
create_independent_stake_account(
&mut banks_client,
@ -406,6 +384,7 @@ async fn test_stake_pool_deposit_with_wrong_validator_list_account() {
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.err()
@ -432,10 +411,8 @@ async fn test_stake_pool_deposit_to_unknown_validator() {
.await
.unwrap();
let validator_stake_account = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let validator_stake_account =
ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
validator_stake_account
.create_and_delegate(
&mut banks_client,
@ -462,8 +439,8 @@ async fn test_stake_pool_deposit_to_unknown_validator() {
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
create_independent_stake_account(
&mut banks_client,
@ -484,6 +461,7 @@ async fn test_stake_pool_deposit_to_unknown_validator() {
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.err()
@ -503,75 +481,6 @@ async fn test_stake_pool_deposit_to_unknown_validator() {
}
}
#[tokio::test]
async fn test_stake_pool_deposit_with_wrong_deposit_authority() {
let (
mut banks_client,
payer,
recent_blockhash,
mut stake_pool_accounts,
validator_stake_account,
) = setup().await;
let user = Keypair::new();
// make stake account
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
};
create_independent_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake,
&authorized,
&lockup,
TEST_STAKE_AMOUNT,
)
.await;
// make pool token account
let user_pool_account = Keypair::new();
create_token_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_pool_account,
&stake_pool_accounts.pool_mint.pubkey(),
&user.pubkey(),
)
.await
.unwrap();
stake_pool_accounts.deposit_authority = Keypair::new().pubkey();
let transaction_error = stake_pool_accounts
.deposit_stake(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
)
.await
.err()
.unwrap();
match transaction_error {
TransportError::TransactionError(TransactionError::InstructionError(
_,
InstructionError::Custom(error_index),
)) => {
let program_error = error::StakePoolError::InvalidProgramAddress as u32;
assert_eq!(error_index, program_error);
}
_ => panic!("Wrong error occurs while try to make a deposit with wrong deposit authority"),
}
}
#[tokio::test]
async fn test_stake_pool_deposit_with_wrong_withdraw_authority() {
let (
@ -587,8 +496,8 @@ async fn test_stake_pool_deposit_with_wrong_withdraw_authority() {
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
create_independent_stake_account(
&mut banks_client,
@ -624,6 +533,7 @@ async fn test_stake_pool_deposit_with_wrong_withdraw_authority() {
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.err()
@ -641,76 +551,18 @@ async fn test_stake_pool_deposit_with_wrong_withdraw_authority() {
}
}
#[tokio::test]
async fn test_stake_pool_deposit_with_wrong_set_deposit_authority() {
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake_account) =
setup().await;
let user = Keypair::new();
// make stake account
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: Keypair::new().pubkey(),
withdrawer: stake_pool_accounts.deposit_authority,
};
create_independent_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake,
&authorized,
&lockup,
TEST_STAKE_AMOUNT,
)
.await;
// make pool token account
let user_pool_account = Keypair::new();
create_token_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_pool_account,
&stake_pool_accounts.pool_mint.pubkey(),
&user.pubkey(),
)
.await
.unwrap();
let transaction_error = stake_pool_accounts
.deposit_stake(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
)
.await
.err()
.unwrap();
match transaction_error {
TransportError::TransactionError(TransactionError::InstructionError(_, error)) => {
assert_eq!(error, InstructionError::MissingRequiredSignature);
}
_ => {
panic!("Wrong error occurs while try to make deposit with wrong set deposit authority")
}
}
}
#[tokio::test]
async fn test_stake_pool_deposit_with_wrong_mint_for_receiver_acc() {
let (mut banks_client, payer, recent_blockhash, stake_pool_accounts, validator_stake_account) =
setup().await;
// make stake account
let user = Keypair::new();
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: stake_pool_accounts.deposit_authority,
withdrawer: stake_pool_accounts.deposit_authority,
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
create_independent_stake_account(
&mut banks_client,
@ -757,6 +609,7 @@ async fn test_stake_pool_deposit_with_wrong_mint_for_receiver_acc() {
&user_stake.pubkey(),
&outside_pool_fee_acc.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.err()
@ -779,3 +632,180 @@ async fn test_deposit_with_uninitialized_validator_list() {} // TODO
#[tokio::test]
async fn test_deposit_with_out_of_dated_pool_balances() {} // TODO
#[tokio::test]
async fn success_with_deposit_authority() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let deposit_authority = Keypair::new();
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
let validator_stake_account = simple_add_validator_to_pool(
&mut banks_client,
&payer,
&recent_blockhash,
&stake_pool_accounts,
)
.await;
let user = Keypair::new();
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
let _stake_lamports = create_independent_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake,
&authorized,
&lockup,
TEST_STAKE_AMOUNT,
)
.await;
create_vote(
&mut banks_client,
&payer,
&recent_blockhash,
&validator_stake_account.validator,
&validator_stake_account.vote,
)
.await;
delegate_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user,
&validator_stake_account.vote.pubkey(),
)
.await;
// make pool token account
let user_pool_account = Keypair::new();
create_token_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_pool_account,
&stake_pool_accounts.pool_mint.pubkey(),
&user.pubkey(),
)
.await
.unwrap();
stake_pool_accounts
.deposit_stake(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.unwrap();
}
#[tokio::test]
async fn fail_without_deposit_authority_signature() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let deposit_authority = Keypair::new();
let mut stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
let validator_stake_account = simple_add_validator_to_pool(
&mut banks_client,
&payer,
&recent_blockhash,
&stake_pool_accounts,
)
.await;
let user = Keypair::new();
let user_stake = Keypair::new();
let lockup = stake_program::Lockup::default();
let authorized = stake_program::Authorized {
staker: user.pubkey(),
withdrawer: user.pubkey(),
};
let _stake_lamports = create_independent_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake,
&authorized,
&lockup,
TEST_STAKE_AMOUNT,
)
.await;
create_vote(
&mut banks_client,
&payer,
&recent_blockhash,
&validator_stake_account.validator,
&validator_stake_account.vote,
)
.await;
delegate_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user,
&validator_stake_account.vote.pubkey(),
)
.await;
// make pool token account
let user_pool_account = Keypair::new();
create_token_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_pool_account,
&stake_pool_accounts.pool_mint.pubkey(),
&user.pubkey(),
)
.await
.unwrap();
let wrong_depositor = Keypair::new();
stake_pool_accounts.deposit_authority = wrong_depositor.pubkey();
stake_pool_accounts.deposit_authority_keypair = Some(wrong_depositor);
let error = stake_pool_accounts
.deposit_stake(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.pubkey(),
&user_pool_account.pubkey(),
&validator_stake_account.stake_account,
&user,
)
.await
.unwrap_err()
.unwrap();
match error {
TransactionError::InstructionError(_, InstructionError::Custom(error_index)) => {
assert_eq!(
error_index,
error::StakePoolError::InvalidProgramAddress as u32
);
}
_ => panic!("Wrong error occurs while try to make a deposit with wrong stake program ID"),
}
}

View File

@ -210,6 +210,7 @@ pub async fn create_stake_pool(
pool_token_account: &Pubkey,
manager: &Keypair,
staker: &Pubkey,
deposit_authority: &Option<Keypair>,
fee: &state::Fee,
max_validators: u32,
) -> Result<(), TransportError> {
@ -245,6 +246,7 @@ pub async fn create_stake_pool(
pool_mint,
pool_token_account,
&spl_token::id(),
deposit_authority.as_ref().map(|k| k.pubkey()),
*fee,
max_validators,
)
@ -252,10 +254,11 @@ pub async fn create_stake_pool(
],
Some(&payer.pubkey()),
);
transaction.sign(
&[payer, stake_pool, validator_list, manager],
*recent_blockhash,
);
let mut signers = vec![payer, stake_pool, validator_list, manager];
if let Some(deposit_authority) = deposit_authority.as_ref() {
signers.push(deposit_authority);
}
transaction.sign(&signers, *recent_blockhash);
banks_client.process_transaction(transaction).await?;
Ok(())
}
@ -424,14 +427,13 @@ pub async fn authorize_stake_account(
pub struct ValidatorStakeAccount {
pub stake_account: Pubkey,
pub transient_stake_account: Pubkey,
pub target_authority: Pubkey,
pub vote: Keypair,
pub validator: Keypair,
pub stake_pool: Pubkey,
}
impl ValidatorStakeAccount {
pub fn new_with_target_authority(authority: &Pubkey, stake_pool: &Pubkey) -> Self {
pub fn new(stake_pool: &Pubkey) -> Self {
let validator = Keypair::new();
let vote = Keypair::new();
let (stake_account, _) = find_stake_program_address(&id(), &vote.pubkey(), stake_pool);
@ -440,7 +442,6 @@ impl ValidatorStakeAccount {
ValidatorStakeAccount {
stake_account,
transient_stake_account,
target_authority: *authority,
vote,
validator,
stake_pool: *stake_pool,
@ -473,28 +474,6 @@ impl ValidatorStakeAccount {
&self.vote.pubkey(),
)
.await;
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&self.stake_account,
&staker,
&self.target_authority,
stake_program::StakeAuthorize::Staker,
)
.await;
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&self.stake_account,
&staker,
&self.target_authority,
stake_program::StakeAuthorize::Withdrawer,
)
.await;
}
}
@ -508,6 +487,7 @@ pub struct StakePoolAccounts {
pub staker: Keypair,
pub withdraw_authority: Pubkey,
pub deposit_authority: Pubkey,
pub deposit_authority_keypair: Option<Keypair>,
pub fee: state::Fee,
pub max_validators: u32,
}
@ -541,6 +521,7 @@ impl StakePoolAccounts {
staker,
withdraw_authority,
deposit_authority,
deposit_authority_keypair: None,
fee: state::Fee {
numerator: 1,
denominator: 100,
@ -549,6 +530,13 @@ impl StakePoolAccounts {
}
}
pub fn new_with_deposit_authority(deposit_authority: Keypair) -> Self {
let mut stake_pool_accounts = Self::new();
stake_pool_accounts.deposit_authority = deposit_authority.pubkey();
stake_pool_accounts.deposit_authority_keypair = Some(deposit_authority);
stake_pool_accounts
}
pub fn calculate_fee(&self, amount: u64) -> u64 {
amount * self.fee.numerator / self.fee.denominator
}
@ -601,6 +589,7 @@ impl StakePoolAccounts {
&self.pool_fee_account.pubkey(),
&self.manager,
&self.staker.pubkey(),
&self.deposit_authority_keypair,
&self.fee,
self.max_validators,
)
@ -608,6 +597,7 @@ impl StakePoolAccounts {
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub async fn deposit_stake(
&self,
banks_client: &mut BanksClient,
@ -616,23 +606,43 @@ impl StakePoolAccounts {
stake: &Pubkey,
pool_account: &Pubkey,
validator_stake_account: &Pubkey,
current_staker: &Keypair,
) -> Result<(), TransportError> {
let transaction = Transaction::new_signed_with_payer(
&[instruction::deposit(
let mut signers = vec![payer, current_staker];
let instructions = if let Some(deposit_authority) = self.deposit_authority_keypair.as_ref()
{
signers.push(deposit_authority);
instruction::deposit_with_authority(
&id(),
&self.stake_pool.pubkey(),
&self.validator_list.pubkey(),
&self.deposit_authority,
&self.withdraw_authority,
stake,
&current_staker.pubkey(),
validator_stake_account,
pool_account,
&self.pool_mint.pubkey(),
&spl_token::id(),
)
.unwrap()],
} else {
instruction::deposit(
&id(),
&self.stake_pool.pubkey(),
&self.validator_list.pubkey(),
&self.withdraw_authority,
stake,
&current_staker.pubkey(),
validator_stake_account,
pool_account,
&self.pool_mint.pubkey(),
&spl_token::id(),
)
};
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer],
&signers,
*recent_blockhash,
);
banks_client.process_transaction(transaction).await?;
@ -771,7 +781,6 @@ impl StakePoolAccounts {
&id(),
&self.stake_pool.pubkey(),
&self.staker.pubkey(),
&self.deposit_authority,
&self.withdraw_authority,
&self.validator_list.pubkey(),
stake,
@ -874,10 +883,7 @@ pub async fn simple_add_validator_to_pool(
recent_blockhash: &Hash,
stake_pool_accounts: &StakePoolAccounts,
) -> ValidatorStakeAccount {
let validator_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let validator_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
validator_stake
.create_and_delegate(
banks_client,
@ -970,26 +976,6 @@ impl DepositStakeAccount {
recent_blockhash: &Hash,
stake_pool_accounts: &StakePoolAccounts,
) {
authorize_stake_account(
banks_client,
payer,
recent_blockhash,
&self.stake.pubkey(),
&self.authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Staker,
)
.await;
authorize_stake_account(
banks_client,
&payer,
&recent_blockhash,
&self.stake.pubkey(),
&self.authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Withdrawer,
)
.await;
// make pool token account
create_token_account(
banks_client,
@ -1010,6 +996,7 @@ impl DepositStakeAccount {
&self.stake.pubkey(),
&self.pool_account.pubkey(),
&self.validator_stake_account,
&self.authority,
)
.await
.unwrap();
@ -1052,26 +1039,6 @@ pub async fn simple_deposit(
&vote_account,
)
.await;
authorize_stake_account(
banks_client,
payer,
recent_blockhash,
&stake.pubkey(),
&authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Staker,
)
.await;
authorize_stake_account(
banks_client,
&payer,
&recent_blockhash,
&stake.pubkey(),
&authority,
&stake_pool_accounts.deposit_authority,
stake_program::StakeAuthorize::Withdrawer,
)
.await;
// make pool token account
let pool_account = Keypair::new();
create_token_account(
@ -1094,6 +1061,7 @@ pub async fn simple_deposit(
&stake.pubkey(),
&pool_account.pubkey(),
&validator_stake_account,
&authority,
)
.await
.unwrap();

View File

@ -233,10 +233,7 @@ async fn fail_with_unknown_validator() {
reserve_lamports,
) = setup().await;
let unknown_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let unknown_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
unknown_stake
.create_and_delegate(
&mut banks_client,

View File

@ -3,7 +3,7 @@
mod helpers;
use {
borsh::BorshSerialize,
borsh::{BorshDeserialize, BorshSerialize},
helpers::*,
solana_program::{
borsh::get_packed_len,
@ -226,6 +226,7 @@ async fn fail_with_wrong_max_validators() {
&stake_pool_accounts.pool_mint.pubkey(),
&stake_pool_accounts.pool_fee_account.pubkey(),
&spl_token::id(),
None,
stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -296,6 +297,7 @@ async fn fail_with_wrong_mint_authority() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -384,6 +386,7 @@ async fn fail_with_wrong_token_program_id() {
&stake_pool_accounts.pool_mint.pubkey(),
&stake_pool_accounts.pool_fee_account.pubkey(),
&wrong_token_program.pubkey(),
None,
stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -460,6 +463,7 @@ async fn fail_with_wrong_fee_account() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -547,6 +551,7 @@ async fn fail_with_not_rent_exempt_pool() {
&stake_pool_accounts.pool_mint.pubkey(),
&stake_pool_accounts.pool_fee_account.pubkey(),
&spl_token::id(),
None,
stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -621,6 +626,7 @@ async fn fail_with_not_rent_exempt_validator_list() {
&stake_pool_accounts.pool_mint.pubkey(),
&stake_pool_accounts.pool_fee_account.pubkey(),
&spl_token::id(),
None,
stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -793,6 +799,7 @@ async fn fail_with_pre_minted_pool_tokens() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -853,6 +860,7 @@ async fn fail_with_bad_reserve() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -897,6 +905,7 @@ async fn fail_with_bad_reserve() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -944,6 +953,7 @@ async fn fail_with_bad_reserve() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -991,6 +1001,7 @@ async fn fail_with_bad_reserve() {
&stake_pool_accounts.pool_fee_account.pubkey(),
&stake_pool_accounts.manager,
&stake_pool_accounts.staker.pubkey(),
&None,
&stake_pool_accounts.fee,
stake_pool_accounts.max_validators,
)
@ -1008,3 +1019,23 @@ async fn fail_with_bad_reserve() {
);
}
}
#[tokio::test]
async fn success_with_required_deposit_authority() {
let (mut banks_client, payer, recent_blockhash) = program_test().start().await;
let deposit_authority = Keypair::new();
let stake_pool_accounts = StakePoolAccounts::new_with_deposit_authority(deposit_authority);
stake_pool_accounts
.initialize_stake_pool(&mut banks_client, &payer, &recent_blockhash, 1)
.await
.unwrap();
// Stake pool now exists
let stake_pool_account =
get_account(&mut banks_client, &stake_pool_accounts.stake_pool.pubkey()).await;
let stake_pool = state::StakePool::try_from_slice(stake_pool_account.data.as_slice()).unwrap();
assert_eq!(
stake_pool.deposit_authority,
stake_pool_accounts.deposit_authority
);
}

View File

@ -66,10 +66,7 @@ async fn setup(
let mut stake_accounts: Vec<ValidatorStakeAccount> = vec![];
let mut deposit_accounts: Vec<DepositStakeAccount> = vec![];
for _ in 0..num_validators {
let stake_account = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let stake_account = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
stake_account
.create_and_delegate(
&mut context.banks_client,

View File

@ -38,10 +38,7 @@ async fn setup() -> (
.await
.unwrap();
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,
@ -126,7 +123,6 @@ async fn fail_with_wrong_validator_list_account() {
&id(),
&stake_pool_accounts.stake_pool.pubkey(),
&stake_pool_accounts.staker.pubkey(),
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.withdraw_authority,
&wrong_validator_list.pubkey(),
&user_stake.stake_account,
@ -162,10 +158,7 @@ async fn fail_too_little_stake() {
.await
.unwrap();
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
create_vote(
&mut banks_client,
&payer,
@ -203,28 +196,6 @@ async fn fail_too_little_stake() {
banks_client.process_transaction(transaction).await.unwrap();
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.stake_account,
&stake_pool_accounts.staker,
&user_stake.target_authority,
stake_program::StakeAuthorize::Staker,
)
.await;
authorize_stake_account(
&mut banks_client,
&payer,
&recent_blockhash,
&user_stake.stake_account,
&stake_pool_accounts.staker,
&user_stake.target_authority,
stake_program::StakeAuthorize::Withdrawer,
)
.await;
let error = stake_pool_accounts
.add_validator_to_pool(
&mut banks_client,
@ -253,10 +224,7 @@ async fn fail_too_much_stake() {
.await
.unwrap();
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,
@ -344,7 +312,6 @@ async fn fail_wrong_staker() {
&id(),
&stake_pool_accounts.stake_pool.pubkey(),
&malicious.pubkey(),
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.withdraw_authority,
&stake_pool_accounts.validator_list.pubkey(),
&user_stake.stake_account,
@ -379,7 +346,6 @@ async fn fail_without_signature() {
let accounts = vec![
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
AccountMeta::new_readonly(stake_pool_accounts.staker.pubkey(), false),
AccountMeta::new_readonly(stake_pool_accounts.deposit_authority, false),
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
AccountMeta::new(user_stake.stake_account, false),
@ -425,7 +391,6 @@ async fn fail_with_wrong_stake_program_id() {
let accounts = vec![
AccountMeta::new(stake_pool_accounts.stake_pool.pubkey(), false),
AccountMeta::new_readonly(stake_pool_accounts.staker.pubkey(), true),
AccountMeta::new_readonly(stake_pool_accounts.deposit_authority, false),
AccountMeta::new_readonly(stake_pool_accounts.withdraw_authority, false),
AccountMeta::new(stake_pool_accounts.validator_list.pubkey(), false),
AccountMeta::new(user_stake.stake_account, false),
@ -468,10 +433,7 @@ async fn fail_add_too_many_validator_stake_accounts() {
.await
.unwrap();
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,
@ -491,10 +453,7 @@ async fn fail_add_too_many_validator_stake_accounts() {
.await;
assert!(error.is_none());
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,

View File

@ -38,10 +38,7 @@ async fn setup() -> (
.await
.unwrap();
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,

View File

@ -403,10 +403,8 @@ async fn fail_with_unknown_validator() {
.await
.unwrap();
let validator_stake_account = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let validator_stake_account =
ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
validator_stake_account
.create_and_delegate(
&mut banks_client,
@ -416,10 +414,7 @@ async fn fail_with_unknown_validator() {
)
.await;
let user_stake = ValidatorStakeAccount::new_with_target_authority(
&stake_pool_accounts.deposit_authority,
&stake_pool_accounts.stake_pool.pubkey(),
);
let user_stake = ValidatorStakeAccount::new(&stake_pool_accounts.stake_pool.pubkey());
user_stake
.create_and_delegate(
&mut banks_client,