Feature-gate stake-program-v3 (#14232)
* Remove deprecated legacy stake program * Add legacy stake program * Strip out duplicative legacy code * Feature-deploy stake-program-v3 * Add ownership check in stake processor
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -457,6 +457,10 @@ pub fn process_instruction(
|
|||||||
let keyed_accounts = &mut keyed_accounts.iter();
|
let keyed_accounts = &mut keyed_accounts.iter();
|
||||||
let me = &next_keyed_account(keyed_accounts)?;
|
let me = &next_keyed_account(keyed_accounts)?;
|
||||||
|
|
||||||
|
if me.owner()? != id() {
|
||||||
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
|
}
|
||||||
|
|
||||||
match limited_deserialize(data)? {
|
match limited_deserialize(data)? {
|
||||||
StakeInstruction::Initialize(authorized, lockup) => me.initialize(
|
StakeInstruction::Initialize(authorized, lockup) => me.initialize(
|
||||||
&authorized,
|
&authorized,
|
||||||
@ -538,6 +542,13 @@ mod tests {
|
|||||||
RefCell::new(Account::default())
|
RefCell::new(Account::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_default_stake_account() -> RefCell<Account> {
|
||||||
|
RefCell::new(Account {
|
||||||
|
owner: id(),
|
||||||
|
..Account::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn invalid_stake_state_pubkey() -> Pubkey {
|
fn invalid_stake_state_pubkey() -> Pubkey {
|
||||||
Pubkey::from_str("BadStake11111111111111111111111111111111111").unwrap()
|
Pubkey::from_str("BadStake11111111111111111111111111111111111").unwrap()
|
||||||
}
|
}
|
||||||
@ -546,6 +557,14 @@ mod tests {
|
|||||||
Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap()
|
Pubkey::from_str("BadVote111111111111111111111111111111111111").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn spoofed_stake_state_pubkey() -> Pubkey {
|
||||||
|
Pubkey::from_str("SpoofedStake1111111111111111111111111111111").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spoofed_stake_program_id() -> Pubkey {
|
||||||
|
Pubkey::from_str("Spoofed111111111111111111111111111111111111").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> {
|
fn process_instruction(instruction: &Instruction) -> Result<(), InstructionError> {
|
||||||
let accounts: Vec<_> = instruction
|
let accounts: Vec<_> = instruction
|
||||||
.accounts
|
.accounts
|
||||||
@ -571,8 +590,16 @@ mod tests {
|
|||||||
owner: solana_vote_program::id(),
|
owner: solana_vote_program::id(),
|
||||||
..Account::default()
|
..Account::default()
|
||||||
}
|
}
|
||||||
|
} else if meta.pubkey == spoofed_stake_state_pubkey() {
|
||||||
|
Account {
|
||||||
|
owner: spoofed_stake_program_id(),
|
||||||
|
..Account::default()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Account::default()
|
Account {
|
||||||
|
owner: id(),
|
||||||
|
..Account::default()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -678,6 +705,115 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spoofed_stake_accounts() {
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&initialize(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Authorized::default(),
|
||||||
|
&Lockup::default()
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&authorize(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
StakeAuthorize::Staker
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&split(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
100,
|
||||||
|
&Pubkey::default(),
|
||||||
|
)[1]
|
||||||
|
),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&split(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
100,
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
)[1]
|
||||||
|
),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&merge(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
)[0]
|
||||||
|
),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&merge(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
)[0]
|
||||||
|
),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&split_with_seed(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
100,
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
"seed"
|
||||||
|
)[1]
|
||||||
|
),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&delegate_stake(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&withdraw(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&solana_sdk::pubkey::new_rand(),
|
||||||
|
100,
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&deactivate_stake(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&Pubkey::default()
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&set_lockup(
|
||||||
|
&spoofed_stake_state_pubkey(),
|
||||||
|
&LockupArgs::default(),
|
||||||
|
&Pubkey::default()
|
||||||
|
)),
|
||||||
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stake_process_instruction_decode_bail() {
|
fn test_stake_process_instruction_decode_bail() {
|
||||||
// these will not call stake_state, have bogus contents
|
// these will not call stake_state, have bogus contents
|
||||||
@ -704,7 +840,7 @@ mod tests {
|
|||||||
&[KeyedAccount::new(
|
&[KeyedAccount::new(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
false,
|
false,
|
||||||
&create_default_account(),
|
&create_default_stake_account(),
|
||||||
)],
|
)],
|
||||||
&serialize(&StakeInstruction::Initialize(
|
&serialize(&StakeInstruction::Initialize(
|
||||||
Authorized::default(),
|
Authorized::default(),
|
||||||
@ -721,8 +857,8 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &create_default_account(),),
|
KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()),
|
||||||
KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account(),)
|
KeyedAccount::new(&sysvar::rent::id(), false, &create_default_account())
|
||||||
],
|
],
|
||||||
&serialize(&StakeInstruction::Initialize(
|
&serialize(&StakeInstruction::Initialize(
|
||||||
Authorized::default(),
|
Authorized::default(),
|
||||||
@ -739,7 +875,7 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &create_default_account()),
|
KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()),
|
||||||
KeyedAccount::new(
|
KeyedAccount::new(
|
||||||
&sysvar::rent::id(),
|
&sysvar::rent::id(),
|
||||||
false,
|
false,
|
||||||
@ -763,7 +899,7 @@ mod tests {
|
|||||||
&[KeyedAccount::new(
|
&[KeyedAccount::new(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
false,
|
false,
|
||||||
&create_default_account()
|
&create_default_stake_account()
|
||||||
),],
|
),],
|
||||||
&serialize(&StakeInstruction::DelegateStake).unwrap(),
|
&serialize(&StakeInstruction::DelegateStake).unwrap(),
|
||||||
&mut MockInvokeContext::default()
|
&mut MockInvokeContext::default()
|
||||||
@ -778,7 +914,7 @@ mod tests {
|
|||||||
&[KeyedAccount::new(
|
&[KeyedAccount::new(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
false,
|
false,
|
||||||
&create_default_account()
|
&create_default_stake_account()
|
||||||
)],
|
)],
|
||||||
&serialize(&StakeInstruction::DelegateStake).unwrap(),
|
&serialize(&StakeInstruction::DelegateStake).unwrap(),
|
||||||
&mut MockInvokeContext::default()
|
&mut MockInvokeContext::default()
|
||||||
@ -793,7 +929,7 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::default(), true, &create_default_account()),
|
KeyedAccount::new(&Pubkey::default(), true, &create_default_stake_account()),
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &bad_vote_account),
|
KeyedAccount::new(&Pubkey::default(), false, &bad_vote_account),
|
||||||
KeyedAccount::new(
|
KeyedAccount::new(
|
||||||
&sysvar::clock::id(),
|
&sysvar::clock::id(),
|
||||||
@ -825,7 +961,7 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &create_default_account()),
|
KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()),
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &create_default_account()),
|
KeyedAccount::new(&Pubkey::default(), false, &create_default_account()),
|
||||||
KeyedAccount::new(
|
KeyedAccount::new(
|
||||||
&sysvar::rewards::id(),
|
&sysvar::rewards::id(),
|
||||||
@ -854,7 +990,7 @@ mod tests {
|
|||||||
&[KeyedAccount::new(
|
&[KeyedAccount::new(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
false,
|
false,
|
||||||
&create_default_account()
|
&create_default_stake_account()
|
||||||
)],
|
)],
|
||||||
&serialize(&StakeInstruction::Withdraw(42)).unwrap(),
|
&serialize(&StakeInstruction::Withdraw(42)).unwrap(),
|
||||||
&mut MockInvokeContext::default()
|
&mut MockInvokeContext::default()
|
||||||
@ -867,7 +1003,7 @@ mod tests {
|
|||||||
super::process_instruction(
|
super::process_instruction(
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::default(), false, &create_default_account()),
|
KeyedAccount::new(&Pubkey::default(), false, &create_default_stake_account()),
|
||||||
KeyedAccount::new(
|
KeyedAccount::new(
|
||||||
&sysvar::rewards::id(),
|
&sysvar::rewards::id(),
|
||||||
false,
|
false,
|
||||||
|
@ -42,7 +42,7 @@ pub enum InflationPointCalculationEvent {
|
|||||||
CreditsObserved(u64, u64),
|
CreditsObserved(u64, u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
|
pub(crate) fn null_tracer() -> Option<impl FnMut(&InflationPointCalculationEvent)> {
|
||||||
None::<fn(&_)>
|
None::<fn(&_)>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -415,7 +415,7 @@ impl Delegation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rewrite_stake(
|
pub(crate) fn rewrite_stake(
|
||||||
&mut self,
|
&mut self,
|
||||||
account_balance: u64,
|
account_balance: u64,
|
||||||
rent_exempt_balance: u64,
|
rent_exempt_balance: u64,
|
||||||
|
@ -104,11 +104,11 @@ fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> {
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
Builtin::new(
|
Builtin::new(
|
||||||
"stake_program_v2",
|
"stake_program_v3",
|
||||||
solana_stake_program::id(),
|
solana_stake_program::id(),
|
||||||
solana_stake_program::stake_instruction::process_instruction,
|
solana_stake_program::stake_instruction::process_instruction,
|
||||||
),
|
),
|
||||||
feature_set::stake_program_v2::id(),
|
feature_set::stake_program_v3::id(),
|
||||||
ActivationType::NewVersion,
|
ActivationType::NewVersion,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
@ -114,6 +114,10 @@ pub mod warp_testnet_timestamp {
|
|||||||
solana_sdk::declare_id!("Bfqm7fGk5MBptqa2WHXWFLH7uJvq8hkJcAQPipy2bAMk");
|
solana_sdk::declare_id!("Bfqm7fGk5MBptqa2WHXWFLH7uJvq8hkJcAQPipy2bAMk");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod stake_program_v3 {
|
||||||
|
solana_sdk::declare_id!("Ego6nTu7WsBcZBvVqJQKp6Yku2N3mrfG8oYCfaLZkAeK");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
@ -144,6 +148,7 @@ lazy_static! {
|
|||||||
(bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"),
|
(bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"),
|
||||||
(try_find_program_address_syscall_enabled::id(), "add try_find_program_address syscall"),
|
(try_find_program_address_syscall_enabled::id(), "add try_find_program_address syscall"),
|
||||||
(warp_testnet_timestamp::id(), "warp testnet timestamp to current #14210"),
|
(warp_testnet_timestamp::id(), "warp testnet timestamp to current #14210"),
|
||||||
|
(stake_program_v3::id(), "solana_stake_program v3"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
Reference in New Issue
Block a user