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-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=$!
solana-fullnode --identity /config/leader-config.json --ledger /ledger/ --rpc 8899 &
fullnode=$!

View File

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

View File

@ -16,7 +16,7 @@ use rand::{thread_rng, Rng};
use rayon::prelude::*;
use solana::client::mk_client;
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::logger;
use solana::metrics;
@ -28,7 +28,6 @@ use solana::thin_client::{poll_gossip_for_leader, ThinClient};
use solana::timing::timestamp;
use solana::timing::{duration_as_ms, duration_as_s};
use solana::transaction::Transaction;
use solana::wallet::request_airdrop;
use solana::window::default_window;
use std::cmp;
use std::collections::VecDeque;
@ -393,27 +392,26 @@ fn airdrop_tokens(client: &mut ThinClient, leader: &NodeInfo, id: &Keypair, tx_c
id.pubkey(),
);
if let Err(e) = request_airdrop(&drone_addr, &id.pubkey(), airdrop_amount as u64) {
panic!(
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
e, drone_addr, airdrop_amount
);
}
// TODO: return airdrop Result from Drone instead of polling the
// 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);
starting_balance
});
if starting_balance != current_balance {
break;
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();
}
println!("current balance {}...", current_balance);
}
Err(err) => {
panic!(
"Error requesting airdrop: {:?} to addr: {:?} amount: {}",
err, drone_addr, airdrop_amount
);
}
};
let current_balance = client.poll_get_balance(&id.pubkey()).unwrap_or_else(|e| {
println!("airdrop error {}", e);
starting_balance
});
println!("current balance {}...", current_balance);
metrics_submit_token_balance(current_balance);
if current_balance - starting_balance != airdrop_amount {
println!(

View File

@ -1,4 +1,5 @@
extern crate bincode;
extern crate byteorder;
extern crate bytes;
#[macro_use]
extern crate clap;
@ -9,6 +10,7 @@ extern crate tokio;
extern crate tokio_codec;
use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use bytes::Bytes;
use clap::{App, Arg};
use solana::drone::{Drone, DroneRequest, DRONE_PORT};
@ -18,7 +20,6 @@ use solana::signature::read_keypair;
use std::error;
use std::io;
use std::net::{Ipv4Addr, SocketAddr};
use std::process::exit;
use std::sync::{Arc, Mutex};
use std::thread;
use tokio::net::TcpListener;
@ -41,14 +42,6 @@ fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("drone")
.version(crate_version!())
.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")
.short("k")
.long("keypair")
@ -70,15 +63,6 @@ fn main() -> Result<(), Box<error::Error>> {
.help("Request limit for time slice"),
).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 =
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(
mint_keypair,
drone_addr,
network,
time_slice,
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 res1 = drone2.lock().unwrap().send_airdrop(req);
match res1 {
Ok(_) => println!("Airdrop sent!"),
Err(_) => println!("Request limit reached for this time slice"),
let res = drone2.lock().unwrap().build_airdrop_transaction(req);
match res {
Ok(tx) => {
let response_vec = serialize(&tx).or_else(|err| {
Err(io::Error::new(
io::ErrorKind::Other,
format!("deserialize packet in drone: {:?}", err),
))
})?;
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)
}
Err(err) => {
println!("Airdrop transaction failed: {:?}", err);
Err(err)
}
}
let response = res1?;
println!("Airdrop tx signature: {:?}", response);
let response_vec = serialize(&response).or_else(|err| {
Err(io::Error::new(
io::ErrorKind::Other,
format!("serialize signature in drone: {:?}", err),
))
})?;
let response_bytes = Bytes::from(response_vec.clone());
Ok(response_bytes)
});
let server = writer
.send_all(processor.or_else(|err| {

View File

@ -10,7 +10,7 @@ extern crate solana;
use clap::{App, Arg};
use solana::client::mk_client;
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::leader_scheduler::LeaderScheduler;
use solana::logger;
@ -19,7 +19,6 @@ use solana::netutil::find_available_port_in_range;
use solana::signature::{Keypair, KeypairUtil};
use solana::thin_client::poll_gossip_for_leader;
use solana::vote_program::VoteProgram;
use solana::wallet::request_airdrop;
use std::fs::File;
use std::net::{Ipv4Addr, SocketAddr};
use std::process::exit;
@ -159,8 +158,13 @@ fn main() {
info!("requesting airdrop from {}", drone_addr);
loop {
if request_airdrop(&drone_addr, &pubkey, 50).is_ok() {
break;
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;
}
}
info!(
"airdrop request, is the drone address correct {:?}, drone running?",

View File

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

View File

@ -5,20 +5,22 @@
//! and (to come) an IP rate limit.
use bincode::{deserialize, serialize};
use byteorder::{ByteOrder, LittleEndian};
use bytes::Bytes;
use hash::Hash;
use influx_db_client as influxdb;
use metrics;
use signature::{Keypair, Signature};
use packet::PACKET_DATA_SIZE;
use signature::Keypair;
use solana_sdk::pubkey::Pubkey;
use std::io;
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::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use system_transaction::SystemTransaction;
use thin_client::{poll_gossip_for_leader, ThinClient};
use tokio;
use tokio::net::TcpListener;
use tokio::prelude::*;
@ -32,16 +34,15 @@ pub const DRONE_PORT: u16 = 9900;
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum DroneRequest {
GetAirdrop {
airdrop_request_amount: u64,
client_pubkey: Pubkey,
tokens: u64,
to: Pubkey,
last_id: Hash,
},
}
pub struct Drone {
mint_keypair: Keypair,
ip_cache: Vec<IpAddr>,
_airdrop_addr: SocketAddr,
network_addr: SocketAddr,
pub time_slice: Duration,
request_cap: u64,
pub request_current: u64,
@ -50,8 +51,6 @@ pub struct Drone {
impl Drone {
pub fn new(
mint_keypair: Keypair,
_airdrop_addr: SocketAddr,
network_addr: SocketAddr,
time_input: Option<u64>,
request_cap_input: Option<u64>,
) -> Drone {
@ -66,8 +65,6 @@ impl Drone {
Drone {
mint_keypair,
ip_cache: Vec::new(),
_airdrop_addr,
network_addr,
time_slice,
request_cap,
request_current: 0,
@ -102,50 +99,37 @@ impl Drone {
}
}
pub fn send_airdrop(&mut self, req: DroneRequest) -> Result<Signature, io::Error> {
let request_amount: u64;
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let leader = poll_gossip_for_leader(self.network_addr, Some(10))
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
let mut client = ThinClient::new(leader.rpc, leader.tpu, transactions_socket);
let last_id = client.get_last_id();
let mut tx = match req {
pub fn build_airdrop_transaction(
&mut self,
req: DroneRequest,
) -> Result<Transaction, io::Error> {
trace!("build_airdrop_transaction: {:?}", req);
match req {
DroneRequest::GetAirdrop {
airdrop_request_amount,
client_pubkey,
tokens,
to,
last_id,
} => {
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,
)
if self.check_request_limit(tokens) {
self.request_current += tokens;
metrics::submit(
influxdb::Point::new("drone")
.add_tag("op", influxdb::Value::String("airdrop".to_string()))
.add_field("request_amount", influxdb::Value::Integer(tokens as i64))
.add_field(
"request_current",
influxdb::Value::Integer(self.request_current as i64),
).to_owned(),
);
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 {
Err(Error::new(ErrorKind::Other, "token limit reached"))
}
}
};
if self.check_request_limit(request_amount) {
self.request_current += request_amount;
metrics::submit(
influxdb::Point::new("drone")
.add_tag("op", influxdb::Value::String("airdrop".to_string()))
.add_field(
"request_amount",
influxdb::Value::Integer(request_amount as i64),
).add_field(
"request_current",
influxdb::Value::Integer(self.request_current as i64),
).to_owned(),
);
client.retry_transfer(&self.mint_keypair, &mut tx, 10)
} else {
Err(Error::new(ErrorKind::Other, "token limit reached"))
}
}
}
@ -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 || {
let drone_addr = socketaddr!(0, 0);
let drone = Arc::new(Mutex::new(Drone::new(
mint_keypair,
drone_addr,
network,
None,
None,
)));
let drone = Arc::new(Mutex::new(Drone::new(mint_keypair, None, None)));
let socket = TcpListener::bind(&drone_addr).unwrap();
sender.send(socket.local_addr().unwrap()).unwrap();
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...");
let res1 = drone2.lock().unwrap().send_airdrop(req);
match res1 {
let res = drone2.lock().unwrap().build_airdrop_transaction(req);
match res {
Ok(_) => info!("Airdrop sent!"),
Err(_) => info!("Request limit reached for this time slice"),
}
let response = res1?;
let response = res?;
info!("Airdrop tx signature: {:?}", response);
let response_vec = serialize(&response).or_else(|err| {
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),
))
})?;
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)
});
let server = writer
@ -238,8 +287,7 @@ mod tests {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, network_addr, None, Some(3));
let mut drone = Drone::new(keypair, None, Some(3));
assert!(drone.check_request_limit(1));
drone.request_current = 3;
assert!(!drone.check_request_limit(1));
@ -250,8 +298,7 @@ mod tests {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
let mut drone = Drone::new(keypair, None, None);
drone.request_current = drone.request_current + 256;
assert_eq!(drone.request_current, 256);
drone.clear_request_count();
@ -263,8 +310,7 @@ mod tests {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
let mut drone = Drone::new(keypair, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip);
@ -277,8 +323,7 @@ mod tests {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let network_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, network_addr, None, None);
let mut drone = Drone::new(keypair, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip);
@ -293,10 +338,9 @@ mod tests {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().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 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.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");
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 =
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 bob_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 50,
client_pubkey: bob_pubkey,
tokens: 50,
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());
// 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 carlos_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 5_000_000,
client_pubkey: carlos_pubkey,
tokens: 5_000_000,
to: carlos_pubkey,
last_id,
};
// 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());
let bob_balance = client.get_balance(&bob_pubkey);

View File

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

View File

@ -56,7 +56,7 @@ impl RpcRequest {
"method": method,
});
if let Some(param_string) = params {
request["params"] = json!(vec![param_string]);
request["params"] = param_string;
}
request
}
@ -99,18 +99,18 @@ mod tests {
let test_request = RpcRequest::GetAccountInfo;
let request = test_request.build_request_json(
1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")),
Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
);
assert_eq!(request["method"], "getAccountInfo");
assert_eq!(
request["params"],
json!(vec!["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])
json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])
);
let test_request = RpcRequest::GetBalance;
let request = test_request.build_request_json(
1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")),
Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
);
assert_eq!(request["method"], "getBalance");
@ -172,7 +172,7 @@ mod tests {
let balance = RpcRequest::GetBalance.make_rpc_request(
&rpc_addr,
1,
Some(json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx")),
Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
);
assert!(balance.is_ok());
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>>> {
let params = json!(format!("{}", pubkey));
let params = json!([format!("{}", pubkey)]);
let rpc_string = format!("http://{}", self.rpc_addr.to_string());
let resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params));
if let Ok(account_json) = resp {
@ -162,7 +162,7 @@ impl ThinClient {
/// by the network, this method will hang indefinitely.
pub fn get_balance(&mut self, pubkey: &Pubkey) -> io::Result<u64> {
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 resp = RpcRequest::GetAccountInfo.make_rpc_request(&rpc_string, 1, Some(params));
if let Ok(account_json) = resp {
@ -303,7 +303,7 @@ impl ThinClient {
/// until the server sends a response.
pub fn check_signature(&mut self, signature: &Signature) -> bool {
trace!("check_signature");
let params = json!(format!("{}", signature));
let params = json!([format!("{}", signature)]);
let now = Instant::now();
let rpc_string = format!("http://{}", self.rpc_addr.to_string());
let mut done = false;

View File

@ -1,11 +1,11 @@
use bincode::{deserialize, serialize};
use bincode::serialize;
use bpf_loader;
use bs58;
use budget_program::BudgetState;
use budget_transaction::BudgetTransaction;
use chrono::prelude::*;
use clap::ArgMatches;
use drone::{DroneRequest, DRONE_PORT};
use drone::{request_airdrop_transaction, DRONE_PORT};
use elf;
use fullnode::Config;
use hash::Hash;
@ -18,14 +18,10 @@ use serde_json;
use signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::pubkey::Pubkey;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io::{Error, ErrorKind, Write};
use std::mem::size_of;
use std::net::{Ipv4Addr, SocketAddr, TcpStream};
use std::io::Write;
use std::net::{Ipv4Addr, SocketAddr};
use std::path::Path;
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use std::{error, fmt, mem};
use system_transaction::SystemTransaction;
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 {}",
tokens, drone_addr
);
let params = json!(format!("{}", config.id.pubkey()));
let params = json!([format!("{}", config.id.pubkey())]);
let previous_balance = match RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.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(),
))?,
};
request_airdrop(&drone_addr, &config.id.pubkey(), tokens as u64)?;
// TODO: return airdrop Result from Drone instead of polling the
// network
let mut current_balance = previous_balance;
for _ in 0..20 {
sleep(Duration::from_millis(500));
let params = json!(format!("{}", config.id.pubkey()));
current_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64()
.unwrap_or(previous_balance);
let last_id = get_last_id(&rpc_addr)?;
let transaction =
request_airdrop_transaction(&drone_addr, &config.id.pubkey(), tokens, last_id)?;
send_and_confirm_tx(&rpc_addr, &transaction)?;
let params = json!([format!("{}", config.id.pubkey())]);
let current_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64()
.unwrap_or(previous_balance);
if previous_balance < current_balance {
break;
}
println!(".");
}
if current_balance - previous_balance < tokens {
Err("Airdrop failed!")?;
}
@ -366,7 +356,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
// Check client balance
WalletCommand::Balance => {
println!("Balance requested...");
let params = json!(format!("{}", config.id.pubkey()));
let params = json!([format!("{}", config.id.pubkey())]);
let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64();
@ -388,7 +378,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
}
// Confirm the last client transaction by signature
WalletCommand::Confirm(signature) => {
let params = json!(format!("{}", signature));
let params = json!([format!("{}", signature)]);
let confirmation = RpcRequest::ConfirmTransaction
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_bool();
@ -407,7 +397,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
}
// Deploy a custom program to the chain
WalletCommand::Deploy(ref program_location) => {
let params = json!(format!("{}", config.id.pubkey()));
let params = json!([format!("{}", config.id.pubkey())]);
let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64();
@ -615,8 +605,12 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
let balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64();
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)?;
@ -628,16 +622,19 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
}
// Apply witness signature to contract
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
.make_rpc_request(&rpc_addr, 1, Some(params))?
.as_u64();
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 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>> {
let rnd = SystemRandom::new();
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>> {
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))?;
if signature.as_str().is_none() {
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>> {
let params = json!(signature.to_string());
let params = json!([signature.to_string()]);
let signature_status =
RpcRequest::GetSignatureStatus.make_rpc_request(&rpc_addr, 1, Some(params))?;
if let Some(status) = signature_status.as_str() {
@ -799,6 +768,8 @@ mod tests {
use std::fs::remove_dir_all;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
#[test]
fn test_wallet_parse_command() {
@ -1114,7 +1085,6 @@ mod tests {
assert!(parse_command(pubkey, &test_bad_timestamp).is_err());
}
#[test]
#[ignore]
fn test_wallet_process_command() {
let bob_pubkey = Keypair::new().pubkey();
@ -1147,7 +1117,7 @@ mod tests {
sleep(Duration::from_millis(900));
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 mut config = WalletConfig::default();
@ -1189,8 +1159,6 @@ mod tests {
}
#[test]
fn test_wallet_request_airdrop() {
let bob_pubkey = Keypair::new().pubkey();
let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone();
@ -1220,20 +1188,25 @@ mod tests {
sleep(Duration::from_millis(900));
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 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);
assert!(signature.is_ok());
let params = json!(format!("{}", signature.unwrap()));
let confirmation = RpcRequest::ConfirmTransaction
let sig_response = process_command(&bob_config);
assert!(sig_response.is_ok());
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))
.unwrap()
.as_bool()
.as_u64()
.unwrap();
assert!(confirmation);
assert_eq!(balance, 50);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
@ -1295,7 +1268,7 @@ mod tests {
sleep(Duration::from_millis(900));
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 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());
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
let date_string = "\"2018-09-19T17:30:59Z\"";
@ -1333,21 +1310,21 @@ mod tests {
.expect("base58-encoded public key");
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
@ -1360,21 +1337,21 @@ mod tests {
let sig_response = process_command(&config_witness);
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
@ -1419,14 +1396,18 @@ mod tests {
sleep(Duration::from_millis(900));
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 rpc_addr = format!("http://{}", leader_data.rpc.to_string());
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
config_payer.command = WalletCommand::Pay(
@ -1447,21 +1428,21 @@ mod tests {
.expect("base58-encoded public key");
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
@ -1474,21 +1455,21 @@ mod tests {
let sig_response = process_command(&config_witness);
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
@ -1532,7 +1513,7 @@ mod tests {
sleep(Duration::from_millis(900));
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 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());
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
config_payer.command = WalletCommand::Pay(
@ -1568,21 +1553,21 @@ mod tests {
.expect("base58-encoded public key");
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 39);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 11);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
@ -1595,21 +1580,21 @@ mod tests {
let sig_response = process_command(&config_payer);
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
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(config_payer_balance, 49);
let params = json!(format!("{}", process_id));
let params = json!([format!("{}", process_id)]);
let contract_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(contract_balance, 1);
let params = json!(format!("{}", bob_pubkey));
let params = json!([format!("{}", bob_pubkey)]);
let recipient_balance = RpcRequest::GetBalance
.make_rpc_request(&rpc_addr, 1, Some(params))
.unwrap()