automerge
This commit is contained in:
@ -397,6 +397,10 @@ pub enum CliCommand {
|
|||||||
vote_account_pubkey: Pubkey,
|
vote_account_pubkey: Pubkey,
|
||||||
new_identity_account: SignerIndex,
|
new_identity_account: SignerIndex,
|
||||||
},
|
},
|
||||||
|
VoteUpdateCommission {
|
||||||
|
vote_account_pubkey: Pubkey,
|
||||||
|
commission: u8,
|
||||||
|
},
|
||||||
// Wallet Commands
|
// Wallet Commands
|
||||||
Address,
|
Address,
|
||||||
Airdrop {
|
Airdrop {
|
||||||
@ -710,6 +714,9 @@ pub fn parse_command(
|
|||||||
("vote-update-validator", Some(matches)) => {
|
("vote-update-validator", Some(matches)) => {
|
||||||
parse_vote_update_validator(matches, default_signer_path, wallet_manager)
|
parse_vote_update_validator(matches, default_signer_path, wallet_manager)
|
||||||
}
|
}
|
||||||
|
("vote-update-commission", Some(matches)) => {
|
||||||
|
parse_vote_update_commission(matches, default_signer_path, wallet_manager)
|
||||||
|
}
|
||||||
("vote-authorize-voter", Some(matches)) => parse_vote_authorize(
|
("vote-authorize-voter", Some(matches)) => parse_vote_authorize(
|
||||||
matches,
|
matches,
|
||||||
default_signer_path,
|
default_signer_path,
|
||||||
@ -2144,6 +2151,10 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
|
|||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
*new_identity_account,
|
*new_identity_account,
|
||||||
),
|
),
|
||||||
|
CliCommand::VoteUpdateCommission {
|
||||||
|
vote_account_pubkey,
|
||||||
|
commission,
|
||||||
|
} => process_vote_update_commission(&rpc_client, config, &vote_account_pubkey, *commission),
|
||||||
|
|
||||||
// Wallet Commands
|
// Wallet Commands
|
||||||
|
|
||||||
|
104
cli/src/vote.rs
104
cli/src/vote.rs
@ -161,6 +161,35 @@ impl VoteSubCommands for App<'_, '_> {
|
|||||||
.help("Authorized withdrawer keypair"),
|
.help("Authorized withdrawer keypair"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("vote-update-commission")
|
||||||
|
.about("Update the vote account's commission")
|
||||||
|
.arg(
|
||||||
|
pubkey!(Arg::with_name("vote_account_pubkey")
|
||||||
|
.index(1)
|
||||||
|
.value_name("VOTE_ACCOUNT_ADDRESS")
|
||||||
|
.required(true),
|
||||||
|
"Vote account to update. "),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("commission")
|
||||||
|
.index(2)
|
||||||
|
.value_name("PERCENTAGE")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_valid_percentage)
|
||||||
|
.help("The new commission")
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authorized_withdrawer")
|
||||||
|
.index(3)
|
||||||
|
.value_name("AUTHORIZED_KEYPAIR")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_valid_signer)
|
||||||
|
.help("Authorized withdrawer keypair"),
|
||||||
|
)
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("vote-account")
|
SubCommand::with_name("vote-account")
|
||||||
.about("Show the contents of a vote account")
|
.about("Show the contents of a vote account")
|
||||||
@ -309,6 +338,33 @@ pub fn parse_vote_update_validator(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_vote_update_commission(
|
||||||
|
matches: &ArgMatches<'_>,
|
||||||
|
default_signer_path: &str,
|
||||||
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||||
|
) -> Result<CliCommandInfo, CliError> {
|
||||||
|
let vote_account_pubkey =
|
||||||
|
pubkey_of_signer(matches, "vote_account_pubkey", wallet_manager)?.unwrap();
|
||||||
|
let (authorized_withdrawer, _) = signer_of(matches, "authorized_withdrawer", wallet_manager)?;
|
||||||
|
let commission = value_t_or_exit!(matches, "commission", u8);
|
||||||
|
|
||||||
|
let payer_provided = None;
|
||||||
|
let signer_info = generate_unique_signers(
|
||||||
|
vec![payer_provided, authorized_withdrawer],
|
||||||
|
matches,
|
||||||
|
default_signer_path,
|
||||||
|
wallet_manager,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(CliCommandInfo {
|
||||||
|
command: CliCommand::VoteUpdateCommission {
|
||||||
|
vote_account_pubkey,
|
||||||
|
commission,
|
||||||
|
},
|
||||||
|
signers: signer_info.signers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_vote_get_account_command(
|
pub fn parse_vote_get_account_command(
|
||||||
matches: &ArgMatches<'_>,
|
matches: &ArgMatches<'_>,
|
||||||
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
||||||
@ -521,6 +577,33 @@ pub fn process_vote_update_validator(
|
|||||||
log_instruction_custom_error::<VoteError>(result, &config)
|
log_instruction_custom_error::<VoteError>(result, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_vote_update_commission(
|
||||||
|
rpc_client: &RpcClient,
|
||||||
|
config: &CliConfig,
|
||||||
|
vote_account_pubkey: &Pubkey,
|
||||||
|
commission: u8,
|
||||||
|
) -> ProcessResult {
|
||||||
|
let authorized_withdrawer = config.signers[1];
|
||||||
|
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
|
let ixs = vec![vote_instruction::update_commission(
|
||||||
|
vote_account_pubkey,
|
||||||
|
&authorized_withdrawer.pubkey(),
|
||||||
|
commission,
|
||||||
|
)];
|
||||||
|
|
||||||
|
let message = Message::new_with_payer(&ixs, Some(&config.signers[0].pubkey()));
|
||||||
|
let mut tx = Transaction::new_unsigned(message);
|
||||||
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
||||||
|
check_account_for_fee(
|
||||||
|
rpc_client,
|
||||||
|
&config.signers[0].pubkey(),
|
||||||
|
&fee_calculator,
|
||||||
|
&tx.message,
|
||||||
|
)?;
|
||||||
|
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
|
||||||
|
log_instruction_custom_error::<VoteError>(result, &config)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_vote_account(
|
fn get_vote_account(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
vote_account_pubkey: &Pubkey,
|
vote_account_pubkey: &Pubkey,
|
||||||
@ -842,6 +925,27 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let test_update_commission = test_commands.clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"vote-update-commission",
|
||||||
|
&pubkey_string,
|
||||||
|
"42",
|
||||||
|
&keypair_file,
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
parse_command(&test_update_commission, &default_keypair_file, &mut None).unwrap(),
|
||||||
|
CliCommandInfo {
|
||||||
|
command: CliCommand::VoteUpdateCommission {
|
||||||
|
vote_account_pubkey: pubkey,
|
||||||
|
commission: 42,
|
||||||
|
},
|
||||||
|
signers: vec![
|
||||||
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
||||||
|
Box::new(read_keypair_file(&keypair_file).unwrap()),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Test WithdrawFromVoteAccount subcommand
|
// Test WithdrawFromVoteAccount subcommand
|
||||||
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
|
let test_withdraw_from_vote_account = test_commands.clone().get_matches_from(vec![
|
||||||
"test",
|
"test",
|
||||||
|
@ -93,6 +93,13 @@ pub enum VoteInstruction {
|
|||||||
/// 2. [SIGNER] Withdraw authority
|
/// 2. [SIGNER] Withdraw authority
|
||||||
UpdateValidatorIdentity,
|
UpdateValidatorIdentity,
|
||||||
|
|
||||||
|
/// Update the commission for the vote account
|
||||||
|
///
|
||||||
|
/// # Account references
|
||||||
|
/// 0. [WRITE] Vote account to be updated
|
||||||
|
/// 1. [SIGNER] Withdraw authority
|
||||||
|
UpdateCommission(u8),
|
||||||
|
|
||||||
/// A Vote instruction with recent votes
|
/// A Vote instruction with recent votes
|
||||||
///
|
///
|
||||||
/// # Account references
|
/// # Account references
|
||||||
@ -190,6 +197,23 @@ pub fn update_validator_identity(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_commission(
|
||||||
|
vote_pubkey: &Pubkey,
|
||||||
|
authorized_withdrawer_pubkey: &Pubkey,
|
||||||
|
commission: u8,
|
||||||
|
) -> Instruction {
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new(*vote_pubkey, false),
|
||||||
|
AccountMeta::new_readonly(*authorized_withdrawer_pubkey, true),
|
||||||
|
];
|
||||||
|
|
||||||
|
Instruction::new(
|
||||||
|
id(),
|
||||||
|
&VoteInstruction::UpdateCommission(commission),
|
||||||
|
account_metas,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
|
pub fn vote(vote_pubkey: &Pubkey, authorized_voter_pubkey: &Pubkey, vote: Vote) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*vote_pubkey, false),
|
AccountMeta::new(*vote_pubkey, false),
|
||||||
@ -271,6 +295,9 @@ pub fn process_instruction(
|
|||||||
next_keyed_account(keyed_accounts)?.unsigned_key(),
|
next_keyed_account(keyed_accounts)?.unsigned_key(),
|
||||||
&signers,
|
&signers,
|
||||||
),
|
),
|
||||||
|
VoteInstruction::UpdateCommission(commission) => {
|
||||||
|
vote_state::update_commission(me, commission, &signers)
|
||||||
|
}
|
||||||
VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
|
VoteInstruction::Vote(vote) | VoteInstruction::VoteSwitch(vote, _) => {
|
||||||
inc_new_counter_info!("vote-native", 1);
|
inc_new_counter_info!("vote-native", 1);
|
||||||
vote_state::process_vote(
|
vote_state::process_vote(
|
||||||
@ -380,6 +407,14 @@ mod tests {
|
|||||||
)),
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(&update_commission(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
0,
|
||||||
|
)),
|
||||||
|
Err(InstructionError::InvalidAccountData),
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
process_instruction(&withdraw(
|
process_instruction(&withdraw(
|
||||||
|
@ -597,6 +597,23 @@ pub fn update_validator_identity<S: std::hash::BuildHasher>(
|
|||||||
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the vote account's commission
|
||||||
|
pub fn update_commission<S: std::hash::BuildHasher>(
|
||||||
|
vote_account: &KeyedAccount,
|
||||||
|
commission: u8,
|
||||||
|
signers: &HashSet<Pubkey, S>,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
let mut vote_state: VoteState =
|
||||||
|
State::<VoteStateVersions>::state(vote_account)?.convert_to_current();
|
||||||
|
|
||||||
|
// current authorized withdrawer must say "yay"
|
||||||
|
verify_authorized_signer(&vote_state.authorized_withdrawer, signers)?;
|
||||||
|
|
||||||
|
vote_state.commission = commission;
|
||||||
|
|
||||||
|
vote_account.set_state(&VoteStateVersions::Current(Box::new(vote_state)))
|
||||||
|
}
|
||||||
|
|
||||||
fn verify_authorized_signer<S: std::hash::BuildHasher>(
|
fn verify_authorized_signer<S: std::hash::BuildHasher>(
|
||||||
authorized: &Pubkey,
|
authorized: &Pubkey,
|
||||||
signers: &HashSet<Pubkey, S>,
|
signers: &HashSet<Pubkey, S>,
|
||||||
@ -997,6 +1014,50 @@ mod tests {
|
|||||||
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
assert_eq!(vote_state.node_pubkey, node_pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_vote_update_commission() {
|
||||||
|
let (vote_pubkey, _authorized_voter, authorized_withdrawer, vote_account) =
|
||||||
|
create_test_account_with_authorized();
|
||||||
|
|
||||||
|
let authorized_withdrawer_account = RefCell::new(Account::default());
|
||||||
|
|
||||||
|
let keyed_accounts = &[
|
||||||
|
KeyedAccount::new(&vote_pubkey, true, &vote_account),
|
||||||
|
KeyedAccount::new(
|
||||||
|
&authorized_withdrawer,
|
||||||
|
false,
|
||||||
|
&authorized_withdrawer_account,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
|
let res = update_commission(&keyed_accounts[0], 42, &signers);
|
||||||
|
assert_eq!(res, Err(InstructionError::MissingRequiredSignature));
|
||||||
|
|
||||||
|
let keyed_accounts = &[
|
||||||
|
KeyedAccount::new(&vote_pubkey, true, &vote_account),
|
||||||
|
KeyedAccount::new(&authorized_withdrawer, true, &authorized_withdrawer_account),
|
||||||
|
];
|
||||||
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
|
let res = update_commission(&keyed_accounts[0], 42, &signers);
|
||||||
|
assert_eq!(res, Ok(()));
|
||||||
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
|
assert_eq!(vote_state.commission, 42);
|
||||||
|
|
||||||
|
let keyed_accounts = &[
|
||||||
|
KeyedAccount::new(&vote_pubkey, true, &vote_account),
|
||||||
|
KeyedAccount::new(&authorized_withdrawer, true, &authorized_withdrawer_account),
|
||||||
|
];
|
||||||
|
let signers: HashSet<Pubkey> = get_signers(keyed_accounts);
|
||||||
|
let res = update_commission(&keyed_accounts[0], u8::MAX, &signers);
|
||||||
|
assert_eq!(res, Ok(()));
|
||||||
|
let vote_state: VoteState = StateMut::<VoteStateVersions>::state(&*vote_account.borrow())
|
||||||
|
.unwrap()
|
||||||
|
.convert_to_current();
|
||||||
|
assert_eq!(vote_state.commission, u8::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vote_signature() {
|
fn test_vote_signature() {
|
||||||
let (vote_pubkey, vote_account) = create_test_account();
|
let (vote_pubkey, vote_account) = create_test_account();
|
||||||
|
Reference in New Issue
Block a user