Drone now returns signed airdrop transactions

This commit is contained in:
Michael Vines 2018-11-14 18:57:34 -08:00
parent 3543a9a49f
commit e791d0f74d
11 changed files with 316 additions and 272 deletions

View File

@ -10,7 +10,7 @@ solana-keygen -o /config/drone-keypair.json
solana-fullnode-config --keypair=/config/leader-keypair.json -l > /config/leader-config.json solana-fullnode-config --keypair=/config/leader-keypair.json -l > /config/leader-config.json
solana-genesis --num_tokens 1000000000 --mint /config/drone-keypair.json --bootstrap_leader /config/leader-config.json --ledger /ledger solana-genesis --num_tokens 1000000000 --mint /config/drone-keypair.json --bootstrap_leader /config/leader-config.json --ledger /ledger
solana-drone --keypair /config/drone-keypair.json --network 127.0.0.1:8001 & solana-drone --keypair /config/drone-keypair.json &
drone=$! drone=$!
solana-fullnode --identity /config/leader-config.json --ledger /ledger/ --rpc 8899 & solana-fullnode --identity /config/leader-config.json --ledger /ledger/ --rpc 8899 &
fullnode=$! fullnode=$!

View File

@ -12,16 +12,13 @@ usage() {
echo "$*" echo "$*"
echo echo
fi fi
echo "usage: $0 [network entry point]" echo "usage: $0]"
echo echo
echo " Run an airdrop drone for the specified network" echo " Run an airdrop drone"
echo echo
exit 1 exit 1
} }
read -r _ leader_address shift < <(find_leader "${@:1:1}")
shift "$shift"
[[ -f "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json ]] || { [[ -f "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json ]] || {
echo "$SOLANA_CONFIG_PRIVATE_DIR/mint.json not found, create it by running:" echo "$SOLANA_CONFIG_PRIVATE_DIR/mint.json not found, create it by running:"
echo echo
@ -34,7 +31,6 @@ set -ex
trap 'kill "$pid" && wait "$pid"' INT TERM trap 'kill "$pid" && wait "$pid"' INT TERM
$solana_drone \ $solana_drone \
--keypair "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json \ --keypair "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json \
--network "$leader_address" \
> >($drone_logger) 2>&1 & > >($drone_logger) 2>&1 &
pid=$! pid=$!
wait "$pid" wait "$pid"

View File

@ -16,7 +16,7 @@ use rand::{thread_rng, Rng};
use rayon::prelude::*; use rayon::prelude::*;
use solana::client::mk_client; use solana::client::mk_client;
use solana::cluster_info::{ClusterInfo, NodeInfo}; use solana::cluster_info::{ClusterInfo, NodeInfo};
use solana::drone::DRONE_PORT; use solana::drone::{request_airdrop_transaction, DRONE_PORT};
use solana::hash::Hash; use solana::hash::Hash;
use solana::logger; use solana::logger;
use solana::metrics; use solana::metrics;
@ -28,7 +28,6 @@ use solana::thin_client::{poll_gossip_for_leader, ThinClient};
use solana::timing::timestamp; use solana::timing::timestamp;
use solana::timing::{duration_as_ms, duration_as_s}; use solana::timing::{duration_as_ms, duration_as_s};
use solana::transaction::Transaction; use solana::transaction::Transaction;
use solana::wallet::request_airdrop;
use solana::window::default_window; use solana::window::default_window;
use std::cmp; use std::cmp;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -393,27 +392,26 @@ fn airdrop_tokens(client: &mut ThinClient, leader: &NodeInfo, id: &Keypair, tx_c
id.pubkey(), id.pubkey(),
); );
if let Err(e) = request_airdrop(&drone_addr, &id.pubkey(), airdrop_amount as u64) { let last_id = client.get_last_id();
match request_airdrop_transaction(&drone_addr, &id.pubkey(), airdrop_amount, last_id) {
Ok(transaction) => {
let signature = client.transfer_signed(&transaction).unwrap();
client.poll_for_signature(&signature).unwrap();
}
Err(err) => {
panic!( panic!(
"Error requesting airdrop: {:?} to addr: {:?} amount: {}", "Error requesting airdrop: {:?} to addr: {:?} amount: {}",
e, drone_addr, airdrop_amount err, drone_addr, airdrop_amount
); );
} }
};
// TODO: return airdrop Result from Drone instead of polling the let current_balance = client.poll_get_balance(&id.pubkey()).unwrap_or_else(|e| {
// network
let mut current_balance = starting_balance;
for _ in 0..20 {
sleep(Duration::from_millis(500));
current_balance = client.poll_get_balance(&id.pubkey()).unwrap_or_else(|e| {
println!("airdrop error {}", e); println!("airdrop error {}", e);
starting_balance starting_balance
}); });
if starting_balance != current_balance {
break;
}
println!("current balance {}...", current_balance); println!("current balance {}...", current_balance);
}
metrics_submit_token_balance(current_balance); metrics_submit_token_balance(current_balance);
if current_balance - starting_balance != airdrop_amount { if current_balance - starting_balance != airdrop_amount {
println!( println!(

View File

@ -1,4 +1,5 @@
extern crate bincode; extern crate bincode;
extern crate byteorder;
extern crate bytes; extern crate bytes;
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
@ -9,6 +10,7 @@ extern crate tokio;
extern crate tokio_codec; extern crate tokio_codec;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use bytes::Bytes; use bytes::Bytes;
use clap::{App, Arg}; use clap::{App, Arg};
use solana::drone::{Drone, DroneRequest, DRONE_PORT}; use solana::drone::{Drone, DroneRequest, DRONE_PORT};
@ -18,7 +20,6 @@ use solana::signature::read_keypair;
use std::error; use std::error;
use std::io; use std::io;
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use std::process::exit;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use tokio::net::TcpListener; use tokio::net::TcpListener;
@ -41,14 +42,6 @@ fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("drone") let matches = App::new("drone")
.version(crate_version!()) .version(crate_version!())
.arg( .arg(
Arg::with_name("network")
.short("n")
.long("network")
.value_name("HOST:PORT")
.takes_value(true)
.required(true)
.help("Rendezvous with the network at this gossip entry point"),
).arg(
Arg::with_name("keypair") Arg::with_name("keypair")
.short("k") .short("k")
.long("keypair") .long("keypair")
@ -70,15 +63,6 @@ fn main() -> Result<(), Box<error::Error>> {
.help("Request limit for time slice"), .help("Request limit for time slice"),
).get_matches(); ).get_matches();
let network = matches
.value_of("network")
.unwrap()
.parse()
.unwrap_or_else(|e| {
eprintln!("failed to parse network: {}", e);
exit(1)
});
let mint_keypair = let mint_keypair =
read_keypair(matches.value_of("keypair").unwrap()).expect("failed to read client keypair"); read_keypair(matches.value_of("keypair").unwrap()).expect("failed to read client keypair");
@ -99,8 +83,6 @@ fn main() -> Result<(), Box<error::Error>> {
let drone = Arc::new(Mutex::new(Drone::new( let drone = Arc::new(Mutex::new(Drone::new(
mint_keypair, mint_keypair,
drone_addr,
network,
time_slice, time_slice,
request_cap, request_cap,
))); )));
@ -131,23 +113,34 @@ fn main() -> Result<(), Box<error::Error>> {
)) ))
})?; })?;
println!("Airdrop requested..."); println!("Airdrop transaction requested...{:?}", req);
// let res = drone2.lock().unwrap().check_rate_limit(client_ip); // let res = drone2.lock().unwrap().check_rate_limit(client_ip);
let res1 = drone2.lock().unwrap().send_airdrop(req); let res = drone2.lock().unwrap().build_airdrop_transaction(req);
match res1 { match res {
Ok(_) => println!("Airdrop sent!"), Ok(tx) => {
Err(_) => println!("Request limit reached for this time slice"), let response_vec = serialize(&tx).or_else(|err| {
}
let response = res1?;
println!("Airdrop tx signature: {:?}", response);
let response_vec = serialize(&response).or_else(|err| {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
format!("serialize signature in drone: {:?}", err), format!("deserialize packet in drone: {:?}", err),
)) ))
})?; })?;
let response_bytes = Bytes::from(response_vec.clone());
let mut response_vec_with_length = vec![0; 2];
LittleEndian::write_u16(
&mut response_vec_with_length,
response_vec.len() as u16,
);
response_vec_with_length.extend_from_slice(&response_vec);
let response_bytes = Bytes::from(response_vec_with_length);
println!("Airdrop transaction granted");
Ok(response_bytes) Ok(response_bytes)
}
Err(err) => {
println!("Airdrop transaction failed: {:?}", err);
Err(err)
}
}
}); });
let server = writer let server = writer
.send_all(processor.or_else(|err| { .send_all(processor.or_else(|err| {

View File

@ -10,7 +10,7 @@ extern crate solana;
use clap::{App, Arg}; use clap::{App, Arg};
use solana::client::mk_client; use solana::client::mk_client;
use solana::cluster_info::{Node, FULLNODE_PORT_RANGE}; use solana::cluster_info::{Node, FULLNODE_PORT_RANGE};
use solana::drone::DRONE_PORT; use solana::drone::{request_airdrop_transaction, DRONE_PORT};
use solana::fullnode::{Config, Fullnode, FullnodeReturnType}; use solana::fullnode::{Config, Fullnode, FullnodeReturnType};
use solana::leader_scheduler::LeaderScheduler; use solana::leader_scheduler::LeaderScheduler;
use solana::logger; use solana::logger;
@ -19,7 +19,6 @@ use solana::netutil::find_available_port_in_range;
use solana::signature::{Keypair, KeypairUtil}; use solana::signature::{Keypair, KeypairUtil};
use solana::thin_client::poll_gossip_for_leader; use solana::thin_client::poll_gossip_for_leader;
use solana::vote_program::VoteProgram; use solana::vote_program::VoteProgram;
use solana::wallet::request_airdrop;
use std::fs::File; use std::fs::File;
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use std::process::exit; use std::process::exit;
@ -159,9 +158,14 @@ fn main() {
info!("requesting airdrop from {}", drone_addr); info!("requesting airdrop from {}", drone_addr);
loop { loop {
if request_airdrop(&drone_addr, &pubkey, 50).is_ok() { let last_id = client.get_last_id();
if let Ok(transaction) = request_airdrop_transaction(&drone_addr, &pubkey, 50, last_id)
{
let signature = client.transfer_signed(&transaction).unwrap();
if client.poll_for_signature(&signature).is_ok() {
break; break;
} }
}
info!( info!(
"airdrop request, is the drone address correct {:?}, drone running?", "airdrop request, is the drone address correct {:?}, drone running?",
drone_addr drone_addr

View File

@ -9,7 +9,7 @@ use clap::{App, Arg};
use solana::chacha::{chacha_cbc_encrypt_file, CHACHA_BLOCK_SIZE}; use solana::chacha::{chacha_cbc_encrypt_file, CHACHA_BLOCK_SIZE};
use solana::client::mk_client; use solana::client::mk_client;
use solana::cluster_info::Node; use solana::cluster_info::Node;
use solana::drone::DRONE_PORT; use solana::drone::{request_airdrop_transaction, DRONE_PORT};
use solana::fullnode::Config; use solana::fullnode::Config;
use solana::ledger::LEDGER_DATA_FILE; use solana::ledger::LEDGER_DATA_FILE;
use solana::logger; use solana::logger;
@ -17,7 +17,6 @@ use solana::replicator::{sample_file, Replicator};
use solana::signature::{Keypair, KeypairUtil}; use solana::signature::{Keypair, KeypairUtil};
use solana::storage_transaction::StorageTransaction; use solana::storage_transaction::StorageTransaction;
use solana::transaction::Transaction; use solana::transaction::Transaction;
use solana::wallet::request_airdrop;
use std::fs::File; use std::fs::File;
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use std::path::Path; use std::path::Path;
@ -132,9 +131,12 @@ fn main() {
let mut drone_addr = leader_info.tpu; let mut drone_addr = leader_info.tpu;
drone_addr.set_port(DRONE_PORT); drone_addr.set_port(DRONE_PORT);
let airdrop_amount = 5; let airdrop_amount = 5;
if let Err(e) = request_airdrop(&drone_addr, &keypair.pubkey(), airdrop_amount) { let last_id = client.get_last_id();
panic!("couldn't get airdrop {}: {}!", airdrop_amount, e); let transaction =
} request_airdrop_transaction(&drone_addr, &keypair.pubkey(), airdrop_amount, last_id)
.unwrap();
let signature = client.transfer_signed(&transaction).unwrap();
client.poll_for_signature(&signature).unwrap();
match sample_file(&ledger_data_file_encrypted, &sampling_offsets) { match sample_file(&ledger_data_file_encrypted, &sampling_offsets) {
Ok(hash) => { Ok(hash) => {

View File

@ -5,20 +5,22 @@
//! and (to come) an IP rate limit. //! and (to come) an IP rate limit.
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use bytes::Bytes; use bytes::Bytes;
use hash::Hash;
use influx_db_client as influxdb; use influx_db_client as influxdb;
use metrics; use metrics;
use signature::{Keypair, Signature}; use packet::PACKET_DATA_SIZE;
use signature::Keypair;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::io; use std::io;
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use system_transaction::SystemTransaction; use system_transaction::SystemTransaction;
use thin_client::{poll_gossip_for_leader, ThinClient};
use tokio; use tokio;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio::prelude::*; use tokio::prelude::*;
@ -32,16 +34,15 @@ pub const DRONE_PORT: u16 = 9900;
#[derive(Serialize, Deserialize, Debug, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum DroneRequest { pub enum DroneRequest {
GetAirdrop { GetAirdrop {
airdrop_request_amount: u64, tokens: u64,
client_pubkey: Pubkey, to: Pubkey,
last_id: Hash,
}, },
} }
pub struct Drone { pub struct Drone {
mint_keypair: Keypair, mint_keypair: Keypair,
ip_cache: Vec<IpAddr>, ip_cache: Vec<IpAddr>,
_airdrop_addr: SocketAddr,
network_addr: SocketAddr,
pub time_slice: Duration, pub time_slice: Duration,
request_cap: u64, request_cap: u64,
pub request_current: u64, pub request_current: u64,
@ -50,8 +51,6 @@ pub struct Drone {
impl Drone { impl Drone {
pub fn new( pub fn new(
mint_keypair: Keypair, mint_keypair: Keypair,
_airdrop_addr: SocketAddr,
network_addr: SocketAddr,
time_input: Option<u64>, time_input: Option<u64>,
request_cap_input: Option<u64>, request_cap_input: Option<u64>,
) -> Drone { ) -> Drone {
@ -66,8 +65,6 @@ impl Drone {
Drone { Drone {
mint_keypair, mint_keypair,
ip_cache: Vec::new(), ip_cache: Vec::new(),
_airdrop_addr,
network_addr,
time_slice, time_slice,
request_cap, request_cap,
request_current: 0, request_current: 0,
@ -102,52 +99,39 @@ impl Drone {
} }
} }
pub fn send_airdrop(&mut self, req: DroneRequest) -> Result<Signature, io::Error> { pub fn build_airdrop_transaction(
let request_amount: u64; &mut self,
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap(); req: DroneRequest,
) -> Result<Transaction, io::Error> {
let leader = poll_gossip_for_leader(self.network_addr, Some(10)) trace!("build_airdrop_transaction: {:?}", req);
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; match req {
let mut client = ThinClient::new(leader.rpc, leader.tpu, transactions_socket);
let last_id = client.get_last_id();
let mut tx = match req {
DroneRequest::GetAirdrop { DroneRequest::GetAirdrop {
airdrop_request_amount, tokens,
client_pubkey, to,
} => {
info!(
"Requesting airdrop of {} to {:?}",
airdrop_request_amount, client_pubkey
);
request_amount = airdrop_request_amount;
Transaction::system_new(
&self.mint_keypair,
client_pubkey,
airdrop_request_amount as u64,
last_id, last_id,
) } => {
} if self.check_request_limit(tokens) {
}; self.request_current += tokens;
if self.check_request_limit(request_amount) {
self.request_current += request_amount;
metrics::submit( metrics::submit(
influxdb::Point::new("drone") influxdb::Point::new("drone")
.add_tag("op", influxdb::Value::String("airdrop".to_string())) .add_tag("op", influxdb::Value::String("airdrop".to_string()))
.add_field("request_amount", influxdb::Value::Integer(tokens as i64))
.add_field( .add_field(
"request_amount",
influxdb::Value::Integer(request_amount as i64),
).add_field(
"request_current", "request_current",
influxdb::Value::Integer(self.request_current as i64), influxdb::Value::Integer(self.request_current as i64),
).to_owned(), ).to_owned(),
); );
client.retry_transfer(&self.mint_keypair, &mut tx, 10)
info!("Requesting airdrop of {} to {:?}", tokens, to);
let mut tx = Transaction::system_new(&self.mint_keypair, to, tokens, last_id);
tx.sign(&[&self.mint_keypair], last_id);
Ok(tx)
} else { } else {
Err(Error::new(ErrorKind::Other, "token limit reached")) Err(Error::new(ErrorKind::Other, "token limit reached"))
} }
} }
}
}
} }
impl Drop for Drone { impl Drop for Drone {
@ -156,16 +140,67 @@ impl Drop for Drone {
} }
} }
pub fn run_local_drone(mint_keypair: Keypair, network: SocketAddr, sender: Sender<SocketAddr>) { pub fn request_airdrop_transaction(
drone_addr: &SocketAddr,
id: &Pubkey,
tokens: u64,
last_id: Hash,
) -> Result<Transaction, Error> {
// TODO: make this async tokio client
let mut stream = TcpStream::connect_timeout(drone_addr, Duration::new(3, 0))?;
stream.set_read_timeout(Some(Duration::new(10, 0)))?;
let req = DroneRequest::GetAirdrop {
tokens,
last_id,
to: *id,
};
let req = serialize(&req).expect("serialize drone request");
stream.write_all(&req)?;
// Read length of transaction
let mut buffer = [0; 2];
stream.read_exact(&mut buffer).or_else(|err| {
info!(
"request_airdrop_transaction: buffer length read_exact error: {:?}",
err
);
Err(Error::new(ErrorKind::Other, "Airdrop failed"))
})?;
let transaction_length = LittleEndian::read_u16(&buffer) as usize;
if transaction_length >= PACKET_DATA_SIZE {
Err(Error::new(
ErrorKind::Other,
format!(
"request_airdrop_transaction: invalid transaction_length from drone: {}",
transaction_length
),
))?;
}
// Read the transaction
let mut buffer = Vec::new();
buffer.resize(transaction_length, 0);
stream.read_exact(&mut buffer).or_else(|err| {
info!(
"request_airdrop_transaction: buffer read_exact error: {:?}",
err
);
Err(Error::new(ErrorKind::Other, "Airdrop failed"))
})?;
let transaction: Transaction = deserialize(&buffer).or_else(|err| {
Err(Error::new(
ErrorKind::Other,
format!("request_airdrop_transaction deserialize failure: {:?}", err),
))
})?;
Ok(transaction)
}
pub fn run_local_drone(mint_keypair: Keypair, sender: Sender<SocketAddr>) {
thread::spawn(move || { thread::spawn(move || {
let drone_addr = socketaddr!(0, 0); let drone_addr = socketaddr!(0, 0);
let drone = Arc::new(Mutex::new(Drone::new( let drone = Arc::new(Mutex::new(Drone::new(mint_keypair, None, None)));
mint_keypair,
drone_addr,
network,
None,
None,
)));
let socket = TcpListener::bind(&drone_addr).unwrap(); let socket = TcpListener::bind(&drone_addr).unwrap();
sender.send(socket.local_addr().unwrap()).unwrap(); sender.send(socket.local_addr().unwrap()).unwrap();
info!("Drone started. Listening on: {}", drone_addr); info!("Drone started. Listening on: {}", drone_addr);
@ -186,12 +221,13 @@ pub fn run_local_drone(mint_keypair: Keypair, network: SocketAddr, sender: Sende
})?; })?;
info!("Airdrop requested..."); info!("Airdrop requested...");
let res1 = drone2.lock().unwrap().send_airdrop(req); let res = drone2.lock().unwrap().build_airdrop_transaction(req);
match res1 { match res {
Ok(_) => info!("Airdrop sent!"), Ok(_) => info!("Airdrop sent!"),
Err(_) => info!("Request limit reached for this time slice"), Err(_) => info!("Request limit reached for this time slice"),
} }
let response = res1?; let response = res?;
info!("Airdrop tx signature: {:?}", response); info!("Airdrop tx signature: {:?}", response);
let response_vec = serialize(&response).or_else(|err| { let response_vec = serialize(&response).or_else(|err| {
Err(io::Error::new( Err(io::Error::new(
@ -199,7 +235,20 @@ pub fn run_local_drone(mint_keypair: Keypair, network: SocketAddr, sender: Sende
format!("serialize signature in drone: {:?}", err), format!("serialize signature in drone: {:?}", err),
)) ))
})?; })?;
let response_bytes = Bytes::from(response_vec.clone());
let mut response_vec_with_length = vec![0; 2];
LittleEndian::write_u16(
&mut response_vec_with_length,
response_vec.len() as u16,
);
info!(
"Airdrop response_vec_with_length: {:?}",
response_vec_with_length
);
response_vec_with_length.extend_from_slice(&response_vec);
let response_bytes = Bytes::from(response_vec_with_length.clone());
info!("Airdrop response_bytes: {:?}", response_bytes);
Ok(response_bytes) Ok(response_bytes)
}); });
let server = writer let server = writer
@ -238,8 +287,7 @@ mod tests {
let keypair = Keypair::new(); let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap(); let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap()); addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap(); let mut drone = Drone::new(keypair, None, Some(3));
let mut drone = Drone::new(keypair, addr, network_addr, None, Some(3));
assert!(drone.check_request_limit(1)); assert!(drone.check_request_limit(1));
drone.request_current = 3; drone.request_current = 3;
assert!(!drone.check_request_limit(1)); assert!(!drone.check_request_limit(1));
@ -250,8 +298,7 @@ mod tests {
let keypair = Keypair::new(); let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap(); let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap()); addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap(); let mut drone = Drone::new(keypair, None, None);
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
drone.request_current = drone.request_current + 256; drone.request_current = drone.request_current + 256;
assert_eq!(drone.request_current, 256); assert_eq!(drone.request_current, 256);
drone.clear_request_count(); drone.clear_request_count();
@ -263,8 +310,7 @@ mod tests {
let keypair = Keypair::new(); let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap(); let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap()); addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap(); let mut drone = Drone::new(keypair, None, None);
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string"); let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0); assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip); drone.add_ip_to_cache(ip);
@ -277,8 +323,7 @@ mod tests {
let keypair = Keypair::new(); let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap(); let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap()); addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap(); let mut drone = Drone::new(keypair, None, None);
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string"); let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0); assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip); drone.add_ip_to_cache(ip);
@ -293,10 +338,9 @@ mod tests {
let keypair = Keypair::new(); let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap(); let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap()); addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap();
let time_slice: Option<u64> = None; let time_slice: Option<u64> = None;
let request_cap: Option<u64> = None; let request_cap: Option<u64> = None;
let drone = Drone::new(keypair, addr, network_addr, time_slice, request_cap); let drone = Drone::new(keypair, time_slice, request_cap);
assert_eq!(drone.time_slice, Duration::new(TIME_SLICE, 0)); assert_eq!(drone.time_slice, Duration::new(TIME_SLICE, 0));
assert_eq!(drone.request_cap, REQUEST_CAP); assert_eq!(drone.request_cap, REQUEST_CAP);
} }
@ -339,7 +383,7 @@ mod tests {
let mut addr: SocketAddr = "0.0.0.0:9900".parse().expect("bind to drone socket"); let mut addr: SocketAddr = "0.0.0.0:9900".parse().expect("bind to drone socket");
addr.set_ip(get_ip_addr().expect("drone get_ip_addr")); addr.set_ip(get_ip_addr().expect("drone get_ip_addr"));
let mut drone = Drone::new(alice.keypair(), addr, leader_data.ncp, None, Some(150_000)); let mut drone = Drone::new(alice.keypair(), None, Some(150_000));
let transactions_socket = let transactions_socket =
UdpSocket::bind("0.0.0.0:0").expect("drone bind to transactions socket"); UdpSocket::bind("0.0.0.0:0").expect("drone bind to transactions socket");
@ -347,10 +391,12 @@ mod tests {
let mut client = ThinClient::new(leader_data.rpc, leader_data.tpu, transactions_socket); let mut client = ThinClient::new(leader_data.rpc, leader_data.tpu, transactions_socket);
let bob_req = DroneRequest::GetAirdrop { let bob_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 50, tokens: 50,
client_pubkey: bob_pubkey, to: bob_pubkey,
last_id,
}; };
let bob_sig = drone.send_airdrop(bob_req).unwrap(); let bob_tx = drone.build_airdrop_transaction(bob_req).unwrap();
let bob_sig = client.transfer_signed(&bob_tx).unwrap();
assert!(client.poll_for_signature(&bob_sig).is_ok()); assert!(client.poll_for_signature(&bob_sig).is_ok());
// restart the leader, drone should find the new one at the same gossip port // restart the leader, drone should find the new one at the same gossip port
@ -376,12 +422,14 @@ mod tests {
let mut client = ThinClient::new(leader_data.rpc, leader_data.tpu, transactions_socket); let mut client = ThinClient::new(leader_data.rpc, leader_data.tpu, transactions_socket);
let carlos_req = DroneRequest::GetAirdrop { let carlos_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 5_000_000, tokens: 5_000_000,
client_pubkey: carlos_pubkey, to: carlos_pubkey,
last_id,
}; };
// using existing drone, new thin client // using existing drone, new thin client
let carlos_sig = drone.send_airdrop(carlos_req).unwrap(); let carlos_tx = drone.build_airdrop_transaction(carlos_req).unwrap();
let carlos_sig = client.transfer_signed(&carlos_tx).unwrap();
assert!(client.poll_for_signature(&carlos_sig).is_ok()); assert!(client.poll_for_signature(&carlos_sig).is_ok());
let bob_balance = client.get_balance(&bob_pubkey); let bob_balance = client.get_balance(&bob_pubkey);

View File

@ -1,10 +1,10 @@
//! The `rpc` module implements the Solana RPC interface. //! The `rpc` module implements the Solana RPC interface.
use bank::{Bank, BankError}; use bank::{Bank, BankError};
use bincode::deserialize; use bincode::{deserialize, serialize};
use bs58; use bs58;
use cluster_info::ClusterInfo; use cluster_info::ClusterInfo;
use drone::DRONE_PORT; use drone::{request_airdrop_transaction, DRONE_PORT};
use jsonrpc_core::*; use jsonrpc_core::*;
use jsonrpc_http_server::*; use jsonrpc_http_server::*;
use packet::PACKET_DATA_SIZE; use packet::PACKET_DATA_SIZE;
@ -22,7 +22,6 @@ use std::thread::{self, sleep, Builder, JoinHandle};
use std::time::Duration; use std::time::Duration;
use std::time::Instant; use std::time::Instant;
use transaction::Transaction; use transaction::Transaction;
use wallet::request_airdrop;
pub const RPC_PORT: u16 = 8899; pub const RPC_PORT: u16 = 8899;
@ -211,17 +210,35 @@ impl RpcSol for RpcSolImpl {
let mut drone_addr = get_leader_addr(&meta.cluster_info)?; let mut drone_addr = get_leader_addr(&meta.cluster_info)?;
drone_addr.set_port(DRONE_PORT); drone_addr.set_port(DRONE_PORT);
let signature = request_airdrop(&drone_addr, &pubkey, tokens).map_err(|err| { let last_id = meta.request_processor.bank.last_id();
info!("request_airdrop failed: {:?}", err); let transaction = request_airdrop_transaction(&drone_addr, &pubkey, tokens, last_id)
.map_err(|err| {
info!("request_airdrop_transaction failed: {:?}", err);
Error::internal_error() Error::internal_error()
})?;; })?;;
let data = serialize(&transaction).map_err(|err| {
info!("request_airdrop: serialize error: {:?}", err);
Error::internal_error()
})?;
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let transactions_addr = get_leader_addr(&meta.cluster_info)?;
transactions_socket
.send_to(&data, transactions_addr)
.map_err(|err| {
info!("request_airdrop: send_to error: {:?}", err);
Error::internal_error()
})?;
let signature = transaction.signatures[0];
let now = Instant::now(); let now = Instant::now();
let mut signature_status; let mut signature_status;
loop { loop {
signature_status = meta.request_processor.get_signature_status(signature); signature_status = meta.request_processor.get_signature_status(signature);
if signature_status.is_ok() { if signature_status.is_ok() {
info!("airdrop signature ok");
return Ok(bs58::encode(signature).into_string()); return Ok(bs58::encode(signature).into_string());
} else if now.elapsed().as_secs() > 5 { } else if now.elapsed().as_secs() > 5 {
info!("airdrop signature timeout"); info!("airdrop signature timeout");
@ -404,7 +421,7 @@ mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "getBalance", "method": "getBalance",
"params": vec![alice.pubkey().to_string()], "params": [alice.pubkey().to_string()],
}); });
let mut response = client let mut response = client
.post(&rpc_string) .post(&rpc_string)
@ -639,7 +656,7 @@ mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "sendTransaction", "method": "sendTransaction",
"params": json!(vec![serial_tx]) "params": json!([serial_tx])
}); });
let rpc_addr = leader_data.rpc; let rpc_addr = leader_data.rpc;
let rpc_string = format!("http://{}", rpc_addr.to_string()); let rpc_string = format!("http://{}", rpc_addr.to_string());
@ -659,7 +676,7 @@ mod tests {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "confirmTransaction", "method": "confirmTransaction",
"params": vec![signature], "params": [signature],
}); });
let mut response = client let mut response = client
.post(&rpc_string) .post(&rpc_string)
@ -667,7 +684,8 @@ mod tests {
.body(request.to_string()) .body(request.to_string())
.send() .send()
.unwrap(); .unwrap();
let json: Value = serde_json::from_str(&response.text().unwrap()).unwrap(); let response_json_text = response.text().unwrap();
let json: Value = serde_json::from_str(&response_json_text).unwrap();
assert_eq!(true, json["result"]); assert_eq!(true, json["result"]);

View File

@ -56,7 +56,7 @@ impl RpcRequest {
"method": method, "method": method,
}); });
if let Some(param_string) = params { if let Some(param_string) = params {
request["params"] = json!(vec![param_string]); request["params"] = param_string;
} }
request request
} }
@ -99,18 +99,18 @@ mod tests {
let test_request = RpcRequest::GetAccountInfo; let test_request = RpcRequest::GetAccountInfo;
let request = test_request.build_request_json( let request = test_request.build_request_json(
1, 1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")), Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
); );
assert_eq!(request["method"], "getAccountInfo"); assert_eq!(request["method"], "getAccountInfo");
assert_eq!( assert_eq!(
request["params"], request["params"],
json!(vec!["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"]) json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])
); );
let test_request = RpcRequest::GetBalance; let test_request = RpcRequest::GetBalance;
let request = test_request.build_request_json( let request = test_request.build_request_json(
1, 1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")), Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
); );
assert_eq!(request["method"], "getBalance"); assert_eq!(request["method"], "getBalance");
@ -172,7 +172,7 @@ mod tests {
let balance = RpcRequest::GetBalance.make_rpc_request( let balance = RpcRequest::GetBalance.make_rpc_request(
&rpc_addr, &rpc_addr,
1, 1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")), Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
); );
assert!(balance.is_ok()); assert!(balance.is_ok());
assert_eq!(balance.unwrap().as_u64().unwrap(), 50); assert_eq!(balance.unwrap().as_u64().unwrap(), 50);

View File

@ -143,7 +143,7 @@ impl ThinClient {
} }
pub fn get_account_userdata(&mut self, pubkey: &Pubkey) -> io::Result<Option<Vec<u8>>> { pub fn get_account_userdata(&mut self, pubkey: &Pubkey) -> io::Result<Option<Vec<u8>>> {
let params = json!(format!("{}", pubkey)); let params = json!([format!("{}", pubkey)]);
let rpc_string = format!("http://{}", self.rpc_addr.to_string()); let rpc_string = format!("http://{}", self.rpc_addr.to_string());
let resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params)); let resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params));
if let Ok(account_json) = resp { if let Ok(account_json) = resp {
@ -162,7 +162,7 @@ impl ThinClient {
/// by the network, this method will hang indefinitely. /// by the network, this method will hang indefinitely.
pub fn get_balance(&mut self, pubkey: &Pubkey) -> io::Result<u64> { pub fn get_balance(&mut self, pubkey: &Pubkey) -> io::Result<u64> {
trace!("get_balance sending request to {}", self.rpc_addr); trace!("get_balance sending request to {}", self.rpc_addr);
let params = json!(format!("{}", pubkey)); let params = json!([format!("{}", pubkey)]);
let rpc_string = format!("http://{}", self.rpc_addr.to_string()); let rpc_string = format!("http://{}", self.rpc_addr.to_string());
let resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params)); let resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params));
if let Ok(account_json) = resp { if let Ok(account_json) = resp {
@ -303,7 +303,7 @@ impl ThinClient {
/// until the server sends a response. /// until the server sends a response.
pub fn check_signature(&mut self, signature: &Signature) -> bool { pub fn check_signature(&mut self, signature: &Signature) -> bool {
trace!("check_signature"); trace!("check_signature");
let params = json!(format!("{}", signature)); let params = json!([format!("{}", signature)]);
let now = Instant::now(); let now = Instant::now();
let rpc_string = format!("http://{}", self.rpc_addr.to_string()); let rpc_string = format!("http://{}", self.rpc_addr.to_string());
let mut done = false; let mut done = false;

View File

@ -1,11 +1,11 @@
use bincode::{deserialize, serialize}; use bincode::serialize;
use bpf_loader; use bpf_loader;
use bs58; use bs58;
use budget_program::BudgetState; use budget_program::BudgetState;
use budget_transaction::BudgetTransaction; use budget_transaction::BudgetTransaction;
use chrono::prelude::*; use chrono::prelude::*;
use clap::ArgMatches; use clap::ArgMatches;
use drone::{DroneRequest, DRONE_PORT}; use drone::{request_airdrop_transaction, DRONE_PORT};
use elf; use elf;
use fullnode::Config; use fullnode::Config;
use hash::Hash; use hash::Hash;
@ -18,14 +18,10 @@ use serde_json;
use signature::{Keypair, KeypairUtil, Signature}; use signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::prelude::*; use std::io::Write;
use std::io::{Error, ErrorKind, Write}; use std::net::{Ipv4Addr, SocketAddr};
use std::mem::size_of;
use std::net::{Ipv4Addr, SocketAddr, TcpStream};
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use std::{error, fmt, mem}; use std::{error, fmt, mem};
use system_transaction::SystemTransaction; use system_transaction::SystemTransaction;
use thin_client::poll_gossip_for_leader; use thin_client::poll_gossip_for_leader;
@ -330,7 +326,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
"Requesting airdrop of {:?} tokens from {}", "Requesting airdrop of {:?} tokens from {}",
tokens, drone_addr tokens, drone_addr
); );
let params = json!(format!("{}", config.id.pubkey())); let params = json!([format!("{}", config.id.pubkey())]);
let previous_balance = match RpcRequest::GetBalance let previous_balance = match RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64() .as_u64()
@ -340,24 +336,18 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
"Received result of an unexpected type".to_string(), "Received result of an unexpected type".to_string(),
))?, ))?,
}; };
request_airdrop(&drone_addr, &config.id.pubkey(), tokens as u64)?;
// TODO: return airdrop Result from Drone instead of polling the let last_id = get_last_id(&rpc_addr)?;
// network let transaction =
let mut current_balance = previous_balance; request_airdrop_transaction(&drone_addr, &config.id.pubkey(), tokens, last_id)?;
for _ in 0..20 { send_and_confirm_tx(&rpc_addr, &transaction)?;
sleep(Duration::from_millis(500));
let params = json!(format!("{}", config.id.pubkey())); let params = json!([format!("{}", config.id.pubkey())]);
current_balance = RpcRequest::GetBalance let current_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64() .as_u64()
.unwrap_or(previous_balance); .unwrap_or(previous_balance);
if previous_balance < current_balance {
break;
}
println!(".");
}
if current_balance - previous_balance < tokens { if current_balance - previous_balance < tokens {
Err("Airdrop failed!")?; Err("Airdrop failed!")?;
} }
@ -366,7 +356,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
// Check client balance // Check client balance
WalletCommand::Balance => { WalletCommand::Balance => {
println!("Balance requested..."); println!("Balance requested...");
let params = json!(format!("{}", config.id.pubkey())); let params = json!([format!("{}", config.id.pubkey())]);
let balance = RpcRequest::GetBalance let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64(); .as_u64();
@ -388,7 +378,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 = json!(format!("{}", signature)); let params = json!([format!("{}", signature)]);
let confirmation = RpcRequest::ConfirmTransaction let confirmation = RpcRequest::ConfirmTransaction
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_bool(); .as_bool();
@ -407,7 +397,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
} }
// Deploy a custom program to the chain // Deploy a custom program to the chain
WalletCommand::Deploy(ref program_location) => { WalletCommand::Deploy(ref program_location) => {
let params = json!(format!("{}", config.id.pubkey())); let params = json!([format!("{}", config.id.pubkey())]);
let balance = RpcRequest::GetBalance let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64(); .as_u64();
@ -615,8 +605,12 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
let balance = RpcRequest::GetBalance let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64(); .as_u64();
if let Some(0) = balance { if let Some(0) = balance {
request_airdrop(&drone_addr, &config.id.pubkey(), 1)?; let params = json!([format!("{}", config.id.pubkey()), 1]);
RpcRequest::RequestAirdrop
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap();
} }
let last_id = get_last_id(&rpc_addr)?; let last_id = get_last_id(&rpc_addr)?;
@ -628,16 +622,19 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
} }
// Apply witness signature to contract // Apply witness signature to contract
WalletCommand::Witness(to, pubkey) => { WalletCommand::Witness(to, pubkey) => {
let last_id = get_last_id(&rpc_addr)?; let params = json!([format!("{}", config.id.pubkey())]);
let params = json!(format!("{}", config.id.pubkey()));
let balance = RpcRequest::GetBalance let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))? .make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64(); .as_u64();
if let Some(0) = balance { if let Some(0) = balance {
request_airdrop(&drone_addr, &config.id.pubkey(), 1)?; let params = json!([format!("{}", config.id.pubkey()), 1]);
RpcRequest::RequestAirdrop
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap();
} }
let last_id = get_last_id(&rpc_addr)?;
let tx = Transaction::budget_new_signature(&config.id, pubkey, to, last_id); let tx = Transaction::budget_new_signature(&config.id, pubkey, to, last_id);
let signature_str = send_tx(&rpc_addr, &tx)?; let signature_str = send_tx(&rpc_addr, &tx)?;
@ -662,34 +659,6 @@ pub fn read_leader(path: &str) -> Result<Config, WalletError> {
}) })
} }
pub fn request_airdrop(
drone_addr: &SocketAddr,
id: &Pubkey,
tokens: u64,
) -> Result<Signature, Error> {
// TODO: make this async tokio client
let mut stream = TcpStream::connect_timeout(drone_addr, Duration::new(3, 0))?;
stream.set_read_timeout(Some(Duration::new(10, 0)))?;
let req = DroneRequest::GetAirdrop {
airdrop_request_amount: tokens,
client_pubkey: *id,
};
let tx = serialize(&req).expect("serialize drone request");
stream.write_all(&tx)?;
let mut buffer = [0; size_of::<Signature>()];
stream.read_exact(&mut buffer).or_else(|err| {
info!("request_airdrop: read_exact error: {:?}", err);
Err(Error::new(ErrorKind::Other, "Airdrop failed"))
})?;
let signature: Signature = deserialize(&buffer).or_else(|err| {
Err(Error::new(
ErrorKind::Other,
format!("deserialize signature in request_airdrop: {:?}", err),
))
})?;
Ok(signature)
}
pub fn gen_keypair_file(outfile: String) -> Result<String, Box<error::Error>> { pub fn gen_keypair_file(outfile: String) -> Result<String, Box<error::Error>> {
let rnd = SystemRandom::new(); let rnd = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rnd)?; let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rnd)?;
@ -721,7 +690,7 @@ fn get_last_id(rpc_addr: &str) -> Result<Hash, Box<error::Error>> {
fn send_tx(rpc_addr: &str, tx: &Transaction) -> Result<String, Box<error::Error>> { fn send_tx(rpc_addr: &str, tx: &Transaction) -> Result<String, Box<error::Error>> {
let serialized = serialize(tx).unwrap(); let serialized = serialize(tx).unwrap();
let params = json!(serialized); let params = json!([serialized]);
let signature = RpcRequest::SendTransaction.make_rpc_request(&rpc_addr, 2, Some(params))?; let signature = RpcRequest::SendTransaction.make_rpc_request(&rpc_addr, 2, Some(params))?;
if signature.as_str().is_none() { if signature.as_str().is_none() {
Err(WalletError::RpcRequestError( Err(WalletError::RpcRequestError(
@ -732,7 +701,7 @@ fn send_tx(rpc_addr: &str, tx: &Transaction) -> Result<String, Box<error::Error>
} }
fn confirm_tx(rpc_addr: &str, signature: &str) -> Result<RpcSignatureStatus, Box<error::Error>> { fn confirm_tx(rpc_addr: &str, signature: &str) -> Result<RpcSignatureStatus, Box<error::Error>> {
let params = json!(signature.to_string()); let params = json!([signature.to_string()]);
let signature_status = let signature_status =
RpcRequest::GetSignatureStatus.make_rpc_request(&rpc_addr, 1, Some(params))?; RpcRequest::GetSignatureStatus.make_rpc_request(&rpc_addr, 1, Some(params))?;
if let Some(status) = signature_status.as_str() { if let Some(status) = signature_status.as_str() {
@ -799,6 +768,8 @@ mod tests {
use std::fs::remove_dir_all; use std::fs::remove_dir_all;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
#[test] #[test]
fn test_wallet_parse_command() { fn test_wallet_parse_command() {
@ -1114,7 +1085,6 @@ mod tests {
assert!(parse_command(pubkey, &test_bad_timestamp).is_err()); assert!(parse_command(pubkey, &test_bad_timestamp).is_err());
} }
#[test] #[test]
#[ignore]
fn test_wallet_process_command() { fn test_wallet_process_command() {
let bob_pubkey = Keypair::new().pubkey(); let bob_pubkey = Keypair::new().pubkey();
@ -1147,7 +1117,7 @@ mod tests {
sleep(Duration::from_millis(900)); sleep(Duration::from_millis(900));
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_drone(alice.keypair(), leader_data.ncp, sender); run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let mut config = WalletConfig::default(); let mut config = WalletConfig::default();
@ -1189,8 +1159,6 @@ mod tests {
} }
#[test] #[test]
fn test_wallet_request_airdrop() { fn test_wallet_request_airdrop() {
let bob_pubkey = Keypair::new().pubkey();
let leader_keypair = Arc::new(Keypair::new()); let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey()); let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone(); let leader_data = leader.info.clone();
@ -1220,20 +1188,25 @@ mod tests {
sleep(Duration::from_millis(900)); sleep(Duration::from_millis(900));
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_drone(alice.keypair(), leader_data.ncp, sender); run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let rpc_addr = format!("http://{}", leader_data.rpc.to_string()); let mut bob_config = WalletConfig::default();
bob_config.network = leader_data.ncp;
bob_config.drone_port = Some(drone_addr.port());
bob_config.command = WalletCommand::AirDrop(50);
let signature = request_airdrop(&drone_addr, &bob_pubkey, 50); let sig_response = process_command(&bob_config);
assert!(signature.is_ok()); assert!(sig_response.is_ok());
let params = json!(format!("{}", signature.unwrap()));
let confirmation = RpcRequest::ConfirmTransaction let rpc_addr = format!("http://{}", leader_data.rpc.to_string());
let params = json!([format!("{}", bob_config.id.pubkey())]);
let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_bool() .as_u64()
.unwrap(); .unwrap();
assert!(confirmation); assert_eq!(balance, 50);
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
@ -1295,7 +1268,7 @@ mod tests {
sleep(Duration::from_millis(900)); sleep(Duration::from_millis(900));
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_drone(alice.keypair(), leader_data.ncp, sender); run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let rpc_addr = format!("http://{}", leader_data.rpc.to_string()); let rpc_addr = format!("http://{}", leader_data.rpc.to_string());
@ -1310,7 +1283,11 @@ mod tests {
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
let _signature = request_airdrop(&drone_addr, &config_payer.id.pubkey(), 50); let last_id = get_last_id(&rpc_addr).unwrap();
let transaction =
request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id)
.unwrap();
send_and_confirm_tx(&rpc_addr, &transaction).unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness // Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness
let date_string = "\"2018-09-19T17:30:59Z\""; let date_string = "\"2018-09-19T17:30:59Z\"";
@ -1333,21 +1310,21 @@ mod tests {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 39); assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 11); assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
@ -1360,21 +1337,21 @@ mod tests {
let sig_response = process_command(&config_witness); let sig_response = process_command(&config_witness);
assert!(sig_response.is_ok()); assert!(sig_response.is_ok());
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 39); assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 1); assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
@ -1419,14 +1396,18 @@ mod tests {
sleep(Duration::from_millis(900)); sleep(Duration::from_millis(900));
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_drone(alice.keypair(), leader_data.ncp, sender); run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let rpc_addr = format!("http://{}", leader_data.rpc.to_string()); let rpc_addr = format!("http://{}", leader_data.rpc.to_string());
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
let _signature = request_airdrop(&drone_addr, &config_payer.id.pubkey(), 50); let last_id = get_last_id(&rpc_addr).unwrap();
let transaction =
request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id)
.unwrap();
send_and_confirm_tx(&rpc_addr, &transaction).unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
config_payer.command = WalletCommand::Pay( config_payer.command = WalletCommand::Pay(
@ -1447,21 +1428,21 @@ mod tests {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 39); assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 11); assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
@ -1474,21 +1455,21 @@ mod tests {
let sig_response = process_command(&config_witness); let sig_response = process_command(&config_witness);
assert!(sig_response.is_ok()); assert!(sig_response.is_ok());
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 39); assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 1); assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
@ -1532,7 +1513,7 @@ mod tests {
sleep(Duration::from_millis(900)); sleep(Duration::from_millis(900));
let (sender, receiver) = channel(); let (sender, receiver) = channel();
run_local_drone(alice.keypair(), leader_data.ncp, sender); run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap(); let drone_addr = receiver.recv().unwrap();
let rpc_addr = format!("http://{}", leader_data.rpc.to_string()); let rpc_addr = format!("http://{}", leader_data.rpc.to_string());
@ -1547,7 +1528,11 @@ mod tests {
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey()); assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
let _signature = request_airdrop(&drone_addr, &config_payer.id.pubkey(), 50); let last_id = get_last_id(&rpc_addr).unwrap();
let transaction =
request_airdrop_transaction(&drone_addr, &config_payer.id.pubkey(), 50, last_id)
.unwrap();
send_and_confirm_tx(&rpc_addr, &transaction).unwrap();
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
config_payer.command = WalletCommand::Pay( config_payer.command = WalletCommand::Pay(
@ -1568,21 +1553,21 @@ mod tests {
.expect("base58-encoded public key"); .expect("base58-encoded public key");
let process_id = Pubkey::new(&process_id_vec); let process_id = Pubkey::new(&process_id_vec);
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 39); assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 11); assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
@ -1595,21 +1580,21 @@ mod tests {
let sig_response = process_command(&config_payer); let sig_response = process_command(&config_payer);
assert!(sig_response.is_ok()); assert!(sig_response.is_ok());
let params = json!(format!("{}", config_payer.id.pubkey())); let params = json!([format!("{}", config_payer.id.pubkey())]);
let config_payer_balance = RpcRequest::GetBalance let config_payer_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(config_payer_balance, 49); assert_eq!(config_payer_balance, 49);
let params = json!(format!("{}", process_id)); let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()
.as_u64() .as_u64()
.unwrap(); .unwrap();
assert_eq!(contract_balance, 1); assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey)); let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params)) .make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap() .unwrap()