parent
58727463e1
commit
6309c97697
|
@ -334,6 +334,17 @@ pub enum CliCommand {
|
||||||
nonce_authority: Option<SigningAuthority>,
|
nonce_authority: Option<SigningAuthority>,
|
||||||
fee_payer: Option<SigningAuthority>,
|
fee_payer: Option<SigningAuthority>,
|
||||||
},
|
},
|
||||||
|
StakeSetLockup {
|
||||||
|
stake_account_pubkey: Pubkey,
|
||||||
|
lockup: Lockup,
|
||||||
|
custodian: Option<SigningAuthority>,
|
||||||
|
sign_only: bool,
|
||||||
|
signers: Option<Vec<(Pubkey, Signature)>>,
|
||||||
|
blockhash_query: BlockhashQuery,
|
||||||
|
nonce_account: Option<Pubkey>,
|
||||||
|
nonce_authority: Option<SigningAuthority>,
|
||||||
|
fee_payer: Option<SigningAuthority>,
|
||||||
|
},
|
||||||
WithdrawStake {
|
WithdrawStake {
|
||||||
stake_account_pubkey: Pubkey,
|
stake_account_pubkey: Pubkey,
|
||||||
destination_account_pubkey: Pubkey,
|
destination_account_pubkey: Pubkey,
|
||||||
|
@ -565,6 +576,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, Box<dyn
|
||||||
("stake-authorize-withdrawer", Some(matches)) => {
|
("stake-authorize-withdrawer", Some(matches)) => {
|
||||||
parse_stake_authorize(matches, StakeAuthorize::Withdrawer)
|
parse_stake_authorize(matches, StakeAuthorize::Withdrawer)
|
||||||
}
|
}
|
||||||
|
("stake-set-lockup", Some(matches)) => parse_stake_set_lockup(matches),
|
||||||
("stake-account", Some(matches)) => parse_show_stake_account(matches),
|
("stake-account", Some(matches)) => parse_show_stake_account(matches),
|
||||||
("stake-history", Some(matches)) => parse_show_stake_history(matches),
|
("stake-history", Some(matches)) => parse_show_stake_history(matches),
|
||||||
// Storage Commands
|
// Storage Commands
|
||||||
|
@ -1665,7 +1677,29 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||||
nonce_authority.as_ref(),
|
nonce_authority.as_ref(),
|
||||||
fee_payer.as_ref(),
|
fee_payer.as_ref(),
|
||||||
),
|
),
|
||||||
|
CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
mut lockup,
|
||||||
|
ref custodian,
|
||||||
|
sign_only,
|
||||||
|
ref signers,
|
||||||
|
blockhash_query,
|
||||||
|
nonce_account,
|
||||||
|
ref nonce_authority,
|
||||||
|
ref fee_payer,
|
||||||
|
} => process_stake_set_lockup(
|
||||||
|
&rpc_client,
|
||||||
|
config,
|
||||||
|
&stake_account_pubkey,
|
||||||
|
&mut lockup,
|
||||||
|
custodian.as_ref(),
|
||||||
|
*sign_only,
|
||||||
|
signers,
|
||||||
|
blockhash_query,
|
||||||
|
*nonce_account,
|
||||||
|
nonce_authority.as_ref(),
|
||||||
|
fee_payer.as_ref(),
|
||||||
|
),
|
||||||
CliCommand::WithdrawStake {
|
CliCommand::WithdrawStake {
|
||||||
stake_account_pubkey,
|
stake_account_pubkey,
|
||||||
destination_account_pubkey,
|
destination_account_pubkey,
|
||||||
|
|
160
cli/src/stake.rs
160
cli/src/stake.rs
|
@ -47,7 +47,7 @@ fn stake_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
Arg::with_name(STAKE_AUTHORITY_ARG.name)
|
Arg::with_name(STAKE_AUTHORITY_ARG.name)
|
||||||
.long(STAKE_AUTHORITY_ARG.long)
|
.long(STAKE_AUTHORITY_ARG.long)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("KEYPAIR of PUBKEY")
|
.value_name("KEYPAIR or PUBKEY")
|
||||||
.validator(is_pubkey_or_keypair_or_ask_keyword)
|
.validator(is_pubkey_or_keypair_or_ask_keyword)
|
||||||
.help(STAKE_AUTHORITY_ARG.help)
|
.help(STAKE_AUTHORITY_ARG.help)
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("custodian")
|
Arg::with_name("custodian")
|
||||||
.long("custodian")
|
.long("custodian")
|
||||||
.value_name("PUBKEY")
|
.value_name("KEYPAIR or PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.validator(is_pubkey_or_keypair)
|
.validator(is_pubkey_or_keypair)
|
||||||
.help("Identity of the custodian (can withdraw before lockup expires)")
|
.help("Identity of the custodian (can withdraw before lockup expires)")
|
||||||
|
@ -337,7 +337,55 @@ impl StakeSubCommands for App<'_, '_> {
|
||||||
.help("Specify unit to use for request")
|
.help("Specify unit to use for request")
|
||||||
)
|
)
|
||||||
.arg(withdraw_authority_arg())
|
.arg(withdraw_authority_arg())
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("stake-set-lockup")
|
||||||
|
.about("Set Lockup for the stake account")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("stake_account_pubkey")
|
||||||
|
.index(1)
|
||||||
|
.value_name("STAKE ACCOUNT")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Stake account for which to set Lockup")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("lockup_epoch")
|
||||||
|
.long("lockup-epoch")
|
||||||
|
.value_name("EPOCH")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The epoch height at which this account will be available for withdrawal")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("lockup_date")
|
||||||
|
.long("lockup-date")
|
||||||
|
.value_name("RFC3339 DATE TIME")
|
||||||
|
.validator(is_rfc3339_datetime)
|
||||||
|
.takes_value(true)
|
||||||
|
.help("The date and time at which this account will be available for withdrawal")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("new_custodian")
|
||||||
|
.long("new-custodian")
|
||||||
|
.value_name("KEYPAIR or PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.help("Identity of the new lockup custodian (can withdraw before lockup expires)")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("custodian")
|
||||||
|
.long("custodian")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("KEYPAIR or PUBKEY")
|
||||||
|
.validator(is_pubkey_or_keypair_or_ask_keyword)
|
||||||
|
.help("Public key of signing custodian (defaults to cli config pubkey)")
|
||||||
|
)
|
||||||
|
.offline_args()
|
||||||
|
.arg(nonce_arg())
|
||||||
|
.arg(nonce_authority_arg())
|
||||||
|
.arg(fee_payer_arg())
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("stake-account")
|
SubCommand::with_name("stake-account")
|
||||||
.about("Show the contents of a stake account")
|
.about("Show the contents of a stake account")
|
||||||
|
@ -553,6 +601,44 @@ pub fn parse_stake_withdraw_stake(matches: &ArgMatches<'_>) -> Result<CliCommand
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_stake_set_lockup(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
||||||
|
let epoch = value_of(&matches, "lockup_epoch").unwrap_or(0);
|
||||||
|
let unix_timestamp = unix_timestamp_from_rfc3339_datetime(&matches, "lockup_date").unwrap_or(0);
|
||||||
|
let new_custodian = pubkey_of(matches, "new_custodian").unwrap_or_default();
|
||||||
|
|
||||||
|
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
||||||
|
let signers = pubkeys_sigs_of(&matches, SIGNER_ARG.name);
|
||||||
|
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
||||||
|
let require_keypair = signers.is_none();
|
||||||
|
let nonce_account = pubkey_of(&matches, NONCE_ARG.name);
|
||||||
|
|
||||||
|
let custodian = SigningAuthority::new_from_matches(&matches, "custodian", signers.as_deref())?;
|
||||||
|
let nonce_authority =
|
||||||
|
SigningAuthority::new_from_matches(&matches, NONCE_AUTHORITY_ARG.name, signers.as_deref())?;
|
||||||
|
let fee_payer =
|
||||||
|
SigningAuthority::new_from_matches(&matches, FEE_PAYER_ARG.name, signers.as_deref())?;
|
||||||
|
|
||||||
|
Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup: Lockup {
|
||||||
|
custodian: new_custodian,
|
||||||
|
epoch,
|
||||||
|
unix_timestamp,
|
||||||
|
},
|
||||||
|
custodian,
|
||||||
|
sign_only,
|
||||||
|
signers,
|
||||||
|
blockhash_query,
|
||||||
|
nonce_account,
|
||||||
|
nonce_authority,
|
||||||
|
fee_payer,
|
||||||
|
},
|
||||||
|
require_keypair,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_show_stake_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
pub fn parse_show_stake_account(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
|
||||||
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
|
||||||
let use_lamports_unit = matches.is_present("lamports");
|
let use_lamports_unit = matches.is_present("lamports");
|
||||||
|
@ -984,6 +1070,74 @@ pub fn process_split_stake(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn process_stake_set_lockup(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
config: &CliConfig,
|
||||||
|
stake_account_pubkey: &Pubkey,
|
||||||
|
lockup: &mut Lockup,
|
||||||
|
custodian: Option<&SigningAuthority>,
|
||||||
|
sign_only: bool,
|
||||||
|
signers: &Option<Vec<(Pubkey, Signature)>>,
|
||||||
|
blockhash_query: &BlockhashQuery,
|
||||||
|
nonce_account: Option<Pubkey>,
|
||||||
|
nonce_authority: Option<&SigningAuthority>,
|
||||||
|
fee_payer: Option<&SigningAuthority>,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let (recent_blockhash, fee_calculator) =
|
||||||
|
blockhash_query.get_blockhash_fee_calculator(rpc_client)?;
|
||||||
|
let custodian = custodian.map(|a| a.keypair()).unwrap_or(&config.keypair);
|
||||||
|
// If new custodian is not explicitly set, default to current custodian
|
||||||
|
if lockup.custodian == Pubkey::default() {
|
||||||
|
lockup.custodian = custodian.pubkey();
|
||||||
|
}
|
||||||
|
let ixs = vec![stake_instruction::set_lockup(
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
&custodian.pubkey(),
|
||||||
|
)];
|
||||||
|
let (nonce_authority, nonce_authority_pubkey) = nonce_authority
|
||||||
|
.map(|a| (a.keypair(), a.pubkey()))
|
||||||
|
.unwrap_or((&config.keypair, config.keypair.pubkey()));
|
||||||
|
let fee_payer = fee_payer.map(|f| f.keypair()).unwrap_or(&config.keypair);
|
||||||
|
let mut tx = if let Some(nonce_account) = &nonce_account {
|
||||||
|
Transaction::new_signed_with_nonce(
|
||||||
|
ixs,
|
||||||
|
Some(&fee_payer.pubkey()),
|
||||||
|
&[fee_payer, nonce_authority, custodian],
|
||||||
|
nonce_account,
|
||||||
|
&nonce_authority.pubkey(),
|
||||||
|
recent_blockhash,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Transaction::new_signed_with_payer(
|
||||||
|
ixs,
|
||||||
|
Some(&fee_payer.pubkey()),
|
||||||
|
&[fee_payer, custodian],
|
||||||
|
recent_blockhash,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if let Some(signers) = signers {
|
||||||
|
replace_signatures(&mut tx, &signers)?;
|
||||||
|
}
|
||||||
|
if sign_only {
|
||||||
|
return_signers(&tx)
|
||||||
|
} else {
|
||||||
|
if let Some(nonce_account) = &nonce_account {
|
||||||
|
let nonce_account = rpc_client.get_account(nonce_account)?;
|
||||||
|
check_nonce_account(&nonce_account, &nonce_authority_pubkey, &recent_blockhash)?;
|
||||||
|
}
|
||||||
|
check_account_for_fee(
|
||||||
|
rpc_client,
|
||||||
|
&tx.message.account_keys[0],
|
||||||
|
&fee_calculator,
|
||||||
|
&tx.message,
|
||||||
|
)?;
|
||||||
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
|
||||||
|
log_instruction_custom_error::<StakeError>(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_stake_state(stake_lamports: u64, stake_state: &StakeState, use_lamports_unit: bool) {
|
pub fn print_stake_state(stake_lamports: u64, stake_state: &StakeState, use_lamports_unit: bool) {
|
||||||
fn show_authorized(authorized: &Authorized) {
|
fn show_authorized(authorized: &Authorized) {
|
||||||
println!("Authorized Staker: {}", authorized.staker);
|
println!("Authorized Staker: {}", authorized.staker);
|
||||||
|
|
|
@ -860,6 +860,7 @@ fn test_stake_split() {
|
||||||
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
let mut config_offline = CliConfig::default();
|
let mut config_offline = CliConfig::default();
|
||||||
|
config_offline.json_rpc_url = String::default();
|
||||||
let offline_pubkey = config_offline.keypair.pubkey();
|
let offline_pubkey = config_offline.keypair.pubkey();
|
||||||
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
@ -878,7 +879,6 @@ fn test_stake_split() {
|
||||||
let minimum_stake_balance = rpc_client
|
let minimum_stake_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeState>())
|
.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeState>())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("stake min: {}", minimum_stake_balance);
|
|
||||||
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
let stake_account_pubkey = stake_keypair.pubkey();
|
let stake_account_pubkey = stake_keypair.pubkey();
|
||||||
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
@ -902,7 +902,6 @@ fn test_stake_split() {
|
||||||
let minimum_nonce_balance = rpc_client
|
let minimum_nonce_balance = rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("nonce min: {}", minimum_nonce_balance);
|
|
||||||
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
let nonce_account_pubkey = nonce_account.pubkey();
|
let nonce_account_pubkey = nonce_account.pubkey();
|
||||||
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
@ -935,7 +934,7 @@ fn test_stake_split() {
|
||||||
sign_only: true,
|
sign_only: true,
|
||||||
signers: None,
|
signers: None,
|
||||||
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||||
nonce_account: Some(nonce_account_pubkey.into()),
|
nonce_account: Some(nonce_account_pubkey),
|
||||||
nonce_authority: None,
|
nonce_authority: None,
|
||||||
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
||||||
seed: None,
|
seed: None,
|
||||||
|
@ -950,7 +949,7 @@ fn test_stake_split() {
|
||||||
sign_only: false,
|
sign_only: false,
|
||||||
signers: Some(signers),
|
signers: Some(signers),
|
||||||
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||||
nonce_account: Some(nonce_account_pubkey.into()),
|
nonce_account: Some(nonce_account_pubkey),
|
||||||
nonce_authority: Some(offline_pubkey.into()),
|
nonce_authority: Some(offline_pubkey.into()),
|
||||||
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
split_stake_account: read_keypair_file(&split_keypair_file).unwrap().into(),
|
||||||
seed: None,
|
seed: None,
|
||||||
|
@ -972,3 +971,225 @@ fn test_stake_split() {
|
||||||
server.close().unwrap();
|
server.close().unwrap();
|
||||||
remove_dir_all(ledger_path).unwrap();
|
remove_dir_all(ledger_path).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stake_set_lockup() {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
let (server, leader_data, alice, ledger_path, _voter) = new_validator_for_tests_ex(1, 42_000);
|
||||||
|
let (sender, receiver) = channel();
|
||||||
|
run_local_faucet(alice, sender, None);
|
||||||
|
let faucet_addr = receiver.recv().unwrap();
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_socket(leader_data.rpc);
|
||||||
|
|
||||||
|
let mut config = CliConfig::default();
|
||||||
|
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
|
||||||
|
|
||||||
|
let mut config_offline = CliConfig::default();
|
||||||
|
config_offline.json_rpc_url = String::default();
|
||||||
|
let offline_pubkey = config_offline.keypair.pubkey();
|
||||||
|
let (_offline_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&config_offline.keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
// Verify we're offline
|
||||||
|
config_offline.command = CliCommand::ClusterVersion;
|
||||||
|
process_command(&config_offline).unwrap_err();
|
||||||
|
|
||||||
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &config.keypair.pubkey(), 500_000)
|
||||||
|
.unwrap();
|
||||||
|
check_balance(500_000, &rpc_client, &config.keypair.pubkey());
|
||||||
|
|
||||||
|
request_and_confirm_airdrop(&rpc_client, &faucet_addr, &offline_pubkey, 100_000).unwrap();
|
||||||
|
check_balance(100_000, &rpc_client, &offline_pubkey);
|
||||||
|
|
||||||
|
// Create stake account, identity is authority
|
||||||
|
let minimum_stake_balance = rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeState>())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
||||||
|
let stake_account_pubkey = stake_keypair.pubkey();
|
||||||
|
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&stake_keypair, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
|
let mut lockup = Lockup::default();
|
||||||
|
lockup.custodian = config.keypair.pubkey();
|
||||||
|
|
||||||
|
config.command = CliCommand::CreateStakeAccount {
|
||||||
|
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
|
||||||
|
seed: None,
|
||||||
|
staker: Some(offline_pubkey),
|
||||||
|
withdrawer: Some(offline_pubkey),
|
||||||
|
lockup,
|
||||||
|
lamports: 10 * minimum_stake_balance,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(
|
||||||
|
10 * minimum_stake_balance,
|
||||||
|
&rpc_client,
|
||||||
|
&stake_account_pubkey,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Online set lockup
|
||||||
|
let mut lockup = Lockup {
|
||||||
|
unix_timestamp: 1581534570,
|
||||||
|
epoch: 200,
|
||||||
|
custodian: Pubkey::default(),
|
||||||
|
};
|
||||||
|
config.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: None,
|
||||||
|
sign_only: false,
|
||||||
|
signers: None,
|
||||||
|
blockhash_query: BlockhashQuery::default(),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: None,
|
||||||
|
fee_payer: None,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
|
let current_lockup = match stake_state {
|
||||||
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
|
_ => panic!("Unexpected stake state!"),
|
||||||
|
};
|
||||||
|
lockup.custodian = config.keypair.pubkey(); // Default new_custodian is config.keypair
|
||||||
|
assert_eq!(current_lockup, lockup);
|
||||||
|
|
||||||
|
// Set custodian to another pubkey
|
||||||
|
let online_custodian = Keypair::new();
|
||||||
|
let online_custodian_pubkey = online_custodian.pubkey();
|
||||||
|
let (online_custodian_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&online_custodian, tmp_file.as_file_mut()).unwrap();
|
||||||
|
|
||||||
|
let lockup = Lockup {
|
||||||
|
unix_timestamp: 1581534571,
|
||||||
|
epoch: 201,
|
||||||
|
custodian: online_custodian_pubkey,
|
||||||
|
};
|
||||||
|
config.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: None,
|
||||||
|
sign_only: false,
|
||||||
|
signers: None,
|
||||||
|
blockhash_query: BlockhashQuery::default(),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: None,
|
||||||
|
fee_payer: None,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
|
let mut lockup = Lockup {
|
||||||
|
unix_timestamp: 1581534572,
|
||||||
|
epoch: 202,
|
||||||
|
custodian: Pubkey::default(),
|
||||||
|
};
|
||||||
|
config.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: Some(read_keypair_file(&online_custodian_file).unwrap().into()),
|
||||||
|
sign_only: false,
|
||||||
|
signers: None,
|
||||||
|
blockhash_query: BlockhashQuery::default(),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: None,
|
||||||
|
fee_payer: None,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
|
let current_lockup = match stake_state {
|
||||||
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
|
_ => panic!("Unexpected stake state!"),
|
||||||
|
};
|
||||||
|
lockup.custodian = online_custodian_pubkey; // Default new_custodian is designated custodian
|
||||||
|
assert_eq!(current_lockup, lockup);
|
||||||
|
|
||||||
|
// Set custodian to offline pubkey
|
||||||
|
let lockup = Lockup {
|
||||||
|
unix_timestamp: 1581534573,
|
||||||
|
epoch: 203,
|
||||||
|
custodian: offline_pubkey,
|
||||||
|
};
|
||||||
|
config.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: Some(online_custodian.into()),
|
||||||
|
sign_only: false,
|
||||||
|
signers: None,
|
||||||
|
blockhash_query: BlockhashQuery::default(),
|
||||||
|
nonce_account: None,
|
||||||
|
nonce_authority: None,
|
||||||
|
fee_payer: None,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
|
||||||
|
// Create nonce account
|
||||||
|
let minimum_nonce_balance = rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(NonceState::size())
|
||||||
|
.unwrap();
|
||||||
|
let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
|
||||||
|
let nonce_account_pubkey = nonce_account.pubkey();
|
||||||
|
let (nonce_keypair_file, mut tmp_file) = make_tmp_file();
|
||||||
|
write_keypair(&nonce_account, tmp_file.as_file_mut()).unwrap();
|
||||||
|
config.command = CliCommand::CreateNonceAccount {
|
||||||
|
nonce_account: read_keypair_file(&nonce_keypair_file).unwrap().into(),
|
||||||
|
seed: None,
|
||||||
|
nonce_authority: Some(offline_pubkey),
|
||||||
|
lamports: minimum_nonce_balance,
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
check_balance(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
|
||||||
|
|
||||||
|
// Fetch nonce hash
|
||||||
|
let account = rpc_client.get_account(&nonce_account_pubkey).unwrap();
|
||||||
|
let nonce_state: NonceState = account.state().unwrap();
|
||||||
|
let nonce_hash = match nonce_state {
|
||||||
|
NonceState::Initialized(_meta, hash) => hash,
|
||||||
|
_ => panic!("Nonce is not initialized"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nonced offline set lockup
|
||||||
|
let lockup = Lockup {
|
||||||
|
unix_timestamp: 1581534576,
|
||||||
|
epoch: 222,
|
||||||
|
custodian: offline_pubkey,
|
||||||
|
};
|
||||||
|
config_offline.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: None,
|
||||||
|
sign_only: true,
|
||||||
|
signers: None,
|
||||||
|
blockhash_query: BlockhashQuery::None(nonce_hash, FeeCalculator::default()),
|
||||||
|
nonce_account: Some(nonce_account_pubkey),
|
||||||
|
nonce_authority: None,
|
||||||
|
fee_payer: None,
|
||||||
|
};
|
||||||
|
let sig_response = process_command(&config_offline).unwrap();
|
||||||
|
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
|
||||||
|
config.command = CliCommand::StakeSetLockup {
|
||||||
|
stake_account_pubkey,
|
||||||
|
lockup,
|
||||||
|
custodian: Some(offline_pubkey.into()),
|
||||||
|
sign_only: false,
|
||||||
|
signers: Some(signers),
|
||||||
|
blockhash_query: BlockhashQuery::FeeCalculator(blockhash),
|
||||||
|
nonce_account: Some(nonce_account_pubkey),
|
||||||
|
nonce_authority: Some(offline_pubkey.into()),
|
||||||
|
fee_payer: Some(offline_pubkey.into()),
|
||||||
|
};
|
||||||
|
process_command(&config).unwrap();
|
||||||
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
let stake_state: StakeState = stake_account.state().unwrap();
|
||||||
|
let current_lockup = match stake_state {
|
||||||
|
StakeState::Initialized(meta) => meta.lockup,
|
||||||
|
_ => panic!("Unexpected stake state!"),
|
||||||
|
};
|
||||||
|
assert_eq!(current_lockup, lockup);
|
||||||
|
|
||||||
|
server.close().unwrap();
|
||||||
|
remove_dir_all(ledger_path).unwrap();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue