From cc299053cc92a4f4a0377b2cbb4688df9b02da1a Mon Sep 17 00:00:00 2001 From: Rob Walker Date: Mon, 20 Jan 2020 12:33:27 -0800 Subject: [PATCH] Add support for stake::split() via create_account_with_seed() (#7879) * Add split with seed * move to new system_program APIs * de-replicode --- programs/stake/src/stake_instruction.rs | 68 +++++++++++++++++++++---- programs/stake/src/stake_state.rs | 12 +++++ runtime/tests/stake.rs | 61 ++++++++++++++++++++++ 3 files changed, 130 insertions(+), 11 deletions(-) diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index f240b92ce7..ad51c4f7d9 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -128,7 +128,7 @@ pub enum StakeInstruction { Deactivate, } -pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction { +fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction { Instruction::new( id(), &StakeInstruction::Initialize(*authorized, *lockup), @@ -181,6 +181,21 @@ pub fn create_account( ] } +fn _split( + stake_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + lamports: u64, + split_stake_pubkey: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*stake_pubkey, false), + AccountMeta::new(*split_stake_pubkey, false), + ] + .with_signer(authorized_pubkey); + + Instruction::new(id(), &StakeInstruction::Split(lamports), account_metas) +} + pub fn split( stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey, @@ -188,21 +203,39 @@ pub fn split( split_stake_pubkey: &Pubkey, ) -> Vec { vec![ - system_instruction::create_account( + system_instruction::allocate(split_stake_pubkey, std::mem::size_of::() as u64), + system_instruction::assign(split_stake_pubkey, &id()), + _split( stake_pubkey, + authorized_pubkey, + lamports, split_stake_pubkey, - 0, // creates an ephemeral, uninitialized Stake + ), + ] +} + +pub fn split_with_seed( + stake_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + lamports: u64, + split_stake_pubkey: &Pubkey, // derived using create_address_with_seed() + base: &Pubkey, // base + seed: &str, // seed +) -> Vec { + vec![ + system_instruction::allocate_with_seed( + split_stake_pubkey, + base, + seed, std::mem::size_of::() as u64, &id(), ), - { - let account_metas = vec![ - AccountMeta::new(*stake_pubkey, false), - AccountMeta::new(*split_stake_pubkey, false), - ] - .with_signer(authorized_pubkey); - Instruction::new(id(), &StakeInstruction::Split(lamports), account_metas) - }, + _split( + stake_pubkey, + authorized_pubkey, + lamports, + split_stake_pubkey, + ), ] } @@ -478,6 +511,19 @@ mod tests { &Pubkey::default(), 100, &Pubkey::default() + )[2] + ), + Err(InstructionError::InvalidAccountData), + ); + assert_eq!( + process_instruction( + &split_with_seed( + &Pubkey::default(), + &Pubkey::default(), + 100, + &Pubkey::default(), + &Pubkey::default(), + "seed" )[1] ), Err(InstructionError::InvalidAccountData), diff --git a/programs/stake/src/stake_state.rs b/programs/stake/src/stake_state.rs index df1f59ac89..aaffaf8646 100644 --- a/programs/stake/src/stake_state.rs +++ b/programs/stake/src/stake_state.rs @@ -2779,6 +2779,18 @@ mod tests { ); } + #[test] + #[ignore] + #[should_panic] + fn test_dbg_stake_minimum_balance() { + let minimum_balance = Rent::default().minimum_balance(std::mem::size_of::()); + panic!( + "stake minimum_balance: {} lamports, {} SOL", + minimum_balance, + minimum_balance as f64 / solana_sdk::native_token::LAMPORTS_PER_SOL as f64 + ); + } + #[test] fn test_authorize_delegated_stake() { let stake_pubkey = Pubkey::new_rand(); diff --git a/runtime/tests/stake.rs b/runtime/tests/stake.rs index 2d9fafebb4..c5f7443ce4 100644 --- a/runtime/tests/stake.rs +++ b/runtime/tests/stake.rs @@ -97,6 +97,67 @@ fn get_staked(bank: &Bank, stake_pubkey: &Pubkey) -> u64 { ) } +#[test] +fn test_stake_create_and_split_single_signature() { + solana_logger::setup(); + + let GenesisConfigInfo { + mut genesis_config, + mint_keypair: staker_keypair, + .. + } = create_genesis_config_with_leader(100_000_000_000, &Pubkey::new_rand(), 1_000_000); + genesis_config + .native_instruction_processors + .push(solana_stake_program::solana_stake_program!()); + + let staker_pubkey = staker_keypair.pubkey(); + + let bank_client = BankClient::new_shared(&Arc::new(Bank::new(&genesis_config))); + + let stake_address = + create_address_with_seed(&staker_pubkey, "stake", &solana_stake_program::id()).unwrap(); + + let authorized = stake_state::Authorized::auto(&staker_pubkey); + + let lamports = 1_000_000; + + // Create stake account with seed + let message = Message::new(stake_instruction::create_account_with_seed( + &staker_pubkey, // from + &stake_address, // to + &staker_pubkey, // base + "stake", // seed + &authorized, + &stake_state::Lockup::default(), + lamports, + )); + + // only one signature required + bank_client + .send_message(&[&staker_keypair], message) + .expect("failed to create and delegate stake account"); + + // split the stake + let split_stake_address = + create_address_with_seed(&staker_pubkey, "split_stake", &solana_stake_program::id()) + .unwrap(); + // Test split + let message = Message::new(stake_instruction::split_with_seed( + &stake_address, // original + &staker_pubkey, // authorized + lamports / 2, + &split_stake_address, // new address + &staker_pubkey, // base + "split_stake", // seed + )); + + assert!(bank_client + .send_message(&[&staker_keypair], message) + .is_ok()); + + // w00t! +} + #[test] fn test_stake_account_lifetime() { let stake_keypair = Keypair::new();