Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit c149b0a

Browse files
authored
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
1 parent 804a61e commit c149b0a

File tree

13 files changed

+602
-453
lines changed

13 files changed

+602
-453
lines changed

stake-pool/cli/src/main.rs

Lines changed: 91 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use {
1414
input_validators::{is_amount, is_keypair, is_parsable, is_pubkey, is_url},
1515
keypair::signer_from_path,
1616
},
17-
solana_client::{rpc_client::RpcClient, rpc_response::StakeActivationState},
17+
solana_client::rpc_client::RpcClient,
1818
solana_program::{
1919
borsh::get_packed_len, instruction::Instruction, program_pack::Pack, pubkey::Pubkey,
2020
},
@@ -28,9 +28,9 @@ use {
2828
spl_stake_pool::{
2929
self,
3030
borsh::get_instance_packed_len,
31-
find_deposit_authority_program_address, find_stake_program_address,
32-
find_transient_stake_program_address, find_withdraw_authority_program_address,
33-
stake_program::{self, StakeAuthorize, StakeState},
31+
find_stake_program_address, find_transient_stake_program_address,
32+
find_withdraw_authority_program_address,
33+
stake_program::{self, StakeState},
3434
state::{Fee, StakePool, ValidatorList},
3535
MAX_VALIDATORS_TO_UPDATE,
3636
},
@@ -42,6 +42,7 @@ struct Config {
4242
verbose: bool,
4343
manager: Box<dyn Signer>,
4444
staker: Box<dyn Signer>,
45+
depositor: Option<Box<dyn Signer>>,
4546
token_owner: Box<dyn Signer>,
4647
fee_payer: Box<dyn Signer>,
4748
dry_run: bool,
@@ -94,7 +95,12 @@ fn send_transaction(
9495
Ok(())
9596
}
9697

97-
fn command_create_pool(config: &Config, fee: Fee, max_validators: u32) -> CommandResult {
98+
fn command_create_pool(
99+
config: &Config,
100+
deposit_authority: Option<Pubkey>,
101+
fee: Fee,
102+
max_validators: u32,
103+
) -> CommandResult {
98104
let reserve_stake = Keypair::new();
99105
println!("Creating reserve stake {}", reserve_stake.pubkey());
100106

@@ -224,6 +230,7 @@ fn command_create_pool(config: &Config, fee: Fee, max_validators: u32) -> Comman
224230
&mint_account.pubkey(),
225231
&pool_fee_account.pubkey(),
226232
&spl_token::id(),
233+
deposit_authority,
227234
fee,
228235
max_validators,
229236
)?,
@@ -286,7 +293,17 @@ fn command_vsa_create(
286293
}
287294

288295
fn command_vsa_add(config: &Config, stake_pool_address: &Pubkey, stake: &Pubkey) -> CommandResult {
289-
if config.rpc_client.get_stake_activation(*stake, None)?.state != StakeActivationState::Active {
296+
let stake_state = get_stake_state(&config.rpc_client, &stake)?;
297+
if let stake_program::StakeState::Stake(meta, _stake) = stake_state {
298+
if meta.authorized.withdrawer != config.staker.pubkey() {
299+
let error = format!(
300+
"Stake account withdraw authority must be the staker {}, actual {}",
301+
config.staker.pubkey(),
302+
meta.authorized.withdrawer
303+
);
304+
return Err(error.into());
305+
}
306+
} else {
290307
return Err("Stake account is not active.".into());
291308
}
292309

@@ -299,34 +316,16 @@ fn command_vsa_add(config: &Config, stake_pool_address: &Pubkey, stake: &Pubkey)
299316
let mut instructions: Vec<Instruction> = vec![];
300317
let mut signers = vec![config.fee_payer.as_ref(), config.staker.as_ref()];
301318

302-
// Calculate Deposit and Withdraw stake pool authorities
303-
let pool_deposit_authority =
304-
find_deposit_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
305-
319+
// Calculate Withdraw stake pool authorities
306320
let pool_withdraw_authority =
307321
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
308322

309323
instructions.extend(vec![
310-
// Set Withdrawer on stake account to Deposit authority of the stake pool
311-
stake_program::authorize(
312-
&stake,
313-
&config.staker.pubkey(),
314-
&pool_deposit_authority,
315-
StakeAuthorize::Withdrawer,
316-
),
317-
// Set Staker on stake account to Deposit authority of the stake pool
318-
stake_program::authorize(
319-
&stake,
320-
&config.staker.pubkey(),
321-
&pool_deposit_authority,
322-
StakeAuthorize::Staker,
323-
),
324324
// Add validator stake account to the pool
325325
spl_stake_pool::instruction::add_validator_to_pool(
326326
&spl_stake_pool::id(),
327327
&stake_pool_address,
328328
&config.staker.pubkey(),
329-
&pool_deposit_authority,
330329
&pool_withdraw_authority,
331330
&stake_pool.validator_list,
332331
&stake,
@@ -588,42 +587,49 @@ fn command_deposit(
588587
},
589588
)?;
590589

591-
// Calculate Deposit and Withdraw stake pool authorities
592-
let pool_deposit_authority =
593-
find_deposit_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
594-
595590
let pool_withdraw_authority =
596591
find_withdraw_authority_program_address(&spl_stake_pool::id(), stake_pool_address).0;
597592

598-
instructions.extend(vec![
599-
// Set Withdrawer on stake account to Deposit authority of the stake pool
600-
stake_program::authorize(
601-
&stake,
602-
&config.staker.pubkey(),
603-
&pool_deposit_authority,
604-
StakeAuthorize::Withdrawer,
605-
),
606-
// Set Staker on stake account to Deposit authority of the stake pool
607-
stake_program::authorize(
593+
let mut deposit_instructions = if let Some(deposit_authority) = config.depositor.as_ref() {
594+
signers.push(deposit_authority.as_ref());
595+
if deposit_authority.pubkey() != stake_pool.deposit_authority {
596+
let error = format!(
597+
"Invalid deposit authority specified, expected {}, received {}",
598+
stake_pool.deposit_authority,
599+
deposit_authority.pubkey()
600+
);
601+
return Err(error.into());
602+
}
603+
604+
spl_stake_pool::instruction::deposit_with_authority(
605+
&spl_stake_pool::id(),
606+
&stake_pool_address,
607+
&stake_pool.validator_list,
608+
&deposit_authority.pubkey(),
609+
&pool_withdraw_authority,
608610
&stake,
609611
&config.staker.pubkey(),
610-
&pool_deposit_authority,
611-
StakeAuthorize::Staker,
612-
),
613-
// Add stake account to the pool
612+
&validator_stake_account,
613+
&token_receiver,
614+
&stake_pool.pool_mint,
615+
&spl_token::id(),
616+
)
617+
} else {
614618
spl_stake_pool::instruction::deposit(
615619
&spl_stake_pool::id(),
616620
&stake_pool_address,
617621
&stake_pool.validator_list,
618-
&pool_deposit_authority,
619622
&pool_withdraw_authority,
620623
&stake,
624+
&config.staker.pubkey(),
621625
&validator_stake_account,
622626
&token_receiver,
623627
&stake_pool.pool_mint,
624628
&spl_token::id(),
625-
)?,
626-
]);
629+
)
630+
};
631+
632+
instructions.append(&mut deposit_instructions);
627633

628634
let mut transaction =
629635
Transaction::new_with_payer(&instructions, Some(&config.fee_payer.pubkey()));
@@ -1130,6 +1136,17 @@ fn main() {
11301136
Defaults to the client keypair.",
11311137
),
11321138
)
1139+
.arg(
1140+
Arg::with_name("depositor")
1141+
.long("depositor")
1142+
.value_name("KEYPAIR")
1143+
.validator(is_keypair)
1144+
.takes_value(true)
1145+
.help(
1146+
"Specify the stake pool depositor. \
1147+
This may be a keypair file, the ASK keyword.",
1148+
),
1149+
)
11331150
.arg(
11341151
Arg::with_name("token_owner")
11351152
.long("token-owner")
@@ -1186,6 +1203,15 @@ fn main() {
11861203
.required(true)
11871204
.help("Max number of validators included in the stake pool"),
11881205
)
1206+
.arg(
1207+
Arg::with_name("deposit_authority")
1208+
.long("deposit-authority")
1209+
.short("a")
1210+
.validator(is_pubkey)
1211+
.value_name("DEPOSIT_AUTHORITY_ADDRESS")
1212+
.takes_value(true)
1213+
.help("Deposit authority required to sign all deposits into the stake pool"),
1214+
)
11891215
)
11901216
.subcommand(SubCommand::with_name("create-validator-stake")
11911217
.about("Create a new stake account to use with the pool. Must be signed by the pool staker.")
@@ -1515,6 +1541,22 @@ fn main() {
15151541
eprintln!("error: {}", e);
15161542
exit(1);
15171543
});
1544+
let depositor = if matches.is_present("depositor") {
1545+
Some(
1546+
signer_from_path(
1547+
&matches,
1548+
&cli_config.keypair_path,
1549+
"depositor",
1550+
&mut wallet_manager,
1551+
)
1552+
.unwrap_or_else(|e| {
1553+
eprintln!("error: {}", e);
1554+
exit(1);
1555+
}),
1556+
)
1557+
} else {
1558+
None
1559+
};
15181560
let manager = signer_from_path(
15191561
&matches,
15201562
&cli_config.keypair_path,
@@ -1554,6 +1596,7 @@ fn main() {
15541596
verbose,
15551597
manager,
15561598
staker,
1599+
depositor,
15571600
token_owner,
15581601
fee_payer,
15591602
dry_run,
@@ -1563,11 +1606,13 @@ fn main() {
15631606

15641607
let _ = match matches.subcommand() {
15651608
("create-pool", Some(arg_matches)) => {
1609+
let deposit_authority = pubkey_of(arg_matches, "deposit_authority");
15661610
let numerator = value_t_or_exit!(arg_matches, "fee_numerator", u64);
15671611
let denominator = value_t_or_exit!(arg_matches, "fee_denominator", u64);
15681612
let max_validators = value_t_or_exit!(arg_matches, "max_validators", u32);
15691613
command_create_pool(
15701614
&config,
1615+
deposit_authority,
15711616
Fee {
15721617
denominator,
15731618
numerator,

0 commit comments

Comments
 (0)