support issuing vote instructions from system account (#4338)
* issue vote instructions from system account * fixup * bring back KeypairUtil
This commit is contained in:
parent
114e2989fa
commit
86e03a6d1b
|
@ -438,8 +438,6 @@ mod tests {
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
use solana_sdk::system_transaction;
|
use solana_sdk::system_transaction;
|
||||||
use solana_vote_api::vote_instruction;
|
|
||||||
use solana_vote_api::vote_state::Vote;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
fn create_sample_payment(keypair: &Keypair, hash: Hash) -> Transaction {
|
fn create_sample_payment(keypair: &Keypair, hash: Hash) -> Transaction {
|
||||||
|
@ -460,12 +458,6 @@ mod tests {
|
||||||
Transaction::new_signed_instructions(&[keypair], vec![ix], hash)
|
Transaction::new_signed_instructions(&[keypair], vec![ix], hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_sample_vote(keypair: &Keypair, hash: Hash) -> Transaction {
|
|
||||||
let pubkey = keypair.pubkey();
|
|
||||||
let ix = vote_instruction::vote(&pubkey, vec![Vote::new(1)]);
|
|
||||||
Transaction::new_signed_instructions(&[keypair], vec![ix], hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_entry_verify() {
|
fn test_entry_verify() {
|
||||||
let zero = Hash::default();
|
let zero = Hash::default();
|
||||||
|
@ -647,8 +639,7 @@ mod tests {
|
||||||
let hash = Hash::default();
|
let hash = Hash::default();
|
||||||
let next_hash = solana_sdk::hash::hash(&hash.as_ref());
|
let next_hash = solana_sdk::hash::hash(&hash.as_ref());
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let vote_account = Keypair::new();
|
let tx_small = create_sample_timestamp(&keypair, next_hash);
|
||||||
let tx_small = create_sample_vote(&vote_account, next_hash);
|
|
||||||
let tx_large = create_sample_payment(&keypair, next_hash);
|
let tx_large = create_sample_payment(&keypair, next_hash);
|
||||||
|
|
||||||
let tx_small_size = serialized_size(&tx_small).unwrap() as usize;
|
let tx_small_size = serialized_size(&tx_small).unwrap() as usize;
|
||||||
|
|
|
@ -71,19 +71,16 @@ pub struct Fullnode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fullnode {
|
impl Fullnode {
|
||||||
pub fn new<T>(
|
pub fn new(
|
||||||
mut node: Node,
|
mut node: Node,
|
||||||
keypair: &Arc<Keypair>,
|
keypair: &Arc<Keypair>,
|
||||||
ledger_path: &str,
|
ledger_path: &str,
|
||||||
vote_account: &Pubkey,
|
vote_account: &Pubkey,
|
||||||
voting_keypair: &Arc<T>,
|
voting_keypair: &Arc<Keypair>,
|
||||||
storage_keypair: &Arc<Keypair>,
|
storage_keypair: &Arc<Keypair>,
|
||||||
entrypoint_info_option: Option<&ContactInfo>,
|
entrypoint_info_option: Option<&ContactInfo>,
|
||||||
config: &FullnodeConfig,
|
config: &FullnodeConfig,
|
||||||
) -> Self
|
) -> Self {
|
||||||
where
|
|
||||||
T: 'static + KeypairUtil + Sync + Send,
|
|
||||||
{
|
|
||||||
info!("creating bank...");
|
info!("creating bank...");
|
||||||
|
|
||||||
let id = keypair.pubkey();
|
let id = keypair.pubkey();
|
||||||
|
|
|
@ -95,7 +95,6 @@ impl ReplayStage {
|
||||||
let bank_forks = bank_forks.clone();
|
let bank_forks = bank_forks.clone();
|
||||||
let poh_recorder = poh_recorder.clone();
|
let poh_recorder = poh_recorder.clone();
|
||||||
let my_id = *my_id;
|
let my_id = *my_id;
|
||||||
let vote_account = *vote_account;
|
|
||||||
let mut ticks_per_slot = 0;
|
let mut ticks_per_slot = 0;
|
||||||
let mut locktower = Locktower::new_from_forks(&bank_forks.read().unwrap(), &my_id);
|
let mut locktower = Locktower::new_from_forks(&bank_forks.read().unwrap(), &my_id);
|
||||||
if let Some(root) = locktower.root() {
|
if let Some(root) = locktower.root() {
|
||||||
|
@ -105,6 +104,7 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
// Start the replay stage loop
|
// Start the replay stage loop
|
||||||
let leader_schedule_cache = leader_schedule_cache.clone();
|
let leader_schedule_cache = leader_schedule_cache.clone();
|
||||||
|
let vote_account = *vote_account;
|
||||||
let voting_keypair = voting_keypair.cloned();
|
let voting_keypair = voting_keypair.cloned();
|
||||||
let t_replay = Builder::new()
|
let t_replay = Builder::new()
|
||||||
.name("solana-replay-stage".to_string())
|
.name("solana-replay-stage".to_string())
|
||||||
|
@ -152,8 +152,8 @@ impl ReplayStage {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&mut locktower,
|
&mut locktower,
|
||||||
&mut progress,
|
&mut progress,
|
||||||
&voting_keypair,
|
|
||||||
&vote_account,
|
&vote_account,
|
||||||
|
&voting_keypair,
|
||||||
&cluster_info,
|
&cluster_info,
|
||||||
&blocktree,
|
&blocktree,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
|
@ -297,8 +297,8 @@ impl ReplayStage {
|
||||||
bank_forks: &Arc<RwLock<BankForks>>,
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
locktower: &mut Locktower,
|
locktower: &mut Locktower,
|
||||||
progress: &mut HashMap<u64, ForkProgress>,
|
progress: &mut HashMap<u64, ForkProgress>,
|
||||||
|
vote_account: &Pubkey,
|
||||||
voting_keypair: &Option<Arc<T>>,
|
voting_keypair: &Option<Arc<T>>,
|
||||||
vote_account_pubkey: &Pubkey,
|
|
||||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||||
blocktree: &Arc<Blocktree>,
|
blocktree: &Arc<Blocktree>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
|
@ -322,13 +322,20 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
locktower.update_epoch(&bank);
|
locktower.update_epoch(&bank);
|
||||||
if let Some(ref voting_keypair) = voting_keypair {
|
if let Some(ref voting_keypair) = voting_keypair {
|
||||||
|
let node_keypair = cluster_info.read().unwrap().keypair.clone();
|
||||||
|
|
||||||
// Send our last few votes along with the new one
|
// Send our last few votes along with the new one
|
||||||
let vote_ix = vote_instruction::vote(vote_account_pubkey, locktower.recent_votes());
|
let vote_ix = vote_instruction::vote(
|
||||||
let vote_tx = Transaction::new_signed_instructions(
|
&node_keypair.pubkey(),
|
||||||
&[voting_keypair.as_ref()],
|
&vote_account,
|
||||||
vec![vote_ix],
|
&voting_keypair.pubkey(),
|
||||||
bank.last_blockhash(),
|
locktower.recent_votes(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut vote_tx = Transaction::new_unsigned_instructions(vec![vote_ix]);
|
||||||
|
let blockhash = bank.last_blockhash();
|
||||||
|
vote_tx.partial_sign(&[node_keypair.as_ref()], blockhash);
|
||||||
|
vote_tx.partial_sign(&[voting_keypair.as_ref()], blockhash);
|
||||||
cluster_info.write().unwrap().push_vote(vote_tx);
|
cluster_info.write().unwrap().push_vote(vote_tx);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -25,8 +25,16 @@ pub enum VoteInstruction {
|
||||||
Vote(Vec<Vote>),
|
Vote(Vec<Vote>),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_account(vote_id: &Pubkey, node_id: &Pubkey, commission: u32) -> Instruction {
|
fn initialize_account(
|
||||||
let account_metas = vec![AccountMeta::new(*vote_id, false)];
|
from_id: &Pubkey,
|
||||||
|
vote_id: &Pubkey,
|
||||||
|
node_id: &Pubkey,
|
||||||
|
commission: u32,
|
||||||
|
) -> Instruction {
|
||||||
|
let account_metas = vec![
|
||||||
|
AccountMeta::new(*from_id, true),
|
||||||
|
AccountMeta::new(*vote_id, false),
|
||||||
|
];
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&VoteInstruction::InitializeAccount(*node_id, commission),
|
&VoteInstruction::InitializeAccount(*node_id, commission),
|
||||||
|
@ -42,22 +50,51 @@ pub fn create_account(
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> Vec<Instruction> {
|
) -> Vec<Instruction> {
|
||||||
let space = VoteState::size_of() as u64;
|
let space = VoteState::size_of() as u64;
|
||||||
let create_ix = system_instruction::create_account(&from_id, vote_id, lamports, space, &id());
|
let create_ix = system_instruction::create_account(from_id, vote_id, lamports, space, &id());
|
||||||
let init_ix = initialize_account(vote_id, node_id, commission);
|
let init_ix = initialize_account(from_id, vote_id, node_id, commission);
|
||||||
vec![create_ix, init_ix]
|
vec![create_ix, init_ix]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authorize_voter(vote_id: &Pubkey, authorized_voter_id: &Pubkey) -> Instruction {
|
fn metas_for_authorized_signer(
|
||||||
let account_metas = vec![AccountMeta::new(*vote_id, true)];
|
from_id: &Pubkey,
|
||||||
|
vote_id: &Pubkey,
|
||||||
|
authorized_voter_id: &Pubkey, // currently authorized
|
||||||
|
) -> Vec<AccountMeta> {
|
||||||
|
let mut account_metas = vec![AccountMeta::new(*from_id, true)]; // sender
|
||||||
|
|
||||||
|
let is_own_signer = authorized_voter_id == vote_id;
|
||||||
|
|
||||||
|
account_metas.push(AccountMeta::new(*vote_id, is_own_signer)); // vote account
|
||||||
|
|
||||||
|
if !is_own_signer {
|
||||||
|
account_metas.push(AccountMeta::new(*authorized_voter_id, true)) // signer
|
||||||
|
}
|
||||||
|
account_metas
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authorize_voter(
|
||||||
|
from_id: &Pubkey,
|
||||||
|
vote_id: &Pubkey,
|
||||||
|
authorized_voter_id: &Pubkey, // currently authorized
|
||||||
|
new_authorized_voter_id: &Pubkey,
|
||||||
|
) -> Instruction {
|
||||||
|
let account_metas = metas_for_authorized_signer(from_id, vote_id, authorized_voter_id);
|
||||||
|
|
||||||
Instruction::new(
|
Instruction::new(
|
||||||
id(),
|
id(),
|
||||||
&VoteInstruction::AuthorizeVoter(*authorized_voter_id),
|
&VoteInstruction::AuthorizeVoter(*new_authorized_voter_id),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn vote(vote_id: &Pubkey, recent_votes: Vec<Vote>) -> Instruction {
|
pub fn vote(
|
||||||
let account_metas = vec![AccountMeta::new(*vote_id, true)];
|
from_id: &Pubkey,
|
||||||
|
vote_id: &Pubkey,
|
||||||
|
authorized_voter_id: &Pubkey,
|
||||||
|
recent_votes: Vec<Vote>,
|
||||||
|
) -> Instruction {
|
||||||
|
let account_metas = metas_for_authorized_signer(from_id, vote_id, authorized_voter_id);
|
||||||
|
|
||||||
Instruction::new(id(), &VoteInstruction::Vote(recent_votes), account_metas)
|
Instruction::new(id(), &VoteInstruction::Vote(recent_votes), account_metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,27 +109,25 @@ pub fn process_instruction(
|
||||||
trace!("process_instruction: {:?}", data);
|
trace!("process_instruction: {:?}", data);
|
||||||
trace!("keyed_accounts: {:?}", keyed_accounts);
|
trace!("keyed_accounts: {:?}", keyed_accounts);
|
||||||
|
|
||||||
if keyed_accounts.is_empty() {
|
if keyed_accounts.len() < 2 {
|
||||||
Err(InstructionError::InvalidInstructionData)?;
|
Err(InstructionError::InvalidInstructionData)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0th index is the guy who paid for the transaction
|
||||||
|
let (me, other_signers) = &mut keyed_accounts.split_at_mut(2);
|
||||||
|
let me = &mut me[1];
|
||||||
|
|
||||||
|
// TODO: data-driven unpack and dispatch of KeyedAccounts
|
||||||
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? {
|
||||||
VoteInstruction::InitializeAccount(node_id, commission) => {
|
VoteInstruction::InitializeAccount(node_id, commission) => {
|
||||||
let vote_account = &mut keyed_accounts[0];
|
vote_state::initialize_account(me, &node_id, commission)
|
||||||
vote_state::initialize_account(vote_account, &node_id, commission)
|
|
||||||
}
|
}
|
||||||
VoteInstruction::AuthorizeVoter(voter_id) => {
|
VoteInstruction::AuthorizeVoter(voter_id) => {
|
||||||
let (vote_account, other_signers) = keyed_accounts.split_at_mut(1);
|
vote_state::authorize_voter(me, other_signers, &voter_id)
|
||||||
let vote_account = &mut vote_account[0];
|
|
||||||
|
|
||||||
vote_state::authorize_voter(vote_account, other_signers, &voter_id)
|
|
||||||
}
|
}
|
||||||
VoteInstruction::Vote(votes) => {
|
VoteInstruction::Vote(votes) => {
|
||||||
datapoint_warn!("vote-native", ("count", 1, i64));
|
datapoint_warn!("vote-native", ("count", 1, i64));
|
||||||
let (vote_account, other_signers) = keyed_accounts.split_at_mut(1);
|
vote_state::process_votes(me, other_signers, &votes)
|
||||||
let vote_account = &mut vote_account[0];
|
|
||||||
|
|
||||||
vote_state::process_votes(vote_account, other_signers, &votes)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +137,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
|
|
||||||
// these are for 100% coverage
|
// these are for 100% coverage in this file
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vote_process_instruction_decode_bail() {
|
fn test_vote_process_instruction_decode_bail() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -146,11 +181,21 @@ mod tests {
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
process_instruction(&vote(&Pubkey::default(), vec![Vote::default()])),
|
process_instruction(&vote(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
vec![Vote::default()]
|
||||||
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
process_instruction(&authorize_voter(&Pubkey::default(), &Pubkey::default())),
|
process_instruction(&authorize_voter(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
&Pubkey::default(),
|
||||||
|
)),
|
||||||
Err(InstructionError::InvalidAccountData),
|
Err(InstructionError::InvalidAccountData),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub enum WalletCommand {
|
||||||
Balance(Pubkey),
|
Balance(Pubkey),
|
||||||
Cancel(Pubkey),
|
Cancel(Pubkey),
|
||||||
Confirm(Signature),
|
Confirm(Signature),
|
||||||
AuthorizeVoter(Pubkey),
|
AuthorizeVoter(Pubkey, Keypair, Pubkey),
|
||||||
CreateVoteAccount(Pubkey, Pubkey, u32, u64),
|
CreateVoteAccount(Pubkey, Pubkey, u32, u64),
|
||||||
ShowVoteAccount(Pubkey),
|
ShowVoteAccount(Pubkey),
|
||||||
CreateStakeAccount(Pubkey, u64),
|
CreateStakeAccount(Pubkey, u64),
|
||||||
|
@ -193,8 +193,16 @@ pub fn parse_command(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
("authorize-voter", Some(matches)) => {
|
("authorize-voter", Some(matches)) => {
|
||||||
let authorized_voter_id = pubkey_of(matches, "authorized_voter_id").unwrap();
|
let voting_account_id = pubkey_of(matches, "voting_account_id").unwrap();
|
||||||
Ok(WalletCommand::AuthorizeVoter(authorized_voter_id))
|
let authorized_voter_keypair =
|
||||||
|
keypair_of(matches, "authorized_voter_keypair_file").unwrap();
|
||||||
|
let new_authorized_voter_id = pubkey_of(matches, "new_authorized_voter_id").unwrap();
|
||||||
|
|
||||||
|
Ok(WalletCommand::AuthorizeVoter(
|
||||||
|
voting_account_id,
|
||||||
|
authorized_voter_keypair,
|
||||||
|
new_authorized_voter_id,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
("show-vote-account", Some(matches)) => {
|
("show-vote-account", Some(matches)) => {
|
||||||
let voting_account_id = pubkey_of(matches, "voting_account_id").unwrap();
|
let voting_account_id = pubkey_of(matches, "voting_account_id").unwrap();
|
||||||
|
@ -392,15 +400,23 @@ fn process_create_vote_account(
|
||||||
fn process_authorize_voter(
|
fn process_authorize_voter(
|
||||||
rpc_client: &RpcClient,
|
rpc_client: &RpcClient,
|
||||||
config: &WalletConfig,
|
config: &WalletConfig,
|
||||||
authorized_voter_id: &Pubkey,
|
voting_account_id: &Pubkey,
|
||||||
|
authorized_voter_keypair: &Keypair,
|
||||||
|
new_authorized_voter_id: &Pubkey,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
|
||||||
let ixs = vec![vote_instruction::authorize_voter(
|
let ixs = vec![vote_instruction::authorize_voter(
|
||||||
&config.keypair.pubkey(),
|
&config.keypair.pubkey(), // from
|
||||||
authorized_voter_id,
|
voting_account_id, // vote account to update
|
||||||
|
&authorized_voter_keypair.pubkey(), // current authorized voter (often the vote account itself)
|
||||||
|
new_authorized_voter_id, // new vote signer
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
|
let mut tx = Transaction::new_signed_instructions(
|
||||||
|
&[&config.keypair, &authorized_voter_keypair],
|
||||||
|
ixs,
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair])?;
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
@ -776,9 +792,17 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Configure staking account already created
|
// Configure staking account already created
|
||||||
WalletCommand::AuthorizeVoter(authorized_voter_id) => {
|
WalletCommand::AuthorizeVoter(
|
||||||
process_authorize_voter(&rpc_client, config, &authorized_voter_id)
|
voting_account_id,
|
||||||
}
|
authorized_voter_keypair,
|
||||||
|
new_authorized_voter_id,
|
||||||
|
) => process_authorize_voter(
|
||||||
|
&rpc_client,
|
||||||
|
config,
|
||||||
|
&voting_account_id,
|
||||||
|
&authorized_voter_keypair,
|
||||||
|
&new_authorized_voter_id,
|
||||||
|
),
|
||||||
// Show a vote account
|
// Show a vote account
|
||||||
WalletCommand::ShowVoteAccount(voting_account_id) => {
|
WalletCommand::ShowVoteAccount(voting_account_id) => {
|
||||||
process_show_vote_account(&rpc_client, config, &voting_account_id)
|
process_show_vote_account(&rpc_client, config, &voting_account_id)
|
||||||
|
@ -996,15 +1020,32 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("authorize-voter")
|
SubCommand::with_name("authorize-voter")
|
||||||
.about("Authorize a different voter for this vote account")
|
.about("Authorize a new vote signing keypair for the given vote account")
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("authorized_voter_id")
|
Arg::with_name("voting_account_id")
|
||||||
.index(1)
|
.index(1)
|
||||||
.value_name("PUBKEY")
|
.value_name("PUBKEY")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)
|
.required(true)
|
||||||
.validator(is_pubkey)
|
.validator(is_pubkey)
|
||||||
.help("Vote signer to authorize"),
|
.help("Vote account in which to set the authorized voter"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("authorized_voter_keypair_file")
|
||||||
|
.index(2)
|
||||||
|
.value_name("KEYPAIR_FILE")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.help("Keypair file for the currently authorized vote signer"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("new_authorized_voter_id")
|
||||||
|
.index(3)
|
||||||
|
.value_name("PUBKEY")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
|
.validator(is_pubkey)
|
||||||
|
.help("New vote signer to authorize"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
|
@ -1313,13 +1354,20 @@ mod tests {
|
||||||
assert!(parse_command(&pubkey, &test_bad_signature).is_err());
|
assert!(parse_command(&pubkey, &test_bad_signature).is_err());
|
||||||
|
|
||||||
// Test AuthorizeVoter Subcommand
|
// Test AuthorizeVoter Subcommand
|
||||||
let test_authorize_voter =
|
let keypair_file = make_tmp_path("keypair_file");
|
||||||
test_commands
|
gen_keypair_file(&keypair_file).unwrap();
|
||||||
.clone()
|
let keypair = read_keypair(&keypair_file).unwrap();
|
||||||
.get_matches_from(vec!["test", "authorize-voter", &pubkey_string]);
|
|
||||||
|
let test_authorize_voter = test_commands.clone().get_matches_from(vec![
|
||||||
|
"test",
|
||||||
|
"authorize-voter",
|
||||||
|
&pubkey_string,
|
||||||
|
&keypair_file,
|
||||||
|
&pubkey_string,
|
||||||
|
]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_command(&pubkey, &test_authorize_voter).unwrap(),
|
parse_command(&pubkey, &test_authorize_voter).unwrap(),
|
||||||
WalletCommand::AuthorizeVoter(pubkey)
|
WalletCommand::AuthorizeVoter(pubkey, keypair, pubkey)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Test CreateVoteAccount SubCommand
|
// Test CreateVoteAccount SubCommand
|
||||||
|
@ -1545,7 +1593,8 @@ mod tests {
|
||||||
let signature = process_command(&config);
|
let signature = process_command(&config);
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
config.command = WalletCommand::AuthorizeVoter(bob_pubkey);
|
let bob_keypair = Keypair::new();
|
||||||
|
config.command = WalletCommand::AuthorizeVoter(bob_pubkey, bob_keypair, bob_pubkey);
|
||||||
let signature = process_command(&config);
|
let signature = process_command(&config);
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
|
@ -1663,7 +1712,7 @@ mod tests {
|
||||||
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_id, 0, 10);
|
config.command = WalletCommand::CreateVoteAccount(bob_pubkey, node_id, 0, 10);
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
|
|
||||||
config.command = WalletCommand::AuthorizeVoter(bob_pubkey);
|
config.command = WalletCommand::AuthorizeVoter(bob_pubkey, Keypair::new(), bob_pubkey);
|
||||||
assert!(process_command(&config).is_err());
|
assert!(process_command(&config).is_err());
|
||||||
|
|
||||||
config.command = WalletCommand::GetTransactionCount;
|
config.command = WalletCommand::GetTransactionCount;
|
||||||
|
|
Loading…
Reference in New Issue