2019-09-10 16:16:40 -07:00
|
|
|
use crate::{
|
2020-12-21 13:02:53 -08:00
|
|
|
cluster_query::*, feature::*, inflation::*, nonce::*, program::*, spend_utils::*, stake::*,
|
|
|
|
validator_info::*, vote::*,
|
2019-09-10 16:16:40 -07:00
|
|
|
};
|
2020-08-15 17:56:09 -07:00
|
|
|
use clap::{value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
|
2019-02-27 11:17:32 -08:00
|
|
|
use log::*;
|
2019-04-25 10:29:44 -07:00
|
|
|
use num_traits::FromPrimitive;
|
2020-12-21 13:02:53 -08:00
|
|
|
use serde_json::{self, Value};
|
2020-06-30 21:55:11 -07:00
|
|
|
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
2020-02-21 13:55:53 -08:00
|
|
|
use solana_clap_utils::{
|
2020-09-21 17:12:52 -07:00
|
|
|
self,
|
|
|
|
commitment::commitment_arg_with_default,
|
|
|
|
fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
|
|
|
|
input_parsers::*,
|
|
|
|
input_validators::*,
|
2020-09-22 14:25:10 -07:00
|
|
|
keypair::*,
|
2020-09-21 17:12:52 -07:00
|
|
|
nonce::*,
|
|
|
|
offline::*,
|
2020-02-21 13:55:53 -08:00
|
|
|
};
|
2020-09-22 17:29:11 -07:00
|
|
|
use solana_cli_output::{
|
2020-12-21 13:02:53 -08:00
|
|
|
display::{build_balance_message, println_name_value, println_transaction},
|
2020-09-22 19:29:32 -07:00
|
|
|
return_signers, CliAccount, CliSignature, OutputFormat,
|
2020-09-22 17:29:11 -07:00
|
|
|
};
|
2020-03-12 23:20:49 -07:00
|
|
|
use solana_client::{
|
2020-09-22 15:06:14 -07:00
|
|
|
blockhash_query::BlockhashQuery,
|
2020-05-14 11:24:14 -07:00
|
|
|
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
2020-09-21 12:26:06 -07:00
|
|
|
nonce_utils,
|
2020-03-12 23:20:49 -07:00
|
|
|
rpc_client::RpcClient,
|
2020-11-20 13:52:58 -08:00
|
|
|
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig, RpcTransactionLogsFilter},
|
2020-12-21 13:02:53 -08:00
|
|
|
rpc_response::RpcKeyedAccount,
|
2020-03-12 23:20:49 -07:00
|
|
|
};
|
2019-01-13 23:10:03 -08:00
|
|
|
#[cfg(not(test))]
|
2019-12-16 13:05:17 -08:00
|
|
|
use solana_faucet::faucet::request_airdrop_transaction;
|
2019-03-16 21:47:16 -07:00
|
|
|
#[cfg(test)]
|
2019-12-16 13:05:17 -08:00
|
|
|
use solana_faucet::faucet_mock::request_airdrop_transaction;
|
2020-03-09 17:49:01 -07:00
|
|
|
use solana_remote_wallet::remote_wallet::RemoteWalletManager;
|
2019-09-09 18:17:32 -07:00
|
|
|
use solana_sdk::{
|
2020-10-23 09:03:29 -07:00
|
|
|
clock::{Epoch, Slot},
|
2019-11-06 17:54:17 -08:00
|
|
|
commitment_config::CommitmentConfig,
|
2020-06-17 10:39:14 -07:00
|
|
|
decode_error::DecodeError,
|
2019-09-09 18:17:32 -07:00
|
|
|
hash::Hash,
|
2020-12-21 13:02:53 -08:00
|
|
|
instruction::InstructionError,
|
2019-09-09 18:17:32 -07:00
|
|
|
message::Message,
|
2020-03-20 15:20:48 -07:00
|
|
|
pubkey::{Pubkey, MAX_SEED_LEN},
|
2020-12-21 13:02:53 -08:00
|
|
|
signature::{Signature, Signer, SignerError},
|
2020-03-20 15:20:48 -07:00
|
|
|
system_instruction::{self, SystemError},
|
2020-03-19 09:36:53 -07:00
|
|
|
system_program,
|
2019-09-09 18:17:32 -07:00
|
|
|
transaction::{Transaction, TransactionError},
|
|
|
|
};
|
2020-03-02 12:28:43 -08:00
|
|
|
use solana_stake_program::{
|
|
|
|
stake_instruction::LockupArgs,
|
|
|
|
stake_state::{Lockup, StakeAuthorize},
|
|
|
|
};
|
2020-07-01 13:06:40 -07:00
|
|
|
use solana_transaction_status::{EncodedTransaction, UiTransactionEncoding};
|
2019-11-20 10:12:43 -08:00
|
|
|
use solana_vote_program::vote_state::VoteAuthorize;
|
2019-09-25 13:53:49 -07:00
|
|
|
use std::{
|
2020-06-17 23:09:33 -07:00
|
|
|
collections::HashMap,
|
2020-03-12 23:20:49 -07:00
|
|
|
error,
|
2020-05-06 19:27:15 -07:00
|
|
|
fmt::Write as FmtWrite,
|
2019-09-25 13:53:49 -07:00
|
|
|
fs::File,
|
2020-12-21 13:02:53 -08:00
|
|
|
io::Write,
|
|
|
|
net::{IpAddr, SocketAddr},
|
2020-07-23 08:21:59 -07:00
|
|
|
str::FromStr,
|
2020-02-24 16:03:30 -08:00
|
|
|
sync::Arc,
|
2019-09-25 13:53:49 -07:00
|
|
|
thread::sleep,
|
2019-10-04 19:54:09 -07:00
|
|
|
time::Duration,
|
2019-09-25 13:53:49 -07:00
|
|
|
};
|
2020-03-12 23:20:49 -07:00
|
|
|
use thiserror::Error;
|
2020-02-29 08:39:07 -08:00
|
|
|
use url::Url;
|
2018-09-14 01:58:39 -07:00
|
|
|
|
2020-07-23 08:21:59 -07:00
|
|
|
pub const DEFAULT_RPC_TIMEOUT_SECONDS: &str = "30";
|
2018-10-22 21:21:33 -07:00
|
|
|
|
2018-09-14 01:59:09 -07:00
|
|
|
#[derive(Debug, PartialEq)]
|
2019-05-09 19:31:42 -07:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2019-10-04 15:13:21 -07:00
|
|
|
pub enum CliCommand {
|
2019-10-04 19:54:09 -07:00
|
|
|
// Cluster Query Commands
|
2019-11-13 14:58:14 -08:00
|
|
|
Catchup {
|
|
|
|
node_pubkey: Pubkey,
|
2020-03-04 11:44:13 -08:00
|
|
|
node_json_rpc_url: Option<String>,
|
2020-04-02 11:05:29 -07:00
|
|
|
follow: bool,
|
2019-11-13 14:58:14 -08:00
|
|
|
},
|
2020-05-05 08:42:03 -07:00
|
|
|
ClusterDate,
|
2019-10-04 19:54:09 -07:00
|
|
|
ClusterVersion,
|
2019-12-14 04:38:24 -08:00
|
|
|
CreateAddressWithSeed {
|
|
|
|
from_pubkey: Option<Pubkey>,
|
|
|
|
seed: String,
|
|
|
|
program_id: Pubkey,
|
|
|
|
},
|
2020-09-23 13:36:34 -07:00
|
|
|
Feature(FeatureCliCommand),
|
2020-09-30 20:17:29 -07:00
|
|
|
Inflation(InflationCliCommand),
|
2019-06-07 13:11:56 -07:00
|
|
|
Fees,
|
2020-09-06 10:54:01 -07:00
|
|
|
FirstAvailableBlock,
|
2020-09-06 11:10:19 -07:00
|
|
|
GetBlock {
|
2020-10-09 12:55:35 -07:00
|
|
|
slot: Option<Slot>,
|
2020-09-06 11:10:19 -07:00
|
|
|
},
|
2019-11-25 23:40:36 -08:00
|
|
|
GetBlockTime {
|
2020-05-05 08:42:03 -07:00
|
|
|
slot: Option<Slot>,
|
2019-11-25 23:40:36 -08:00
|
|
|
},
|
2020-06-17 11:18:48 -07:00
|
|
|
GetEpoch,
|
|
|
|
GetEpochInfo,
|
2019-11-08 20:56:57 -08:00
|
|
|
GetGenesisHash,
|
2020-06-17 11:18:48 -07:00
|
|
|
GetSlot,
|
2020-07-22 08:36:12 -07:00
|
|
|
GetBlockHeight,
|
2020-06-17 11:18:48 -07:00
|
|
|
GetTransactionCount,
|
2020-05-12 20:05:05 -07:00
|
|
|
LargestAccounts {
|
|
|
|
filter: Option<RpcLargestAccountsFilter>,
|
|
|
|
},
|
2020-12-28 10:38:15 -08:00
|
|
|
LeaderSchedule {
|
|
|
|
epoch: Option<Epoch>,
|
|
|
|
},
|
2020-02-29 08:39:07 -08:00
|
|
|
LiveSlots,
|
2020-11-20 13:52:58 -08:00
|
|
|
Logs {
|
|
|
|
filter: RpcTransactionLogsFilter,
|
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
Ping {
|
2019-11-18 21:50:09 -08:00
|
|
|
lamports: u64,
|
2019-10-04 14:18:19 -07:00
|
|
|
interval: Duration,
|
|
|
|
count: Option<u64>,
|
|
|
|
timeout: Duration,
|
2020-12-07 04:10:50 -08:00
|
|
|
blockhash: Option<Hash>,
|
|
|
|
print_timestamp: bool,
|
2019-08-29 20:45:53 -07:00
|
|
|
},
|
2019-12-18 10:38:54 -08:00
|
|
|
ShowBlockProduction {
|
|
|
|
epoch: Option<Epoch>,
|
|
|
|
slot_limit: Option<u64>,
|
|
|
|
},
|
2019-11-15 12:15:34 -08:00
|
|
|
ShowGossip,
|
2020-01-17 11:10:52 -08:00
|
|
|
ShowStakes {
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
vote_account_pubkeys: Option<Vec<Pubkey>>,
|
|
|
|
},
|
2019-10-06 21:47:24 -07:00
|
|
|
ShowValidators {
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2020-06-17 11:18:48 -07:00
|
|
|
Supply {
|
|
|
|
print_accounts: bool,
|
|
|
|
},
|
|
|
|
TotalSupply,
|
2020-04-20 22:01:09 -07:00
|
|
|
TransactionHistory {
|
|
|
|
address: Pubkey,
|
2020-08-05 11:30:21 -07:00
|
|
|
before: Option<Signature>,
|
2020-08-15 09:42:17 -07:00
|
|
|
until: Option<Signature>,
|
2020-07-28 20:46:23 -07:00
|
|
|
limit: usize,
|
2020-09-04 22:26:02 -07:00
|
|
|
show_transactions: bool,
|
2020-04-20 22:01:09 -07:00
|
|
|
},
|
2020-11-12 13:48:34 -08:00
|
|
|
WaitForMaxStake {
|
|
|
|
max_stake_percent: f32,
|
|
|
|
},
|
2019-12-10 00:24:44 -08:00
|
|
|
// Nonce commands
|
2019-12-19 16:13:01 -08:00
|
|
|
AuthorizeNonceAccount {
|
|
|
|
nonce_account: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
2019-12-19 16:13:01 -08:00
|
|
|
new_authority: Pubkey,
|
|
|
|
},
|
2019-12-10 00:24:44 -08:00
|
|
|
CreateNonceAccount {
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_account: SignerIndex,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: Option<String>,
|
2020-01-02 17:05:08 -08:00
|
|
|
nonce_authority: Option<Pubkey>,
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount,
|
2019-12-10 00:24:44 -08:00
|
|
|
},
|
|
|
|
GetNonce(Pubkey),
|
2019-12-17 06:34:21 -08:00
|
|
|
NewNonce {
|
|
|
|
nonce_account: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
2019-12-17 06:34:21 -08:00
|
|
|
},
|
2019-12-10 00:24:44 -08:00
|
|
|
ShowNonceAccount {
|
|
|
|
nonce_account_pubkey: Pubkey,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
|
|
|
WithdrawFromNonceAccount {
|
2019-12-17 06:34:21 -08:00
|
|
|
nonce_account: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
2019-12-10 00:24:44 -08:00
|
|
|
destination_account_pubkey: Pubkey,
|
|
|
|
lamports: u64,
|
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
// Program Deployment
|
2020-12-21 13:02:53 -08:00
|
|
|
Deploy {
|
2020-06-04 19:14:12 -07:00
|
|
|
program_location: String,
|
2020-12-21 13:02:53 -08:00
|
|
|
address: Option<SignerIndex>,
|
2020-08-17 18:06:22 -07:00
|
|
|
use_deprecated_loader: bool,
|
2020-10-22 21:42:35 -07:00
|
|
|
allow_excessive_balance: bool,
|
2020-06-04 19:14:12 -07:00
|
|
|
},
|
2020-12-21 13:02:53 -08:00
|
|
|
Program(ProgramCliCommand),
|
2019-10-04 14:18:19 -07:00
|
|
|
// Stake Commands
|
2019-10-21 16:08:09 -07:00
|
|
|
CreateStakeAccount {
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_account: SignerIndex,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: Option<String>,
|
2019-10-21 16:08:09 -07:00
|
|
|
staker: Option<Pubkey>,
|
|
|
|
withdrawer: Option<Pubkey>,
|
|
|
|
lockup: Lockup,
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount,
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only: bool,
|
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
|
|
|
from: SignerIndex,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2019-11-25 21:09:57 -08:00
|
|
|
DeactivateStake {
|
|
|
|
stake_account_pubkey: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority: SignerIndex,
|
2019-11-25 21:09:57 -08:00
|
|
|
sign_only: bool,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query: BlockhashQuery,
|
2019-12-27 12:35:49 -08:00
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2019-11-25 21:09:57 -08:00
|
|
|
},
|
|
|
|
DelegateStake {
|
|
|
|
stake_account_pubkey: Pubkey,
|
|
|
|
vote_account_pubkey: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority: SignerIndex,
|
2019-11-25 21:09:57 -08:00
|
|
|
force: bool,
|
|
|
|
sign_only: bool,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query: BlockhashQuery,
|
2019-12-27 12:35:49 -08:00
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2019-11-25 21:09:57 -08:00
|
|
|
},
|
2020-02-02 21:20:28 -08:00
|
|
|
SplitStake {
|
|
|
|
stake_account_pubkey: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority: SignerIndex,
|
2020-02-02 21:20:28 -08:00
|
|
|
sign_only: bool,
|
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
split_stake_account: SignerIndex,
|
2020-02-02 21:20:28 -08:00
|
|
|
seed: Option<String>,
|
|
|
|
lamports: u64,
|
2020-02-24 16:03:30 -08:00
|
|
|
fee_payer: SignerIndex,
|
2020-02-02 21:20:28 -08:00
|
|
|
},
|
2020-06-10 21:02:04 -07:00
|
|
|
MergeStake {
|
|
|
|
stake_account_pubkey: Pubkey,
|
|
|
|
source_stake_account_pubkey: Pubkey,
|
|
|
|
stake_authority: SignerIndex,
|
|
|
|
sign_only: bool,
|
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
|
|
|
},
|
2019-10-25 09:20:08 -07:00
|
|
|
ShowStakeHistory {
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
ShowStakeAccount {
|
2019-09-10 12:36:59 -07:00
|
|
|
pubkey: Pubkey,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2020-01-15 13:32:06 -08:00
|
|
|
StakeAuthorize {
|
|
|
|
stake_account_pubkey: Pubkey,
|
2020-03-21 18:56:17 -07:00
|
|
|
new_authorizations: Vec<(StakeAuthorize, Pubkey, SignerIndex)>,
|
2020-01-17 09:30:56 -08:00
|
|
|
sign_only: bool,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query: BlockhashQuery,
|
2020-01-17 09:30:56 -08:00
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2020-01-15 13:32:06 -08:00
|
|
|
},
|
2020-02-12 15:36:29 -08:00
|
|
|
StakeSetLockup {
|
|
|
|
stake_account_pubkey: Pubkey,
|
2020-03-02 12:28:43 -08:00
|
|
|
lockup: LockupArgs,
|
2020-02-24 16:03:30 -08:00
|
|
|
custodian: SignerIndex,
|
2020-02-12 15:36:29 -08:00
|
|
|
sign_only: bool,
|
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2020-02-12 15:36:29 -08:00
|
|
|
},
|
2020-01-15 13:32:06 -08:00
|
|
|
WithdrawStake {
|
|
|
|
stake_account_pubkey: Pubkey,
|
|
|
|
destination_account_pubkey: Pubkey,
|
|
|
|
lamports: u64,
|
2020-02-24 16:03:30 -08:00
|
|
|
withdraw_authority: SignerIndex,
|
2020-04-22 15:00:18 -07:00
|
|
|
custodian: Option<SignerIndex>,
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only: bool,
|
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2020-01-15 13:32:06 -08:00
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
// Validator Info Commands
|
|
|
|
GetValidatorInfo(Option<Pubkey>),
|
2019-10-21 16:08:09 -07:00
|
|
|
SetValidatorInfo {
|
|
|
|
validator_info: Value,
|
|
|
|
force_keybase: bool,
|
|
|
|
info_pubkey: Option<Pubkey>,
|
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
// Vote Commands
|
2019-10-21 16:08:09 -07:00
|
|
|
CreateVoteAccount {
|
2020-07-13 20:27:41 -07:00
|
|
|
vote_account: SignerIndex,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: Option<String>,
|
2020-03-19 01:58:52 -07:00
|
|
|
identity_account: SignerIndex,
|
2019-10-21 16:08:09 -07:00
|
|
|
authorized_voter: Option<Pubkey>,
|
|
|
|
authorized_withdrawer: Option<Pubkey>,
|
|
|
|
commission: u8,
|
|
|
|
},
|
2019-09-26 10:26:47 -07:00
|
|
|
ShowVoteAccount {
|
|
|
|
pubkey: Pubkey,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2020-03-13 13:30:04 -07:00
|
|
|
WithdrawFromVoteAccount {
|
|
|
|
vote_account_pubkey: Pubkey,
|
|
|
|
destination_account_pubkey: Pubkey,
|
|
|
|
withdraw_authority: SignerIndex,
|
2020-06-15 14:36:47 -07:00
|
|
|
withdraw_amount: SpendAmount,
|
2020-03-13 13:30:04 -07:00
|
|
|
},
|
2019-12-12 15:04:03 -08:00
|
|
|
VoteAuthorize {
|
|
|
|
vote_account_pubkey: Pubkey,
|
|
|
|
new_authorized_pubkey: Pubkey,
|
|
|
|
vote_authorize: VoteAuthorize,
|
|
|
|
},
|
|
|
|
VoteUpdateValidator {
|
|
|
|
vote_account_pubkey: Pubkey,
|
2020-03-19 01:58:52 -07:00
|
|
|
new_identity_account: SignerIndex,
|
2020-07-13 20:49:59 -07:00
|
|
|
withdraw_authority: SignerIndex,
|
2019-12-12 15:04:03 -08:00
|
|
|
},
|
2020-06-09 21:15:46 -07:00
|
|
|
VoteUpdateCommission {
|
|
|
|
vote_account_pubkey: Pubkey,
|
|
|
|
commission: u8,
|
2020-07-13 19:30:49 -07:00
|
|
|
withdraw_authority: SignerIndex,
|
2020-06-09 21:15:46 -07:00
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
// Wallet Commands
|
|
|
|
Address,
|
|
|
|
Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host: Option<IpAddr>,
|
|
|
|
faucet_port: u16,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey: Option<Pubkey>,
|
2019-10-04 14:18:19 -07:00
|
|
|
lamports: u64,
|
|
|
|
},
|
|
|
|
Balance {
|
2019-10-21 16:08:09 -07:00
|
|
|
pubkey: Option<Pubkey>,
|
2019-09-26 10:26:47 -07:00
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2019-10-04 14:18:19 -07:00
|
|
|
Confirm(Signature),
|
2020-04-20 10:00:20 -07:00
|
|
|
DecodeTransaction(Transaction),
|
2020-03-14 20:48:41 -07:00
|
|
|
ResolveSigner(Option<String>),
|
2019-10-04 19:54:09 -07:00
|
|
|
ShowAccount {
|
|
|
|
pubkey: Pubkey,
|
|
|
|
output_file: Option<String>,
|
|
|
|
use_lamports_unit: bool,
|
|
|
|
},
|
2020-02-07 11:16:35 -08:00
|
|
|
Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount,
|
2020-02-07 11:16:35 -08:00
|
|
|
to: Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: SignerIndex,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: bool,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: bool,
|
2020-02-07 11:16:35 -08:00
|
|
|
blockhash_query: BlockhashQuery,
|
|
|
|
nonce_account: Option<Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
|
2019-10-21 16:08:09 -07:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub struct CliCommandInfo {
|
|
|
|
pub command: CliCommand,
|
2020-02-24 16:03:30 -08:00
|
|
|
pub signers: CliSigners,
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
|
|
|
|
2020-05-14 11:24:14 -07:00
|
|
|
#[derive(Debug, Error)]
|
2019-10-04 15:13:21 -07:00
|
|
|
pub enum CliError {
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error("bad parameter: {0}")]
|
2018-09-14 01:58:39 -07:00
|
|
|
BadParameter(String),
|
2020-05-14 11:24:14 -07:00
|
|
|
#[error(transparent)]
|
|
|
|
ClientError(#[from] ClientError),
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error("command not recognized: {0}")]
|
2019-08-09 14:52:06 -07:00
|
|
|
CommandNotRecognized(String),
|
2020-05-14 11:24:14 -07:00
|
|
|
#[error("insufficient funds for fee ({0} SOL)")]
|
|
|
|
InsufficientFundsForFee(f64),
|
|
|
|
#[error("insufficient funds for spend ({0} SOL)")]
|
|
|
|
InsufficientFundsForSpend(f64),
|
|
|
|
#[error("insufficient funds for spend ({0} SOL) and fee ({1} SOL)")]
|
|
|
|
InsufficientFundsForSpendAndFee(f64, f64),
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error(transparent)]
|
2020-09-21 12:26:06 -07:00
|
|
|
InvalidNonce(nonce_utils::Error),
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error("dynamic program error: {0}")]
|
2018-10-22 21:21:33 -07:00
|
|
|
DynamicProgramError(String),
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error("rpc request error: {0}")]
|
2018-09-20 22:27:06 -07:00
|
|
|
RpcRequestError(String),
|
2020-03-12 23:20:49 -07:00
|
|
|
#[error("keypair file not found: {0}")]
|
2019-09-12 18:37:29 -07:00
|
|
|
KeypairFileNotFound(String),
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
|
2020-02-21 13:55:53 -08:00
|
|
|
impl From<Box<dyn error::Error>> for CliError {
|
|
|
|
fn from(error: Box<dyn error::Error>) -> Self {
|
2020-03-12 23:20:49 -07:00
|
|
|
CliError::DynamicProgramError(error.to_string())
|
2020-02-21 13:55:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 12:26:06 -07:00
|
|
|
impl From<nonce_utils::Error> for CliError {
|
|
|
|
fn from(error: nonce_utils::Error) -> Self {
|
2020-03-10 12:00:15 -07:00
|
|
|
match error {
|
2020-09-21 12:26:06 -07:00
|
|
|
nonce_utils::Error::Client(client_error) => Self::RpcRequestError(client_error),
|
2020-03-10 12:00:15 -07:00
|
|
|
_ => Self::InvalidNonce(error),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-29 08:39:07 -08:00
|
|
|
pub enum SettingType {
|
|
|
|
Explicit,
|
|
|
|
Computed,
|
|
|
|
SystemDefault,
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
pub struct CliConfig<'a> {
|
2019-10-04 15:13:21 -07:00
|
|
|
pub command: CliCommand,
|
2019-05-06 07:38:26 -07:00
|
|
|
pub json_rpc_url: String,
|
2020-02-29 08:39:07 -08:00
|
|
|
pub websocket_url: String,
|
2020-02-24 16:03:30 -08:00
|
|
|
pub signers: Vec<&'a dyn Signer>,
|
|
|
|
pub keypair_path: String,
|
2019-01-13 23:10:03 -08:00
|
|
|
pub rpc_client: Option<RpcClient>,
|
2020-07-23 08:21:59 -07:00
|
|
|
pub rpc_timeout: Duration,
|
2019-12-19 14:37:47 -08:00
|
|
|
pub verbose: bool,
|
2020-04-14 12:10:25 -07:00
|
|
|
pub output_format: OutputFormat,
|
2020-06-17 11:18:48 -07:00
|
|
|
pub commitment: CommitmentConfig,
|
|
|
|
pub send_transaction_config: RpcSendTransactionConfig,
|
2020-06-17 23:09:33 -07:00
|
|
|
pub address_labels: HashMap<String, String>,
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
impl CliConfig<'_> {
|
2020-02-29 08:39:07 -08:00
|
|
|
fn default_keypair_path() -> String {
|
2020-03-09 12:29:31 -07:00
|
|
|
solana_cli_config::Config::default().keypair_path
|
2019-11-23 08:55:43 -08:00
|
|
|
}
|
|
|
|
|
2020-02-29 08:39:07 -08:00
|
|
|
fn default_json_rpc_url() -> String {
|
2020-03-09 12:29:31 -07:00
|
|
|
solana_cli_config::Config::default().json_rpc_url
|
2019-11-23 08:55:43 -08:00
|
|
|
}
|
2020-02-07 10:26:56 -08:00
|
|
|
|
2020-02-29 08:39:07 -08:00
|
|
|
fn default_websocket_url() -> String {
|
2020-03-09 12:29:31 -07:00
|
|
|
solana_cli_config::Config::default().websocket_url
|
2020-02-29 08:39:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn first_nonempty_setting(
|
|
|
|
settings: std::vec::Vec<(SettingType, String)>,
|
|
|
|
) -> (SettingType, String) {
|
|
|
|
settings
|
|
|
|
.into_iter()
|
2020-12-13 17:26:34 -08:00
|
|
|
.find(|(_, value)| !value.is_empty())
|
2020-02-29 08:39:07 -08:00
|
|
|
.expect("no nonempty setting")
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute_websocket_url_setting(
|
|
|
|
websocket_cmd_url: &str,
|
|
|
|
websocket_cfg_url: &str,
|
|
|
|
json_rpc_cmd_url: &str,
|
|
|
|
json_rpc_cfg_url: &str,
|
|
|
|
) -> (SettingType, String) {
|
|
|
|
Self::first_nonempty_setting(vec![
|
|
|
|
(SettingType::Explicit, websocket_cmd_url.to_string()),
|
|
|
|
(SettingType::Explicit, websocket_cfg_url.to_string()),
|
|
|
|
(
|
|
|
|
SettingType::Computed,
|
2020-03-09 12:29:31 -07:00
|
|
|
solana_cli_config::Config::compute_websocket_url(json_rpc_cmd_url),
|
2020-02-29 08:39:07 -08:00
|
|
|
),
|
|
|
|
(
|
|
|
|
SettingType::Computed,
|
2020-03-09 12:29:31 -07:00
|
|
|
solana_cli_config::Config::compute_websocket_url(json_rpc_cfg_url),
|
2020-02-29 08:39:07 -08:00
|
|
|
),
|
|
|
|
(SettingType::SystemDefault, Self::default_websocket_url()),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute_json_rpc_url_setting(
|
|
|
|
json_rpc_cmd_url: &str,
|
|
|
|
json_rpc_cfg_url: &str,
|
|
|
|
) -> (SettingType, String) {
|
|
|
|
Self::first_nonempty_setting(vec![
|
|
|
|
(SettingType::Explicit, json_rpc_cmd_url.to_string()),
|
|
|
|
(SettingType::Explicit, json_rpc_cfg_url.to_string()),
|
|
|
|
(SettingType::SystemDefault, Self::default_json_rpc_url()),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn compute_keypair_path_setting(
|
|
|
|
keypair_cmd_path: &str,
|
|
|
|
keypair_cfg_path: &str,
|
|
|
|
) -> (SettingType, String) {
|
|
|
|
Self::first_nonempty_setting(vec![
|
|
|
|
(SettingType::Explicit, keypair_cmd_path.to_string()),
|
|
|
|
(SettingType::Explicit, keypair_cfg_path.to_string()),
|
|
|
|
(SettingType::SystemDefault, Self::default_keypair_path()),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
2020-02-21 13:55:53 -08:00
|
|
|
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
|
2020-02-24 16:03:30 -08:00
|
|
|
if !self.signers.is_empty() {
|
|
|
|
self.signers[0].try_pubkey()
|
|
|
|
} else {
|
2020-04-01 11:13:31 -07:00
|
|
|
Err(SignerError::Custom(
|
2020-02-24 16:03:30 -08:00
|
|
|
"Default keypair must be set if pubkey arg not provided".to_string(),
|
|
|
|
))
|
|
|
|
}
|
2020-02-07 10:26:56 -08:00
|
|
|
}
|
2020-06-17 11:18:48 -07:00
|
|
|
|
|
|
|
pub fn recent_for_tests() -> Self {
|
2020-12-13 17:26:34 -08:00
|
|
|
Self {
|
|
|
|
commitment: CommitmentConfig::recent(),
|
|
|
|
send_transaction_config: RpcSendTransactionConfig {
|
|
|
|
skip_preflight: true,
|
2020-12-17 14:37:22 -08:00
|
|
|
preflight_commitment: Some(CommitmentConfig::recent().commitment),
|
2020-12-13 17:26:34 -08:00
|
|
|
..RpcSendTransactionConfig::default()
|
|
|
|
},
|
|
|
|
..Self::default()
|
|
|
|
}
|
2020-06-17 11:18:48 -07:00
|
|
|
}
|
2019-11-23 08:55:43 -08:00
|
|
|
}
|
2019-09-05 10:14:23 -07:00
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
impl Default for CliConfig<'_> {
|
|
|
|
fn default() -> CliConfig<'static> {
|
2019-10-04 15:13:21 -07:00
|
|
|
CliConfig {
|
|
|
|
command: CliCommand::Balance {
|
2019-10-21 16:08:09 -07:00
|
|
|
pubkey: Some(Pubkey::default()),
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: false,
|
|
|
|
},
|
2019-11-23 08:55:43 -08:00
|
|
|
json_rpc_url: Self::default_json_rpc_url(),
|
2020-02-29 08:39:07 -08:00
|
|
|
websocket_url: Self::default_websocket_url(),
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: Vec::new(),
|
|
|
|
keypair_path: Self::default_keypair_path(),
|
2019-01-13 23:10:03 -08:00
|
|
|
rpc_client: None,
|
2020-07-23 08:21:59 -07:00
|
|
|
rpc_timeout: Duration::from_secs(u64::from_str(DEFAULT_RPC_TIMEOUT_SECONDS).unwrap()),
|
2019-12-19 14:37:47 -08:00
|
|
|
verbose: false,
|
2020-04-14 12:10:25 -07:00
|
|
|
output_format: OutputFormat::Display,
|
2020-06-17 11:18:48 -07:00
|
|
|
commitment: CommitmentConfig::default(),
|
|
|
|
send_transaction_config: RpcSendTransactionConfig::default(),
|
2020-06-17 23:09:33 -07:00
|
|
|
address_labels: HashMap::new(),
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
pub fn parse_command(
|
|
|
|
matches: &ArgMatches<'_>,
|
2020-09-21 20:53:15 -07:00
|
|
|
default_signer: &DefaultSigner,
|
2020-04-18 11:54:21 -07:00
|
|
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
2020-02-24 16:03:30 -08:00
|
|
|
) -> Result<CliCommandInfo, Box<dyn error::Error>> {
|
2018-09-14 01:58:39 -07:00
|
|
|
let response = match matches.subcommand() {
|
2019-10-04 19:54:09 -07:00
|
|
|
// Cluster Query Commands
|
2020-03-16 15:17:13 -07:00
|
|
|
("catchup", Some(matches)) => parse_catchup(matches, wallet_manager),
|
2020-05-05 08:42:03 -07:00
|
|
|
("cluster-date", Some(_matches)) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ClusterDate,
|
|
|
|
signers: vec![],
|
|
|
|
}),
|
2019-10-21 16:08:09 -07:00
|
|
|
("cluster-version", Some(_matches)) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ClusterVersion,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}),
|
2020-03-08 23:02:24 -07:00
|
|
|
("create-address-with-seed", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_create_address_with_seed(matches, default_signer, wallet_manager)
|
2020-03-08 23:02:24 -07:00
|
|
|
}
|
2020-09-24 13:30:38 -07:00
|
|
|
("feature", Some(matches)) => {
|
|
|
|
parse_feature_subcommand(matches, default_signer, wallet_manager)
|
|
|
|
}
|
2019-10-21 16:08:09 -07:00
|
|
|
("fees", Some(_matches)) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Fees,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}),
|
2020-09-06 10:54:01 -07:00
|
|
|
("first-available-block", Some(_matches)) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::FirstAvailableBlock,
|
|
|
|
signers: vec![],
|
|
|
|
}),
|
2020-09-06 11:10:19 -07:00
|
|
|
("block", Some(matches)) => parse_get_block(matches),
|
2020-01-20 22:06:47 -08:00
|
|
|
("block-time", Some(matches)) => parse_get_block_time(matches),
|
|
|
|
("epoch-info", Some(matches)) => parse_get_epoch_info(matches),
|
|
|
|
("genesis-hash", Some(_matches)) => Ok(CliCommandInfo {
|
2019-11-08 20:56:57 -08:00
|
|
|
command: CliCommand::GetGenesisHash,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}),
|
2020-04-01 20:42:28 -07:00
|
|
|
("epoch", Some(matches)) => parse_get_epoch(matches),
|
2020-01-20 22:06:47 -08:00
|
|
|
("slot", Some(matches)) => parse_get_slot(matches),
|
2020-07-22 08:36:12 -07:00
|
|
|
("block-height", Some(matches)) => parse_get_block_height(matches),
|
2020-09-30 20:17:29 -07:00
|
|
|
("inflation", Some(matches)) => {
|
|
|
|
parse_inflation_subcommand(matches, default_signer, wallet_manager)
|
|
|
|
}
|
2020-05-12 20:05:05 -07:00
|
|
|
("largest-accounts", Some(matches)) => parse_largest_accounts(matches),
|
2020-05-10 11:05:14 -07:00
|
|
|
("supply", Some(matches)) => parse_supply(matches),
|
2020-03-09 01:28:44 -07:00
|
|
|
("total-supply", Some(matches)) => parse_total_supply(matches),
|
2020-01-20 22:06:47 -08:00
|
|
|
("transaction-count", Some(matches)) => parse_get_transaction_count(matches),
|
2020-12-28 10:38:15 -08:00
|
|
|
("leader-schedule", Some(matches)) => parse_leader_schedule(matches),
|
2020-09-21 20:53:15 -07:00
|
|
|
("ping", Some(matches)) => parse_cluster_ping(matches, default_signer, wallet_manager),
|
2020-02-29 08:39:07 -08:00
|
|
|
("live-slots", Some(_matches)) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::LiveSlots,
|
|
|
|
signers: vec![],
|
|
|
|
}),
|
2020-11-20 13:52:58 -08:00
|
|
|
("logs", Some(matches)) => parse_logs(matches, wallet_manager),
|
2020-01-20 22:06:47 -08:00
|
|
|
("block-production", Some(matches)) => parse_show_block_production(matches),
|
|
|
|
("gossip", Some(_matches)) => Ok(CliCommandInfo {
|
2019-11-15 12:15:34 -08:00
|
|
|
command: CliCommand::ShowGossip,
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-11-15 12:15:34 -08:00
|
|
|
}),
|
2020-03-16 15:17:13 -07:00
|
|
|
("stakes", Some(matches)) => parse_show_stakes(matches, wallet_manager),
|
2020-01-20 22:06:47 -08:00
|
|
|
("validators", Some(matches)) => parse_show_validators(matches),
|
2020-04-20 22:01:09 -07:00
|
|
|
("transaction-history", Some(matches)) => {
|
|
|
|
parse_transaction_history(matches, wallet_manager)
|
|
|
|
}
|
2019-12-10 00:24:44 -08:00
|
|
|
// Nonce Commands
|
2020-02-24 16:03:30 -08:00
|
|
|
("authorize-nonce-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_authorize_nonce_account(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
|
|
|
("create-nonce-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_nonce_create_account(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
2020-03-16 15:17:13 -07:00
|
|
|
("nonce", Some(matches)) => parse_get_nonce(matches, wallet_manager),
|
2020-09-21 20:53:15 -07:00
|
|
|
("new-nonce", Some(matches)) => parse_new_nonce(matches, default_signer, wallet_manager),
|
2020-03-16 15:17:13 -07:00
|
|
|
("nonce-account", Some(matches)) => parse_show_nonce_account(matches, wallet_manager),
|
2019-12-10 00:24:44 -08:00
|
|
|
("withdraw-from-nonce-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_withdraw_from_nonce_account(matches, default_signer, wallet_manager)
|
2019-12-10 00:24:44 -08:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
// Program Deployment
|
2020-12-21 13:02:53 -08:00
|
|
|
("deploy", Some(matches)) => {
|
|
|
|
let (address_signer, _address) = signer_of(matches, "address_signer", wallet_manager)?;
|
2020-09-21 20:53:15 -07:00
|
|
|
let mut signers = vec![default_signer.signer_from_path(matches, wallet_manager)?];
|
2020-12-21 13:02:53 -08:00
|
|
|
let address = address_signer.map(|signer| {
|
2020-06-04 19:14:12 -07:00
|
|
|
signers.push(signer);
|
|
|
|
1
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(CliCommandInfo {
|
2020-12-21 13:02:53 -08:00
|
|
|
command: CliCommand::Deploy {
|
2020-06-04 19:14:12 -07:00
|
|
|
program_location: matches.value_of("program_location").unwrap().to_string(),
|
2020-12-21 13:02:53 -08:00
|
|
|
address,
|
2020-10-22 21:42:35 -07:00
|
|
|
use_deprecated_loader: matches.is_present("use_deprecated_loader"),
|
|
|
|
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
|
2020-06-04 19:14:12 -07:00
|
|
|
},
|
|
|
|
signers,
|
|
|
|
})
|
|
|
|
}
|
2020-12-21 13:02:53 -08:00
|
|
|
("program", Some(matches)) => {
|
|
|
|
parse_program_subcommand(matches, default_signer, wallet_manager)
|
2020-12-14 15:35:10 -08:00
|
|
|
}
|
2020-11-12 13:48:34 -08:00
|
|
|
("wait-for-max-stake", Some(matches)) => {
|
|
|
|
let max_stake_percent = value_t_or_exit!(matches, "max_percent", f32);
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::WaitForMaxStake { max_stake_percent },
|
|
|
|
signers: vec![],
|
|
|
|
})
|
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
// Stake Commands
|
2020-02-24 16:03:30 -08:00
|
|
|
("create-stake-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_create_account(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
|
|
|
("delegate-stake", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_delegate_stake(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
|
|
|
("withdraw-stake", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_withdraw_stake(matches, default_signer, wallet_manager)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
2020-02-24 16:03:30 -08:00
|
|
|
("deactivate-stake", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_deactivate_stake(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
|
|
|
("split-stake", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_split_stake(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
2020-06-10 21:02:04 -07:00
|
|
|
("merge-stake", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_merge_stake(matches, default_signer, wallet_manager)
|
2020-06-10 21:02:04 -07:00
|
|
|
}
|
2020-03-21 18:56:17 -07:00
|
|
|
("stake-authorize", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_authorize(matches, default_signer, wallet_manager)
|
2020-03-21 18:56:17 -07:00
|
|
|
}
|
2020-02-24 16:03:30 -08:00
|
|
|
("stake-set-lockup", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_stake_set_lockup(matches, default_signer, wallet_manager)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
2020-03-16 15:17:13 -07:00
|
|
|
("stake-account", Some(matches)) => parse_show_stake_account(matches, wallet_manager),
|
2020-01-26 20:00:57 -08:00
|
|
|
("stake-history", Some(matches)) => parse_show_stake_history(matches),
|
2019-10-04 19:54:09 -07:00
|
|
|
// Validator Info Commands
|
|
|
|
("validator-info", Some(matches)) => match matches.subcommand() {
|
2020-02-24 16:03:30 -08:00
|
|
|
("publish", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_validator_info_command(matches, default_signer, wallet_manager)
|
2020-02-24 16:03:30 -08:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
("get", Some(matches)) => parse_get_validator_info_command(matches),
|
|
|
|
_ => unreachable!(),
|
|
|
|
},
|
|
|
|
// Vote Commands
|
2020-02-24 16:03:30 -08:00
|
|
|
("create-vote-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_create_vote_account(matches, default_signer, wallet_manager)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
2020-02-24 16:03:30 -08:00
|
|
|
("vote-update-validator", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_vote_update_validator(matches, default_signer, wallet_manager)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
2020-06-09 21:15:46 -07:00
|
|
|
("vote-update-commission", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_vote_update_commission(matches, default_signer, wallet_manager)
|
2020-06-09 21:15:46 -07:00
|
|
|
}
|
2020-02-24 16:03:30 -08:00
|
|
|
("vote-authorize-voter", Some(matches)) => parse_vote_authorize(
|
|
|
|
matches,
|
2020-09-21 20:53:15 -07:00
|
|
|
default_signer,
|
2020-02-24 16:03:30 -08:00
|
|
|
wallet_manager,
|
|
|
|
VoteAuthorize::Voter,
|
|
|
|
),
|
|
|
|
("vote-authorize-withdrawer", Some(matches)) => parse_vote_authorize(
|
|
|
|
matches,
|
2020-09-21 20:53:15 -07:00
|
|
|
default_signer,
|
2020-02-24 16:03:30 -08:00
|
|
|
wallet_manager,
|
|
|
|
VoteAuthorize::Withdrawer,
|
|
|
|
),
|
2020-03-16 15:17:13 -07:00
|
|
|
("vote-account", Some(matches)) => parse_vote_get_account_command(matches, wallet_manager),
|
2020-03-13 13:30:04 -07:00
|
|
|
("withdraw-from-vote-account", Some(matches)) => {
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_withdraw_from_vote_account(matches, default_signer, wallet_manager)
|
2020-03-13 13:30:04 -07:00
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
// Wallet Commands
|
2020-02-26 14:24:44 -08:00
|
|
|
("address", Some(matches)) => Ok(CliCommandInfo {
|
2019-10-21 16:08:09 -07:00
|
|
|
command: CliCommand::Address,
|
2020-09-21 20:53:15 -07:00
|
|
|
signers: vec![default_signer.signer_from_path(matches, wallet_manager)?],
|
2019-10-21 16:08:09 -07:00
|
|
|
}),
|
|
|
|
("airdrop", Some(matches)) => {
|
2019-12-16 13:05:17 -08:00
|
|
|
let faucet_port = matches
|
|
|
|
.value_of("faucet_port")
|
2020-06-08 17:38:14 -07:00
|
|
|
.ok_or_else(|| CliError::BadParameter("Missing faucet port".to_string()))?
|
2019-08-29 20:45:53 -07:00
|
|
|
.parse()
|
2020-06-08 17:38:14 -07:00
|
|
|
.map_err(|err| CliError::BadParameter(format!("Invalid faucet port: {}", err)))?;
|
|
|
|
|
|
|
|
let faucet_host = matches
|
|
|
|
.value_of("faucet_host")
|
|
|
|
.map(|faucet_host| {
|
|
|
|
solana_net_utils::parse_host(faucet_host).map_err(|err| {
|
|
|
|
CliError::BadParameter(format!("Invalid faucet host: {}", err))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.transpose()?;
|
2020-03-16 15:17:13 -07:00
|
|
|
let pubkey = pubkey_of_signer(matches, "to", wallet_manager)?;
|
2020-02-24 16:03:30 -08:00
|
|
|
let signers = if pubkey.is_some() {
|
|
|
|
vec![]
|
|
|
|
} else {
|
2020-09-21 20:53:15 -07:00
|
|
|
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
2020-02-24 16:03:30 -08:00
|
|
|
};
|
2020-02-15 11:53:52 -08:00
|
|
|
let lamports = lamports_of_sol(matches, "amount").unwrap();
|
2019-10-21 16:08:09 -07:00
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host,
|
|
|
|
faucet_port,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey,
|
2019-10-21 16:08:09 -07:00
|
|
|
lamports,
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers,
|
2019-08-29 20:45:53 -07:00
|
|
|
})
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
2019-10-21 16:08:09 -07:00
|
|
|
("balance", Some(matches)) => {
|
2020-03-16 15:17:13 -07:00
|
|
|
let pubkey = pubkey_of_signer(matches, "pubkey", wallet_manager)?;
|
2020-02-24 16:03:30 -08:00
|
|
|
let signers = if pubkey.is_some() {
|
|
|
|
vec![]
|
|
|
|
} else {
|
2020-09-21 20:53:15 -07:00
|
|
|
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
2020-02-24 16:03:30 -08:00
|
|
|
};
|
2019-10-21 16:08:09 -07:00
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Balance {
|
|
|
|
pubkey,
|
|
|
|
use_lamports_unit: matches.is_present("lamports"),
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers,
|
2019-09-10 12:36:59 -07:00
|
|
|
})
|
2019-03-20 09:44:16 -07:00
|
|
|
}
|
2019-10-21 16:08:09 -07:00
|
|
|
("confirm", Some(matches)) => match matches.value_of("signature").unwrap().parse() {
|
|
|
|
Ok(signature) => Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Confirm(signature),
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}),
|
2020-04-20 10:00:20 -07:00
|
|
|
_ => Err(CliError::BadParameter("Invalid signature".to_string())),
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-04-20 10:00:20 -07:00
|
|
|
("decode-transaction", Some(matches)) => {
|
2020-08-15 17:26:00 -07:00
|
|
|
let blob = value_t_or_exit!(matches, "transaction", String);
|
|
|
|
let encoding = match matches.value_of("encoding").unwrap() {
|
2020-09-15 20:21:52 -07:00
|
|
|
"base58" => UiTransactionEncoding::Base58,
|
2020-08-15 22:06:39 -07:00
|
|
|
"base64" => UiTransactionEncoding::Base64,
|
2020-08-15 17:56:09 -07:00
|
|
|
_ => unreachable!(),
|
2020-08-15 17:26:00 -07:00
|
|
|
};
|
|
|
|
|
2020-08-15 17:56:09 -07:00
|
|
|
let encoded_transaction = EncodedTransaction::Binary(blob, encoding);
|
2020-04-20 10:00:20 -07:00
|
|
|
if let Some(transaction) = encoded_transaction.decode() {
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::DecodeTransaction(transaction),
|
|
|
|
signers: vec![],
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Err(CliError::BadParameter(
|
|
|
|
"Unable to decode transaction".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
2020-01-20 22:06:47 -08:00
|
|
|
("account", Some(matches)) => {
|
2020-03-16 15:17:13 -07:00
|
|
|
let account_pubkey =
|
|
|
|
pubkey_of_signer(matches, "account_pubkey", wallet_manager)?.unwrap();
|
2019-10-04 19:54:09 -07:00
|
|
|
let output_file = matches.value_of("output_file");
|
|
|
|
let use_lamports_unit = matches.is_present("lamports");
|
2019-10-21 16:08:09 -07:00
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ShowAccount {
|
|
|
|
pubkey: account_pubkey,
|
|
|
|
output_file: output_file.map(ToString::to_string),
|
|
|
|
use_lamports_unit,
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-08-12 21:33:13 -07:00
|
|
|
})
|
|
|
|
}
|
2020-03-14 20:48:41 -07:00
|
|
|
("resolve-signer", Some(matches)) => {
|
|
|
|
let signer_path = resolve_signer(matches, "signer", wallet_manager)?;
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::ResolveSigner(signer_path),
|
|
|
|
signers: vec![],
|
|
|
|
})
|
|
|
|
}
|
2020-08-07 15:01:51 -07:00
|
|
|
("pay", Some(matches)) | ("transfer", Some(matches)) => {
|
2020-05-14 11:24:14 -07:00
|
|
|
let amount = SpendAmount::new_from_matches(matches, "amount");
|
2020-03-16 15:17:13 -07:00
|
|
|
let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap();
|
2020-02-07 11:16:35 -08:00
|
|
|
let sign_only = matches.is_present(SIGN_ONLY_ARG.name);
|
2020-04-08 22:46:19 -07:00
|
|
|
let no_wait = matches.is_present("no_wait");
|
2020-02-07 11:16:35 -08:00
|
|
|
let blockhash_query = BlockhashQuery::new_from_matches(matches);
|
2020-03-16 15:17:13 -07:00
|
|
|
let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?;
|
2020-02-24 16:03:30 -08:00
|
|
|
let (nonce_authority, nonce_authority_pubkey) =
|
|
|
|
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 (from, from_pubkey) = signer_of(matches, "from", wallet_manager)?;
|
|
|
|
|
|
|
|
let mut bulk_signers = vec![fee_payer, from];
|
|
|
|
if nonce_account.is_some() {
|
|
|
|
bulk_signers.push(nonce_authority);
|
|
|
|
}
|
|
|
|
|
2020-09-21 20:53:15 -07:00
|
|
|
let signer_info =
|
|
|
|
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
|
2020-02-24 16:03:30 -08:00
|
|
|
|
2020-02-07 11:16:35 -08:00
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount,
|
2020-02-07 11:16:35 -08:00
|
|
|
to,
|
|
|
|
sign_only,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait,
|
2020-02-07 11:16:35 -08:00
|
|
|
blockhash_query,
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(),
|
|
|
|
fee_payer: signer_info.index_of(fee_payer_pubkey).unwrap(),
|
|
|
|
from: signer_info.index_of(from_pubkey).unwrap(),
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: signer_info.signers,
|
2020-02-07 11:16:35 -08:00
|
|
|
})
|
|
|
|
}
|
2019-12-10 00:24:44 -08:00
|
|
|
//
|
2018-09-14 01:58:39 -07:00
|
|
|
("", None) => {
|
2018-09-24 09:23:16 -07:00
|
|
|
eprintln!("{}", matches.usage());
|
2019-10-04 15:13:21 -07:00
|
|
|
Err(CliError::CommandNotRecognized(
|
2018-09-14 01:58:39 -07:00
|
|
|
"no subcommand given".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}?;
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2019-10-08 22:34:26 -07:00
|
|
|
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
|
2019-02-09 13:15:44 -08:00
|
|
|
|
2019-12-14 04:38:24 -08:00
|
|
|
pub fn parse_create_address_with_seed(
|
|
|
|
matches: &ArgMatches<'_>,
|
2020-09-21 20:53:15 -07:00
|
|
|
default_signer: &DefaultSigner,
|
2020-04-18 11:54:21 -07:00
|
|
|
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
|
2019-12-14 04:38:24 -08:00
|
|
|
) -> Result<CliCommandInfo, CliError> {
|
2020-03-08 23:02:24 -07:00
|
|
|
let from_pubkey = pubkey_of_signer(matches, "from", wallet_manager)?;
|
|
|
|
let signers = if from_pubkey.is_some() {
|
|
|
|
vec![]
|
|
|
|
} else {
|
2020-09-21 20:53:15 -07:00
|
|
|
vec![default_signer.signer_from_path(matches, wallet_manager)?]
|
2020-03-08 23:02:24 -07:00
|
|
|
};
|
2019-12-14 04:38:24 -08:00
|
|
|
|
|
|
|
let program_id = match matches.value_of("program_id").unwrap() {
|
2020-03-19 09:36:53 -07:00
|
|
|
"NONCE" => system_program::id(),
|
2019-12-14 04:38:24 -08:00
|
|
|
"STAKE" => solana_stake_program::id(),
|
2020-03-19 09:36:53 -07:00
|
|
|
"VOTE" => solana_vote_program::id(),
|
2019-12-14 04:38:24 -08:00
|
|
|
_ => pubkey_of(matches, "program_id").unwrap(),
|
|
|
|
};
|
|
|
|
|
|
|
|
let seed = matches.value_of("seed").unwrap().to_string();
|
|
|
|
|
2020-03-20 15:20:48 -07:00
|
|
|
if seed.len() > MAX_SEED_LEN {
|
2019-12-14 04:38:24 -08:00
|
|
|
return Err(CliError::BadParameter(
|
|
|
|
"Address seed must not be longer than 32 bytes".to_string(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(CliCommandInfo {
|
|
|
|
command: CliCommand::CreateAddressWithSeed {
|
|
|
|
from_pubkey,
|
|
|
|
seed,
|
|
|
|
program_id,
|
|
|
|
},
|
2020-03-08 23:02:24 -07:00
|
|
|
signers,
|
2019-12-14 04:38:24 -08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_create_address_with_seed(
|
|
|
|
config: &CliConfig,
|
|
|
|
from_pubkey: Option<&Pubkey>,
|
|
|
|
seed: &str,
|
|
|
|
program_id: &Pubkey,
|
|
|
|
) -> ProcessResult {
|
2020-03-08 23:02:24 -07:00
|
|
|
let from_pubkey = if let Some(pubkey) = from_pubkey {
|
|
|
|
*pubkey
|
|
|
|
} else {
|
|
|
|
config.pubkey()?
|
|
|
|
};
|
2020-03-20 15:20:48 -07:00
|
|
|
let address = Pubkey::create_with_seed(&from_pubkey, seed, program_id)?;
|
2019-12-14 04:38:24 -08:00
|
|
|
Ok(address.to_string())
|
|
|
|
}
|
|
|
|
|
2019-02-09 13:15:44 -08:00
|
|
|
fn process_airdrop(
|
|
|
|
rpc_client: &RpcClient,
|
2019-10-04 15:13:21 -07:00
|
|
|
config: &CliConfig,
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_addr: &SocketAddr,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey: &Option<Pubkey>,
|
2019-03-05 17:22:46 -08:00
|
|
|
lamports: u64,
|
2019-02-09 13:15:44 -08:00
|
|
|
) -> ProcessResult {
|
2020-02-24 16:03:30 -08:00
|
|
|
let pubkey = if let Some(pubkey) = pubkey {
|
|
|
|
*pubkey
|
|
|
|
} else {
|
|
|
|
config.pubkey()?
|
|
|
|
};
|
2019-02-09 13:15:44 -08:00
|
|
|
println!(
|
2019-09-17 22:59:35 -07:00
|
|
|
"Requesting airdrop of {} from {}",
|
2020-02-15 11:53:52 -08:00
|
|
|
build_balance_message(lamports, false, true),
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_addr
|
2019-02-09 13:15:44 -08:00
|
|
|
);
|
|
|
|
|
2020-05-06 21:21:48 -07:00
|
|
|
request_and_confirm_airdrop(&rpc_client, faucet_addr, &pubkey, lamports, &config)?;
|
2019-02-09 13:15:44 -08:00
|
|
|
|
2020-05-14 11:24:14 -07:00
|
|
|
let current_balance = rpc_client.get_balance(&pubkey)?;
|
2019-02-09 13:15:44 -08:00
|
|
|
|
2020-02-15 11:53:52 -08:00
|
|
|
Ok(build_balance_message(current_balance, false, true))
|
2019-02-09 13:15:44 -08:00
|
|
|
}
|
|
|
|
|
2019-09-10 12:36:59 -07:00
|
|
|
fn process_balance(
|
|
|
|
rpc_client: &RpcClient,
|
2019-10-21 16:08:09 -07:00
|
|
|
config: &CliConfig,
|
|
|
|
pubkey: &Option<Pubkey>,
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: bool,
|
|
|
|
) -> ProcessResult {
|
2020-02-24 16:03:30 -08:00
|
|
|
let pubkey = if let Some(pubkey) = pubkey {
|
|
|
|
*pubkey
|
|
|
|
} else {
|
|
|
|
config.pubkey()?
|
|
|
|
};
|
2020-05-11 16:07:40 -07:00
|
|
|
let balance = rpc_client
|
2020-06-17 11:18:48 -07:00
|
|
|
.get_balance_with_commitment(&pubkey, config.commitment)?
|
2020-05-11 16:07:40 -07:00
|
|
|
.value;
|
|
|
|
Ok(build_balance_message(balance, use_lamports_unit, true))
|
2019-02-09 13:15:44 -08:00
|
|
|
}
|
|
|
|
|
2020-04-15 20:51:05 -07:00
|
|
|
fn process_confirm(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
config: &CliConfig,
|
|
|
|
signature: &Signature,
|
|
|
|
) -> ProcessResult {
|
|
|
|
match rpc_client.get_signature_status_with_commitment_and_history(
|
|
|
|
&signature,
|
|
|
|
CommitmentConfig::max(),
|
|
|
|
true,
|
|
|
|
) {
|
2019-03-17 00:40:45 -07:00
|
|
|
Ok(status) => {
|
2020-04-16 20:02:02 -07:00
|
|
|
if let Some(transaction_status) = status {
|
|
|
|
if config.verbose {
|
2020-04-20 10:00:20 -07:00
|
|
|
match rpc_client
|
2020-08-15 22:06:39 -07:00
|
|
|
.get_confirmed_transaction(signature, UiTransactionEncoding::Base64)
|
2020-04-20 10:00:20 -07:00
|
|
|
{
|
2020-04-16 20:02:02 -07:00
|
|
|
Ok(confirmed_transaction) => {
|
|
|
|
println!(
|
|
|
|
"\nTransaction executed in slot {}:",
|
|
|
|
confirmed_transaction.slot
|
|
|
|
);
|
2020-05-31 23:00:51 -07:00
|
|
|
println_transaction(
|
2020-04-16 20:02:02 -07:00
|
|
|
&confirmed_transaction
|
|
|
|
.transaction
|
|
|
|
.transaction
|
|
|
|
.decode()
|
|
|
|
.expect("Successful decode"),
|
|
|
|
&confirmed_transaction.transaction.meta,
|
|
|
|
" ",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
println!("Unable to get confirmed transaction details: {}", err)
|
2020-04-15 20:51:05 -07:00
|
|
|
}
|
|
|
|
}
|
2020-04-16 20:02:02 -07:00
|
|
|
println!();
|
|
|
|
}
|
|
|
|
|
|
|
|
match transaction_status {
|
|
|
|
Ok(_) => Ok("Confirmed".to_string()),
|
|
|
|
Err(err) => Ok(format!("Transaction failed: {}", err)),
|
2019-04-05 19:56:17 -07:00
|
|
|
}
|
2019-02-09 13:15:44 -08:00
|
|
|
} else {
|
|
|
|
Ok("Not found".to_string())
|
|
|
|
}
|
|
|
|
}
|
2020-03-12 23:20:49 -07:00
|
|
|
Err(err) => Err(CliError::RpcRequestError(format!("Unable to confirm: {}", err)).into()),
|
2019-02-09 13:15:44 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-13 17:26:34 -08:00
|
|
|
#[allow(clippy::unnecessary_wraps)]
|
2020-04-20 10:00:20 -07:00
|
|
|
fn process_decode_transaction(transaction: &Transaction) -> ProcessResult {
|
2020-05-31 23:00:51 -07:00
|
|
|
println_transaction(transaction, &None, "");
|
2020-04-20 10:00:20 -07:00
|
|
|
Ok("".to_string())
|
|
|
|
}
|
|
|
|
|
2019-08-09 22:48:57 -07:00
|
|
|
fn process_show_account(
|
|
|
|
rpc_client: &RpcClient,
|
2020-04-14 12:10:25 -07:00
|
|
|
config: &CliConfig,
|
2019-08-09 22:48:57 -07:00
|
|
|
account_pubkey: &Pubkey,
|
|
|
|
output_file: &Option<String>,
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: bool,
|
2019-08-09 22:48:57 -07:00
|
|
|
) -> ProcessResult {
|
|
|
|
let account = rpc_client.get_account(account_pubkey)?;
|
2020-04-14 12:10:25 -07:00
|
|
|
let data = account.data.clone();
|
|
|
|
let cli_account = CliAccount {
|
|
|
|
keyed_account: RpcKeyedAccount {
|
|
|
|
pubkey: account_pubkey.to_string(),
|
2020-08-10 15:35:29 -07:00
|
|
|
account: UiAccount::encode(
|
|
|
|
account_pubkey,
|
|
|
|
account,
|
2020-08-15 22:06:39 -07:00
|
|
|
UiAccountEncoding::Base64,
|
2020-08-10 15:35:29 -07:00
|
|
|
None,
|
|
|
|
None,
|
|
|
|
),
|
2020-04-14 12:10:25 -07:00
|
|
|
},
|
|
|
|
use_lamports_unit,
|
|
|
|
};
|
2019-08-09 22:48:57 -07:00
|
|
|
|
2020-05-06 19:27:15 -07:00
|
|
|
let mut account_string = config.output_format.formatted_string(&cli_account);
|
2020-04-14 12:10:25 -07:00
|
|
|
|
|
|
|
if config.output_format == OutputFormat::Display {
|
|
|
|
if let Some(output_file) = output_file {
|
|
|
|
let mut f = File::create(output_file)?;
|
|
|
|
f.write_all(&data)?;
|
2020-05-06 19:27:15 -07:00
|
|
|
writeln!(&mut account_string)?;
|
|
|
|
writeln!(&mut account_string, "Wrote account data to {}", output_file)?;
|
2020-04-14 12:10:25 -07:00
|
|
|
} else if !data.is_empty() {
|
|
|
|
use pretty_hex::*;
|
2020-05-06 19:27:15 -07:00
|
|
|
writeln!(&mut account_string, "{:?}", data.hex_dump())?;
|
2020-04-14 12:10:25 -07:00
|
|
|
}
|
2019-08-09 22:48:57 -07:00
|
|
|
}
|
|
|
|
|
2020-05-06 19:27:15 -07:00
|
|
|
Ok(account_string)
|
2019-08-09 22:48:57 -07:00
|
|
|
}
|
|
|
|
|
2020-02-07 11:16:35 -08:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn process_transfer(
|
|
|
|
rpc_client: &RpcClient,
|
|
|
|
config: &CliConfig,
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount,
|
2020-02-07 11:16:35 -08:00
|
|
|
to: &Pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: SignerIndex,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: bool,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: bool,
|
2020-02-07 11:16:35 -08:00
|
|
|
blockhash_query: &BlockhashQuery,
|
|
|
|
nonce_account: Option<&Pubkey>,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: SignerIndex,
|
|
|
|
fee_payer: SignerIndex,
|
2020-02-07 11:16:35 -08:00
|
|
|
) -> ProcessResult {
|
2020-02-24 16:03:30 -08:00
|
|
|
let from = config.signers[from];
|
2020-02-07 11:16:35 -08:00
|
|
|
|
|
|
|
let (recent_blockhash, fee_calculator) =
|
2020-06-17 11:18:48 -07:00
|
|
|
blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?;
|
2020-02-07 11:16:35 -08:00
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
let nonce_authority = config.signers[nonce_authority];
|
|
|
|
let fee_payer = config.signers[fee_payer];
|
|
|
|
|
2020-05-14 11:24:14 -07:00
|
|
|
let build_message = |lamports| {
|
|
|
|
let ixs = vec![system_instruction::transfer(&from.pubkey(), to, lamports)];
|
|
|
|
|
|
|
|
if let Some(nonce_account) = &nonce_account {
|
|
|
|
Message::new_with_nonce(
|
|
|
|
ixs,
|
|
|
|
Some(&fee_payer.pubkey()),
|
|
|
|
nonce_account,
|
|
|
|
&nonce_authority.pubkey(),
|
|
|
|
)
|
|
|
|
} else {
|
2020-06-24 13:52:38 -07:00
|
|
|
Message::new(&ixs, Some(&fee_payer.pubkey()))
|
2020-05-14 11:24:14 -07:00
|
|
|
}
|
2020-02-07 11:16:35 -08:00
|
|
|
};
|
2020-05-14 11:24:14 -07:00
|
|
|
|
|
|
|
let (message, _) = resolve_spend_tx_and_check_account_balances(
|
|
|
|
rpc_client,
|
|
|
|
sign_only,
|
|
|
|
amount,
|
|
|
|
&fee_calculator,
|
|
|
|
&from.pubkey(),
|
|
|
|
&fee_payer.pubkey(),
|
|
|
|
build_message,
|
2020-06-17 11:18:48 -07:00
|
|
|
config.commitment,
|
2020-05-14 11:24:14 -07:00
|
|
|
)?;
|
2020-02-24 16:03:30 -08:00
|
|
|
let mut tx = Transaction::new_unsigned(message);
|
2020-02-07 11:16:35 -08:00
|
|
|
|
|
|
|
if sign_only {
|
2020-03-18 20:49:38 -07:00
|
|
|
tx.try_partial_sign(&config.signers, recent_blockhash)?;
|
2020-09-21 18:07:52 -07:00
|
|
|
return_signers(&tx, &config.output_format)
|
2020-02-07 11:16:35 -08:00
|
|
|
} else {
|
2020-05-14 11:24:14 -07:00
|
|
|
if let Some(nonce_account) = &nonce_account {
|
2020-09-21 12:55:44 -07:00
|
|
|
let nonce_account = nonce_utils::get_account_with_commitment(
|
|
|
|
rpc_client,
|
|
|
|
nonce_account,
|
|
|
|
config.commitment,
|
|
|
|
)?;
|
2020-05-14 11:24:14 -07:00
|
|
|
check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &recent_blockhash)?;
|
|
|
|
}
|
|
|
|
|
2020-03-18 20:49:38 -07:00
|
|
|
tx.try_sign(&config.signers, recent_blockhash)?;
|
2020-04-08 22:46:19 -07:00
|
|
|
let result = if no_wait {
|
|
|
|
rpc_client.send_transaction(&tx)
|
|
|
|
} else {
|
2020-06-17 11:18:48 -07:00
|
|
|
rpc_client.send_and_confirm_transaction_with_spinner_and_config(
|
|
|
|
&tx,
|
|
|
|
config.commitment,
|
|
|
|
config.send_transaction_config,
|
|
|
|
)
|
2020-04-08 22:46:19 -07:00
|
|
|
};
|
2020-05-06 21:21:48 -07:00
|
|
|
log_instruction_custom_error::<SystemError>(result, &config)
|
2020-02-07 11:16:35 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-04 15:13:21 -07:00
|
|
|
pub fn process_command(config: &CliConfig) -> ProcessResult {
|
2020-04-14 12:10:25 -07:00
|
|
|
if config.verbose && config.output_format == OutputFormat::Display {
|
2020-02-05 10:14:44 -08:00
|
|
|
println_name_value("RPC URL:", &config.json_rpc_url);
|
2020-02-24 16:03:30 -08:00
|
|
|
println_name_value("Default Signer Path:", &config.keypair_path);
|
|
|
|
if config.keypair_path.starts_with("usb://") {
|
|
|
|
println_name_value("Pubkey:", &format!("{:?}", config.pubkey()?));
|
2019-11-24 16:34:18 -08:00
|
|
|
}
|
2018-11-09 14:42:11 -08:00
|
|
|
}
|
|
|
|
|
2019-03-16 21:51:41 -07:00
|
|
|
let mut _rpc_client;
|
2019-01-13 23:10:03 -08:00
|
|
|
let rpc_client = if config.rpc_client.is_none() {
|
2020-07-23 08:21:59 -07:00
|
|
|
_rpc_client =
|
|
|
|
RpcClient::new_with_timeout(config.json_rpc_url.to_string(), config.rpc_timeout);
|
2019-03-16 21:51:41 -07:00
|
|
|
&_rpc_client
|
2019-01-13 23:10:03 -08:00
|
|
|
} else {
|
|
|
|
// Primarily for testing
|
2019-03-16 21:51:41 -07:00
|
|
|
config.rpc_client.as_ref().unwrap()
|
2019-01-13 23:10:03 -08:00
|
|
|
};
|
2018-11-09 14:36:08 -08:00
|
|
|
|
2019-05-09 19:31:42 -07:00
|
|
|
match &config.command {
|
2019-10-04 19:54:09 -07:00
|
|
|
// Cluster Query Commands
|
2019-12-19 14:37:47 -08:00
|
|
|
// Get address of this client
|
2020-02-07 10:26:56 -08:00
|
|
|
CliCommand::Address => Ok(format!("{}", config.pubkey()?)),
|
2018-11-14 18:57:34 -08:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Return software version of solana-cli and cluster entrypoint node
|
2020-03-04 11:44:13 -08:00
|
|
|
CliCommand::Catchup {
|
|
|
|
node_pubkey,
|
|
|
|
node_json_rpc_url,
|
2020-04-02 11:05:29 -07:00
|
|
|
follow,
|
2020-06-17 11:18:48 -07:00
|
|
|
} => process_catchup(&rpc_client, config, node_pubkey, node_json_rpc_url, *follow),
|
2020-05-05 08:42:03 -07:00
|
|
|
CliCommand::ClusterDate => process_cluster_date(&rpc_client, config),
|
2020-09-21 20:20:51 -07:00
|
|
|
CliCommand::ClusterVersion => process_cluster_version(&rpc_client, config),
|
2019-12-14 04:38:24 -08:00
|
|
|
CliCommand::CreateAddressWithSeed {
|
|
|
|
from_pubkey,
|
|
|
|
seed,
|
|
|
|
program_id,
|
|
|
|
} => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id),
|
2020-05-26 14:08:07 -07:00
|
|
|
CliCommand::Fees => process_fees(&rpc_client, config),
|
2020-09-23 13:36:34 -07:00
|
|
|
CliCommand::Feature(feature_subcommand) => {
|
|
|
|
process_feature_subcommand(&rpc_client, config, feature_subcommand)
|
|
|
|
}
|
2020-09-06 10:54:01 -07:00
|
|
|
CliCommand::FirstAvailableBlock => process_first_available_block(&rpc_client),
|
2020-09-06 11:10:19 -07:00
|
|
|
CliCommand::GetBlock { slot } => process_get_block(&rpc_client, config, *slot),
|
2020-05-05 08:42:03 -07:00
|
|
|
CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, config, *slot),
|
2020-06-17 11:18:48 -07:00
|
|
|
CliCommand::GetEpoch => process_get_epoch(&rpc_client, config),
|
|
|
|
CliCommand::GetEpochInfo => process_get_epoch_info(&rpc_client, config),
|
2019-11-08 20:56:57 -08:00
|
|
|
CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client),
|
2020-06-17 11:18:48 -07:00
|
|
|
CliCommand::GetSlot => process_get_slot(&rpc_client, config),
|
2020-07-22 08:36:12 -07:00
|
|
|
CliCommand::GetBlockHeight => process_get_block_height(&rpc_client, config),
|
2020-06-17 11:18:48 -07:00
|
|
|
CliCommand::LargestAccounts { filter } => {
|
|
|
|
process_largest_accounts(&rpc_client, config, filter.clone())
|
2019-11-24 16:34:18 -08:00
|
|
|
}
|
2020-06-17 11:18:48 -07:00
|
|
|
CliCommand::GetTransactionCount => process_get_transaction_count(&rpc_client, config),
|
2020-09-30 20:17:29 -07:00
|
|
|
CliCommand::Inflation(inflation_subcommand) => {
|
|
|
|
process_inflation_subcommand(&rpc_client, config, inflation_subcommand)
|
|
|
|
}
|
2020-12-28 10:38:15 -08:00
|
|
|
CliCommand::LeaderSchedule { epoch } => process_leader_schedule(&rpc_client, *epoch),
|
2020-11-20 13:52:58 -08:00
|
|
|
CliCommand::LiveSlots => process_live_slots(&config),
|
|
|
|
CliCommand::Logs { filter } => process_logs(&config, filter),
|
2019-10-04 19:54:09 -07:00
|
|
|
CliCommand::Ping {
|
2019-11-18 21:50:09 -08:00
|
|
|
lamports,
|
2019-10-04 19:54:09 -07:00
|
|
|
interval,
|
|
|
|
count,
|
|
|
|
timeout,
|
2020-12-07 04:10:50 -08:00
|
|
|
blockhash,
|
|
|
|
print_timestamp,
|
|
|
|
} => process_ping(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
*lamports,
|
|
|
|
interval,
|
|
|
|
count,
|
|
|
|
timeout,
|
|
|
|
blockhash,
|
|
|
|
*print_timestamp,
|
|
|
|
),
|
2019-12-18 10:38:54 -08:00
|
|
|
CliCommand::ShowBlockProduction { epoch, slot_limit } => {
|
2019-12-20 08:38:08 -08:00
|
|
|
process_show_block_production(&rpc_client, config, *epoch, *slot_limit)
|
2019-12-18 10:38:54 -08:00
|
|
|
}
|
2020-06-17 23:09:33 -07:00
|
|
|
CliCommand::ShowGossip => process_show_gossip(&rpc_client, config),
|
2020-01-17 11:10:52 -08:00
|
|
|
CliCommand::ShowStakes {
|
|
|
|
use_lamports_unit,
|
|
|
|
vote_account_pubkeys,
|
|
|
|
} => process_show_stakes(
|
|
|
|
&rpc_client,
|
2020-04-14 12:10:25 -07:00
|
|
|
config,
|
2020-01-17 11:10:52 -08:00
|
|
|
*use_lamports_unit,
|
|
|
|
vote_account_pubkeys.as_deref(),
|
|
|
|
),
|
2020-11-12 13:48:34 -08:00
|
|
|
CliCommand::WaitForMaxStake { max_stake_percent } => {
|
|
|
|
process_wait_for_max_stake(&rpc_client, config, *max_stake_percent)
|
|
|
|
}
|
2020-06-17 11:18:48 -07:00
|
|
|
CliCommand::ShowValidators { use_lamports_unit } => {
|
|
|
|
process_show_validators(&rpc_client, config, *use_lamports_unit)
|
|
|
|
}
|
|
|
|
CliCommand::Supply { print_accounts } => {
|
|
|
|
process_supply(&rpc_client, config, *print_accounts)
|
|
|
|
}
|
|
|
|
CliCommand::TotalSupply => process_total_supply(&rpc_client, config),
|
2020-04-20 22:01:09 -07:00
|
|
|
CliCommand::TransactionHistory {
|
|
|
|
address,
|
2020-08-05 11:30:21 -07:00
|
|
|
before,
|
2020-08-15 09:42:17 -07:00
|
|
|
until,
|
2020-07-28 20:46:23 -07:00
|
|
|
limit,
|
2020-09-04 22:26:02 -07:00
|
|
|
show_transactions,
|
|
|
|
} => process_transaction_history(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
address,
|
|
|
|
*before,
|
|
|
|
*until,
|
|
|
|
*limit,
|
|
|
|
*show_transactions,
|
|
|
|
),
|
2019-06-07 13:11:56 -07:00
|
|
|
|
2019-12-10 00:24:44 -08:00
|
|
|
// Nonce Commands
|
|
|
|
|
2019-12-19 16:13:01 -08:00
|
|
|
// Assign authority to nonce account
|
|
|
|
CliCommand::AuthorizeNonceAccount {
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
2019-12-19 16:13:01 -08:00
|
|
|
new_authority,
|
|
|
|
} => process_authorize_nonce_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
2019-12-19 16:13:01 -08:00
|
|
|
new_authority,
|
|
|
|
),
|
2019-12-10 00:24:44 -08:00
|
|
|
// Create nonce account
|
|
|
|
CliCommand::CreateNonceAccount {
|
|
|
|
nonce_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed,
|
2019-12-17 06:34:21 -08:00
|
|
|
nonce_authority,
|
2020-05-14 11:24:14 -07:00
|
|
|
amount,
|
2019-12-17 06:34:21 -08:00
|
|
|
} => process_create_nonce_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed.clone(),
|
2020-01-02 17:05:08 -08:00
|
|
|
*nonce_authority,
|
2020-05-14 11:24:14 -07:00
|
|
|
*amount,
|
2019-12-17 06:34:21 -08:00
|
|
|
),
|
2019-12-10 00:24:44 -08:00
|
|
|
// Get the current nonce
|
|
|
|
CliCommand::GetNonce(nonce_account_pubkey) => {
|
2020-06-17 11:18:48 -07:00
|
|
|
process_get_nonce(&rpc_client, config, &nonce_account_pubkey)
|
2019-12-10 00:24:44 -08:00
|
|
|
}
|
|
|
|
// Get a new nonce
|
2019-12-17 06:34:21 -08:00
|
|
|
CliCommand::NewNonce {
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
} => process_new_nonce(&rpc_client, config, nonce_account, *nonce_authority),
|
2019-12-10 00:24:44 -08:00
|
|
|
// Show the contents of a nonce account
|
|
|
|
CliCommand::ShowNonceAccount {
|
|
|
|
nonce_account_pubkey,
|
|
|
|
use_lamports_unit,
|
2020-04-14 12:10:25 -07:00
|
|
|
} => process_show_nonce_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&nonce_account_pubkey,
|
|
|
|
*use_lamports_unit,
|
|
|
|
),
|
2019-12-10 00:24:44 -08:00
|
|
|
// Withdraw lamports from a nonce account
|
|
|
|
CliCommand::WithdrawFromNonceAccount {
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
2019-12-10 00:24:44 -08:00
|
|
|
destination_account_pubkey,
|
|
|
|
lamports,
|
|
|
|
} => process_withdraw_from_nonce_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
2019-12-10 00:24:44 -08:00
|
|
|
&destination_account_pubkey,
|
|
|
|
*lamports,
|
|
|
|
),
|
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Program Deployment
|
2019-08-29 20:45:53 -07:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Deploy a custom program to the chain
|
2020-12-21 13:02:53 -08:00
|
|
|
CliCommand::Deploy {
|
2020-06-04 19:14:12 -07:00
|
|
|
program_location,
|
2020-12-21 13:02:53 -08:00
|
|
|
address,
|
2020-08-17 18:06:22 -07:00
|
|
|
use_deprecated_loader,
|
2020-10-22 21:42:35 -07:00
|
|
|
allow_excessive_balance,
|
2020-12-21 13:02:53 -08:00
|
|
|
} => process_deploy(
|
2020-08-17 18:06:22 -07:00
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
program_location,
|
2020-12-21 13:02:53 -08:00
|
|
|
*address,
|
2020-08-17 18:06:22 -07:00
|
|
|
*use_deprecated_loader,
|
2020-10-22 21:42:35 -07:00
|
|
|
*allow_excessive_balance,
|
2020-08-17 18:06:22 -07:00
|
|
|
),
|
2020-12-21 13:02:53 -08:00
|
|
|
CliCommand::Program(program_subcommand) => {
|
|
|
|
process_program_subcommand(&rpc_client, config, program_subcommand)
|
|
|
|
}
|
2020-12-14 15:35:10 -08:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Stake Commands
|
2019-07-26 09:34:12 -07:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Create stake account
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommand::CreateStakeAccount {
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed,
|
2019-10-21 16:08:09 -07:00
|
|
|
staker,
|
|
|
|
withdrawer,
|
|
|
|
lockup,
|
2020-05-14 11:24:14 -07:00
|
|
|
amount,
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
ref nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
|
|
|
from,
|
2019-10-21 16:08:09 -07:00
|
|
|
} => process_create_stake_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
2020-02-24 16:03:30 -08:00
|
|
|
*stake_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed,
|
2019-10-21 16:08:09 -07:00
|
|
|
staker,
|
|
|
|
withdrawer,
|
|
|
|
lockup,
|
2020-05-14 11:24:14 -07:00
|
|
|
*amount,
|
2020-02-12 22:00:28 -08:00
|
|
|
*sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
nonce_account.as_ref(),
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
|
|
|
*from,
|
2019-10-21 16:08:09 -07:00
|
|
|
),
|
2019-11-25 21:09:57 -08:00
|
|
|
CliCommand::DeactivateStake {
|
|
|
|
stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority,
|
2019-11-25 21:09:57 -08:00
|
|
|
sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2019-12-27 12:35:49 -08:00
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2019-11-25 21:09:57 -08:00
|
|
|
} => process_deactivate_stake_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
*stake_authority,
|
2019-11-25 21:09:57 -08:00
|
|
|
*sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2019-12-27 12:35:49 -08:00
|
|
|
*nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2019-11-25 21:09:57 -08:00
|
|
|
),
|
|
|
|
CliCommand::DelegateStake {
|
|
|
|
stake_account_pubkey,
|
|
|
|
vote_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority,
|
2019-11-25 21:09:57 -08:00
|
|
|
force,
|
|
|
|
sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2019-12-27 12:35:49 -08:00
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2019-11-25 21:09:57 -08:00
|
|
|
} => process_delegate_stake(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
|
|
|
&vote_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
*stake_authority,
|
2019-11-25 21:09:57 -08:00
|
|
|
*force,
|
|
|
|
*sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2019-12-27 12:35:49 -08:00
|
|
|
*nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2019-11-25 21:09:57 -08:00
|
|
|
),
|
2020-02-02 21:20:28 -08:00
|
|
|
CliCommand::SplitStake {
|
|
|
|
stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority,
|
2020-02-02 21:20:28 -08:00
|
|
|
sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
2020-02-02 21:20:28 -08:00
|
|
|
split_stake_account,
|
|
|
|
seed,
|
|
|
|
lamports,
|
2020-02-24 16:03:30 -08:00
|
|
|
fee_payer,
|
2020-02-02 21:20:28 -08:00
|
|
|
} => process_split_stake(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
*stake_authority,
|
2020-02-02 21:20:28 -08:00
|
|
|
*sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
*nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*split_stake_account,
|
2020-02-02 21:20:28 -08:00
|
|
|
seed,
|
|
|
|
*lamports,
|
2020-02-24 16:03:30 -08:00
|
|
|
*fee_payer,
|
2020-02-02 21:20:28 -08:00
|
|
|
),
|
2020-06-10 21:02:04 -07:00
|
|
|
CliCommand::MergeStake {
|
|
|
|
stake_account_pubkey,
|
|
|
|
source_stake_account_pubkey,
|
|
|
|
stake_authority,
|
|
|
|
sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
nonce_account,
|
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
|
|
|
} => process_merge_stake(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
|
|
|
&source_stake_account_pubkey,
|
|
|
|
*stake_authority,
|
|
|
|
*sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
*nonce_account,
|
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
CliCommand::ShowStakeAccount {
|
|
|
|
pubkey: stake_account_pubkey,
|
|
|
|
use_lamports_unit,
|
|
|
|
} => process_show_stake_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
|
|
|
*use_lamports_unit,
|
|
|
|
),
|
2019-10-25 09:20:08 -07:00
|
|
|
CliCommand::ShowStakeHistory { use_lamports_unit } => {
|
|
|
|
process_show_stake_history(&rpc_client, config, *use_lamports_unit)
|
|
|
|
}
|
2020-01-15 13:32:06 -08:00
|
|
|
CliCommand::StakeAuthorize {
|
2019-09-29 21:18:15 -07:00
|
|
|
stake_account_pubkey,
|
2020-03-21 18:56:17 -07:00
|
|
|
ref new_authorizations,
|
2020-01-17 09:30:56 -08:00
|
|
|
sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2020-01-17 09:30:56 -08:00
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2020-01-15 13:32:06 -08:00
|
|
|
} => process_stake_authorize(
|
2019-08-01 21:08:24 -07:00
|
|
|
&rpc_client,
|
|
|
|
config,
|
2019-09-29 21:18:15 -07:00
|
|
|
&stake_account_pubkey,
|
2020-03-21 18:56:17 -07:00
|
|
|
new_authorizations,
|
2020-01-17 09:30:56 -08:00
|
|
|
*sign_only,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query,
|
2020-01-17 09:30:56 -08:00
|
|
|
*nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2019-08-01 21:08:24 -07:00
|
|
|
),
|
2020-02-12 15:36:29 -08:00
|
|
|
CliCommand::StakeSetLockup {
|
|
|
|
stake_account_pubkey,
|
|
|
|
mut lockup,
|
2020-02-24 16:03:30 -08:00
|
|
|
custodian,
|
2020-02-12 15:36:29 -08:00
|
|
|
sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2020-02-12 15:36:29 -08:00
|
|
|
} => process_stake_set_lockup(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
|
|
|
&mut lockup,
|
2020-02-24 16:03:30 -08:00
|
|
|
*custodian,
|
2020-02-12 15:36:29 -08:00
|
|
|
*sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
*nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2020-02-12 15:36:29 -08:00
|
|
|
),
|
2020-01-15 13:32:06 -08:00
|
|
|
CliCommand::WithdrawStake {
|
|
|
|
stake_account_pubkey,
|
|
|
|
destination_account_pubkey,
|
|
|
|
lamports,
|
2020-02-24 16:03:30 -08:00
|
|
|
withdraw_authority,
|
2020-04-22 15:00:18 -07:00
|
|
|
custodian,
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
ref nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2020-01-15 13:32:06 -08:00
|
|
|
} => process_withdraw_stake(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&stake_account_pubkey,
|
|
|
|
&destination_account_pubkey,
|
|
|
|
*lamports,
|
2020-02-24 16:03:30 -08:00
|
|
|
*withdraw_authority,
|
2020-04-22 15:00:18 -07:00
|
|
|
*custodian,
|
2020-02-12 22:00:28 -08:00
|
|
|
*sign_only,
|
|
|
|
blockhash_query,
|
|
|
|
nonce_account.as_ref(),
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2020-01-15 13:32:06 -08:00
|
|
|
),
|
2019-06-21 22:28:34 -07:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Validator Info Commands
|
|
|
|
|
|
|
|
// Return all or single validator info
|
|
|
|
CliCommand::GetValidatorInfo(info_pubkey) => {
|
2020-04-14 12:10:25 -07:00
|
|
|
process_get_validator_info(&rpc_client, config, *info_pubkey)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
// Publish validator info
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommand::SetValidatorInfo {
|
|
|
|
validator_info,
|
|
|
|
force_keybase,
|
|
|
|
info_pubkey,
|
|
|
|
} => process_set_validator_info(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&validator_info,
|
|
|
|
*force_keybase,
|
|
|
|
*info_pubkey,
|
|
|
|
),
|
2018-10-22 21:21:33 -07:00
|
|
|
|
2019-10-04 19:54:09 -07:00
|
|
|
// Vote Commands
|
|
|
|
|
|
|
|
// Create vote account
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommand::CreateVoteAccount {
|
2020-07-13 20:27:41 -07:00
|
|
|
vote_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed,
|
2020-03-19 01:58:52 -07:00
|
|
|
identity_account,
|
2019-10-21 16:08:09 -07:00
|
|
|
authorized_voter,
|
|
|
|
authorized_withdrawer,
|
|
|
|
commission,
|
|
|
|
} => process_create_vote_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
2020-07-13 20:27:41 -07:00
|
|
|
*vote_account,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed,
|
2020-03-19 01:58:52 -07:00
|
|
|
*identity_account,
|
2019-10-21 16:08:09 -07:00
|
|
|
authorized_voter,
|
|
|
|
authorized_withdrawer,
|
|
|
|
*commission,
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
CliCommand::ShowVoteAccount {
|
|
|
|
pubkey: vote_account_pubkey,
|
|
|
|
use_lamports_unit,
|
|
|
|
} => process_show_vote_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&vote_account_pubkey,
|
|
|
|
*use_lamports_unit,
|
|
|
|
),
|
2020-03-13 13:30:04 -07:00
|
|
|
CliCommand::WithdrawFromVoteAccount {
|
|
|
|
vote_account_pubkey,
|
|
|
|
withdraw_authority,
|
2020-06-15 14:36:47 -07:00
|
|
|
withdraw_amount,
|
2020-03-13 13:30:04 -07:00
|
|
|
destination_account_pubkey,
|
|
|
|
} => process_withdraw_from_vote_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
vote_account_pubkey,
|
|
|
|
*withdraw_authority,
|
2020-06-15 14:36:47 -07:00
|
|
|
*withdraw_amount,
|
2020-03-13 13:30:04 -07:00
|
|
|
destination_account_pubkey,
|
|
|
|
),
|
2019-12-12 15:04:03 -08:00
|
|
|
CliCommand::VoteAuthorize {
|
|
|
|
vote_account_pubkey,
|
|
|
|
new_authorized_pubkey,
|
|
|
|
vote_authorize,
|
|
|
|
} => process_vote_authorize(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&vote_account_pubkey,
|
|
|
|
&new_authorized_pubkey,
|
|
|
|
*vote_authorize,
|
|
|
|
),
|
|
|
|
CliCommand::VoteUpdateValidator {
|
|
|
|
vote_account_pubkey,
|
2020-03-19 01:58:52 -07:00
|
|
|
new_identity_account,
|
2020-07-13 20:49:59 -07:00
|
|
|
withdraw_authority,
|
2019-12-12 15:04:03 -08:00
|
|
|
} => process_vote_update_validator(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&vote_account_pubkey,
|
2020-03-19 01:58:52 -07:00
|
|
|
*new_identity_account,
|
2020-07-13 20:49:59 -07:00
|
|
|
*withdraw_authority,
|
2019-12-12 15:04:03 -08:00
|
|
|
),
|
2020-06-09 21:15:46 -07:00
|
|
|
CliCommand::VoteUpdateCommission {
|
|
|
|
vote_account_pubkey,
|
|
|
|
commission,
|
2020-07-13 19:30:49 -07:00
|
|
|
withdraw_authority,
|
|
|
|
} => process_vote_update_commission(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&vote_account_pubkey,
|
|
|
|
*commission,
|
|
|
|
*withdraw_authority,
|
|
|
|
),
|
2019-10-04 19:54:09 -07:00
|
|
|
|
|
|
|
// Wallet Commands
|
|
|
|
|
2019-12-16 13:05:17 -08:00
|
|
|
// Request an airdrop from Solana Faucet;
|
2019-10-04 19:54:09 -07:00
|
|
|
CliCommand::Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host,
|
|
|
|
faucet_port,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey,
|
2019-10-04 19:54:09 -07:00
|
|
|
lamports,
|
|
|
|
} => {
|
2019-12-16 13:05:17 -08:00
|
|
|
let faucet_addr = SocketAddr::new(
|
|
|
|
faucet_host.unwrap_or_else(|| {
|
2020-03-09 12:29:31 -07:00
|
|
|
let faucet_host = Url::parse(&config.json_rpc_url)
|
2019-10-04 19:54:09 -07:00
|
|
|
.unwrap()
|
|
|
|
.host()
|
|
|
|
.unwrap()
|
|
|
|
.to_string();
|
2019-12-16 13:05:17 -08:00
|
|
|
solana_net_utils::parse_host(&faucet_host).unwrap_or_else(|err| {
|
|
|
|
panic!("Unable to resolve {}: {}", faucet_host, err);
|
2019-10-04 19:54:09 -07:00
|
|
|
})
|
|
|
|
}),
|
2019-12-16 13:05:17 -08:00
|
|
|
*faucet_port,
|
2019-10-04 19:54:09 -07:00
|
|
|
);
|
2018-10-24 09:01:19 -07:00
|
|
|
|
2020-02-16 10:41:00 -08:00
|
|
|
process_airdrop(&rpc_client, config, &faucet_addr, pubkey, *lamports)
|
2019-10-04 19:54:09 -07:00
|
|
|
}
|
|
|
|
// Check client balance
|
|
|
|
CliCommand::Balance {
|
|
|
|
pubkey,
|
|
|
|
use_lamports_unit,
|
2020-06-17 11:18:48 -07:00
|
|
|
} => process_balance(&rpc_client, config, &pubkey, *use_lamports_unit),
|
2019-10-04 19:54:09 -07:00
|
|
|
// Confirm the last client transaction by signature
|
2020-04-15 20:51:05 -07:00
|
|
|
CliCommand::Confirm(signature) => process_confirm(&rpc_client, config, signature),
|
2020-04-20 10:00:20 -07:00
|
|
|
CliCommand::DecodeTransaction(transaction) => process_decode_transaction(transaction),
|
2020-03-14 20:48:41 -07:00
|
|
|
CliCommand::ResolveSigner(path) => {
|
|
|
|
if let Some(path) = path {
|
|
|
|
Ok(path.to_string())
|
|
|
|
} else {
|
|
|
|
Ok("Signer is valid".to_string())
|
|
|
|
}
|
|
|
|
}
|
2019-10-04 19:54:09 -07:00
|
|
|
CliCommand::ShowAccount {
|
|
|
|
pubkey,
|
|
|
|
output_file,
|
|
|
|
use_lamports_unit,
|
|
|
|
} => process_show_account(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
|
|
|
&pubkey,
|
|
|
|
&output_file,
|
|
|
|
*use_lamports_unit,
|
|
|
|
),
|
2020-02-07 11:16:35 -08:00
|
|
|
CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount,
|
2020-02-07 11:16:35 -08:00
|
|
|
to,
|
2020-02-24 16:03:30 -08:00
|
|
|
from,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait,
|
2020-02-07 11:16:35 -08:00
|
|
|
ref blockhash_query,
|
|
|
|
ref nonce_account,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority,
|
|
|
|
fee_payer,
|
2020-02-07 11:16:35 -08:00
|
|
|
} => process_transfer(
|
|
|
|
&rpc_client,
|
|
|
|
config,
|
2020-05-14 11:24:14 -07:00
|
|
|
*amount,
|
2020-02-07 11:16:35 -08:00
|
|
|
to,
|
2020-02-24 16:03:30 -08:00
|
|
|
*from,
|
2020-02-07 11:16:35 -08:00
|
|
|
*sign_only,
|
2020-04-08 22:46:19 -07:00
|
|
|
*no_wait,
|
2020-02-07 11:16:35 -08:00
|
|
|
blockhash_query,
|
|
|
|
nonce_account.as_ref(),
|
2020-02-24 16:03:30 -08:00
|
|
|
*nonce_authority,
|
|
|
|
*fee_payer,
|
2020-02-07 11:16:35 -08:00
|
|
|
),
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 06:39:42 -07:00
|
|
|
// Quick and dirty Keypair that assumes the client will do retries but not update the
|
|
|
|
// blockhash. If the client updates the blockhash, the signature will be invalid.
|
2019-12-16 13:05:17 -08:00
|
|
|
struct FaucetKeypair {
|
2019-03-15 06:39:42 -07:00
|
|
|
transaction: Transaction,
|
|
|
|
}
|
|
|
|
|
2019-12-16 13:05:17 -08:00
|
|
|
impl FaucetKeypair {
|
2019-03-15 06:39:42 -07:00
|
|
|
fn new_keypair(
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_addr: &SocketAddr,
|
2019-03-15 06:39:42 -07:00
|
|
|
to_pubkey: &Pubkey,
|
|
|
|
lamports: u64,
|
|
|
|
blockhash: Hash,
|
|
|
|
) -> Result<Self, Box<dyn error::Error>> {
|
2019-12-16 13:05:17 -08:00
|
|
|
let transaction = request_airdrop_transaction(faucet_addr, to_pubkey, lamports, blockhash)?;
|
2019-03-15 06:39:42 -07:00
|
|
|
Ok(Self { transaction })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn airdrop_transaction(&self) -> Transaction {
|
|
|
|
self.transaction.clone()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 13:28:55 -08:00
|
|
|
impl Signer for FaucetKeypair {
|
2019-03-15 06:39:42 -07:00
|
|
|
/// Return the public key of the keypair used to sign votes
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
2019-03-29 09:05:06 -07:00
|
|
|
self.transaction.message().account_keys[0]
|
2019-03-15 06:39:42 -07:00
|
|
|
}
|
|
|
|
|
2020-02-20 19:04:53 -08:00
|
|
|
fn try_pubkey(&self) -> Result<Pubkey, SignerError> {
|
2020-02-13 13:08:35 -08:00
|
|
|
Ok(self.pubkey())
|
|
|
|
}
|
|
|
|
|
2019-03-15 06:39:42 -07:00
|
|
|
fn sign_message(&self, _msg: &[u8]) -> Signature {
|
|
|
|
self.transaction.signatures[0]
|
|
|
|
}
|
2020-02-13 13:08:35 -08:00
|
|
|
|
2020-02-20 19:04:53 -08:00
|
|
|
fn try_sign_message(&self, message: &[u8]) -> Result<Signature, SignerError> {
|
2020-02-13 13:08:35 -08:00
|
|
|
Ok(self.sign_message(message))
|
|
|
|
}
|
2019-03-15 06:39:42 -07:00
|
|
|
}
|
|
|
|
|
2019-01-13 23:10:03 -08:00
|
|
|
pub fn request_and_confirm_airdrop(
|
2018-12-10 11:20:55 -08:00
|
|
|
rpc_client: &RpcClient,
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_addr: &SocketAddr,
|
2019-03-14 19:18:28 -07:00
|
|
|
to_pubkey: &Pubkey,
|
2019-03-05 17:22:46 -08:00
|
|
|
lamports: u64,
|
2020-05-06 21:21:48 -07:00
|
|
|
config: &CliConfig,
|
2019-09-06 10:55:03 -07:00
|
|
|
) -> ProcessResult {
|
2019-05-13 12:49:37 -07:00
|
|
|
let (blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?;
|
2019-05-09 09:48:27 -07:00
|
|
|
let keypair = {
|
|
|
|
let mut retries = 5;
|
|
|
|
loop {
|
2019-12-16 13:05:17 -08:00
|
|
|
let result = FaucetKeypair::new_keypair(faucet_addr, to_pubkey, lamports, blockhash);
|
2019-05-09 09:48:27 -07:00
|
|
|
if result.is_ok() || retries == 0 {
|
|
|
|
break result;
|
|
|
|
}
|
|
|
|
retries -= 1;
|
|
|
|
sleep(Duration::from_secs(1));
|
|
|
|
}
|
|
|
|
}?;
|
2020-05-09 09:06:32 -07:00
|
|
|
let tx = keypair.airdrop_transaction();
|
2020-06-17 11:18:48 -07:00
|
|
|
let result =
|
|
|
|
rpc_client.send_and_confirm_transaction_with_spinner_and_commitment(&tx, config.commitment);
|
2020-05-06 21:21:48 -07:00
|
|
|
log_instruction_custom_error::<SystemError>(result, &config)
|
2018-10-22 21:21:33 -07:00
|
|
|
}
|
|
|
|
|
2020-05-06 21:21:48 -07:00
|
|
|
pub fn log_instruction_custom_error<E>(
|
|
|
|
result: ClientResult<Signature>,
|
|
|
|
config: &CliConfig,
|
|
|
|
) -> ProcessResult
|
2019-04-25 10:29:44 -07:00
|
|
|
where
|
|
|
|
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
|
|
|
|
{
|
2019-10-02 18:33:01 -07:00
|
|
|
match result {
|
|
|
|
Err(err) => {
|
2020-03-12 23:20:49 -07:00
|
|
|
if let ClientErrorKind::TransactionError(TransactionError::InstructionError(
|
2019-10-02 18:33:01 -07:00
|
|
|
_,
|
2020-04-01 09:01:11 -07:00
|
|
|
InstructionError::Custom(code),
|
2020-03-12 23:20:49 -07:00
|
|
|
)) = err.kind()
|
2019-10-02 18:33:01 -07:00
|
|
|
{
|
2020-03-12 23:20:49 -07:00
|
|
|
if let Some(specific_error) = E::decode_custom_error_to_enum(*code) {
|
2019-10-02 18:33:01 -07:00
|
|
|
return Err(specific_error.into());
|
|
|
|
}
|
2019-04-25 10:29:44 -07:00
|
|
|
}
|
2019-10-02 18:33:01 -07:00
|
|
|
Err(err.into())
|
2019-04-25 10:29:44 -07:00
|
|
|
}
|
2020-05-06 21:21:48 -07:00
|
|
|
Ok(sig) => {
|
|
|
|
let signature = CliSignature {
|
|
|
|
signature: sig.clone().to_string(),
|
|
|
|
};
|
|
|
|
Ok(config.output_format.formatted_string(&signature))
|
|
|
|
}
|
2019-04-25 10:29:44 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 19:31:42 -07:00
|
|
|
pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, 'v> {
|
|
|
|
App::new(name)
|
|
|
|
.about(about)
|
|
|
|
.version(version)
|
|
|
|
.setting(AppSettings::SubcommandRequiredElseHelp)
|
2020-02-26 14:24:44 -08:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("address")
|
|
|
|
.about("Get your public key")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("confirm_key")
|
|
|
|
.long("confirm-key")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Confirm key on device; only relevant if using remote wallet"),
|
|
|
|
),
|
|
|
|
)
|
2019-10-04 19:54:09 -07:00
|
|
|
.cluster_query_subcommands()
|
2020-09-24 13:30:38 -07:00
|
|
|
.feature_subcommands()
|
2020-09-30 20:17:29 -07:00
|
|
|
.inflation_subcommands()
|
2019-12-10 00:24:44 -08:00
|
|
|
.nonce_subcommands()
|
2020-12-21 13:02:53 -08:00
|
|
|
.program_subcommands()
|
2019-10-04 19:54:09 -07:00
|
|
|
.stake_subcommands()
|
2019-05-09 19:31:42 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("airdrop")
|
2019-08-29 20:45:53 -07:00
|
|
|
.about("Request lamports")
|
2019-08-30 11:13:23 -07:00
|
|
|
.arg(
|
2019-12-16 13:05:17 -08:00
|
|
|
Arg::with_name("faucet_host")
|
|
|
|
.long("faucet-host")
|
2020-03-16 08:24:59 -07:00
|
|
|
.value_name("URL")
|
2019-08-30 11:13:23 -07:00
|
|
|
.takes_value(true)
|
2019-12-16 13:05:17 -08:00
|
|
|
.help("Faucet host to use [default: the --url host]"),
|
2019-08-30 11:13:23 -07:00
|
|
|
)
|
|
|
|
.arg(
|
2019-12-16 13:05:17 -08:00
|
|
|
Arg::with_name("faucet_port")
|
|
|
|
.long("faucet-port")
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("PORT_NUMBER")
|
2019-08-30 11:13:23 -07:00
|
|
|
.takes_value(true)
|
2019-12-16 13:05:17 -08:00
|
|
|
.default_value(solana_faucet::faucet::FAUCET_PORT_STR)
|
|
|
|
.help("Faucet port to use"),
|
2019-08-30 11:13:23 -07:00
|
|
|
)
|
2019-05-09 19:31:42 -07:00
|
|
|
.arg(
|
2019-09-10 16:16:40 -07:00
|
|
|
Arg::with_name("amount")
|
2019-05-09 19:31:42 -07:00
|
|
|
.index(1)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("AMOUNT")
|
2019-05-09 19:31:42 -07:00
|
|
|
.takes_value(true)
|
2019-12-10 10:29:17 -08:00
|
|
|
.validator(is_amount)
|
2019-05-09 19:31:42 -07:00
|
|
|
.required(true)
|
2020-02-15 11:53:52 -08:00
|
|
|
.help("The airdrop amount to request, in SOL"),
|
2020-02-16 10:41:00 -08:00
|
|
|
)
|
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("to")
|
2020-02-16 10:41:00 -08:00
|
|
|
.index(2)
|
2020-04-01 19:45:37 -07:00
|
|
|
.value_name("RECIPIENT_ADDRESS"),
|
|
|
|
"The account address of airdrop recipient. "),
|
2019-05-09 19:31:42 -07:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("balance")
|
|
|
|
.about("Get your balance")
|
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("pubkey")
|
2019-05-09 19:31:42 -07:00
|
|
|
.index(1)
|
2020-04-01 19:45:37 -07:00
|
|
|
.value_name("ACCOUNT_ADDRESS"),
|
|
|
|
"The account address of the balance to check. ")
|
2019-09-10 12:36:59 -07:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("lamports")
|
|
|
|
.long("lamports")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Display balance in lamports instead of SOL"),
|
2020-05-11 16:07:40 -07:00
|
|
|
)
|
|
|
|
.arg(commitment_arg_with_default("max")),
|
2019-05-09 19:31:42 -07:00
|
|
|
)
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("confirm")
|
|
|
|
.about("Confirm transaction by signature")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("signature")
|
|
|
|
.index(1)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("TRANSACTION_SIGNATURE")
|
2019-05-09 19:31:42 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The transaction signature to confirm"),
|
|
|
|
),
|
|
|
|
)
|
2020-04-20 10:00:20 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("decode-transaction")
|
2020-05-14 16:23:29 -07:00
|
|
|
.about("Decode a base-58 binary transaction")
|
2020-04-20 10:00:20 -07:00
|
|
|
.arg(
|
2020-08-15 17:26:00 -07:00
|
|
|
Arg::with_name("transaction")
|
2020-04-20 10:00:20 -07:00
|
|
|
.index(1)
|
2020-08-15 17:26:00 -07:00
|
|
|
.value_name("TRANSACTION")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("transaction to decode"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("encoding")
|
|
|
|
.index(2)
|
|
|
|
.value_name("ENCODING")
|
2020-08-15 22:06:39 -07:00
|
|
|
.possible_values(&["base58", "base64"]) // Subset of `UiTransactionEncoding` enum
|
|
|
|
.default_value("base58")
|
2020-04-20 10:00:20 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
2020-08-15 17:26:00 -07:00
|
|
|
.help("transaction encoding"),
|
2020-04-20 10:00:20 -07:00
|
|
|
),
|
|
|
|
)
|
2019-12-14 04:38:24 -08:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("create-address-with-seed")
|
2020-01-25 22:22:23 -08:00
|
|
|
.about("Generate a derived account address with a seed")
|
2019-12-14 04:38:24 -08:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("seed")
|
|
|
|
.index(1)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("SEED_STRING")
|
2019-12-14 04:38:24 -08:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The seed. Must not take more than 32 bytes to encode as utf-8"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("program_id")
|
|
|
|
.index(2)
|
2020-03-21 12:30:01 -07:00
|
|
|
.value_name("PROGRAM_ID")
|
2019-12-14 04:38:24 -08:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help(
|
|
|
|
"The program_id that the address will ultimately be used for, \n\
|
2020-05-14 18:22:47 -07:00
|
|
|
or one of NONCE, STAKE, and VOTE keywords",
|
2019-12-14 04:38:24 -08:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("from")
|
2019-12-14 04:38:24 -08:00
|
|
|
.long("from")
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("FROM_PUBKEY")
|
2020-04-01 19:45:37 -07:00
|
|
|
.required(false),
|
|
|
|
"From (base) key, [default: cli config keypair]. "),
|
2019-12-14 04:38:24 -08:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.subcommand(
|
2020-12-21 13:02:53 -08:00
|
|
|
SubCommand::with_name("deploy")
|
2019-12-14 04:38:24 -08:00
|
|
|
.about("Deploy a program")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("program_location")
|
|
|
|
.index(1)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("PROGRAM_FILEPATH")
|
2019-12-14 04:38:24 -08:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
2020-12-21 13:02:53 -08:00
|
|
|
.help("/path/to/program.o"),
|
2020-06-04 19:14:12 -07:00
|
|
|
)
|
|
|
|
.arg(
|
2020-12-21 13:02:53 -08:00
|
|
|
Arg::with_name("address_signer")
|
2020-06-04 19:14:12 -07:00
|
|
|
.index(2)
|
2020-12-21 13:02:53 -08:00
|
|
|
.value_name("PROGRAM_ADDRESS_SIGNER")
|
2020-06-04 19:14:12 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.validator(is_valid_signer)
|
2020-12-21 13:02:53 -08:00
|
|
|
.help("The signer for the desired address of the program [default: new random address]")
|
2020-08-17 18:06:22 -07:00
|
|
|
)
|
|
|
|
.arg(
|
2020-08-31 01:41:00 -07:00
|
|
|
Arg::with_name("use_deprecated_loader")
|
|
|
|
.long("use-deprecated-loader")
|
2020-08-17 18:06:22 -07:00
|
|
|
.takes_value(false)
|
|
|
|
.hidden(true) // Don't document this argument to discourage its use
|
|
|
|
.help("Use the deprecated BPF loader")
|
2020-09-28 15:24:38 -07:00
|
|
|
)
|
2020-10-22 21:42:35 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("allow_excessive_balance")
|
|
|
|
.long("allow-excessive-deploy-account-balance")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Use the designated program id, even if the account already holds a large balance of SOL")
|
|
|
|
)
|
2020-12-14 15:35:10 -08:00
|
|
|
.arg(commitment_arg_with_default("max")),
|
|
|
|
)
|
2019-05-09 19:31:42 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("pay")
|
2020-08-07 15:01:51 -07:00
|
|
|
.about("Deprecated alias for the transfer command")
|
2019-05-09 19:31:42 -07:00
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("to")
|
2019-05-09 19:31:42 -07:00
|
|
|
.index(1)
|
2020-03-21 12:30:01 -07:00
|
|
|
.value_name("RECIPIENT_ADDRESS")
|
2020-04-01 19:45:37 -07:00
|
|
|
.required(true),
|
|
|
|
"The account address of recipient. "),
|
2019-05-09 19:31:42 -07:00
|
|
|
)
|
|
|
|
.arg(
|
2019-09-10 16:16:40 -07:00
|
|
|
Arg::with_name("amount")
|
2019-05-09 19:31:42 -07:00
|
|
|
.index(2)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("AMOUNT")
|
2019-05-09 19:31:42 -07:00
|
|
|
.takes_value(true)
|
2020-05-14 11:24:14 -07:00
|
|
|
.validator(is_amount_or_all)
|
2019-05-09 19:31:42 -07:00
|
|
|
.required(true)
|
2020-05-14 11:24:14 -07:00
|
|
|
.help("The amount to send, in SOL; accepts keyword ALL"),
|
2019-05-09 19:31:42 -07:00
|
|
|
)
|
2020-10-07 12:18:25 -07:00
|
|
|
.offline_args()
|
2020-09-28 19:59:37 -07:00
|
|
|
.nonce_args(false)
|
2019-05-09 19:31:42 -07:00
|
|
|
)
|
2020-03-14 20:48:41 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("resolve-signer")
|
|
|
|
.about("Checks that a signer is valid, and returns its specific path; useful for signers that may be specified generally, eg. usb://ledger")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("signer")
|
|
|
|
.index(1)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("SIGNER_KEYPAIR")
|
2020-03-14 20:48:41 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.validator(is_valid_signer)
|
|
|
|
.help("The signer path to resolve")
|
|
|
|
)
|
|
|
|
)
|
2020-02-07 11:16:35 -08:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("transfer")
|
|
|
|
.about("Transfer funds between system accounts")
|
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("to")
|
2020-02-07 11:16:35 -08:00
|
|
|
.index(1)
|
2020-03-21 12:30:01 -07:00
|
|
|
.value_name("RECIPIENT_ADDRESS")
|
2020-04-01 19:45:37 -07:00
|
|
|
.required(true),
|
|
|
|
"The account address of recipient. "),
|
2020-02-07 11:16:35 -08:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("amount")
|
|
|
|
.index(2)
|
2020-03-19 20:43:11 -07:00
|
|
|
.value_name("AMOUNT")
|
2020-02-07 11:16:35 -08:00
|
|
|
.takes_value(true)
|
2020-05-14 11:24:14 -07:00
|
|
|
.validator(is_amount_or_all)
|
2020-02-07 11:16:35 -08:00
|
|
|
.required(true)
|
2020-05-14 11:24:14 -07:00
|
|
|
.help("The amount to send, in SOL; accepts keyword ALL"),
|
2020-02-07 11:16:35 -08:00
|
|
|
)
|
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("from")
|
2020-02-07 11:16:35 -08:00
|
|
|
.long("from")
|
2020-04-01 19:45:37 -07:00
|
|
|
.value_name("FROM_ADDRESS"),
|
|
|
|
"Source account of funds (if different from client local account). "),
|
2020-02-07 11:16:35 -08:00
|
|
|
)
|
2020-04-08 22:46:19 -07:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("no_wait")
|
|
|
|
.long("no-wait")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Return signature immediately after submitting the transaction, instead of waiting for confirmations"),
|
|
|
|
)
|
2020-10-07 12:18:25 -07:00
|
|
|
.offline_args()
|
2020-09-28 19:59:37 -07:00
|
|
|
.nonce_args(false)
|
2020-02-07 11:16:35 -08:00
|
|
|
.arg(fee_payer_arg()),
|
|
|
|
)
|
2019-08-08 10:13:06 -07:00
|
|
|
.subcommand(
|
2020-01-20 22:06:47 -08:00
|
|
|
SubCommand::with_name("account")
|
2019-10-04 19:54:09 -07:00
|
|
|
.about("Show the contents of an account")
|
2020-01-20 22:06:47 -08:00
|
|
|
.alias("account")
|
2019-10-04 19:54:09 -07:00
|
|
|
.arg(
|
2020-04-01 19:45:37 -07:00
|
|
|
pubkey!(Arg::with_name("account_pubkey")
|
2019-10-04 19:54:09 -07:00
|
|
|
.index(1)
|
2020-03-21 12:30:01 -07:00
|
|
|
.value_name("ACCOUNT_ADDRESS")
|
2020-04-01 19:45:37 -07:00
|
|
|
.required(true),
|
|
|
|
"Account key URI. ")
|
2019-10-04 19:54:09 -07:00
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("output_file")
|
2020-04-14 12:10:25 -07:00
|
|
|
.long("output-file")
|
2019-10-04 19:54:09 -07:00
|
|
|
.short("o")
|
2020-03-16 08:24:59 -07:00
|
|
|
.value_name("FILEPATH")
|
2019-10-04 19:54:09 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.help("Write the account data to this file"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("lamports")
|
|
|
|
.long("lamports")
|
|
|
|
.takes_value(false)
|
|
|
|
.help("Display balance in lamports instead of SOL"),
|
|
|
|
),
|
2019-08-08 10:13:06 -07:00
|
|
|
)
|
2019-10-21 16:08:09 -07:00
|
|
|
.validator_info_subcommands()
|
2019-10-04 19:54:09 -07:00
|
|
|
.vote_subcommands()
|
2019-05-09 19:31:42 -07:00
|
|
|
}
|
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
#[cfg(test)]
|
2018-09-14 01:59:09 -07:00
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-12-21 13:02:53 -08:00
|
|
|
use serde_json::{json, Value};
|
2020-10-02 12:35:39 -07:00
|
|
|
use solana_client::{
|
|
|
|
blockhash_query,
|
|
|
|
mock_sender::SIGNATURE,
|
|
|
|
rpc_request::RpcRequest,
|
|
|
|
rpc_response::{Response, RpcResponseContext},
|
|
|
|
};
|
2019-09-18 09:29:57 -07:00
|
|
|
use solana_sdk::{
|
2020-01-25 21:14:07 -08:00
|
|
|
pubkey::Pubkey,
|
2020-12-21 13:02:53 -08:00
|
|
|
signature::{keypair_from_seed, read_keypair_file, write_keypair_file, Keypair, Presigner},
|
2019-09-18 09:29:57 -07:00
|
|
|
transaction::TransactionError,
|
|
|
|
};
|
2020-03-11 11:14:15 -07:00
|
|
|
use std::path::PathBuf;
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2019-09-29 21:18:15 -07:00
|
|
|
fn make_tmp_path(name: &str) -> String {
|
|
|
|
let out_dir = std::env::var("FARF_DIR").unwrap_or_else(|_| "farf".to_string());
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
|
|
|
let path = format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey());
|
|
|
|
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_dir_all(&path);
|
|
|
|
// whack any possible collision
|
|
|
|
let _ignored = std::fs::remove_file(&path);
|
|
|
|
|
|
|
|
path
|
|
|
|
}
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
#[test]
|
|
|
|
fn test_generate_unique_signers() {
|
|
|
|
let matches = ArgMatches::default();
|
|
|
|
|
|
|
|
let default_keypair = Keypair::new();
|
|
|
|
let default_keypair_file = make_tmp_path("keypair_file");
|
|
|
|
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
|
|
|
|
2020-09-21 20:53:15 -07:00
|
|
|
let default_signer = DefaultSigner {
|
|
|
|
arg_name: "keypair".to_string(),
|
|
|
|
path: default_keypair_file,
|
|
|
|
};
|
|
|
|
|
|
|
|
let signer_info = default_signer
|
|
|
|
.generate_unique_signers(vec![], &matches, &mut None)
|
|
|
|
.unwrap();
|
2020-02-24 16:03:30 -08:00
|
|
|
assert_eq!(signer_info.signers.len(), 0);
|
|
|
|
|
2020-09-21 20:53:15 -07:00
|
|
|
let signer_info = default_signer
|
|
|
|
.generate_unique_signers(vec![None, None], &matches, &mut None)
|
|
|
|
.unwrap();
|
2020-02-24 16:03:30 -08:00
|
|
|
assert_eq!(signer_info.signers.len(), 1);
|
|
|
|
assert_eq!(signer_info.index_of(None), Some(0));
|
2020-10-19 12:23:14 -07:00
|
|
|
assert_eq!(
|
|
|
|
signer_info.index_of(Some(solana_sdk::pubkey::new_rand())),
|
|
|
|
None
|
|
|
|
);
|
2020-02-24 16:03:30 -08:00
|
|
|
|
|
|
|
let keypair0 = keypair_from_seed(&[1u8; 32]).unwrap();
|
|
|
|
let keypair0_pubkey = keypair0.pubkey();
|
|
|
|
let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap();
|
|
|
|
let keypair0_clone_pubkey = keypair0.pubkey();
|
|
|
|
let signers = vec![None, Some(keypair0.into()), Some(keypair0_clone.into())];
|
2020-09-21 20:53:15 -07:00
|
|
|
let signer_info = default_signer
|
|
|
|
.generate_unique_signers(signers, &matches, &mut None)
|
|
|
|
.unwrap();
|
2020-02-24 16:03:30 -08:00
|
|
|
assert_eq!(signer_info.signers.len(), 2);
|
|
|
|
assert_eq!(signer_info.index_of(None), Some(0));
|
|
|
|
assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(1));
|
|
|
|
assert_eq!(signer_info.index_of(Some(keypair0_clone_pubkey)), Some(1));
|
|
|
|
|
|
|
|
let keypair0 = keypair_from_seed(&[1u8; 32]).unwrap();
|
|
|
|
let keypair0_pubkey = keypair0.pubkey();
|
|
|
|
let keypair0_clone = keypair_from_seed(&[1u8; 32]).unwrap();
|
|
|
|
let signers = vec![Some(keypair0.into()), Some(keypair0_clone.into())];
|
2020-09-21 20:53:15 -07:00
|
|
|
let signer_info = default_signer
|
|
|
|
.generate_unique_signers(signers, &matches, &mut None)
|
|
|
|
.unwrap();
|
2020-02-24 16:03:30 -08:00
|
|
|
assert_eq!(signer_info.signers.len(), 1);
|
|
|
|
assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0));
|
|
|
|
|
|
|
|
// Signers with the same pubkey are not distinct
|
|
|
|
let keypair0 = keypair_from_seed(&[2u8; 32]).unwrap();
|
|
|
|
let keypair0_pubkey = keypair0.pubkey();
|
|
|
|
let keypair1 = keypair_from_seed(&[3u8; 32]).unwrap();
|
|
|
|
let keypair1_pubkey = keypair1.pubkey();
|
|
|
|
let message = vec![0, 1, 2, 3];
|
|
|
|
let presigner0 = Presigner::new(&keypair0.pubkey(), &keypair0.sign_message(&message));
|
|
|
|
let presigner0_pubkey = presigner0.pubkey();
|
|
|
|
let presigner1 = Presigner::new(&keypair1.pubkey(), &keypair1.sign_message(&message));
|
|
|
|
let presigner1_pubkey = presigner1.pubkey();
|
|
|
|
let signers = vec![
|
|
|
|
Some(keypair0.into()),
|
|
|
|
Some(presigner0.into()),
|
|
|
|
Some(presigner1.into()),
|
|
|
|
Some(keypair1.into()),
|
|
|
|
];
|
2020-09-21 20:53:15 -07:00
|
|
|
let signer_info = default_signer
|
|
|
|
.generate_unique_signers(signers, &matches, &mut None)
|
|
|
|
.unwrap();
|
2020-02-24 16:03:30 -08:00
|
|
|
assert_eq!(signer_info.signers.len(), 2);
|
|
|
|
assert_eq!(signer_info.index_of(Some(keypair0_pubkey)), Some(0));
|
|
|
|
assert_eq!(signer_info.index_of(Some(keypair1_pubkey)), Some(1));
|
|
|
|
assert_eq!(signer_info.index_of(Some(presigner0_pubkey)), Some(0));
|
|
|
|
assert_eq!(signer_info.index_of(Some(presigner1_pubkey)), Some(1));
|
|
|
|
}
|
|
|
|
|
2018-09-14 01:59:09 -07:00
|
|
|
#[test]
|
2020-05-15 09:35:43 -07:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2019-10-04 15:13:21 -07:00
|
|
|
fn test_cli_parse_command() {
|
2019-05-09 19:31:42 -07:00
|
|
|
let test_commands = app("test", "desc", "version");
|
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let pubkey = solana_sdk::pubkey::new_rand();
|
2018-09-19 12:44:16 -07:00
|
|
|
let pubkey_string = format!("{}", pubkey);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2020-09-21 20:53:15 -07:00
|
|
|
let default_keypair = Keypair::new();
|
|
|
|
let keypair_file = make_tmp_path("keypair_file");
|
|
|
|
write_keypair_file(&default_keypair, &keypair_file).unwrap();
|
|
|
|
let keypair = read_keypair_file(&keypair_file).unwrap();
|
|
|
|
let default_signer = DefaultSigner {
|
|
|
|
path: keypair_file.clone(),
|
|
|
|
arg_name: "".to_string(),
|
|
|
|
};
|
2018-09-19 12:44:16 -07:00
|
|
|
// Test Airdrop Subcommand
|
2020-02-16 10:41:00 -08:00
|
|
|
let test_airdrop =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "airdrop", "50", &pubkey_string]);
|
2018-09-14 01:59:09 -07:00
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_airdrop, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host: None,
|
|
|
|
faucet_port: solana_faucet::faucet::FAUCET_PORT,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey: Some(pubkey),
|
2020-02-15 11:53:52 -08:00
|
|
|
lamports: 50_000_000_000,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-08-29 20:45:53 -07:00
|
|
|
}
|
2018-09-14 01:59:09 -07:00
|
|
|
);
|
|
|
|
|
2019-08-08 17:10:09 -07:00
|
|
|
// Test Balance Subcommand, incl pubkey and keypair-file inputs
|
|
|
|
let test_balance = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"balance",
|
|
|
|
&keypair.pubkey().to_string(),
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_balance, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Balance {
|
|
|
|
pubkey: Some(keypair.pubkey()),
|
2020-05-11 16:07:40 -07:00
|
|
|
use_lamports_unit: false,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-09-10 12:36:59 -07:00
|
|
|
}
|
2019-08-08 17:10:09 -07:00
|
|
|
);
|
2019-09-10 12:36:59 -07:00
|
|
|
let test_balance = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"balance",
|
|
|
|
&keypair_file,
|
|
|
|
"--lamports",
|
|
|
|
]);
|
2019-08-08 17:10:09 -07:00
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_balance, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Balance {
|
|
|
|
pubkey: Some(keypair.pubkey()),
|
2020-05-11 16:07:40 -07:00
|
|
|
use_lamports_unit: true,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
|
|
|
);
|
|
|
|
let test_balance =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "balance", "--lamports"]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_balance, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Balance {
|
|
|
|
pubkey: None,
|
2020-05-11 16:07:40 -07:00
|
|
|
use_lamports_unit: true,
|
2019-10-21 16:08:09 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
2019-09-10 12:36:59 -07:00
|
|
|
}
|
2019-08-08 17:10:09 -07:00
|
|
|
);
|
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
// Test Confirm Subcommand
|
2020-05-15 09:35:43 -07:00
|
|
|
let signature = Signature::new(&[1; 64]);
|
2018-09-14 01:59:09 -07:00
|
|
|
let signature_string = format!("{:?}", signature);
|
|
|
|
let test_confirm =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "confirm", &signature_string]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_confirm, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Confirm(signature),
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2018-09-14 01:59:09 -07:00
|
|
|
);
|
2018-09-19 12:44:16 -07:00
|
|
|
let test_bad_signature = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "confirm", "deadbeef"]);
|
2020-09-21 20:53:15 -07:00
|
|
|
assert!(parse_command(&test_bad_signature, &default_signer, &mut None).is_err());
|
2018-09-19 12:44:16 -07:00
|
|
|
|
2019-12-14 04:38:24 -08:00
|
|
|
// Test CreateAddressWithSeed
|
2020-10-19 12:12:08 -07:00
|
|
|
let from_pubkey = Some(solana_sdk::pubkey::new_rand());
|
2019-12-14 04:38:24 -08:00
|
|
|
let from_str = from_pubkey.unwrap().to_string();
|
|
|
|
for (name, program_id) in &[
|
|
|
|
("STAKE", solana_stake_program::id()),
|
|
|
|
("VOTE", solana_vote_program::id()),
|
2020-03-19 09:36:53 -07:00
|
|
|
("NONCE", system_program::id()),
|
2019-12-14 04:38:24 -08:00
|
|
|
] {
|
|
|
|
let test_create_address_with_seed = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"create-address-with-seed",
|
|
|
|
"seed",
|
|
|
|
name,
|
|
|
|
"--from",
|
|
|
|
&from_str,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_create_address_with_seed, &default_signer, &mut None).unwrap(),
|
2019-12-14 04:38:24 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::CreateAddressWithSeed {
|
|
|
|
from_pubkey,
|
|
|
|
seed: "seed".to_string(),
|
|
|
|
program_id: *program_id
|
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![],
|
2019-12-14 04:38:24 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let test_create_address_with_seed = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"create-address-with-seed",
|
|
|
|
"seed",
|
|
|
|
"STAKE",
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_create_address_with_seed, &default_signer, &mut None).unwrap(),
|
2019-12-14 04:38:24 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::CreateAddressWithSeed {
|
|
|
|
from_pubkey: None,
|
|
|
|
seed: "seed".to_string(),
|
|
|
|
program_id: solana_stake_program::id(),
|
|
|
|
},
|
2020-03-08 23:02:24 -07:00
|
|
|
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
2019-12-14 04:38:24 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2018-11-12 12:48:59 -08:00
|
|
|
// Test Deploy Subcommand
|
2020-12-21 13:02:53 -08:00
|
|
|
let test_deploy =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "deploy", "/Users/test/program.o"]);
|
2018-11-12 12:48:59 -08:00
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
2019-10-21 16:08:09 -07:00
|
|
|
CliCommandInfo {
|
2020-12-21 13:02:53 -08:00
|
|
|
command: CliCommand::Deploy {
|
|
|
|
program_location: "/Users/test/program.o".to_string(),
|
|
|
|
address: None,
|
2020-08-17 18:06:22 -07:00
|
|
|
use_deprecated_loader: false,
|
2020-10-22 21:42:35 -07:00
|
|
|
allow_excessive_balance: false,
|
2020-06-04 19:14:12 -07:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
|
2019-10-21 16:08:09 -07:00
|
|
|
}
|
2018-11-12 12:48:59 -08:00
|
|
|
);
|
|
|
|
|
2020-06-04 19:14:12 -07:00
|
|
|
let custom_address = Keypair::new();
|
|
|
|
let custom_address_file = make_tmp_path("custom_address_file");
|
|
|
|
write_keypair_file(&custom_address, &custom_address_file).unwrap();
|
|
|
|
let test_deploy = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"deploy",
|
2020-12-21 13:02:53 -08:00
|
|
|
"/Users/test/program.o",
|
2020-12-14 15:35:10 -08:00
|
|
|
&custom_address_file,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(&test_deploy, &default_signer, &mut None).unwrap(),
|
|
|
|
CliCommandInfo {
|
2020-12-21 13:02:53 -08:00
|
|
|
command: CliCommand::Deploy {
|
|
|
|
program_location: "/Users/test/program.o".to_string(),
|
|
|
|
address: Some(1),
|
2020-12-14 15:35:10 -08:00
|
|
|
use_deprecated_loader: false,
|
|
|
|
allow_excessive_balance: false,
|
|
|
|
},
|
|
|
|
signers: vec![
|
|
|
|
read_keypair_file(&keypair_file).unwrap().into(),
|
|
|
|
read_keypair_file(&custom_address_file).unwrap().into(),
|
|
|
|
],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-03-14 20:48:41 -07:00
|
|
|
// Test ResolveSigner Subcommand, KeypairUrl::Filepath
|
|
|
|
let test_resolve_signer =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "resolve-signer", &keypair_file]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(),
|
2020-03-14 20:48:41 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::ResolveSigner(Some(keypair_file.clone())),
|
|
|
|
signers: vec![],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
// Test ResolveSigner Subcommand, KeypairUrl::Pubkey (Presigner)
|
|
|
|
let test_resolve_signer =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "resolve-signer", &pubkey_string]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_resolve_signer, &default_signer, &mut None).unwrap(),
|
2020-03-14 20:48:41 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::ResolveSigner(Some(pubkey.to_string())),
|
|
|
|
signers: vec![],
|
|
|
|
}
|
|
|
|
);
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|
2019-01-13 23:10:03 -08:00
|
|
|
|
2018-09-14 01:59:09 -07:00
|
|
|
#[test]
|
2020-05-15 09:35:43 -07:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2019-10-04 15:13:21 -07:00
|
|
|
fn test_cli_process_command() {
|
2019-01-13 23:10:03 -08:00
|
|
|
// Success cases
|
2020-12-13 17:26:34 -08:00
|
|
|
let mut config = CliConfig {
|
|
|
|
rpc_client: Some(RpcClient::new_mock("succeeds".to_string())),
|
|
|
|
json_rpc_url: "http://127.0.0.1:8899".to_string(),
|
|
|
|
..CliConfig::default()
|
|
|
|
};
|
2018-11-12 12:48:59 -08:00
|
|
|
|
2019-01-13 23:10:03 -08:00
|
|
|
let keypair = Keypair::new();
|
|
|
|
let pubkey = keypair.pubkey().to_string();
|
2020-02-24 16:03:30 -08:00
|
|
|
config.signers = vec![&keypair];
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Address;
|
2019-01-13 23:10:03 -08:00
|
|
|
assert_eq!(process_command(&config).unwrap(), pubkey);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Balance {
|
2019-10-21 16:08:09 -07:00
|
|
|
pubkey: None,
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: true,
|
|
|
|
};
|
2019-03-20 09:44:16 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "50 lamports");
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Balance {
|
2019-10-21 16:08:09 -07:00
|
|
|
pubkey: None,
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: false,
|
|
|
|
};
|
2019-10-29 19:03:48 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "0.00000005 SOL");
|
2019-09-10 12:36:59 -07:00
|
|
|
|
2019-01-13 23:10:03 -08:00
|
|
|
let good_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap());
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Confirm(good_signature);
|
2018-09-20 22:27:06 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "Confirmed");
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2019-11-06 06:47:34 -08:00
|
|
|
let bob_keypair = Keypair::new();
|
|
|
|
let bob_pubkey = bob_keypair.pubkey();
|
2020-03-19 01:58:52 -07:00
|
|
|
let identity_keypair = Keypair::new();
|
2019-10-21 16:08:09 -07:00
|
|
|
config.command = CliCommand::CreateVoteAccount {
|
2020-07-13 20:27:41 -07:00
|
|
|
vote_account: 1,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: None,
|
2020-03-19 01:58:52 -07:00
|
|
|
identity_account: 2,
|
2019-10-21 16:08:09 -07:00
|
|
|
authorized_voter: Some(bob_pubkey),
|
|
|
|
authorized_withdrawer: Some(bob_pubkey),
|
|
|
|
commission: 0,
|
|
|
|
};
|
2020-03-19 01:58:52 -07:00
|
|
|
config.signers = vec![&keypair, &bob_keypair, &identity_keypair];
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-05-09 19:31:42 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let new_authorized_pubkey = solana_sdk::pubkey::new_rand();
|
2020-02-24 16:03:30 -08:00
|
|
|
config.signers = vec![&bob_keypair];
|
2019-12-12 15:04:03 -08:00
|
|
|
config.command = CliCommand::VoteAuthorize {
|
|
|
|
vote_account_pubkey: bob_pubkey,
|
|
|
|
new_authorized_pubkey,
|
|
|
|
vote_authorize: VoteAuthorize::Voter,
|
|
|
|
};
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-12-12 15:04:03 -08:00
|
|
|
|
2020-03-19 01:58:52 -07:00
|
|
|
let new_identity_keypair = Keypair::new();
|
|
|
|
config.signers = vec![&keypair, &bob_keypair, &new_identity_keypair];
|
2019-12-12 15:04:03 -08:00
|
|
|
config.command = CliCommand::VoteUpdateValidator {
|
|
|
|
vote_account_pubkey: bob_pubkey,
|
2020-03-19 01:58:52 -07:00
|
|
|
new_identity_account: 2,
|
2020-07-13 20:49:59 -07:00
|
|
|
withdraw_authority: 1,
|
2019-12-12 15:04:03 -08:00
|
|
|
};
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-05-09 19:31:42 -07:00
|
|
|
|
2019-11-06 06:47:34 -08:00
|
|
|
let bob_keypair = Keypair::new();
|
|
|
|
let bob_pubkey = bob_keypair.pubkey();
|
2020-10-19 12:12:08 -07:00
|
|
|
let custodian = solana_sdk::pubkey::new_rand();
|
2019-10-21 16:08:09 -07:00
|
|
|
config.command = CliCommand::CreateStakeAccount {
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_account: 1,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: None,
|
2019-10-21 16:08:09 -07:00
|
|
|
staker: None,
|
|
|
|
withdrawer: None,
|
2019-11-25 15:11:55 -08:00
|
|
|
lockup: Lockup {
|
|
|
|
epoch: 0,
|
2019-12-19 14:37:47 -08:00
|
|
|
unix_timestamp: 0,
|
2019-11-25 15:11:55 -08:00
|
|
|
custodian,
|
|
|
|
},
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(30),
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only: false,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
2020-02-12 22:00:28 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
|
|
|
from: 0,
|
2019-10-21 16:08:09 -07:00
|
|
|
};
|
2020-02-24 16:03:30 -08:00
|
|
|
config.signers = vec![&keypair, &bob_keypair];
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-03-06 20:29:08 -08:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
2020-01-15 13:32:06 -08:00
|
|
|
config.command = CliCommand::WithdrawStake {
|
2020-06-10 21:02:04 -07:00
|
|
|
stake_account_pubkey,
|
2020-01-15 13:32:06 -08:00
|
|
|
destination_account_pubkey: to_pubkey,
|
|
|
|
lamports: 100,
|
2020-02-24 16:03:30 -08:00
|
|
|
withdraw_authority: 0,
|
2020-04-22 15:00:18 -07:00
|
|
|
custodian: None,
|
2020-02-12 22:00:28 -08:00
|
|
|
sign_only: false,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
2020-02-12 22:00:28 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
2020-01-15 13:32:06 -08:00
|
|
|
};
|
2020-02-24 16:03:30 -08:00
|
|
|
config.signers = vec![&keypair];
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-06-21 22:28:34 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
2019-11-25 21:09:57 -08:00
|
|
|
config.command = CliCommand::DeactivateStake {
|
2020-06-10 21:02:04 -07:00
|
|
|
stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority: 0,
|
2019-11-25 21:09:57 -08:00
|
|
|
sign_only: false,
|
2020-01-30 08:21:32 -08:00
|
|
|
blockhash_query: BlockhashQuery::default(),
|
2019-12-27 12:35:49 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
2019-11-25 21:09:57 -08:00
|
|
|
};
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2020-02-02 21:20:28 -08:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
2020-02-02 21:20:28 -08:00
|
|
|
let split_stake_account = Keypair::new();
|
|
|
|
config.command = CliCommand::SplitStake {
|
2020-06-10 21:02:04 -07:00
|
|
|
stake_account_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
stake_authority: 0,
|
2020-02-02 21:20:28 -08:00
|
|
|
sign_only: false,
|
|
|
|
blockhash_query: BlockhashQuery::default(),
|
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
split_stake_account: 1,
|
2020-02-02 21:20:28 -08:00
|
|
|
seed: None,
|
2020-05-14 11:24:14 -07:00
|
|
|
lamports: 30,
|
2020-02-24 16:03:30 -08:00
|
|
|
fee_payer: 0,
|
2020-02-02 21:20:28 -08:00
|
|
|
};
|
2020-02-24 16:03:30 -08:00
|
|
|
config.signers = vec![&keypair, &split_stake_account];
|
2020-04-06 18:27:37 -07:00
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(result.is_ok());
|
2019-06-21 23:45:03 -07:00
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
|
|
|
let source_stake_account_pubkey = solana_sdk::pubkey::new_rand();
|
2020-06-10 21:02:04 -07:00
|
|
|
let merge_stake_account = Keypair::new();
|
|
|
|
config.command = CliCommand::MergeStake {
|
|
|
|
stake_account_pubkey,
|
|
|
|
source_stake_account_pubkey,
|
|
|
|
stake_authority: 1,
|
|
|
|
sign_only: false,
|
|
|
|
blockhash_query: BlockhashQuery::default(),
|
|
|
|
nonce_account: None,
|
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
|
|
|
};
|
|
|
|
config.signers = vec![&keypair, &merge_stake_account];
|
|
|
|
let result = process_command(&config);
|
|
|
|
assert!(dbg!(result).is_ok());
|
|
|
|
|
2020-06-17 11:18:48 -07:00
|
|
|
config.command = CliCommand::GetSlot;
|
2019-08-05 13:17:03 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "0");
|
|
|
|
|
2020-06-17 11:18:48 -07:00
|
|
|
config.command = CliCommand::GetTransactionCount;
|
2019-01-13 23:10:03 -08:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "1234");
|
|
|
|
|
2020-03-08 23:02:24 -07:00
|
|
|
// CreateAddressWithSeed
|
2020-10-19 12:12:08 -07:00
|
|
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
2020-03-08 23:02:24 -07:00
|
|
|
config.signers = vec![];
|
|
|
|
config.command = CliCommand::CreateAddressWithSeed {
|
|
|
|
from_pubkey: Some(from_pubkey),
|
|
|
|
seed: "seed".to_string(),
|
|
|
|
program_id: solana_stake_program::id(),
|
|
|
|
};
|
|
|
|
let address = process_command(&config);
|
|
|
|
let expected_address =
|
2020-03-20 15:20:48 -07:00
|
|
|
Pubkey::create_with_seed(&from_pubkey, "seed", &solana_stake_program::id()).unwrap();
|
2020-03-08 23:02:24 -07:00
|
|
|
assert_eq!(address.unwrap(), expected_address.to_string());
|
|
|
|
|
2019-01-13 23:10:03 -08:00
|
|
|
// Need airdrop cases
|
2020-10-19 12:12:08 -07:00
|
|
|
let to = solana_sdk::pubkey::new_rand();
|
2020-03-08 23:02:24 -07:00
|
|
|
config.signers = vec![&keypair];
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host: None,
|
|
|
|
faucet_port: 1234,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey: Some(to),
|
2019-08-29 20:45:53 -07:00
|
|
|
lamports: 50,
|
|
|
|
};
|
2019-08-16 15:23:59 -07:00
|
|
|
assert!(process_command(&config).is_ok());
|
2018-11-14 18:57:34 -08:00
|
|
|
|
2019-04-05 19:56:17 -07:00
|
|
|
// sig_not_found case
|
|
|
|
config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string()));
|
2019-03-17 00:40:45 -07:00
|
|
|
let missing_signature = Signature::new(&bs58::decode("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW").into_vec().unwrap());
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Confirm(missing_signature);
|
2019-03-17 00:40:45 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "Not found");
|
|
|
|
|
2019-04-05 19:56:17 -07:00
|
|
|
// Tx error case
|
|
|
|
config.rpc_client = Some(RpcClient::new_mock("account_in_use".to_string()));
|
|
|
|
let any_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap());
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Confirm(any_signature);
|
2019-04-05 19:56:17 -07:00
|
|
|
assert_eq!(
|
|
|
|
process_command(&config).unwrap(),
|
2020-04-16 20:02:02 -07:00
|
|
|
format!("Transaction failed: {}", TransactionError::AccountInUse)
|
2019-04-05 19:56:17 -07:00
|
|
|
);
|
|
|
|
|
2019-03-17 00:40:45 -07:00
|
|
|
// Failure cases
|
2019-03-16 21:51:41 -07:00
|
|
|
config.rpc_client = Some(RpcClient::new_mock("fails".to_string()));
|
2019-01-13 23:10:03 -08:00
|
|
|
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Airdrop {
|
2019-12-16 13:05:17 -08:00
|
|
|
faucet_host: None,
|
|
|
|
faucet_port: 1234,
|
2020-02-16 10:41:00 -08:00
|
|
|
pubkey: None,
|
2019-08-29 20:45:53 -07:00
|
|
|
lamports: 50,
|
|
|
|
};
|
2019-01-13 23:10:03 -08:00
|
|
|
assert!(process_command(&config).is_err());
|
2018-09-25 11:45:25 -07:00
|
|
|
|
2019-10-04 15:13:21 -07:00
|
|
|
config.command = CliCommand::Balance {
|
2019-10-21 16:08:09 -07:00
|
|
|
pubkey: None,
|
2019-09-10 12:36:59 -07:00
|
|
|
use_lamports_unit: false,
|
|
|
|
};
|
2019-01-13 23:10:03 -08:00
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
|
2019-11-06 06:47:34 -08:00
|
|
|
let bob_keypair = Keypair::new();
|
2020-03-19 01:58:52 -07:00
|
|
|
let identity_keypair = Keypair::new();
|
2019-10-21 16:08:09 -07:00
|
|
|
config.command = CliCommand::CreateVoteAccount {
|
2020-07-13 20:27:41 -07:00
|
|
|
vote_account: 1,
|
2020-01-09 15:22:48 -08:00
|
|
|
seed: None,
|
2020-03-19 01:58:52 -07:00
|
|
|
identity_account: 2,
|
2019-10-21 16:08:09 -07:00
|
|
|
authorized_voter: Some(bob_pubkey),
|
|
|
|
authorized_withdrawer: Some(bob_pubkey),
|
|
|
|
commission: 0,
|
|
|
|
};
|
2020-03-19 01:58:52 -07:00
|
|
|
config.signers = vec![&keypair, &bob_keypair, &identity_keypair];
|
2019-03-06 20:29:08 -08:00
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
|
2019-12-12 15:04:03 -08:00
|
|
|
config.command = CliCommand::VoteAuthorize {
|
|
|
|
vote_account_pubkey: bob_pubkey,
|
|
|
|
new_authorized_pubkey: bob_pubkey,
|
|
|
|
vote_authorize: VoteAuthorize::Voter,
|
|
|
|
};
|
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
|
|
|
|
config.command = CliCommand::VoteUpdateValidator {
|
|
|
|
vote_account_pubkey: bob_pubkey,
|
2020-03-19 01:58:52 -07:00
|
|
|
new_identity_account: 1,
|
2020-07-13 20:49:59 -07:00
|
|
|
withdraw_authority: 1,
|
2019-12-12 15:04:03 -08:00
|
|
|
};
|
2019-03-06 20:29:08 -08:00
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
|
2020-06-17 11:18:48 -07:00
|
|
|
config.command = CliCommand::GetSlot;
|
2019-08-05 13:17:03 -07:00
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
|
2020-06-17 11:18:48 -07:00
|
|
|
config.command = CliCommand::GetTransactionCount;
|
2019-01-13 23:10:03 -08:00
|
|
|
assert!(process_command(&config).is_err());
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|
2019-01-17 08:49:16 -08:00
|
|
|
|
|
|
|
#[test]
|
2019-10-04 15:13:21 -07:00
|
|
|
fn test_cli_deploy() {
|
2019-02-27 11:17:32 -08:00
|
|
|
solana_logger::setup();
|
2019-01-17 08:49:16 -08:00
|
|
|
let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
|
|
pathbuf.push("tests");
|
|
|
|
pathbuf.push("fixtures");
|
|
|
|
pathbuf.push("noop");
|
|
|
|
pathbuf.set_extension("so");
|
|
|
|
|
|
|
|
// Success case
|
2019-10-04 15:13:21 -07:00
|
|
|
let mut config = CliConfig::default();
|
2020-10-02 12:35:39 -07:00
|
|
|
let account_info_response = json!(Response {
|
|
|
|
context: RpcResponseContext { slot: 1 },
|
|
|
|
value: Value::Null,
|
|
|
|
});
|
|
|
|
let mut mocks = HashMap::new();
|
|
|
|
mocks.insert(RpcRequest::GetAccountInfo, account_info_response);
|
|
|
|
let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks);
|
|
|
|
|
|
|
|
config.rpc_client = Some(rpc_client);
|
2020-02-24 16:03:30 -08:00
|
|
|
let default_keypair = Keypair::new();
|
|
|
|
config.signers = vec![&default_keypair];
|
2019-01-17 08:49:16 -08:00
|
|
|
|
2020-12-21 13:02:53 -08:00
|
|
|
config.command = CliCommand::Deploy {
|
2020-06-04 19:14:12 -07:00
|
|
|
program_location: pathbuf.to_str().unwrap().to_string(),
|
2020-12-21 13:02:53 -08:00
|
|
|
address: None,
|
2020-08-17 18:06:22 -07:00
|
|
|
use_deprecated_loader: false,
|
2020-10-22 21:42:35 -07:00
|
|
|
allow_excessive_balance: false,
|
2020-06-04 19:14:12 -07:00
|
|
|
};
|
2019-01-17 08:49:16 -08:00
|
|
|
let result = process_command(&config);
|
|
|
|
let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
|
|
|
|
let program_id = json
|
|
|
|
.as_object()
|
|
|
|
.unwrap()
|
|
|
|
.get("programId")
|
|
|
|
.unwrap()
|
|
|
|
.as_str()
|
|
|
|
.unwrap();
|
2019-05-16 21:43:18 -07:00
|
|
|
|
|
|
|
assert!(program_id.parse::<Pubkey>().is_ok());
|
2019-01-17 08:49:16 -08:00
|
|
|
|
2019-08-09 14:52:06 -07:00
|
|
|
// Failure case
|
2020-12-21 13:02:53 -08:00
|
|
|
config.command = CliCommand::Deploy {
|
2020-06-04 19:14:12 -07:00
|
|
|
program_location: "bad/file/location.so".to_string(),
|
2020-12-21 13:02:53 -08:00
|
|
|
address: None,
|
2020-08-17 18:06:22 -07:00
|
|
|
use_deprecated_loader: false,
|
2020-10-22 21:42:35 -07:00
|
|
|
allow_excessive_balance: false,
|
2020-12-14 15:35:10 -08:00
|
|
|
};
|
|
|
|
assert!(process_command(&config).is_err());
|
|
|
|
}
|
|
|
|
|
2020-02-07 11:16:35 -08:00
|
|
|
#[test]
|
|
|
|
fn test_parse_transfer_subcommand() {
|
|
|
|
let test_commands = app("test", "desc", "version");
|
|
|
|
|
2020-02-24 16:03:30 -08:00
|
|
|
let default_keypair = Keypair::new();
|
|
|
|
let default_keypair_file = make_tmp_path("keypair_file");
|
|
|
|
write_keypair_file(&default_keypair, &default_keypair_file).unwrap();
|
2020-09-21 20:53:15 -07:00
|
|
|
let default_signer = DefaultSigner {
|
|
|
|
path: default_keypair_file.clone(),
|
|
|
|
arg_name: "".to_string(),
|
|
|
|
};
|
2020-02-24 16:03:30 -08:00
|
|
|
|
2020-02-15 11:53:52 -08:00
|
|
|
//Test Transfer Subcommand, SOL
|
2020-02-07 11:16:35 -08:00
|
|
|
let from_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
|
|
|
|
let from_pubkey = from_keypair.pubkey();
|
|
|
|
let from_string = from_pubkey.to_string();
|
|
|
|
let to_keypair = keypair_from_seed(&[1u8; 32]).unwrap();
|
|
|
|
let to_pubkey = to_keypair.pubkey();
|
|
|
|
let to_string = to_pubkey.to_string();
|
|
|
|
let test_transfer = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "transfer", &to_string, "42"]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-02-07 11:16:35 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(42_000_000_000),
|
|
|
|
to: to_pubkey,
|
|
|
|
from: 0,
|
|
|
|
sign_only: false,
|
|
|
|
no_wait: false,
|
|
|
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
|
|
|
nonce_account: None,
|
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
|
|
|
},
|
|
|
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Transfer ALL
|
|
|
|
let test_transfer = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "transfer", &to_string, "ALL"]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-05-14 11:24:14 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
|
|
|
amount: SpendAmount::All,
|
2020-02-07 11:16:35 -08:00
|
|
|
to: to_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: false,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: false,
|
|
|
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
|
|
|
nonce_account: None,
|
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
|
|
|
},
|
|
|
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Transfer no-wait
|
|
|
|
let test_transfer = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"transfer",
|
|
|
|
"--no-wait",
|
|
|
|
&to_string,
|
|
|
|
"42",
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-04-08 22:46:19 -07:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(42_000_000_000),
|
2020-04-08 22:46:19 -07:00
|
|
|
to: to_pubkey,
|
|
|
|
from: 0,
|
|
|
|
sign_only: false,
|
|
|
|
no_wait: true,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
|
2020-02-07 11:16:35 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
2020-02-07 11:16:35 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
//Test Transfer Subcommand, offline sign
|
|
|
|
let blockhash = Hash::new(&[1u8; 32]);
|
|
|
|
let blockhash_string = blockhash.to_string();
|
|
|
|
let test_transfer = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"transfer",
|
|
|
|
&to_string,
|
|
|
|
"42",
|
|
|
|
"--blockhash",
|
|
|
|
&blockhash_string,
|
|
|
|
"--sign-only",
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-02-07 11:16:35 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(42_000_000_000),
|
2020-02-07 11:16:35 -08:00
|
|
|
to: to_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: true,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: false,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::None(blockhash),
|
2020-02-07 11:16:35 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![read_keypair_file(&default_keypair_file).unwrap().into()],
|
2020-02-07 11:16:35 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
//Test Transfer Subcommand, submit offline `from`
|
|
|
|
let from_sig = from_keypair.sign_message(&[0u8]);
|
|
|
|
let from_signer = format!("{}={}", from_pubkey, from_sig);
|
|
|
|
let test_transfer = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"transfer",
|
|
|
|
&to_string,
|
|
|
|
"42",
|
|
|
|
"--from",
|
|
|
|
&from_string,
|
|
|
|
"--fee-payer",
|
|
|
|
&from_string,
|
|
|
|
"--signer",
|
|
|
|
&from_signer,
|
|
|
|
"--blockhash",
|
|
|
|
&blockhash_string,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-02-07 11:16:35 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(42_000_000_000),
|
2020-02-07 11:16:35 -08:00
|
|
|
to: to_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: false,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: false,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::FeeCalculator(
|
|
|
|
blockhash_query::Source::Cluster,
|
|
|
|
blockhash
|
|
|
|
),
|
2020-02-07 11:16:35 -08:00
|
|
|
nonce_account: None,
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 0,
|
|
|
|
fee_payer: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![Presigner::new(&from_pubkey, &from_sig).into()],
|
2020-02-07 11:16:35 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
//Test Transfer Subcommand, with nonce
|
|
|
|
let nonce_address = Pubkey::new(&[1u8; 32]);
|
|
|
|
let nonce_address_string = nonce_address.to_string();
|
|
|
|
let nonce_authority = keypair_from_seed(&[2u8; 32]).unwrap();
|
|
|
|
let nonce_authority_file = make_tmp_path("nonce_authority_file");
|
|
|
|
write_keypair_file(&nonce_authority, &nonce_authority_file).unwrap();
|
|
|
|
let test_transfer = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"transfer",
|
|
|
|
&to_string,
|
|
|
|
"42",
|
|
|
|
"--blockhash",
|
|
|
|
&blockhash_string,
|
|
|
|
"--nonce",
|
|
|
|
&nonce_address_string,
|
|
|
|
"--nonce-authority",
|
|
|
|
&nonce_authority_file,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
2020-09-21 20:53:15 -07:00
|
|
|
parse_command(&test_transfer, &default_signer, &mut None).unwrap(),
|
2020-02-07 11:16:35 -08:00
|
|
|
CliCommandInfo {
|
|
|
|
command: CliCommand::Transfer {
|
2020-05-14 11:24:14 -07:00
|
|
|
amount: SpendAmount::Some(42_000_000_000),
|
2020-02-07 11:16:35 -08:00
|
|
|
to: to_pubkey,
|
2020-02-24 16:03:30 -08:00
|
|
|
from: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
sign_only: false,
|
2020-04-08 22:46:19 -07:00
|
|
|
no_wait: false,
|
2020-03-11 11:14:15 -07:00
|
|
|
blockhash_query: BlockhashQuery::FeeCalculator(
|
|
|
|
blockhash_query::Source::NonceAccount(nonce_address),
|
|
|
|
blockhash
|
|
|
|
),
|
2020-05-15 09:35:43 -07:00
|
|
|
nonce_account: Some(nonce_address),
|
2020-02-24 16:03:30 -08:00
|
|
|
nonce_authority: 1,
|
|
|
|
fee_payer: 0,
|
2020-02-07 11:16:35 -08:00
|
|
|
},
|
2020-02-24 16:03:30 -08:00
|
|
|
signers: vec![
|
|
|
|
read_keypair_file(&default_keypair_file).unwrap().into(),
|
|
|
|
read_keypair_file(&nonce_authority_file).unwrap().into()
|
|
|
|
],
|
2020-02-07 11:16:35 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|