custodian signs withdraw (#7286)
This commit is contained in:
parent
8a28734603
commit
709bda5939
|
@ -16,7 +16,7 @@ The total stake allocated to a Vote account can be calculated by the sum of all
|
|||
|
||||
## Vote and Stake accounts
|
||||
|
||||
The rewards process is split into two on-chain programs. The Vote program solves the problem of making stakes slashable. The Stake account acts as custodian of the rewards pool, and provides passive delegation. The Stake program is responsible for paying out each staker once the staker proves to the Stake program that its delegate has participated in validating the ledger.
|
||||
The rewards process is split into two on-chain programs. The Vote program solves the problem of making stakes slashable. The Stake program acts as custodian of the rewards pool and provides for passive delegation. The Stake program is responsible for paying rewards to staker and voter when shown that a staker's delegate has participated in validating the ledger.
|
||||
|
||||
### VoteState
|
||||
|
||||
|
@ -228,4 +228,4 @@ Only lamports in excess of effective+activating stake may be withdrawn at any ti
|
|||
|
||||
### Lock-up
|
||||
|
||||
Stake accounts support the notion of lock-up, wherein the stake account balance is unavailable for withdrawal until a specified time. Lock-up is specified as a slot height, i.e. the minimum slot height that must be reached by the network before the stake account balance is available for withdrawal, except to a specified custodian. This information is gathered when the stake account is created.
|
||||
Stake accounts support the notion of lock-up, wherein the stake account balance is unavailable for withdrawal until a specified time. Lock-up is specified as an epoch height, i.e. the minimum epoch height that must be reached by the network before the stake account balance is available for withdrawal, unless the transaction is also signed by a specified custodian. This information is gathered when the stake account is created, and stored in the Lockup field of the stake account's state.
|
||||
|
|
|
@ -105,11 +105,13 @@ pub enum StakeInstruction {
|
|||
Split(u64),
|
||||
|
||||
/// Withdraw unstaked lamports from the stake account
|
||||
/// requires Authorized::withdrawer signature
|
||||
/// requires Authorized::withdrawer signature. If withdrawal
|
||||
/// is before lockup has expired, also requires signature
|
||||
/// of the lockup custodian.
|
||||
///
|
||||
/// Expects 4 Accounts:
|
||||
/// 0 - StakeAccount from which to withdraw
|
||||
/// 1 - System account to which the lamports will be transferred,
|
||||
/// 1 - Account to which the lamports will be transferred
|
||||
/// 2 - Syscall Account that carries epoch
|
||||
/// 3 - StakeHistory sysvar that carries stake warmup/cooldown history
|
||||
///
|
||||
|
@ -256,7 +258,7 @@ pub fn delegate_stake(
|
|||
|
||||
pub fn withdraw(
|
||||
stake_pubkey: &Pubkey,
|
||||
authorized_pubkey: &Pubkey,
|
||||
withdrawer_pubkey: &Pubkey,
|
||||
to_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
) -> Instruction {
|
||||
|
@ -266,7 +268,27 @@ pub fn withdraw(
|
|||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
]
|
||||
.with_signer(authorized_pubkey);
|
||||
.with_signer(withdrawer_pubkey);
|
||||
|
||||
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
||||
}
|
||||
|
||||
pub fn withdraw_early(
|
||||
stake_pubkey: &Pubkey,
|
||||
withdrawer_pubkey: &Pubkey,
|
||||
to_pubkey: &Pubkey,
|
||||
lamports: u64,
|
||||
custodian_pubkey: &Pubkey,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*stake_pubkey, false),
|
||||
AccountMeta::new(*to_pubkey, false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::stake_history::id(), false),
|
||||
]
|
||||
.with_signer(withdrawer_pubkey)
|
||||
.with_signer(custodian_pubkey);
|
||||
|
||||
Instruction::new(id(), &StakeInstruction::Withdraw(lamports), account_metas)
|
||||
}
|
||||
|
||||
|
|
|
@ -754,9 +754,9 @@ impl<'a> StakeAccount for KeyedAccount<'a> {
|
|||
_ => return Err(InstructionError::InvalidAccountData),
|
||||
};
|
||||
|
||||
// verify that lockup has expired or that the withdrawal is going back
|
||||
// to the custodian
|
||||
if lockup.epoch > clock.epoch && lockup.custodian != *to.unsigned_key() {
|
||||
// verify that lockup has expired or that the withdrawal is signed by
|
||||
// the custodian
|
||||
if lockup.epoch > clock.epoch && !signers.contains(&lockup.custodian) {
|
||||
return Err(StakeError::LockupInForce.into());
|
||||
}
|
||||
|
||||
|
@ -1792,20 +1792,20 @@ mod tests {
|
|||
Err(StakeError::LockupInForce.into())
|
||||
);
|
||||
|
||||
// but we *can* send to the custodian
|
||||
let mut custodian_account = Account::new(1, 0, &system_program::id());
|
||||
let mut custodian_keyed_account =
|
||||
KeyedAccount::new(&custodian, false, &mut custodian_account);
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
&mut custodian_keyed_account,
|
||||
&clock,
|
||||
&StakeHistory::default(),
|
||||
&signers,
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
{
|
||||
let mut signers_with_custodian = signers.clone();
|
||||
signers_with_custodian.insert(custodian);
|
||||
assert_eq!(
|
||||
stake_keyed_account.withdraw(
|
||||
total_lamports,
|
||||
&mut to_keyed_account,
|
||||
&clock,
|
||||
&StakeHistory::default(),
|
||||
&signers_with_custodian,
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
// reset balance
|
||||
stake_keyed_account.account.lamports = total_lamports;
|
||||
|
||||
|
|
Loading…
Reference in New Issue