Cherry-pick vote and stake authority changes (#6127)

* add authorized parameters to vote api (#6072)

* add authorized parameters to vote api

* code review

* add authorities to stake init (#6104)

* add authorities to stake init

* fixups

* code review
This commit is contained in:
Tyera Eulberg
2019-09-26 17:18:31 -06:00
committed by GitHub
parent 232d2b3899
commit 61930c0dd3
14 changed files with 947 additions and 428 deletions

View File

@@ -15,13 +15,16 @@ use solana_sdk::{
};
use solana_vote_api::{
vote_instruction::{self, VoteError},
vote_state::VoteState,
vote_state::{VoteAuthorize, VoteInit, VoteState},
};
pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<WalletCommand, WalletError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let node_pubkey = pubkey_of(matches, "node_pubkey").unwrap();
let commission = value_of(&matches, "commission").unwrap_or(0);
let authorized_voter = pubkey_of(matches, "authorized_voter").unwrap_or(vote_account_pubkey);
let authorized_withdrawer =
pubkey_of(matches, "authorized_withdrawer").unwrap_or(vote_account_pubkey);
let lamports = crate::wallet::parse_amount_lamports(
matches.value_of("amount").unwrap(),
matches.value_of("unit"),
@@ -30,21 +33,29 @@ pub fn parse_vote_create_account(matches: &ArgMatches<'_>) -> Result<WalletComma
Ok(WalletCommand::CreateVoteAccount(
vote_account_pubkey,
node_pubkey,
commission,
VoteInit {
node_pubkey,
authorized_voter,
authorized_withdrawer,
commission,
},
lamports,
))
}
pub fn parse_vote_authorize_voter(matches: &ArgMatches<'_>) -> Result<WalletCommand, WalletError> {
pub fn parse_vote_authorize(
matches: &ArgMatches<'_>,
vote_authorize: VoteAuthorize,
) -> Result<WalletCommand, WalletError> {
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
let authorized_voter_keypair = keypair_of(matches, "authorized_voter_keypair_file").unwrap();
let new_authorized_voter_pubkey = pubkey_of(matches, "new_authorized_voter_pubkey").unwrap();
let authorized_keypair = keypair_of(matches, "authorized_keypair_file").unwrap();
let new_authorized_pubkey = pubkey_of(matches, "new_authorized_pubkey").unwrap();
Ok(WalletCommand::AuthorizeVoter(
Ok(WalletCommand::VoteAuthorize(
vote_account_pubkey,
authorized_voter_keypair,
new_authorized_voter_pubkey,
authorized_keypair,
new_authorized_pubkey,
vote_authorize,
))
}
@@ -63,13 +74,12 @@ pub fn process_create_vote_account(
rpc_client: &RpcClient,
config: &WalletConfig,
vote_account_pubkey: &Pubkey,
node_pubkey: &Pubkey,
commission: u8,
vote_init: &VoteInit,
lamports: u64,
) -> ProcessResult {
check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()),
(node_pubkey, "node_pubkey".to_string()),
(&vote_init.node_pubkey, "node_pubkey".to_string()),
)?;
check_unique_pubkeys(
(&config.keypair.pubkey(), "wallet keypair".to_string()),
@@ -78,8 +88,7 @@ pub fn process_create_vote_account(
let ixs = vote_instruction::create_account(
&config.keypair.pubkey(),
vote_account_pubkey,
node_pubkey,
commission,
vote_init,
lamports,
);
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
@@ -89,36 +98,35 @@ pub fn process_create_vote_account(
log_instruction_custom_error::<SystemError>(result)
}
pub fn process_authorize_voter(
pub fn process_vote_authorize(
rpc_client: &RpcClient,
config: &WalletConfig,
vote_account_pubkey: &Pubkey,
authorized_voter_keypair: &Keypair,
new_authorized_voter_pubkey: &Pubkey,
authorized_keypair: &Keypair,
new_authorized_pubkey: &Pubkey,
vote_authorize: VoteAuthorize,
) -> ProcessResult {
check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()),
(
new_authorized_voter_pubkey,
"new_authorized_voter_pubkey".to_string(),
),
(new_authorized_pubkey, "new_authorized_pubkey".to_string()),
)?;
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let ixs = vec![vote_instruction::authorize_voter(
vote_account_pubkey, // vote account to update
&authorized_voter_keypair.pubkey(), // current authorized voter (often the vote account itself)
new_authorized_voter_pubkey, // new vote signer
let ixs = vec![vote_instruction::authorize(
vote_account_pubkey, // vote account to update
&authorized_keypair.pubkey(), // current authorized voter (often the vote account itself)
new_authorized_pubkey, // new vote signer
vote_authorize, // vote or withdraw
)];
let mut tx = Transaction::new_signed_with_payer(
ixs,
Some(&config.keypair.pubkey()),
&[&config.keypair, &authorized_voter_keypair],
&[&config.keypair, &authorized_keypair],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
let result = rpc_client
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_voter_keypair]);
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &authorized_keypair]);
log_instruction_custom_error::<VoteError>(result)
}
@@ -162,9 +170,10 @@ pub fn process_show_vote_account(
crate::wallet::build_balance_message(vote_account.lamports, use_lamports_unit)
);
println!("node id: {}", vote_state.node_pubkey);
println!("authorized voter: {}", vote_state.authorized_voter);
println!(
"authorized voter pubkey: {}",
vote_state.authorized_voter_pubkey
"authorized withdrawer: {}",
vote_state.authorized_withdrawer
);
println!("credits: {}", vote_state.credits());
println!(
@@ -230,10 +239,7 @@ pub fn process_uptime(
})?;
println!("Node id: {}", vote_state.node_pubkey);
println!(
"Authorized voter pubkey: {}",
vote_state.authorized_voter_pubkey
);
println!("Authorized voter: {}", vote_state.authorized_voter);
if !vote_state.votes.is_empty() {
println!("Uptime:");
@@ -300,14 +306,14 @@ mod tests {
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
"test",
"authorize-voter",
"vote-authorize-voter",
&pubkey_string,
&keypair_file,
&pubkey_string,
]);
assert_eq!(
parse_command(&pubkey, &test_authorize_voter).unwrap(),
WalletCommand::AuthorizeVoter(pubkey, keypair, pubkey)
WalletCommand::VoteAuthorize(pubkey, keypair, pubkey, VoteAuthorize::Voter)
);
fs::remove_file(&keypair_file).unwrap();
@@ -326,7 +332,16 @@ mod tests {
]);
assert_eq!(
parse_command(&pubkey, &test_create_vote_account).unwrap(),
WalletCommand::CreateVoteAccount(pubkey, node_pubkey, 10, 50)
WalletCommand::CreateVoteAccount(
pubkey,
VoteInit {
node_pubkey,
authorized_voter: pubkey,
authorized_withdrawer: pubkey,
commission: 10
},
50
)
);
let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![
"test",
@@ -337,7 +352,63 @@ mod tests {
]);
assert_eq!(
parse_command(&pubkey, &test_create_vote_account2).unwrap(),
WalletCommand::CreateVoteAccount(pubkey, node_pubkey, 0, 858993459200)
WalletCommand::CreateVoteAccount(
pubkey,
VoteInit {
node_pubkey,
authorized_voter: pubkey,
authorized_withdrawer: pubkey,
commission: 0
},
858993459200
)
);
// test init with an authed voter
let authed = Pubkey::new_rand();
let test_create_vote_account3 = test_commands.clone().get_matches_from(vec![
"test",
"create-vote-account",
&pubkey_string,
&node_pubkey_string,
"50",
"--authorized-voter",
&authed.to_string(),
]);
assert_eq!(
parse_command(&pubkey, &test_create_vote_account3).unwrap(),
WalletCommand::CreateVoteAccount(
pubkey,
VoteInit {
node_pubkey,
authorized_voter: authed,
authorized_withdrawer: pubkey,
commission: 0
},
858993459200
)
);
// test init with an authed withdrawer
let test_create_vote_account4 = test_commands.clone().get_matches_from(vec![
"test",
"create-vote-account",
&pubkey_string,
&node_pubkey_string,
"50",
"--authorized-withdrawer",
&authed.to_string(),
]);
assert_eq!(
parse_command(&pubkey, &test_create_vote_account4).unwrap(),
WalletCommand::CreateVoteAccount(
pubkey,
VoteInit {
node_pubkey,
authorized_voter: pubkey,
authorized_withdrawer: authed,
commission: 0
},
858993459200
)
);
// Test Uptime Subcommand

View File

@@ -7,11 +7,9 @@ use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji};
use log::*;
use num_traits::FromPrimitive;
use serde_json;
use serde_json::{json, Value};
use serde_json::{self, json, Value};
use solana_budget_api::budget_instruction::{self, BudgetError};
use solana_client::client_error::ClientError;
use solana_client::rpc_client::RpcClient;
use solana_client::{client_error::ClientError, rpc_client::RpcClient};
#[cfg(not(test))]
use solana_drone::drone::request_airdrop_transaction;
#[cfg(test)]
@@ -31,16 +29,21 @@ use solana_sdk::{
system_transaction,
transaction::{Transaction, TransactionError},
};
use solana_stake_api::stake_instruction::{self, StakeError};
use solana_stake_api::{
stake_instruction::{self, StakeError},
stake_state::{Authorized, Lockup},
};
use solana_storage_api::storage_instruction;
use solana_vote_api::vote_state::VoteState;
use std::collections::VecDeque;
use std::fs::File;
use std::io::{Read, Write};
use std::net::{IpAddr, SocketAddr};
use std::thread::sleep;
use std::time::{Duration, Instant};
use std::{error, fmt};
use solana_vote_api::vote_state::{VoteAuthorize, VoteInit, VoteState};
use std::{
collections::VecDeque,
fs::File,
io::{Read, Write},
net::{IpAddr, SocketAddr},
thread::sleep,
time::{Duration, Instant},
{error, fmt},
};
const USERDATA_CHUNK_SIZE: usize = 229; // Keep program chunks under PACKET_DATA_SIZE
@@ -64,8 +67,8 @@ pub enum WalletCommand {
},
Cancel(Pubkey),
Confirm(Signature),
AuthorizeVoter(Pubkey, Keypair, Pubkey),
CreateVoteAccount(Pubkey, Pubkey, u8, u64),
VoteAuthorize(Pubkey, Keypair, Pubkey, VoteAuthorize),
CreateVoteAccount(Pubkey, VoteInit, u64),
ShowAccount {
pubkey: Pubkey,
output_file: Option<String>,
@@ -80,7 +83,7 @@ pub enum WalletCommand {
aggregate: bool,
span: Option<u64>,
},
DelegateStake(Keypair, Pubkey, u64, bool),
DelegateStake(Keypair, Pubkey, u64, Authorized, bool),
WithdrawStake(Keypair, Pubkey, u64),
DeactivateStake(Keypair, Pubkey),
RedeemVoteCredits(Pubkey, Pubkey),
@@ -242,7 +245,12 @@ pub fn parse_command(
})
}
("create-vote-account", Some(matches)) => parse_vote_create_account(matches),
("authorize-voter", Some(matches)) => parse_vote_authorize_voter(matches),
("vote-authorize-voter", Some(matches)) => {
parse_vote_authorize(matches, VoteAuthorize::Voter)
}
("vote-authorize-withdrawer", Some(matches)) => {
parse_vote_authorize(matches, VoteAuthorize::Withdrawer)
}
("show-vote-account", Some(matches)) => parse_vote_get_account_command(matches),
("uptime", Some(matches)) => parse_vote_uptime_command(matches),
("delegate-stake", Some(matches)) => {
@@ -252,11 +260,13 @@ pub fn parse_command(
matches.value_of("amount").unwrap(),
matches.value_of("unit"),
)?;
let authorized = Authorized::auto(&stake_account_keypair.pubkey());
let force = matches.is_present("force");
Ok(WalletCommand::DelegateStake(
stake_account_keypair,
vote_account_pubkey,
lamports,
authorized,
force,
))
}
@@ -602,6 +612,7 @@ fn process_delegate_stake(
stake_account_keypair: &Keypair,
vote_account_pubkey: &Pubkey,
lamports: u64,
authorized: &Authorized,
force: bool,
) -> ProcessResult {
check_unique_pubkeys(
@@ -618,6 +629,7 @@ fn process_delegate_stake(
&stake_account_keypair.pubkey(),
vote_account_pubkey,
lamports,
authorized,
);
// Sanity check the vote account to ensure it is attached to a validator that has recently
@@ -735,8 +747,16 @@ fn process_show_stake_account(
format!("{:?} is not a stake account", stake_account_pubkey).to_string(),
))?;
}
fn show_authorized(authorized: &Authorized) {
println!("authorized staker: {}", authorized.staker);
println!("authorized withdrawer: {}", authorized.staker);
}
fn show_lockup(lockup: &Lockup) {
println!("lockup slot: {}", lockup.slot);
println!("lockup custodian: {}", lockup.custodian);
}
match stake_account.state() {
Ok(StakeState::Stake(stake)) => {
Ok(StakeState::Stake(authorized, lockup, stake)) => {
println!(
"total stake: {}",
build_balance_message(stake_account.lamports, use_lamports_unit)
@@ -759,11 +779,17 @@ fn process_show_stake_account(
stake.deactivation_epoch
);
}
show_authorized(&authorized);
show_lockup(&lockup);
Ok("".to_string())
}
Ok(StakeState::RewardsPool) => Ok("Stake account is a rewards pool".to_string()),
Ok(StakeState::Uninitialized) | Ok(StakeState::Lockup(_)) => {
Ok("Stake account is uninitialized".to_string())
Ok(StakeState::Uninitialized) => Ok("Stake account is uninitialized".to_string()),
Ok(StakeState::Initialized(authorized, lockup)) => {
println!("Stake account is undelegated");
show_authorized(&authorized);
show_lockup(&lockup);
Ok("".to_string())
}
Err(err) => Err(WalletError::RpcRequestError(format!(
"Account data could not be deserialized to stake state: {:?}",
@@ -1286,30 +1312,28 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature),
// Create vote account
WalletCommand::CreateVoteAccount(
vote_account_pubkey,
node_pubkey,
commission,
lamports,
) => process_create_vote_account(
&rpc_client,
config,
&vote_account_pubkey,
&node_pubkey,
*commission,
*lamports,
),
WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init, lamports) => {
process_create_vote_account(
&rpc_client,
config,
&vote_account_pubkey,
&vote_init,
*lamports,
)
}
WalletCommand::AuthorizeVoter(
WalletCommand::VoteAuthorize(
vote_account_pubkey,
authorized_voter_keypair,
new_authorized_voter_pubkey,
) => process_authorize_voter(
authorized_keypair,
new_authorized_pubkey,
vote_authorize,
) => process_vote_authorize(
&rpc_client,
config,
&vote_account_pubkey,
&authorized_voter_keypair,
&new_authorized_voter_pubkey,
&authorized_keypair,
&new_authorized_pubkey,
*vote_authorize,
),
WalletCommand::ShowAccount {
@@ -1344,6 +1368,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
stake_account_keypair,
vote_account_pubkey,
lamports,
authorized,
force,
) => process_delegate_stake(
&rpc_client,
@@ -1351,6 +1376,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
&stake_account_keypair,
&vote_account_pubkey,
*lamports,
&authorized,
*force,
),
@@ -1675,7 +1701,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
),
)
.subcommand(
SubCommand::with_name("authorize-voter")
SubCommand::with_name("vote-authorize-voter")
.about("Authorize a new vote signing keypair for the given vote account")
.arg(
Arg::with_name("vote_account_pubkey")
@@ -1687,7 +1713,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("Vote account in which to set the authorized voter"),
)
.arg(
Arg::with_name("authorized_voter_keypair_file")
Arg::with_name("authorized_keypair_file")
.index(2)
.value_name("CURRENT VOTER KEYPAIR FILE")
.takes_value(true)
@@ -1696,7 +1722,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("Keypair file for the currently authorized vote signer"),
)
.arg(
Arg::with_name("new_authorized_voter_pubkey")
Arg::with_name("new_authorized_pubkey")
.index(3)
.value_name("NEW VOTER PUBKEY")
.takes_value(true)
@@ -1705,6 +1731,37 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.help("New vote signer to authorize"),
),
)
.subcommand(
SubCommand::with_name("vote-authorize-withdrawer")
.about("Authorize a new withdraw signing keypair for the given vote account")
.arg(
Arg::with_name("vote_account_pubkey")
.index(1)
.value_name("VOTE ACCOUNT PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey_or_keypair)
.help("Vote account in which to set the authorized withdrawer"),
)
.arg(
Arg::with_name("authorized_keypair_file")
.index(2)
.value_name("CURRENT WITHDRAWER KEYPAIR FILE")
.takes_value(true)
.required(true)
.validator(is_keypair)
.help("Keypair file for the currently authorized withdrawer"),
)
.arg(
Arg::with_name("new_authorized_pubkey")
.index(3)
.value_name("NEW WITHDRAWER PUBKEY")
.takes_value(true)
.required(true)
.validator(is_pubkey_or_keypair)
.help("New withdrawer to authorize"),
),
)
.subcommand(
SubCommand::with_name("create-vote-account")
.about("Create a vote account")
@@ -1747,7 +1804,25 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.value_name("NUM")
.takes_value(true)
.help("The commission taken on reward redemption (0-255), default: 0"),
),
)
.arg(
Arg::with_name("authorized_voter")
.long("authorized-voter")
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey_or_keypair)
.help("Public key of the authorized voter (defaults to vote account pubkey)"),
)
.arg(
Arg::with_name("authorized_withdrawer")
.long("authorized-withdrawer")
.value_name("PUBKEY")
.takes_value(true)
.validator(is_pubkey_or_keypair)
.help("Public key of the authorized withdrawer (defaults to vote account pubkey)"),
)
,
)
.subcommand(
SubCommand::with_name("show-account")
@@ -2425,9 +2500,16 @@ mod tests {
"42",
"lamports",
]);
let stake_pubkey = keypair.pubkey();
assert_eq!(
parse_command(&pubkey, &test_delegate_stake).unwrap(),
WalletCommand::DelegateStake(keypair, pubkey, 42, false)
WalletCommand::DelegateStake(
keypair,
pubkey,
42,
Authorized::auto(&stake_pubkey),
false,
)
);
let keypair = read_keypair(&keypair_file).unwrap();
@@ -2440,9 +2522,16 @@ mod tests {
"42",
"lamports",
]);
let stake_pubkey = keypair.pubkey();
assert_eq!(
parse_command(&pubkey, &test_delegate_stake).unwrap(),
WalletCommand::DelegateStake(keypair, pubkey, 42, true)
WalletCommand::DelegateStake(
keypair,
pubkey,
42,
Authorized::auto(&stake_pubkey),
true
)
);
// Test WithdrawStake Subcommand
@@ -2669,14 +2758,27 @@ mod tests {
let bob_pubkey = Pubkey::new_rand();
let node_pubkey = Pubkey::new_rand();
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_pubkey, 0, 10);
config.command = WalletCommand::CreateVoteAccount(
bob_pubkey,
VoteInit {
node_pubkey,
authorized_voter: bob_pubkey,
authorized_withdrawer: bob_pubkey,
commission: 0,
},
10,
);
let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
let bob_keypair = Keypair::new();
let new_authorized_voter_pubkey = Pubkey::new_rand();
config.command =
WalletCommand::AuthorizeVoter(bob_pubkey, bob_keypair, new_authorized_voter_pubkey);
let new_authorized_pubkey = Pubkey::new_rand();
config.command = WalletCommand::VoteAuthorize(
bob_pubkey,
bob_keypair,
new_authorized_pubkey,
VoteAuthorize::Voter,
);
let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@@ -2826,10 +2928,24 @@ mod tests {
};
assert!(process_command(&config).is_err());
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_pubkey, 0, 10);
config.command = WalletCommand::CreateVoteAccount(
bob_pubkey,
VoteInit {
node_pubkey,
authorized_voter: bob_pubkey,
authorized_withdrawer: bob_pubkey,
commission: 0,
},
10,
);
assert!(process_command(&config).is_err());
config.command = WalletCommand::AuthorizeVoter(bob_pubkey, Keypair::new(), bob_pubkey);
config.command = WalletCommand::VoteAuthorize(
bob_pubkey,
Keypair::new(),
bob_pubkey,
VoteAuthorize::Voter,
);
assert!(process_command(&config).is_err());
config.command = WalletCommand::GetSlot;