cli now supports a custodian for stake authorize operations
This commit is contained in:
parent
ffa5c7dcc8
commit
119e2c75dd
|
@ -260,6 +260,7 @@ pub enum CliCommand {
|
||||||
nonce_account: Option<Pubkey>,
|
nonce_account: Option<Pubkey>,
|
||||||
nonce_authority: SignerIndex,
|
nonce_authority: SignerIndex,
|
||||||
fee_payer: SignerIndex,
|
fee_payer: SignerIndex,
|
||||||
|
custodian: Option<SignerIndex>,
|
||||||
},
|
},
|
||||||
StakeSetLockup {
|
StakeSetLockup {
|
||||||
stake_account_pubkey: Pubkey,
|
stake_account_pubkey: Pubkey,
|
||||||
|
@ -1520,11 +1521,13 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority,
|
nonce_authority,
|
||||||
fee_payer,
|
fee_payer,
|
||||||
|
custodian,
|
||||||
} => process_stake_authorize(
|
} => process_stake_authorize(
|
||||||
&rpc_client,
|
&rpc_client,
|
||||||
config,
|
config,
|
||||||
&stake_account_pubkey,
|
&stake_account_pubkey,
|
||||||
new_authorizations,
|
new_authorizations,
|
||||||
|
*custodian,
|
||||||
*sign_only,
|
*sign_only,
|
||||||
blockhash_query,
|
blockhash_query,
|
||||||
*nonce_account,
|
*nonce_account,
|
||||||
|
|
|
@ -64,6 +64,12 @@ pub const WITHDRAW_AUTHORITY_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
help: "Authorized withdrawer [default: cli config keypair]",
|
help: "Authorized withdrawer [default: cli config keypair]",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const CUSTODIAN_ARG: ArgConstant<'static> = ArgConstant {
|
||||||
|
name: "custodian",
|
||||||
|
long: "custodian",
|
||||||
|
help: "Authority to override account lockup",
|
||||||
|
};
|
||||||
|
|
||||||
fn stake_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
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)
|
||||||
|
@ -82,6 +88,15 @@ fn withdraw_authority_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
.help(WITHDRAW_AUTHORITY_ARG.help)
|
.help(WITHDRAW_AUTHORITY_ARG.help)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn custodian_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||||
|
Arg::with_name(CUSTODIAN_ARG.name)
|
||||||
|
.long(CUSTODIAN_ARG.long)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("KEYPAIR")
|
||||||
|
.validator(is_valid_signer)
|
||||||
|
.help(CUSTODIAN_ARG.help)
|
||||||
|
}
|
||||||
|
|
||||||
pub trait StakeSubCommands {
|
pub trait StakeSubCommands {
|
||||||
fn stake_subcommands(self) -> Self;
|
fn stake_subcommands(self) -> Self;
|
||||||
}
|
}
|
||||||
|
@ -223,6 +238,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||||
.offline_args()
|
.offline_args()
|
||||||
.nonce_args(false)
|
.nonce_args(false)
|
||||||
.arg(fee_payer_arg())
|
.arg(fee_payer_arg())
|
||||||
|
.arg(custodian_arg())
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("deactivate-stake")
|
SubCommand::with_name("deactivate-stake")
|
||||||
|
@ -331,14 +347,7 @@ impl StakeSubCommands for App<'_, '_> {
|
||||||
.offline_args()
|
.offline_args()
|
||||||
.nonce_args(false)
|
.nonce_args(false)
|
||||||
.arg(fee_payer_arg())
|
.arg(fee_payer_arg())
|
||||||
.arg(
|
.arg(custodian_arg())
|
||||||
Arg::with_name("custodian")
|
|
||||||
.long("custodian")
|
|
||||||
.takes_value(true)
|
|
||||||
.value_name("KEYPAIR")
|
|
||||||
.validator(is_valid_signer)
|
|
||||||
.help("Authority to override account lockup")
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("stake-set-lockup")
|
SubCommand::with_name("stake-set-lockup")
|
||||||
|
@ -561,11 +570,15 @@ pub fn parse_stake_authorize(
|
||||||
let (nonce_authority, nonce_authority_pubkey) =
|
let (nonce_authority, nonce_authority_pubkey) =
|
||||||
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?;
|
||||||
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
let (fee_payer, fee_payer_pubkey) = signer_of(matches, FEE_PAYER_ARG.name, wallet_manager)?;
|
||||||
|
let (custodian, custodian_pubkey) = signer_of(matches, "custodian", wallet_manager)?;
|
||||||
|
|
||||||
bulk_signers.push(fee_payer);
|
bulk_signers.push(fee_payer);
|
||||||
if nonce_account.is_some() {
|
if nonce_account.is_some() {
|
||||||
bulk_signers.push(nonce_authority);
|
bulk_signers.push(nonce_authority);
|
||||||
}
|
}
|
||||||
|
if custodian.is_some() {
|
||||||
|
bulk_signers.push(custodian);
|
||||||
|
}
|
||||||
let signer_info =
|
let signer_info =
|
||||||
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
||||||
|
|
||||||
|
@ -591,6 +604,7 @@ pub fn parse_stake_authorize(
|
||||||
nonce_account,
|
nonce_account,
|
||||||
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
||||||
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
||||||
|
custodian: custodian_pubkey.and_then(|_| signer_info.index_of(custodian_pubkey)),
|
||||||
},
|
},
|
||||||
signers: signer_info.signers,
|
signers: signer_info.signers,
|
||||||
})
|
})
|
||||||
|
@ -970,6 +984,7 @@ pub fn process_stake_authorize(
|
||||||
config: &CliConfig,
|
config: &CliConfig,
|
||||||
stake_account_pubkey: &Pubkey,
|
stake_account_pubkey: &Pubkey,
|
||||||
new_authorizations: &[(StakeAuthorize, Pubkey, SignerIndex)],
|
new_authorizations: &[(StakeAuthorize, Pubkey, SignerIndex)],
|
||||||
|
custodian: Option<SignerIndex>,
|
||||||
sign_only: bool,
|
sign_only: bool,
|
||||||
blockhash_query: &BlockhashQuery,
|
blockhash_query: &BlockhashQuery,
|
||||||
nonce_account: Option<Pubkey>,
|
nonce_account: Option<Pubkey>,
|
||||||
|
@ -977,6 +992,7 @@ pub fn process_stake_authorize(
|
||||||
fee_payer: SignerIndex,
|
fee_payer: SignerIndex,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let mut ixs = Vec::new();
|
let mut ixs = Vec::new();
|
||||||
|
let custodian = custodian.map(|index| config.signers[index]);
|
||||||
for (stake_authorize, authorized_pubkey, authority) in new_authorizations.iter() {
|
for (stake_authorize, authorized_pubkey, authority) in new_authorizations.iter() {
|
||||||
check_unique_pubkeys(
|
check_unique_pubkeys(
|
||||||
(stake_account_pubkey, "stake_account_pubkey".to_string()),
|
(stake_account_pubkey, "stake_account_pubkey".to_string()),
|
||||||
|
@ -988,6 +1004,7 @@ pub fn process_stake_authorize(
|
||||||
&authority.pubkey(), // currently authorized
|
&authority.pubkey(), // currently authorized
|
||||||
authorized_pubkey, // new stake signer
|
authorized_pubkey, // new stake signer
|
||||||
*stake_authorize, // stake or withdraw
|
*stake_authorize, // stake or withdraw
|
||||||
|
custodian.map(|signer| signer.pubkey()).as_ref(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1939,6 +1956,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||||
},
|
},
|
||||||
|
@ -1973,6 +1991,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2011,6 +2030,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2038,6 +2058,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||||
},
|
},
|
||||||
|
@ -2062,6 +2083,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2092,6 +2114,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2123,6 +2146,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into(),],
|
||||||
},
|
},
|
||||||
|
@ -2151,6 +2175,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2185,6 +2210,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
|
@ -2221,6 +2247,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 1,
|
fee_payer: 1,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2267,6 +2294,7 @@ mod tests {
|
||||||
nonce_account: Some(nonce_account),
|
nonce_account: Some(nonce_account),
|
||||||
nonce_authority: 2,
|
nonce_authority: 2,
|
||||||
fee_payer: 1,
|
fee_payer: 1,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2299,6 +2327,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
||||||
}
|
}
|
||||||
|
@ -2336,6 +2365,7 @@ mod tests {
|
||||||
nonce_account: Some(nonce_account_pubkey),
|
nonce_account: Some(nonce_account_pubkey),
|
||||||
nonce_authority: 1,
|
nonce_authority: 1,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2369,6 +2399,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 1,
|
fee_payer: 1,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
@ -2406,6 +2437,7 @@ mod tests {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 1,
|
fee_payer: 1,
|
||||||
|
custodian: None,
|
||||||
},
|
},
|
||||||
signers: vec![
|
signers: vec![
|
||||||
read_keypair_file(&default_keypair_file).unwrap().into(),
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
|
|
@ -592,6 +592,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
@ -619,6 +620,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
@ -641,6 +643,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
@ -663,6 +666,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
config_offline.output_format = OutputFormat::JsonCompact;
|
config_offline.output_format = OutputFormat::JsonCompact;
|
||||||
let sign_reply = process_command(&config_offline).unwrap();
|
let sign_reply = process_command(&config_offline).unwrap();
|
||||||
|
@ -678,6 +682,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
@ -724,6 +729,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: Some(nonce_account.pubkey()),
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
let sign_reply = process_command(&config_offline).unwrap();
|
let sign_reply = process_command(&config_offline).unwrap();
|
||||||
let sign_only = parse_sign_only_reply_string(&sign_reply);
|
let sign_only = parse_sign_only_reply_string(&sign_reply);
|
||||||
|
@ -743,6 +749,7 @@ fn test_stake_authorize() {
|
||||||
nonce_account: Some(nonce_account.pubkey()),
|
nonce_account: Some(nonce_account.pubkey()),
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
|
||||||
|
@ -845,6 +852,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 1,
|
fee_payer: 1,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config` balance has not changed, despite submitting the TX
|
// `config` balance has not changed, despite submitting the TX
|
||||||
|
@ -863,6 +871,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
config_offline.output_format = OutputFormat::JsonCompact;
|
config_offline.output_format = OutputFormat::JsonCompact;
|
||||||
let sign_reply = process_command(&config_offline).unwrap();
|
let sign_reply = process_command(&config_offline).unwrap();
|
||||||
|
@ -878,6 +887,7 @@ fn test_stake_authorize_with_fee_payer() {
|
||||||
nonce_account: None,
|
nonce_account: None,
|
||||||
nonce_authority: 0,
|
nonce_authority: 0,
|
||||||
fee_payer: 0,
|
fee_payer: 0,
|
||||||
|
custodian: None,
|
||||||
};
|
};
|
||||||
process_command(&config).unwrap();
|
process_command(&config).unwrap();
|
||||||
// `config`'s balance again has not changed
|
// `config`'s balance again has not changed
|
||||||
|
|
|
@ -781,6 +781,7 @@ mod tests {
|
||||||
&stake_authority.pubkey(),
|
&stake_authority.pubkey(),
|
||||||
&new_stake_authority,
|
&new_stake_authority,
|
||||||
StakeAuthorize::Staker,
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let message = Message::new(&[ix], Some(&stake_authority.pubkey()));
|
let message = Message::new(&[ix], Some(&stake_authority.pubkey()));
|
||||||
let tx = Transaction::new(&[&stake_authority], message, blockhash);
|
let tx = Transaction::new(&[&stake_authority], message, blockhash);
|
||||||
|
|
|
@ -68,6 +68,8 @@ pub enum StakeInstruction {
|
||||||
/// 0. [WRITE] Stake account to be updated
|
/// 0. [WRITE] Stake account to be updated
|
||||||
/// 1. [] Clock sysvar
|
/// 1. [] Clock sysvar
|
||||||
/// 2. [SIGNER] The stake or withdraw authority
|
/// 2. [SIGNER] The stake or withdraw authority
|
||||||
|
/// 3. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
|
||||||
|
/// lockup expiration
|
||||||
Authorize(Pubkey, StakeAuthorize),
|
Authorize(Pubkey, StakeAuthorize),
|
||||||
|
|
||||||
/// Delegate a stake to a particular vote account
|
/// Delegate a stake to a particular vote account
|
||||||
|
@ -139,6 +141,8 @@ pub enum StakeInstruction {
|
||||||
/// 0. [WRITE] Stake account to be updated
|
/// 0. [WRITE] Stake account to be updated
|
||||||
/// 1. [SIGNER] Base key of stake or withdraw authority
|
/// 1. [SIGNER] Base key of stake or withdraw authority
|
||||||
/// 2. [] Clock sysvar
|
/// 2. [] Clock sysvar
|
||||||
|
/// 3. Optional: [SIGNER] Lockup authority, if updating StakeAuthorize::Withdrawer before
|
||||||
|
/// lockup expiration
|
||||||
AuthorizeWithSeed(AuthorizeWithSeedArgs),
|
AuthorizeWithSeed(AuthorizeWithSeedArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,13 +348,18 @@ pub fn authorize(
|
||||||
authorized_pubkey: &Pubkey,
|
authorized_pubkey: &Pubkey,
|
||||||
new_authorized_pubkey: &Pubkey,
|
new_authorized_pubkey: &Pubkey,
|
||||||
stake_authorize: StakeAuthorize,
|
stake_authorize: StakeAuthorize,
|
||||||
|
custodian_pubkey: Option<&Pubkey>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let mut account_metas = vec![
|
||||||
AccountMeta::new(*stake_pubkey, false),
|
AccountMeta::new(*stake_pubkey, false),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(*authorized_pubkey, true),
|
AccountMeta::new_readonly(*authorized_pubkey, true),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||||
|
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
|
||||||
|
}
|
||||||
|
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
|
&StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
|
||||||
|
@ -365,13 +374,18 @@ pub fn authorize_with_seed(
|
||||||
authority_owner: &Pubkey,
|
authority_owner: &Pubkey,
|
||||||
new_authorized_pubkey: &Pubkey,
|
new_authorized_pubkey: &Pubkey,
|
||||||
stake_authorize: StakeAuthorize,
|
stake_authorize: StakeAuthorize,
|
||||||
|
custodian_pubkey: Option<&Pubkey>,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let mut account_metas = vec![
|
||||||
AccountMeta::new(*stake_pubkey, false),
|
AccountMeta::new(*stake_pubkey, false),
|
||||||
AccountMeta::new_readonly(*authority_base, true),
|
AccountMeta::new_readonly(*authority_base, true),
|
||||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if let Some(custodian_pubkey) = custodian_pubkey {
|
||||||
|
account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
|
||||||
|
}
|
||||||
|
|
||||||
let args = AuthorizeWithSeedArgs {
|
let args = AuthorizeWithSeedArgs {
|
||||||
new_authorized_pubkey: *new_authorized_pubkey,
|
new_authorized_pubkey: *new_authorized_pubkey,
|
||||||
stake_authorize,
|
stake_authorize,
|
||||||
|
@ -637,7 +651,8 @@ mod tests {
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
StakeAuthorize::Staker
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
|
@ -722,7 +737,8 @@ mod tests {
|
||||||
&spoofed_stake_state_pubkey(),
|
&spoofed_stake_state_pubkey(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
&Pubkey::default(),
|
&Pubkey::default(),
|
||||||
StakeAuthorize::Staker
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
Err(InstructionError::IncorrectProgramId),
|
Err(InstructionError::IncorrectProgramId),
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,12 +66,14 @@ fn authorize_stake_accounts_instructions(
|
||||||
stake_authority_pubkey,
|
stake_authority_pubkey,
|
||||||
new_stake_authority_pubkey,
|
new_stake_authority_pubkey,
|
||||||
StakeAuthorize::Staker,
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let instruction1 = stake_instruction::authorize(
|
let instruction1 = stake_instruction::authorize(
|
||||||
&stake_account_address,
|
&stake_account_address,
|
||||||
withdraw_authority_pubkey,
|
withdraw_authority_pubkey,
|
||||||
new_withdraw_authority_pubkey,
|
new_withdraw_authority_pubkey,
|
||||||
StakeAuthorize::Withdrawer,
|
StakeAuthorize::Withdrawer,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
vec![instruction0, instruction1]
|
vec![instruction0, instruction1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,7 @@ fn distribution_instructions(
|
||||||
&stake_authority,
|
&stake_authority,
|
||||||
&recipient,
|
&recipient,
|
||||||
StakeAuthorize::Staker,
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Make the recipient the new withdraw authority
|
// Make the recipient the new withdraw authority
|
||||||
|
@ -219,6 +220,7 @@ fn distribution_instructions(
|
||||||
&withdraw_authority,
|
&withdraw_authority,
|
||||||
&recipient,
|
&recipient,
|
||||||
StakeAuthorize::Withdrawer,
|
StakeAuthorize::Withdrawer,
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add lockup
|
// Add lockup
|
||||||
|
|
|
@ -45,15 +45,23 @@ pub fn parse_stake(
|
||||||
}
|
}
|
||||||
StakeInstruction::Authorize(new_authorized, authority_type) => {
|
StakeInstruction::Authorize(new_authorized, authority_type) => {
|
||||||
check_num_stake_accounts(&instruction.accounts, 3)?;
|
check_num_stake_accounts(&instruction.accounts, 3)?;
|
||||||
Ok(ParsedInstructionEnum {
|
let mut value = json!({
|
||||||
instruction_type: "authorize".to_string(),
|
|
||||||
info: json!({
|
|
||||||
"stakeAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
"stakeAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
"clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
|
"clockSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
"authority": account_keys[instruction.accounts[2] as usize].to_string(),
|
"authority": account_keys[instruction.accounts[2] as usize].to_string(),
|
||||||
"newAuthority": new_authorized.to_string(),
|
"newAuthority": new_authorized.to_string(),
|
||||||
"authorityType": authority_type,
|
"authorityType": authority_type,
|
||||||
}),
|
});
|
||||||
|
let map = value.as_object_mut().unwrap();
|
||||||
|
if instruction.accounts.len() >= 4 {
|
||||||
|
map.insert(
|
||||||
|
"custodian".to_string(),
|
||||||
|
json!(account_keys[instruction.accounts[3] as usize].to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(ParsedInstructionEnum {
|
||||||
|
instruction_type: "authorize".to_string(),
|
||||||
|
info: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
StakeInstruction::DelegateStake => {
|
StakeInstruction::DelegateStake => {
|
||||||
|
@ -93,7 +101,7 @@ pub fn parse_stake(
|
||||||
"lamports": lamports,
|
"lamports": lamports,
|
||||||
});
|
});
|
||||||
let map = value.as_object_mut().unwrap();
|
let map = value.as_object_mut().unwrap();
|
||||||
if instruction.accounts.len() == 6 {
|
if instruction.accounts.len() >= 6 {
|
||||||
map.insert(
|
map.insert(
|
||||||
"custodian".to_string(),
|
"custodian".to_string(),
|
||||||
json!(account_keys[instruction.accounts[5] as usize].to_string()),
|
json!(account_keys[instruction.accounts[5] as usize].to_string()),
|
||||||
|
@ -151,16 +159,30 @@ pub fn parse_stake(
|
||||||
}
|
}
|
||||||
StakeInstruction::AuthorizeWithSeed(args) => {
|
StakeInstruction::AuthorizeWithSeed(args) => {
|
||||||
check_num_stake_accounts(&instruction.accounts, 2)?;
|
check_num_stake_accounts(&instruction.accounts, 2)?;
|
||||||
Ok(ParsedInstructionEnum {
|
let mut value = json!({
|
||||||
instruction_type: "authorizeWithSeed".to_string(),
|
|
||||||
info: json!({
|
|
||||||
"stakeAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
"stakeAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||||
"authorityBase": account_keys[instruction.accounts[1] as usize].to_string(),
|
"authorityBase": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||||
"newAuthorized": args.new_authorized_pubkey.to_string(),
|
"newAuthorized": args.new_authorized_pubkey.to_string(),
|
||||||
"authorityType": args.stake_authorize,
|
"authorityType": args.stake_authorize,
|
||||||
"authoritySeed": args.authority_seed,
|
"authoritySeed": args.authority_seed,
|
||||||
"authorityOwner": args.authority_owner.to_string(),
|
"authorityOwner": args.authority_owner.to_string(),
|
||||||
}),
|
});
|
||||||
|
let map = value.as_object_mut().unwrap();
|
||||||
|
if instruction.accounts.len() >= 3 {
|
||||||
|
map.insert(
|
||||||
|
"clockSysvar".to_string(),
|
||||||
|
json!(account_keys[instruction.accounts[2] as usize].to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if instruction.accounts.len() >= 4 {
|
||||||
|
map.insert(
|
||||||
|
"custodian".to_string(),
|
||||||
|
json!(account_keys[instruction.accounts[3] as usize].to_string()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(ParsedInstructionEnum {
|
||||||
|
instruction_type: "authorizeWithSeed".to_string(),
|
||||||
|
info: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,17 +206,17 @@ mod test {
|
||||||
fn test_parse_stake_instruction() {
|
fn test_parse_stake_instruction() {
|
||||||
let mut keys: Vec<Pubkey> = vec![];
|
let mut keys: Vec<Pubkey> = vec![];
|
||||||
for _ in 0..6 {
|
for _ in 0..6 {
|
||||||
keys.push(solana_sdk::pubkey::new_rand());
|
keys.push(Pubkey::new_unique());
|
||||||
}
|
}
|
||||||
|
|
||||||
let authorized = Authorized {
|
let authorized = Authorized {
|
||||||
staker: solana_sdk::pubkey::new_rand(),
|
staker: Pubkey::new_unique(),
|
||||||
withdrawer: solana_sdk::pubkey::new_rand(),
|
withdrawer: Pubkey::new_unique(),
|
||||||
};
|
};
|
||||||
let lockup = Lockup {
|
let lockup = Lockup {
|
||||||
unix_timestamp: 1_234_567_890,
|
unix_timestamp: 1_234_567_890,
|
||||||
epoch: 11,
|
epoch: 11,
|
||||||
custodian: solana_sdk::pubkey::new_rand(),
|
custodian: Pubkey::new_unique(),
|
||||||
};
|
};
|
||||||
let lamports = 55;
|
let lamports = 55;
|
||||||
|
|
||||||
|
@ -222,9 +244,13 @@ mod test {
|
||||||
);
|
);
|
||||||
assert!(parse_stake(&message.instructions[1], &keys[0..2]).is_err());
|
assert!(parse_stake(&message.instructions[1], &keys[0..2]).is_err());
|
||||||
|
|
||||||
let authority_type = StakeAuthorize::Staker;
|
let instruction = stake_instruction::authorize(
|
||||||
let instruction =
|
&keys[1],
|
||||||
stake_instruction::authorize(&keys[1], &keys[0], &keys[3], authority_type);
|
&keys[0],
|
||||||
|
&keys[3],
|
||||||
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
let message = Message::new(&[instruction], None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(),
|
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(),
|
||||||
|
@ -235,7 +261,31 @@ mod test {
|
||||||
"clockSysvar": keys[2].to_string(),
|
"clockSysvar": keys[2].to_string(),
|
||||||
"authority": keys[0].to_string(),
|
"authority": keys[0].to_string(),
|
||||||
"newAuthority": keys[3].to_string(),
|
"newAuthority": keys[3].to_string(),
|
||||||
"authorityType": authority_type,
|
"authorityType": StakeAuthorize::Staker,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert!(parse_stake(&message.instructions[0], &keys[0..2]).is_err());
|
||||||
|
|
||||||
|
let instruction = stake_instruction::authorize(
|
||||||
|
&keys[1],
|
||||||
|
&keys[0],
|
||||||
|
&keys[3],
|
||||||
|
StakeAuthorize::Withdrawer,
|
||||||
|
Some(&keys[1]),
|
||||||
|
);
|
||||||
|
let message = Message::new(&[instruction], None);
|
||||||
|
assert_eq!(
|
||||||
|
parse_stake(&message.instructions[0], &keys[0..3]).unwrap(),
|
||||||
|
ParsedInstructionEnum {
|
||||||
|
instruction_type: "authorize".to_string(),
|
||||||
|
info: json!({
|
||||||
|
"stakeAccount": keys[1].to_string(),
|
||||||
|
"clockSysvar": keys[2].to_string(),
|
||||||
|
"authority": keys[0].to_string(),
|
||||||
|
"newAuthority": keys[3].to_string(),
|
||||||
|
"authorityType": StakeAuthorize::Withdrawer,
|
||||||
|
"custodian": keys[1].to_string(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -350,7 +400,8 @@ mod test {
|
||||||
seed.to_string(),
|
seed.to_string(),
|
||||||
&keys[2],
|
&keys[2],
|
||||||
&keys[3],
|
&keys[3],
|
||||||
authority_type,
|
StakeAuthorize::Staker,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
let message = Message::new(&[instruction], None);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -363,7 +414,36 @@ mod test {
|
||||||
"newAuthorized": keys[3].to_string(),
|
"newAuthorized": keys[3].to_string(),
|
||||||
"authorityBase": keys[0].to_string(),
|
"authorityBase": keys[0].to_string(),
|
||||||
"authoritySeed": seed,
|
"authoritySeed": seed,
|
||||||
"authorityType": authority_type,
|
"authorityType": StakeAuthorize::Staker,
|
||||||
|
"clockSysvar": keys[2].to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert!(parse_stake(&message.instructions[0], &keys[0..1]).is_err());
|
||||||
|
|
||||||
|
let instruction = stake_instruction::authorize_with_seed(
|
||||||
|
&keys[1],
|
||||||
|
&keys[0],
|
||||||
|
seed.to_string(),
|
||||||
|
&keys[2],
|
||||||
|
&keys[3],
|
||||||
|
StakeAuthorize::Withdrawer,
|
||||||
|
Some(&keys[4]),
|
||||||
|
);
|
||||||
|
let message = Message::new(&[instruction], None);
|
||||||
|
assert_eq!(
|
||||||
|
parse_stake(&message.instructions[0], &keys[0..5]).unwrap(),
|
||||||
|
ParsedInstructionEnum {
|
||||||
|
instruction_type: "authorizeWithSeed".to_string(),
|
||||||
|
info: json!({
|
||||||
|
"stakeAccount": keys[2].to_string(),
|
||||||
|
"authorityOwner": keys[2].to_string(),
|
||||||
|
"newAuthorized": keys[3].to_string(),
|
||||||
|
"authorityBase": keys[0].to_string(),
|
||||||
|
"authoritySeed": seed,
|
||||||
|
"authorityType": StakeAuthorize::Withdrawer,
|
||||||
|
"clockSysvar": keys[3].to_string(),
|
||||||
|
"custodian": keys[1].to_string(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -375,11 +455,11 @@ mod test {
|
||||||
fn test_parse_set_lockup() {
|
fn test_parse_set_lockup() {
|
||||||
let mut keys: Vec<Pubkey> = vec![];
|
let mut keys: Vec<Pubkey> = vec![];
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
keys.push(solana_sdk::pubkey::new_rand());
|
keys.push(Pubkey::new_unique());
|
||||||
}
|
}
|
||||||
let unix_timestamp = 1_234_567_890;
|
let unix_timestamp = 1_234_567_890;
|
||||||
let epoch = 11;
|
let epoch = 11;
|
||||||
let custodian = solana_sdk::pubkey::new_rand();
|
let custodian = Pubkey::new_unique();
|
||||||
|
|
||||||
let lockup = LockupArgs {
|
let lockup = LockupArgs {
|
||||||
unix_timestamp: Some(unix_timestamp),
|
unix_timestamp: Some(unix_timestamp),
|
||||||
|
|
Loading…
Reference in New Issue