Request/reqwest improvements

- Use json macro to simplify request builds
- Add proxy option for reqwest to use TLS
- Add rpc port options for configured nodes
This commit is contained in:
Tyera Eulberg 2018-09-21 15:27:03 -06:00 committed by Tyera Eulberg
parent ad4fef4f09
commit e87cac06da
3 changed files with 58 additions and 30 deletions

View File

@ -54,8 +54,18 @@ pub fn parse_args(matches: &ArgMatches) -> Result<WalletConfig, Box<error::Error
let mut drone_addr = leader.contact_info.tpu; let mut drone_addr = leader.contact_info.tpu;
drone_addr.set_port(DRONE_PORT); drone_addr.set_port(DRONE_PORT);
let mut rpc_addr = leader.contact_info.tpu; let rpc_addr = if let Some(proxy) = matches.value_of("proxy") {
rpc_addr.set_port(RPC_PORT); proxy.to_string()
} else {
let rpc_port = if let Some(port) = matches.value_of("rpc-port") {
port.to_string().parse().expect("integer")
} else {
RPC_PORT
};
let mut rpc_addr = leader.contact_info.tpu;
rpc_addr.set_port(rpc_port);
format!("http://{}", rpc_addr.to_string())
};
let command = parse_command(id.pubkey(), &matches)?; let command = parse_command(id.pubkey(), &matches)?;
@ -63,7 +73,7 @@ pub fn parse_args(matches: &ArgMatches) -> Result<WalletConfig, Box<error::Error
leader, leader,
id, id,
drone_addr, // TODO: Add an option for this. drone_addr, // TODO: Add an option for this.
rpc_addr, // TODO: Add an option for this. rpc_addr,
command, command,
}) })
} }
@ -92,6 +102,19 @@ fn main() -> Result<(), Box<error::Error>> {
.value_name("SECS") .value_name("SECS")
.takes_value(true) .takes_value(true)
.help("Max seconds to wait to get necessary gossip from the network"), .help("Max seconds to wait to get necessary gossip from the network"),
).arg(
Arg::with_name("rpc-port")
.long("port")
.takes_value(true)
.value_name("NUM")
.help("Optional rpc-port configuration to connect to non-default nodes")
).arg(
Arg::with_name("proxy")
.long("proxy")
.takes_value(true)
.value_name("URL")
.help("Address of TLS proxy")
.conflicts_with("rpc-port")
).subcommand( ).subcommand(
SubCommand::with_name("airdrop") SubCommand::with_name("airdrop")
.about("Request a batch of tokens") .about("Request a batch of tokens")

View File

@ -94,6 +94,7 @@ extern crate ring;
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
#[macro_use]
extern crate serde_json; extern crate serde_json;
extern crate sha2; extern crate sha2;
extern crate socket2; extern crate socket2;

View File

@ -59,7 +59,7 @@ pub struct WalletConfig {
pub leader: NodeInfo, pub leader: NodeInfo,
pub id: Keypair, pub id: Keypair,
pub drone_addr: SocketAddr, pub drone_addr: SocketAddr,
pub rpc_addr: SocketAddr, pub rpc_addr: String,
pub command: WalletCommand, pub command: WalletCommand,
} }
@ -70,7 +70,7 @@ impl Default for WalletConfig {
leader: NodeInfo::new_with_socketaddr(&default_addr), leader: NodeInfo::new_with_socketaddr(&default_addr),
id: Keypair::new(), id: Keypair::new(),
drone_addr: default_addr, drone_addr: default_addr,
rpc_addr: default_addr, rpc_addr: default_addr.to_string(),
command: WalletCommand::Balance, command: WalletCommand::Balance,
} }
} }
@ -135,17 +135,21 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
// Get address of this client // Get address of this client
WalletCommand::Address => Ok(format!("{}", config.id.pubkey())), WalletCommand::Address => Ok(format!("{}", config.id.pubkey())),
// Request an airdrop from Solana Drone; // Request an airdrop from Solana Drone;
// Request amount is set in request_airdrop function
WalletCommand::AirDrop(tokens) => { WalletCommand::AirDrop(tokens) => {
println!( println!(
"Requesting airdrop of {:?} tokens from {}", "Requesting airdrop of {:?} tokens from {}",
tokens, config.drone_addr tokens, config.drone_addr
); );
let params = format!("[\"{}\"]", config.id.pubkey()); let params = json!(format!("{}", config.id.pubkey()));
let previous_balance = WalletRpcRequest::GetBalance let previous_balance = match WalletRpcRequest::GetBalance
.make_rpc_request(&config.rpc_addr, 1, Some(params))? .make_rpc_request(&config.rpc_addr, 1, Some(params))?
.as_i64() .as_i64()
.unwrap_or(0); {
Some(tokens) => tokens,
None => Err(WalletError::RpcRequestError(
"Received result of an unexpected type".to_string(),
))?,
};
request_airdrop(&config.drone_addr, &config.id.pubkey(), tokens as u64)?; request_airdrop(&config.drone_addr, &config.id.pubkey(), tokens as u64)?;
// TODO: return airdrop Result from Drone instead of polling the // TODO: return airdrop Result from Drone instead of polling the
@ -153,7 +157,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
let mut current_balance = previous_balance; let mut current_balance = previous_balance;
for _ in 0..20 { for _ in 0..20 {
sleep(Duration::from_millis(500)); sleep(Duration::from_millis(500));
let params = format!("[\"{}\"]", config.id.pubkey()); let params = json!(format!("{}", config.id.pubkey()));
current_balance = WalletRpcRequest::GetBalance current_balance = WalletRpcRequest::GetBalance
.make_rpc_request(&config.rpc_addr, 1, Some(params))? .make_rpc_request(&config.rpc_addr, 1, Some(params))?
.as_i64() .as_i64()
@ -171,7 +175,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
} }
WalletCommand::Balance => { WalletCommand::Balance => {
println!("Balance requested..."); println!("Balance requested...");
let params = format!("[\"{}\"]", config.id.pubkey()); let params = json!(format!("{}", config.id.pubkey()));
let balance = WalletRpcRequest::GetBalance let balance = WalletRpcRequest::GetBalance
.make_rpc_request(&config.rpc_addr, 1, Some(params))? .make_rpc_request(&config.rpc_addr, 1, Some(params))?
.as_i64(); .as_i64();
@ -185,7 +189,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
} }
// Confirm the last client transaction by signature // Confirm the last client transaction by signature
WalletCommand::Confirm(signature) => { WalletCommand::Confirm(signature) => {
let params = format!("[\"{}\"]", signature); let params = json!(format!("{}", signature));
let confirmation = WalletRpcRequest::ConfirmTransaction let confirmation = WalletRpcRequest::ConfirmTransaction
.make_rpc_request(&config.rpc_addr, 1, Some(params))? .make_rpc_request(&config.rpc_addr, 1, Some(params))?
.as_bool(); .as_bool();
@ -202,7 +206,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
))?, ))?,
} }
} }
// If client has positive balance, spend tokens in {balance} number of transactions // If client has positive balance, pay tokens to another address
WalletCommand::Pay(tokens, to) => { WalletCommand::Pay(tokens, to) => {
let result = WalletRpcRequest::GetLastId.make_rpc_request(&config.rpc_addr, 1, None)?; let result = WalletRpcRequest::GetLastId.make_rpc_request(&config.rpc_addr, 1, None)?;
if result.as_str().is_none() { if result.as_str().is_none() {
@ -218,11 +222,11 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
let tx = Transaction::new(&config.id, to, tokens, last_id); let tx = Transaction::new(&config.id, to, tokens, last_id);
let serialized = serialize(&tx).unwrap(); let serialized = serialize(&tx).unwrap();
let params = format!("[{}]", serde_json::to_string(&serialized)?); let params = json!(serialized);
let signature = WalletRpcRequest::SendTransaction.make_rpc_request( let signature = WalletRpcRequest::SendTransaction.make_rpc_request(
&config.rpc_addr, &config.rpc_addr,
2, 2,
Some(params.to_string()), Some(params),
)?; )?;
if signature.as_str().is_none() { if signature.as_str().is_none() {
Err(WalletError::RpcRequestError( Err(WalletError::RpcRequestError(
@ -307,11 +311,10 @@ pub enum WalletRpcRequest {
impl WalletRpcRequest { impl WalletRpcRequest {
fn make_rpc_request( fn make_rpc_request(
&self, &self,
rpc_addr: &SocketAddr, rpc_addr: &String,
id: u64, id: u64,
params: Option<String>, params: Option<Value>,
) -> Result<Value, Box<error::Error>> { ) -> Result<Value, Box<error::Error>> {
let rpc_string = format!("http://{}", rpc_addr.to_string());
let jsonrpc = "2.0"; let jsonrpc = "2.0";
let method = match self { let method = match self {
WalletRpcRequest::ConfirmTransaction => "confirmTransaction", WalletRpcRequest::ConfirmTransaction => "confirmTransaction",
@ -324,18 +327,18 @@ impl WalletRpcRequest {
WalletRpcRequest::SendTransaction => "sendTransaction", WalletRpcRequest::SendTransaction => "sendTransaction",
}; };
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let mut request: String = format!( let mut request = json!({
"{{\"jsonrpc\":\"{}\",\"id\":{},\"method\":\"{}\"", "jsonrpc": jsonrpc,
jsonrpc, id, method "id": id,
); "method": method,
});
if let Some(param_string) = params { if let Some(param_string) = params {
request.push_str(&format!(",\"params\":{}", param_string)); request["params"] = json!(vec![param_string]);
} }
request.push_str(&"}".to_string());
let mut response = client let mut response = client
.post(&rpc_string) .post(rpc_addr)
.header(CONTENT_TYPE, "application/json") .header(CONTENT_TYPE, "application/json")
.body(request) .body(request.to_string())
.send()?; .send()?;
let json: Value = serde_json::from_str(&response.text()?)?; let json: Value = serde_json::from_str(&response.text()?)?;
if json["error"].is_object() { if json["error"].is_object() {
@ -499,7 +502,7 @@ mod tests {
let mut rpc_addr = leader_data.contact_info.ncp; let mut rpc_addr = leader_data.contact_info.ncp;
rpc_addr.set_port(rpc_port); rpc_addr.set_port(rpc_port);
config.rpc_addr = rpc_addr; config.rpc_addr = format!("http://{}", rpc_addr.to_string());
let tokens = 50; let tokens = 50;
config.command = WalletCommand::AirDrop(tokens); config.command = WalletCommand::AirDrop(tokens);
@ -569,12 +572,13 @@ mod tests {
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender); run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let mut rpc_addr = leader_data.contact_info.ncp; let mut addr = leader_data.contact_info.ncp;
rpc_addr.set_port(rpc_port); addr.set_port(rpc_port);
let rpc_addr = format!("http://{}", addr.to_string());
let signature = request_airdrop(&drone_addr, &bob_pubkey, 50); let signature = request_airdrop(&drone_addr, &bob_pubkey, 50);
assert!(signature.is_ok()); assert!(signature.is_ok());
let params = format!("[\"{}\"]", signature.unwrap()); let params = json!(format!("{}", signature.unwrap()));
let confirmation = WalletRpcRequest::ConfirmTransaction let confirmation = WalletRpcRequest::ConfirmTransaction
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()