Drone now returns signed airdrop transactions
This commit is contained in:
parent
3543a9a49f
commit
e791d0f74d
|
@ -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=$!
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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| {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
192
src/drone.rs
192
src/drone.rs
|
@ -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);
|
||||||
|
|
36
src/rpc.rs
36
src/rpc.rs
|
@ -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"]);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
177
src/wallet.rs
177
src/wallet.rs
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue