From a4a3995a84a4922537bb5682307d8d6d70318403 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 6 Mar 2019 18:02:20 -0700 Subject: [PATCH] Add staking commands to wallet --- Cargo.lock | 1 + wallet/Cargo.toml | 1 + wallet/src/main.rs | 46 +++++++++++++++++++- wallet/src/wallet.rs | 101 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 305c74bfcf..4a263b2e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2407,6 +2407,7 @@ dependencies = [ "solana-drone 0.12.0", "solana-logger 0.12.0", "solana-sdk 0.12.0", + "solana-vote-api 0.12.0", "solana-vote-signer 0.12.0", ] diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 3f17de7f87..abd9414b73 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -21,6 +21,7 @@ solana-budget-api = { path = "../programs/budget_api", version = "0.12.0" } solana-drone = { path = "../drone", version = "0.12.0" } solana-logger = { path = "../logger", version = "0.12.0" } solana-sdk = { path = "../sdk", version = "0.12.0" } +solana-vote-api = { path = "../programs/vote_api", version = "0.12.0" } solana-vote-signer = { path = "../vote-signer", version = "0.12.0" } [features] diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 7d453e1423..9783afe260 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -1,4 +1,4 @@ -use clap::{crate_version, App, Arg, ArgMatches, SubCommand}; +use clap::{crate_version, App, Arg, ArgGroup, ArgMatches, SubCommand}; use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil}; use solana_wallet::wallet::{parse_command, process_command, WalletConfig, WalletError}; use std::error; @@ -180,6 +180,50 @@ fn main() -> Result<(), Box> { .help("The transaction signature to confirm"), ), ) + .subcommand( + SubCommand::with_name("configure-staking-account") + .about("Configure staking account for node") + .group( + ArgGroup::with_name("options") + .args(&["delegate", "authorized_voter"]) + .multiple(true) + .required(true), + ) + .arg( + Arg::with_name("delegate") + .long("delegate-account") + .value_name("PUBKEY") + .takes_value(true) + .help("Address to delegate this vote account to"), + ) + .arg( + Arg::with_name("authorized_voter") + .long("authorize-voter") + .value_name("PUBKEY") + .takes_value(true) + .help("Vote signer to authorize"), + ), + ) + .subcommand( + SubCommand::with_name("create-staking-account") + .about("Create staking account for node") + .arg( + Arg::with_name("voting_account_id") + .index(1) + .value_name("PUBKEY") + .takes_value(true) + .required(true) + .help("Staking account address to fund"), + ) + .arg( + Arg::with_name("lamports") + .index(2) + .value_name("NUM") + .takes_value(true) + .required(true) + .help("The number of lamports to send to staking account"), + ), + ) .subcommand( SubCommand::with_name("deploy") .about("Deploy a program") diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index 12cc1c484f..ae0c56aa8c 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -25,6 +25,9 @@ use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::system_transaction::SystemTransaction; use solana_sdk::timing::{DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND}; use solana_sdk::transaction::Transaction; +use solana_sdk::transaction_builder::TransactionBuilder; +use solana_vote_api::vote_instruction::VoteInstruction; +use solana_vote_api::vote_transaction::VoteTransaction; use std::fs::File; use std::io::Read; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; @@ -42,6 +45,9 @@ pub enum WalletCommand { Balance, Cancel(Pubkey), Confirm(Signature), + // ConfigureStakingAccount(delegate_id, authorized_voter_id) + ConfigureStakingAccount(Option, Option), + CreateStakingAccount(Pubkey, u64), Deploy(String), GetTransactionCount, // Pay(lamports, to, timestamp, timestamp_pubkey, witness(es), cancelable) @@ -161,6 +167,48 @@ pub fn parse_command( Err(WalletError::BadParameter("Invalid signature".to_string())) } } + ("configure-staking-account", Some(staking_config_matches)) => { + let delegate_id = staking_config_matches + .value_of("delegate") + .map(|pubkey_string| { + let pubkey_vec = bs58::decode(pubkey_string) + .into_vec() + .expect("base58-encoded public key"); + // TODO: Add valid pubkey check + Pubkey::new(&pubkey_vec) + }); + let authorized_voter_id = + staking_config_matches + .value_of("authorize-voter") + .map(|pubkey_string| { + let pubkey_vec = bs58::decode(pubkey_string) + .into_vec() + .expect("base58-encoded public key"); + // TODO: Add valid pubkey check + Pubkey::new(&pubkey_vec) + }); + Ok(WalletCommand::ConfigureStakingAccount( + delegate_id, + authorized_voter_id, + )) + } + ("create-staking-account", Some(staking_matches)) => { + let voting_account_string = staking_matches.value_of("voting_account_id").unwrap(); + let voting_account_vec = bs58::decode(voting_account_string) + .into_vec() + .expect("base58-encoded public key"); + + if voting_account_vec.len() != mem::size_of::() { + eprintln!("{}", staking_matches.usage()); + Err(WalletError::BadParameter("Invalid public key".to_string()))?; + } + let voting_account_id = Pubkey::new(&voting_account_vec); + let lamports = staking_matches.value_of("lamports").unwrap().parse()?; + Ok(WalletCommand::CreateStakingAccount( + voting_account_id, + lamports, + )) + } ("deploy", Some(deploy_matches)) => Ok(WalletCommand::Deploy( deploy_matches .value_of("program-location") @@ -387,6 +435,44 @@ fn process_confirm(rpc_client: &RpcClient, signature: Signature) -> ProcessResul } } +fn process_configure_staking( + rpc_client: &RpcClient, + config: &WalletConfig, + delegate_option: Option, + authorized_voter_option: Option, +) -> ProcessResult { + let recent_blockhash = get_recent_blockhash(&rpc_client)?; + let mut tx = TransactionBuilder::new(0); + if let Some(delegate_id) = delegate_option { + tx.push(VoteInstruction::new_delegate_stake( + config.id.pubkey(), + delegate_id, + )); + } + if let Some(authorized_voter_id) = authorized_voter_option { + tx.push(VoteInstruction::new_authorize_voter( + config.id.pubkey(), + authorized_voter_id, + )); + } + let mut tx = tx.sign(&[&config.id], recent_blockhash); + let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?; + Ok(signature_str.to_string()) +} + +fn process_create_staking( + rpc_client: &RpcClient, + config: &WalletConfig, + voting_account_id: Pubkey, + lamports: u64, +) -> ProcessResult { + let recent_blockhash = get_recent_blockhash(&rpc_client)?; + let mut tx = + VoteTransaction::new_account(&config.id, voting_account_id, recent_blockhash, lamports, 0); + let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?; + Ok(signature_str.to_string()) +} + fn process_deploy( rpc_client: &RpcClient, config: &WalletConfig, @@ -683,6 +769,21 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { // Confirm the last client transaction by signature WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature), + // Configure staking account already created + WalletCommand::ConfigureStakingAccount(delegate_option, authorized_voter_option) => { + process_configure_staking( + &rpc_client, + config, + delegate_option, + authorized_voter_option, + ) + } + + // Create staking account + WalletCommand::CreateStakingAccount(voting_account_id, lamports) => { + process_create_staking(&rpc_client, config, voting_account_id, lamports) + } + // Deploy a custom program to the chain WalletCommand::Deploy(ref program_location) => { process_deploy(&rpc_client, config, program_location)