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;
drone_addr.set_port(DRONE_PORT);
let mut rpc_addr = leader.contact_info.tpu;
rpc_addr.set_port(RPC_PORT);
let rpc_addr = if let Some(proxy) = matches.value_of("proxy") {
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)?;
@ -63,7 +73,7 @@ pub fn parse_args(matches: &ArgMatches) -> Result<WalletConfig, Box<error::Error
leader,
id,
drone_addr, // TODO: Add an option for this.
rpc_addr, // TODO: Add an option for this.
rpc_addr,
command,
})
}
@ -92,6 +102,19 @@ fn main() -> Result<(), Box<error::Error>> {
.value_name("SECS")
.takes_value(true)
.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::with_name("airdrop")
.about("Request a batch of tokens")

View File

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

View File

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