diff --git a/Cargo.lock b/Cargo.lock index 12d393fa39..072568c654 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2775,6 +2775,7 @@ dependencies = [ "solana-sdk 0.15.0", "solana-vote-api 0.15.0", "solana-vote-signer 0.15.0", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/book/src/testnet-participation.md b/book/src/testnet-participation.md index a21c5fbc7e..121915733c 100644 --- a/book/src/testnet-participation.md +++ b/book/src/testnet-participation.md @@ -104,8 +104,8 @@ $ export PATH=$PWD/bin:$PATH Sanity check that you are able to interact with the cluster by receiving a small airdrop of lamports from the testnet drone: ```bash -$ solana-wallet -n testnet.solana.com airdrop 123 -$ solana-wallet -n testnet.solana.com balance +$ solana-wallet airdrop 123 +$ solana-wallet balance ``` Also try running following command to join the gossip network and view all the other nodes in the cluster: diff --git a/ci/localnet-sanity.sh b/ci/localnet-sanity.sh index a88ee26ad2..ecbc4d949f 100755 --- a/ci/localnet-sanity.sh +++ b/ci/localnet-sanity.sh @@ -7,7 +7,7 @@ restartInterval=never rollingRestart=false maybeNoLeaderRotation= extraNodes=0 -walletRpcEndpoint= +walletRpcPort= usage() { exitcode=0 @@ -61,7 +61,7 @@ while getopts "ch?i:k:brxR" opt; do extraNodes=$((extraNodes + 1)) ;; r) - walletRpcEndpoint="--rpc-port 18899" + walletRpcPort=":18899" ;; R) rollingRestart=true @@ -362,8 +362,7 @@ while [[ $iteration -le $iterations ]]; do } ( set -x - # shellcheck disable=SC2086 # Don't want to double quote $walletRpcEndpoint - timeout 60s scripts/wallet-sanity.sh $walletRpcEndpoint + timeout 60s scripts/wallet-sanity.sh --url http://127.0.0.1"$walletRpcPort" ) || flag_error_if_no_leader_rotation iteration=$((iteration + 1)) diff --git a/install/src/main.rs b/install/src/main.rs index 399f049b51..ba9c4b9844 100644 --- a/install/src/main.rs +++ b/install/src/main.rs @@ -10,15 +10,23 @@ mod config; mod defaults; mod update_manifest; -fn url_validator(url: String) -> Result<(), String> { - match url::Url::parse(&url) { - Ok(_) => Ok(()), +// Return an error if a url cannot be parsed. +fn is_url(string: String) -> Result<(), String> { + match url::Url::parse(&string) { + Ok(url) => { + if url.has_host() { + Ok(()) + } else { + Err("no host provided".to_string()) + } + } Err(err) => Err(format!("{:?}", err)), } } -fn pubkey_validator(pubkey: String) -> Result<(), String> { - match pubkey.parse::() { +// Return an error if a pubkey cannot be parsed. +fn is_pubkey(string: String) -> Result<(), String> { + match string.parse::() { Ok(_) => Ok(()), Err(err) => Err(format!("{:?}", err)), } @@ -67,7 +75,7 @@ fn main() -> Result<(), String> { .value_name("URL") .takes_value(true) .default_value(defaults::JSON_RPC_URL) - .validator(url_validator) + .validator(is_url) .help("JSON RPC URL for the solana cluster"), ) .arg( @@ -82,7 +90,7 @@ fn main() -> Result<(), String> { .value_name("PUBKEY") .takes_value(true) .required(true) - .validator(pubkey_validator) + .validator(is_pubkey) .help("Public key of the update manifest"); match defaults::update_manifest_pubkey(build_env::TARGET) { @@ -128,14 +136,14 @@ fn main() -> Result<(), String> { .value_name("URL") .takes_value(true) .default_value(defaults::JSON_RPC_URL) - .validator(url_validator) + .validator(is_url) .help("JSON RPC URL for the solana cluster"), ) .arg( Arg::with_name("download_url") .index(1) .required(true) - .validator(url_validator) + .validator(is_url) .help("URL to the solana release archive"), ) .arg( diff --git a/multinode-demo/fullnode.sh b/multinode-demo/fullnode.sh index 8878b5958b..da308d5903 100755 --- a/multinode-demo/fullnode.sh +++ b/multinode-demo/fullnode.sh @@ -72,7 +72,7 @@ rsync_url() { # adds the 'rsync://` prefix to URLs that need it airdrop() { declare keypair_file=$1 - declare host=$2 + declare entrypoint_ip=$2 declare amount=$3 declare address @@ -83,7 +83,7 @@ airdrop() { # node restart, costing it lamports declare retries=5 - while ! $solana_wallet --keypair "$keypair_file" --host "$host" airdrop "$amount"; do + while ! $solana_wallet --keypair "$keypair_file" --url "http://$entrypoint_ip:8899" airdrop "$amount"; do # TODO: Consider moving this retry logic into `solana-wallet airdrop` # itself, currently it does not retry on "Connection refused" errors. @@ -100,7 +100,7 @@ airdrop() { } setup_vote_account() { - declare drone_address=$1 + declare entrypoint_ip=$1 declare node_id_path=$2 declare vote_id_path=$3 declare stake=$4 @@ -114,16 +114,17 @@ setup_vote_account() { if [[ -f "$vote_id_path".configured ]]; then echo "Vote account has already been configured" else - airdrop "$node_id_path" "$drone_address" "$stake" || return $? + airdrop "$node_id_path" "$entrypoint_ip" "$stake" || return $? # Fund the vote account from the node, with the node as the node_id - $solana_wallet --keypair "$node_id_path" --host "$drone_address" \ + $solana_wallet --keypair "$node_id_path" --url "http://$entrypoint_ip:8899" \ create-vote-account "$vote_id" "$node_id" $((stake - 1)) || return $? touch "$vote_id_path".configured fi - $solana_wallet --keypair "$node_id_path" --host "$drone_address" show-vote-account "$vote_id" + $solana_wallet --keypair "$node_id_path" --url "http://$entrypoint_ip:8899" \ + show-vote-account "$vote_id" return 0 } diff --git a/net/remote/remote-deploy-update.sh b/net/remote/remote-deploy-update.sh index f69e843389..ce8015322d 100755 --- a/net/remote/remote-deploy-update.sh +++ b/net/remote/remote-deploy-update.sh @@ -31,6 +31,6 @@ loadConfigFile PATH="$HOME"/.cargo/bin:"$PATH" set -x -solana-wallet airdrop 42 +solana-wallet --url http://127.0.0.1:8899 airdrop 42 solana-install deploy "$updateDownloadUrl" update_manifest_keypair.json \ - --url http://localhost:8899 + --url http://127.0.0.1:8899 diff --git a/net/remote/remote-sanity.sh b/net/remote/remote-sanity.sh index 69ca0e4336..be3c4bf76e 100755 --- a/net/remote/remote-sanity.sh +++ b/net/remote/remote-sanity.sh @@ -117,7 +117,7 @@ echo "--- RPC API: getTransactionCount" echo "--- $entrypointIp: wallet sanity" ( set -x - scripts/wallet-sanity.sh --host "$entrypointIp" + scripts/wallet-sanity.sh --url http://"$entrypointIp":8899 ) echo "--- $entrypointIp: verify ledger" diff --git a/scripts/wallet-sanity.sh b/scripts/wallet-sanity.sh index 887caf610a..8e5c83651e 100755 --- a/scripts/wallet-sanity.sh +++ b/scripts/wallet-sanity.sh @@ -9,8 +9,8 @@ cd "$(dirname "$0")"/.. # shellcheck source=multinode-demo/common.sh source multinode-demo/common.sh -if [[ -z $1 ]]; then # no network argument, use default - entrypoint=() +if [[ -z $1 ]]; then # no network argument, use localhost by default + entrypoint=(--url http://127.0.0.1:8899) else entrypoint=("$@") fi diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 944e48a3f7..2f2b379b14 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -25,6 +25,7 @@ solana-netutil = { path = "../netutil", version = "0.15.0" } solana-sdk = { path = "../sdk", version = "0.15.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.15.0" } solana-vote-signer = { path = "../vote-signer", version = "0.15.0" } +url = "1.7.2" [dev-dependencies] solana-budget-program = { path = "../programs/budget_program", version = "0.15.0" } diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 786d7b35f3..7c99b99d37 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -7,12 +7,7 @@ use solana_wallet::wallet::{parse_command, process_command, WalletConfig, Wallet use std::error; pub fn parse_args(matches: &ArgMatches<'_>) -> Result> { - let host = solana_netutil::parse_host(matches.value_of("host").unwrap()).or_else(|err| { - Err(WalletError::BadParameter(format!( - "Invalid host: {:?}", - err - ))) - })?; + let json_rpc_url = matches.value_of("json_rpc_url").unwrap().to_string(); let drone_host = if let Some(drone_host) = matches.value_of("drone_host") { Some(solana_netutil::parse_host(drone_host).or_else(|err| { @@ -25,17 +20,6 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result) -> Result) -> Result Result<(), String> { + match url::Url::parse(&string) { + Ok(url) => { + if url.has_host() { + Ok(()) + } else { + Err("no host provided".to_string()) + } + } + Err(err) => Err(format!("{:?}", err)), + } +} + // Return an error if a pubkey cannot be parsed. fn is_pubkey(string: String) -> Result<(), String> { match string.parse::() { @@ -103,54 +87,29 @@ fn is_pubkey(string: String) -> Result<(), String> { fn main() -> Result<(), Box> { solana_logger::setup(); - let (default_host, default_rpc_port, default_drone_port) = { - let defaults = WalletConfig::default(); - ( - defaults.host.to_string(), - defaults.rpc_port.to_string(), - defaults.drone_port.to_string(), - ) - }; + let default = WalletConfig::default(); + let default_drone_port = format!("{}", default.drone_port); let matches = App::new(crate_name!()) .about(crate_description!()) .version(crate_version!()) - .setting(AppSettings::ArgRequiredElseHelp) + .setting(AppSettings::SubcommandRequiredElseHelp) .arg( - Arg::with_name("host") - .short("n") - .long("host") - .value_name("IP ADDRESS") + Arg::with_name("json_rpc_url") + .short("u") + .long("url") + .value_name("URL") .takes_value(true) - .default_value(&default_host) - .help("Host to use for both RPC and drone"), - ) - .arg( - Arg::with_name("rpc_host") - .long("rpc-host") - .value_name("IP ADDRESS") - .takes_value(true) - .help("RPC host to use [default: same as --host]"), - ) - .arg( - Arg::with_name("rpc_port") - .long("rpc-port") - .value_name("PORT") - .takes_value(true) - .default_value(&default_rpc_port) - .help("RPC port to use"), - ) - .arg( - Arg::with_name("rpc_tps") - .long("rpc-tls") - .help("Enable TLS for the RPC endpoint"), + .default_value(&default.json_rpc_url) + .validator(is_url) + .help("JSON RPC URL for the solana cluster"), ) .arg( Arg::with_name("drone_host") .long("drone-host") - .value_name("IP ADDRESS") + .value_name("HOST") .takes_value(true) - .help("Drone host to use [default: same as --host]"), + .help("Drone host to use [default: same as the --url host]"), ) .arg( Arg::with_name("drone_port") diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index 30ba235b51..83c48f8125 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -9,7 +9,7 @@ use solana_budget_api; use solana_budget_api::budget_instruction; use solana_budget_api::budget_state::BudgetError; use solana_client::client_error::ClientError; -use solana_client::rpc_client::{get_rpc_request_str, RpcClient}; +use solana_client::rpc_client::RpcClient; #[cfg(not(test))] use solana_drone::drone::request_airdrop_transaction; use solana_drone::drone::DRONE_PORT; @@ -21,7 +21,6 @@ use solana_sdk::instruction::InstructionError; use solana_sdk::instruction_processor_utils::DecodeError; use solana_sdk::loader_instruction; use solana_sdk::pubkey::Pubkey; -use solana_sdk::rpc_port::DEFAULT_RPC_PORT; use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::system_instruction::SystemError; use solana_sdk::system_transaction; @@ -29,7 +28,7 @@ use solana_sdk::transaction::{Transaction, TransactionError}; use solana_vote_api::vote_instruction; use std::fs::File; use std::io::Read; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, SocketAddr}; use std::{error, fmt, mem}; const USERDATA_CHUNK_SIZE: usize = 256; @@ -88,15 +87,12 @@ impl error::Error for WalletError { } pub struct WalletConfig { - pub keypair: Keypair, pub command: WalletCommand, pub drone_host: Option, pub drone_port: u16, - pub host: IpAddr, + pub json_rpc_url: String, + pub keypair: Keypair, pub rpc_client: Option, - pub rpc_host: Option, - pub rpc_port: u16, - pub rpc_tls: bool, } impl Default for WalletConfig { @@ -105,25 +101,27 @@ impl Default for WalletConfig { command: WalletCommand::Balance(Pubkey::default()), drone_host: None, drone_port: DRONE_PORT, - host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), + json_rpc_url: "http://testnet.solana.com:8899".to_string(), keypair: Keypair::new(), rpc_client: None, - rpc_host: None, - rpc_port: DEFAULT_RPC_PORT, - rpc_tls: false, } } } impl WalletConfig { pub fn drone_addr(&self) -> SocketAddr { - SocketAddr::new(self.drone_host.unwrap_or(self.host), self.drone_port) - } - - pub fn rpc_addr(&self) -> String { - get_rpc_request_str( - SocketAddr::new(self.rpc_host.unwrap_or(self.host), self.rpc_port), - self.rpc_tls, + SocketAddr::new( + self.drone_host.unwrap_or_else(|| { + let drone_host = url::Url::parse(&self.json_rpc_url) + .unwrap() + .host() + .unwrap() + .to_string(); + solana_netutil::parse_host(&drone_host).unwrap_or_else(|err| { + panic!("Unable to resolve {}: {}", drone_host, err); + }) + }), + self.drone_port, ) } } @@ -645,8 +643,7 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { let mut _rpc_client; let rpc_client = if config.rpc_client.is_none() { - let rpc_addr = config.rpc_addr(); - _rpc_client = RpcClient::new(rpc_addr); + _rpc_client = RpcClient::new(config.json_rpc_url.to_string()); &_rpc_client } else { // Primarily for testing @@ -823,31 +820,23 @@ mod tests { #[test] fn test_wallet_config_drone_addr() { let mut config = WalletConfig::default(); + config.json_rpc_url = "http://127.0.0.1:8899".to_string(); + let rpc_host = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); assert_eq!( config.drone_addr(), - SocketAddr::new(config.host, config.drone_port) + SocketAddr::new(rpc_host, config.drone_port) ); config.drone_port = 1234; - assert_eq!(config.drone_addr(), SocketAddr::new(config.host, 1234)); + assert_eq!(config.drone_addr(), SocketAddr::new(rpc_host, 1234)); - config.drone_host = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2))); + config.drone_host = Some(rpc_host); assert_eq!( config.drone_addr(), SocketAddr::new(config.drone_host.unwrap(), 1234) ); } - #[test] - fn test_wallet_config_rpc_addr() { - let mut config = WalletConfig::default(); - assert_eq!(config.rpc_addr(), "http://127.0.0.1:8899"); - config.rpc_port = 1234; - assert_eq!(config.rpc_addr(), "http://127.0.0.1:1234"); - config.rpc_host = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2))); - assert_eq!(config.rpc_addr(), "http://127.0.0.2:1234"); - } - #[test] fn test_wallet_parse_command() { let test_commands = App::new("test") diff --git a/wallet/tests/deploy.rs b/wallet/tests/deploy.rs index ffa68c6d53..1b4a3ea87a 100644 --- a/wallet/tests/deploy.rs +++ b/wallet/tests/deploy.rs @@ -30,7 +30,7 @@ fn test_wallet_deploy_program() { let mut config = WalletConfig::default(); config.drone_port = drone_addr.port(); - config.rpc_port = leader_data.rpc.port(); + config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.command = WalletCommand::Airdrop(50); process_command(&config).unwrap(); diff --git a/wallet/tests/pay.rs b/wallet/tests/pay.rs index 7481eb3b9c..b2843d6822 100644 --- a/wallet/tests/pay.rs +++ b/wallet/tests/pay.rs @@ -31,11 +31,12 @@ fn test_wallet_timestamp_tx() { let mut config_payer = WalletConfig::default(); config_payer.drone_port = drone_addr.port(); - config_payer.rpc_port = leader_data.rpc.port(); + config_payer.json_rpc_url = + format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); let mut config_witness = WalletConfig::default(); - config_witness.drone_port = drone_addr.port(); - config_witness.rpc_port = leader_data.rpc.port(); + config_witness.drone_port = config_payer.drone_port; + config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); assert_ne!( config_payer.keypair.pubkey(), @@ -95,11 +96,12 @@ fn test_wallet_witness_tx() { let mut config_payer = WalletConfig::default(); config_payer.drone_port = drone_addr.port(); - config_payer.rpc_port = leader_data.rpc.port(); + config_payer.json_rpc_url = + format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); let mut config_witness = WalletConfig::default(); - config_witness.drone_port = drone_addr.port(); - config_witness.rpc_port = leader_data.rpc.port(); + config_witness.drone_port = config_payer.drone_port; + config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); assert_ne!( config_payer.keypair.pubkey(), @@ -156,11 +158,12 @@ fn test_wallet_cancel_tx() { let mut config_payer = WalletConfig::default(); config_payer.drone_port = drone_addr.port(); - config_payer.rpc_port = leader_data.rpc.port(); + config_payer.json_rpc_url = + format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); let mut config_witness = WalletConfig::default(); - config_witness.drone_port = drone_addr.port(); - config_witness.rpc_port = leader_data.rpc.port(); + config_witness.drone_port = config_payer.drone_port; + config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); assert_ne!( config_payer.keypair.pubkey(), diff --git a/wallet/tests/request_airdrop.rs b/wallet/tests/request_airdrop.rs index 01bb7f5ed2..29dfd0882e 100644 --- a/wallet/tests/request_airdrop.rs +++ b/wallet/tests/request_airdrop.rs @@ -15,7 +15,8 @@ fn test_wallet_request_airdrop() { let mut bob_config = WalletConfig::default(); bob_config.drone_port = drone_addr.port(); - bob_config.rpc_port = leader_data.rpc.port(); + bob_config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); + bob_config.command = WalletCommand::Airdrop(50); let sig_response = process_command(&bob_config);