Skip to content

Commit

Permalink
add test
Browse files Browse the repository at this point in the history
  • Loading branch information
HaoranYi committed Jun 2, 2023
1 parent 5db005e commit e95ad7a
Show file tree
Hide file tree
Showing 2 changed files with 308 additions and 1 deletion.
304 changes: 304 additions & 0 deletions cli/tests/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2319,3 +2319,307 @@ fn test_stake_minimum_delegation() {
let result = process_command(&config);
assert!(matches!(result, Ok(..)));
}

#[test]
fn test_stake_redelegation_withdraw_not_permitted() {
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let authorized_withdrawer = Keypair::new().pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);

// setup test validator
let slots_per_epoch = 32;
let test_validator = TestValidatorGenesis::default()
.fee_rate_governor(FeeRateGovernor::new(0, 0))
.rent(Rent {
lamports_per_byte_year: 1,
exemption_threshold: 1.0,
..Rent::default()
})
.epoch_schedule(EpochSchedule::custom(
slots_per_epoch,
slots_per_epoch,
/* enable_warmup_epochs = */ false,
))
.faucet_addr(Some(faucet_addr))
.start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
.expect("validator start failed");

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
let default_signer = Keypair::new();

let mut config = CliConfig::recent_for_tests();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&default_signer];

request_and_confirm_airdrop(
&rpc_client,
&config,
&config.signers[0].pubkey(),
100_000_000_000,
)
.unwrap();

// Create vote account
let vote_keypair = Keypair::new();
config.signers = vec![&default_signer, &vote_keypair];
config.command = CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 0,
authorized_voter: None,
authorized_withdrawer,
commission: 0,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
compute_unit_price: None,
};
process_command(&config).unwrap();

// Create second vote account
let vote2_keypair = Keypair::new();
config.signers = vec![&default_signer, &vote2_keypair];
config.command = CliCommand::CreateVoteAccount {
vote_account: 1,
seed: None,
identity_account: 0,
authorized_voter: None,
authorized_withdrawer,
commission: 0,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
compute_unit_price: None,
};
process_command(&config).unwrap();

// Create stake account
let stake_keypair = Keypair::new();
config.signers = vec![&default_signer, &stake_keypair];
config.command = CliCommand::CreateStakeAccount {
stake_account: 1,
seed: None,
staker: None,
withdrawer: None,
withdrawer_signer: None,
lockup: Lockup::default(),
amount: SpendAmount::Some(50_000_000_000),
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
from: 0,
compute_unit_price: None,
};
process_command(&config).unwrap();

// Delegate stake to `vote_keypair`
config.signers = vec![&default_signer];
config.command = CliCommand::DelegateStake {
stake_account_pubkey: stake_keypair.pubkey(),
vote_account_pubkey: vote_keypair.pubkey(),
stake_authority: 0,
force: true,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account: None,
compute_unit_price: None,
};
process_command(&config).unwrap();

// wait for new epoch
wait_for_next_epoch_plus_n_slots(&rpc_client, 0);

// `stake_keypair` should now be delegated to `vote_keypair` and fully activated
let stake_account = rpc_client.get_account(&stake_keypair.pubkey()).unwrap();
let stake_state: StakeState = stake_account.state().unwrap();

let rent_exempt_reserve = match stake_state {
StakeState::Stake(meta, stake, _) => {
assert_eq!(stake.delegation.voter_pubkey, vote_keypair.pubkey());
meta.rent_exempt_reserve
}
_ => panic!("Unexpected stake state!"),
};

assert_eq!(
rpc_client
.get_stake_activation(stake_keypair.pubkey(), None)
.unwrap(),
RpcStakeActivation {
state: StakeActivationState::Active,
active: 50_000_000_000 - rent_exempt_reserve,
inactive: 0
}
);
check_balance!(50_000_000_000, &rpc_client, &stake_keypair.pubkey());

// Now try to withdraw from stake_account. It will fail.
let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
let recipient_pubkey = recipient.pubkey();
config.command = CliCommand::WithdrawStake {
stake_account_pubkey: stake_keypair.pubkey(),
destination_account_pubkey: recipient_pubkey,
amount: SpendAmount::Some(50_000_000_000),
withdraw_authority: 0,
custodian: None,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
seed: None,
fee_payer: 0,
compute_unit_price: None,
};

let withraw_result = process_command(&config);
assert!(withraw_result.is_err());

//
let stake2_keypair = Keypair::new();

// Add an extra `rent_exempt_reserve` amount into `stake2_keypair` before redelegation to
// account for the `rent_exempt_reserve` balance that'll be pealed off the stake during the
// redelegation process
request_and_confirm_airdrop(
&rpc_client,
&config,
&stake2_keypair.pubkey(),
rent_exempt_reserve,
)
.unwrap();

// wait for a new epoch to ensure the `Redelegate` happens as soon as possible in the epoch
// to reduce the risk of a race condition when checking the stake account correctly enters the
// deactivating state for the remainder of the current epoch
// wait_for_next_epoch_plus_n_slots(&rpc_client, 0);

// Redelegate to `vote2_keypair` via `stake2_keypair
config.signers = vec![&default_signer, &stake2_keypair];
config.command = CliCommand::DelegateStake {
stake_account_pubkey: stake_keypair.pubkey(),
vote_account_pubkey: vote2_keypair.pubkey(),
stake_authority: 0,
force: true,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
fee_payer: 0,
redelegation_stake_account: Some(1),
compute_unit_price: None,
};
process_command(&config).unwrap();

// `stake_keypair` should now be deactivating
assert_eq!(
rpc_client
.get_stake_activation(stake_keypair.pubkey(), None)
.unwrap(),
RpcStakeActivation {
state: StakeActivationState::Deactivating,
active: 50_000_000_000 - rent_exempt_reserve,
inactive: 0,
}
);

// `stake_keypair2` should now be activating
assert_eq!(
rpc_client
.get_stake_activation(stake2_keypair.pubkey(), None)
.unwrap(),
RpcStakeActivation {
state: StakeActivationState::Activating,
active: 0,
inactive: 50_000_000_000 - rent_exempt_reserve,
}
);

// check that all the stake, save `rent_exempt_reserve`, have been moved from `stake_keypair`
// to `stake2_keypair`
check_balance!(rent_exempt_reserve, &rpc_client, &stake_keypair.pubkey());
check_balance!(50_000_000_000, &rpc_client, &stake2_keypair.pubkey());

// Deactivate stake2_account
config.signers = vec![&stake2_keypair, &default_signer];
let conf_signers = vec![
stake2_keypair.pubkey(),
stake_keypair.pubkey(),
default_signer.pubkey(),
];

println!("conf_signer: {:?}", conf_signers);

config.command = CliCommand::DeactivateStake {
stake_account_pubkey: stake2_keypair.pubkey(),
stake_authority: 0,
sign_only: false,
deactivate_delinquent: false,
dump_transaction_message: true,
blockhash_query: BlockhashQuery::default(),
nonce_account: None,
nonce_authority: 0,
memo: None,
seed: None,
fee_payer: 1,
compute_unit_price: None,
};
process_command(&config).unwrap();

// `stake_keypair2` should now NOT be Deactivated
assert_eq!(
rpc_client
.get_stake_activation(stake2_keypair.pubkey(), None)
.unwrap(),
RpcStakeActivation {
state: StakeActivationState::Deactivating,
active: 0,
inactive: 50_000_000_000 - rent_exempt_reserve,
}
);

// Withdraw from stake2 account should still fails
let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
let recipient_pubkey = recipient.pubkey();
config.command = CliCommand::WithdrawStake {
stake_account_pubkey: stake2_keypair.pubkey(),
destination_account_pubkey: recipient_pubkey,
amount: SpendAmount::Some(50_000_000_000),
withdraw_authority: 0,
custodian: None,
sign_only: false,
dump_transaction_message: false,
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
nonce_account: None,
nonce_authority: 0,
memo: None,
seed: None,
fee_payer: 0,
compute_unit_price: None,
};

let withraw_result = process_command(&config);
assert!(withraw_result.is_err());
}
5 changes: 4 additions & 1 deletion sdk/src/transaction/versioned/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,16 @@ impl VersionedTransaction {
}?;

let message_data = message.serialize();
println!("expected: {:?}", expected_signer_keys);
println!("signer_keys: {:?}", signer_keys);
let signature_indexes: Vec<usize> = expected_signer_keys
.iter()
.map(|signer_key| {
signer_keys
.iter()
.position(|key| key == signer_key)
.ok_or(SignerError::KeypairPubkeyMismatch)
//.ok_or(SignerError::KeypairPubkeyMismatch)
.ok_or(SignerError::NoDeviceFound)
})
.collect::<std::result::Result<_, SignerError>>()?;

Expand Down

0 comments on commit e95ad7a

Please sign in to comment.