2018-08-26 21:14:20 -07:00
|
|
|
use bincode::{deserialize, serialize};
|
2018-09-14 01:58:39 -07:00
|
|
|
use bs58;
|
2018-09-22 16:51:21 -07:00
|
|
|
use budget_program::BudgetState;
|
2018-09-26 09:33:52 -07:00
|
|
|
use budget_transaction::BudgetTransaction;
|
2018-09-19 12:44:16 -07:00
|
|
|
use chrono::prelude::*;
|
2018-09-14 01:58:39 -07:00
|
|
|
use clap::ArgMatches;
|
2018-10-08 19:55:54 -07:00
|
|
|
use cluster_info::NodeInfo;
|
2018-07-31 22:07:53 -07:00
|
|
|
use drone::DroneRequest;
|
2018-09-14 01:58:39 -07:00
|
|
|
use fullnode::Config;
|
2018-09-20 22:27:06 -07:00
|
|
|
use hash::Hash;
|
2018-09-17 09:48:04 -07:00
|
|
|
use ring::rand::SystemRandom;
|
|
|
|
use ring::signature::Ed25519KeyPair;
|
2018-10-08 11:10:47 -07:00
|
|
|
use rpc_request::RpcRequest;
|
|
|
|
use serde_json;
|
2018-09-26 16:55:36 -07:00
|
|
|
use signature::{Keypair, KeypairUtil, Signature};
|
2018-09-27 07:49:26 -07:00
|
|
|
use solana_program_interface::pubkey::Pubkey;
|
2018-09-17 09:48:04 -07:00
|
|
|
use std::fs::{self, File};
|
2018-08-26 21:14:20 -07:00
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::io::{Error, ErrorKind, Write};
|
|
|
|
use std::mem::size_of;
|
2018-09-14 01:58:39 -07:00
|
|
|
use std::net::{Ipv4Addr, SocketAddr, TcpStream};
|
2018-09-17 09:48:04 -07:00
|
|
|
use std::path::Path;
|
2018-09-14 01:58:39 -07:00
|
|
|
use std::thread::sleep;
|
|
|
|
use std::time::Duration;
|
|
|
|
use std::{error, fmt, mem};
|
2018-09-26 09:07:53 -07:00
|
|
|
use system_transaction::SystemTransaction;
|
2018-09-26 09:33:52 -07:00
|
|
|
use transaction::Transaction;
|
2018-09-14 01:58:39 -07:00
|
|
|
|
2018-09-14 01:59:09 -07:00
|
|
|
#[derive(Debug, PartialEq)]
|
2018-09-14 01:58:39 -07:00
|
|
|
pub enum WalletCommand {
|
|
|
|
Address,
|
|
|
|
AirDrop(i64),
|
2018-09-18 14:42:32 -07:00
|
|
|
Balance,
|
|
|
|
Cancel(Pubkey),
|
2018-09-14 01:58:39 -07:00
|
|
|
Confirm(Signature),
|
2018-09-19 16:44:03 -07:00
|
|
|
// Pay(tokens, to, timestamp, timestamp_pubkey, witness(es), cancelable)
|
2018-09-18 14:42:32 -07:00
|
|
|
Pay(
|
|
|
|
i64,
|
|
|
|
Pubkey,
|
|
|
|
Option<DateTime<Utc>>,
|
|
|
|
Option<Pubkey>,
|
2018-09-19 12:44:16 -07:00
|
|
|
Option<Vec<Pubkey>>,
|
2018-09-22 16:51:21 -07:00
|
|
|
Option<Pubkey>,
|
2018-09-18 14:42:32 -07:00
|
|
|
),
|
2018-09-22 16:51:21 -07:00
|
|
|
// TimeElapsed(to, process_id, timestamp)
|
|
|
|
TimeElapsed(Pubkey, Pubkey, DateTime<Utc>),
|
2018-09-24 09:23:16 -07:00
|
|
|
// Witness(to, process_id)
|
|
|
|
Witness(Pubkey, Pubkey),
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum WalletError {
|
|
|
|
CommandNotRecognized(String),
|
|
|
|
BadParameter(String),
|
2018-09-20 22:27:06 -07:00
|
|
|
RpcRequestError(String),
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for WalletError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "invalid")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl error::Error for WalletError {
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
"invalid"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cause(&self) -> Option<&error::Error> {
|
|
|
|
// Generic error, underlying cause isn't tracked.
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct WalletConfig {
|
|
|
|
pub leader: NodeInfo,
|
|
|
|
pub id: Keypair,
|
|
|
|
pub drone_addr: SocketAddr,
|
2018-09-21 14:27:03 -07:00
|
|
|
pub rpc_addr: String,
|
2018-09-14 01:58:39 -07:00
|
|
|
pub command: WalletCommand,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for WalletConfig {
|
|
|
|
fn default() -> WalletConfig {
|
|
|
|
let default_addr = socketaddr!(0, 8000);
|
|
|
|
WalletConfig {
|
|
|
|
leader: NodeInfo::new_with_socketaddr(&default_addr),
|
|
|
|
id: Keypair::new(),
|
|
|
|
drone_addr: default_addr,
|
2018-09-21 14:27:03 -07:00
|
|
|
rpc_addr: default_addr.to_string(),
|
2018-09-14 01:58:39 -07:00
|
|
|
command: WalletCommand::Balance,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_command(
|
|
|
|
pubkey: Pubkey,
|
|
|
|
matches: &ArgMatches,
|
|
|
|
) -> Result<WalletCommand, Box<error::Error>> {
|
|
|
|
let response = match matches.subcommand() {
|
2018-09-20 22:27:06 -07:00
|
|
|
("address", Some(_address_matches)) => Ok(WalletCommand::Address),
|
2018-09-14 01:58:39 -07:00
|
|
|
("airdrop", Some(airdrop_matches)) => {
|
|
|
|
let tokens = airdrop_matches.value_of("tokens").unwrap().parse()?;
|
|
|
|
Ok(WalletCommand::AirDrop(tokens))
|
|
|
|
}
|
2018-09-20 22:27:06 -07:00
|
|
|
("balance", Some(_balance_matches)) => Ok(WalletCommand::Balance),
|
2018-09-19 12:44:16 -07:00
|
|
|
("cancel", Some(cancel_matches)) => {
|
|
|
|
let pubkey_vec = bs58::decode(cancel_matches.value_of("process-id").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", cancel_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
|
|
|
}
|
|
|
|
let process_id = Pubkey::new(&pubkey_vec);
|
|
|
|
Ok(WalletCommand::Cancel(process_id))
|
|
|
|
}
|
2018-09-20 22:27:06 -07:00
|
|
|
("confirm", Some(confirm_matches)) => {
|
|
|
|
let signatures = bs58::decode(confirm_matches.value_of("signature").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded signature");
|
|
|
|
|
|
|
|
if signatures.len() == mem::size_of::<Signature>() {
|
|
|
|
let signature = Signature::new(&signatures);
|
|
|
|
Ok(WalletCommand::Confirm(signature))
|
|
|
|
} else {
|
|
|
|
eprintln!("{}", confirm_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid signature".to_string()))
|
|
|
|
}
|
|
|
|
}
|
2018-09-14 01:58:39 -07:00
|
|
|
("pay", Some(pay_matches)) => {
|
2018-09-19 12:44:16 -07:00
|
|
|
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
|
2018-09-14 01:58:39 -07:00
|
|
|
let to = if pay_matches.is_present("to") {
|
|
|
|
let pubkey_vec = bs58::decode(pay_matches.value_of("to").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", pay_matches.usage());
|
2018-09-19 12:44:16 -07:00
|
|
|
Err(WalletError::BadParameter(
|
|
|
|
"Invalid to public key".to_string(),
|
|
|
|
))?;
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
Pubkey::new(&pubkey_vec)
|
|
|
|
} else {
|
|
|
|
pubkey
|
|
|
|
};
|
2018-09-19 12:44:16 -07:00
|
|
|
let timestamp = if pay_matches.is_present("timestamp") {
|
|
|
|
// Parse input for serde_json
|
2018-09-21 20:48:46 -07:00
|
|
|
let date_string = if !pay_matches.value_of("timestamp").unwrap().contains('Z') {
|
2018-09-19 12:44:16 -07:00
|
|
|
format!("\"{}Z\"", pay_matches.value_of("timestamp").unwrap())
|
|
|
|
} else {
|
|
|
|
format!("\"{}\"", pay_matches.value_of("timestamp").unwrap())
|
|
|
|
};
|
|
|
|
Some(serde_json::from_str(&date_string)?)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let timestamp_pubkey = if pay_matches.is_present("timestamp-pubkey") {
|
|
|
|
let pubkey_vec = bs58::decode(pay_matches.value_of("timestamp-pubkey").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
2018-09-14 01:58:39 -07:00
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", pay_matches.usage());
|
|
|
|
Err(WalletError::BadParameter(
|
|
|
|
"Invalid timestamp public key".to_string(),
|
|
|
|
))?;
|
|
|
|
}
|
|
|
|
Some(Pubkey::new(&pubkey_vec))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let witness_vec = if pay_matches.is_present("witness") {
|
|
|
|
let witnesses = pay_matches.values_of("witness").unwrap();
|
|
|
|
let mut collection = Vec::new();
|
|
|
|
for witness in witnesses {
|
|
|
|
let pubkey_vec = bs58::decode(witness)
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", pay_matches.usage());
|
|
|
|
Err(WalletError::BadParameter(
|
|
|
|
"Invalid witness public key".to_string(),
|
|
|
|
))?;
|
|
|
|
}
|
|
|
|
collection.push(Pubkey::new(&pubkey_vec));
|
|
|
|
}
|
|
|
|
Some(collection)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let cancelable = if pay_matches.is_present("cancelable") {
|
2018-09-22 16:51:21 -07:00
|
|
|
Some(pubkey)
|
2018-09-19 12:44:16 -07:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(WalletCommand::Pay(
|
|
|
|
tokens,
|
|
|
|
to,
|
|
|
|
timestamp,
|
|
|
|
timestamp_pubkey,
|
|
|
|
witness_vec,
|
|
|
|
cancelable,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
("send-signature", Some(sig_matches)) => {
|
2018-09-24 09:23:16 -07:00
|
|
|
let pubkey_vec = bs58::decode(sig_matches.value_of("to").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", sig_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
|
|
|
}
|
|
|
|
let to = Pubkey::new(&pubkey_vec);
|
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
let pubkey_vec = bs58::decode(sig_matches.value_of("process-id").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
2018-09-14 01:58:39 -07:00
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", sig_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
|
|
|
}
|
|
|
|
let process_id = Pubkey::new(&pubkey_vec);
|
2018-09-24 09:23:16 -07:00
|
|
|
Ok(WalletCommand::Witness(to, process_id))
|
2018-09-19 12:44:16 -07:00
|
|
|
}
|
|
|
|
("send-timestamp", Some(timestamp_matches)) => {
|
2018-09-22 16:51:21 -07:00
|
|
|
let pubkey_vec = bs58::decode(timestamp_matches.value_of("to").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", timestamp_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
|
|
|
}
|
|
|
|
let to = Pubkey::new(&pubkey_vec);
|
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
let pubkey_vec = bs58::decode(timestamp_matches.value_of("process-id").unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
|
|
|
|
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
|
|
|
|
eprintln!("{}", timestamp_matches.usage());
|
|
|
|
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
|
|
|
|
}
|
|
|
|
let process_id = Pubkey::new(&pubkey_vec);
|
2018-09-19 16:44:03 -07:00
|
|
|
let dt = if timestamp_matches.is_present("datetime") {
|
2018-09-19 12:44:16 -07:00
|
|
|
// Parse input for serde_json
|
|
|
|
let date_string = if !timestamp_matches
|
|
|
|
.value_of("datetime")
|
|
|
|
.unwrap()
|
2018-09-21 20:48:46 -07:00
|
|
|
.contains('Z')
|
2018-09-19 12:44:16 -07:00
|
|
|
{
|
|
|
|
format!("\"{}Z\"", timestamp_matches.value_of("datetime").unwrap())
|
|
|
|
} else {
|
|
|
|
format!("\"{}\"", timestamp_matches.value_of("datetime").unwrap())
|
|
|
|
};
|
|
|
|
serde_json::from_str(&date_string)?
|
|
|
|
} else {
|
|
|
|
Utc::now()
|
|
|
|
};
|
2018-09-22 16:51:21 -07:00
|
|
|
Ok(WalletCommand::TimeElapsed(to, process_id, dt))
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
("", None) => {
|
2018-09-24 09:23:16 -07:00
|
|
|
eprintln!("{}", matches.usage());
|
2018-09-14 01:58:39 -07:00
|
|
|
Err(WalletError::CommandNotRecognized(
|
|
|
|
"no subcommand given".to_string(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
}?;
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
|
2018-09-20 22:27:06 -07:00
|
|
|
pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error>> {
|
2018-09-14 01:58:39 -07:00
|
|
|
match config.command {
|
2018-09-20 22:27:06 -07:00
|
|
|
// Get address of this client
|
2018-09-17 12:15:26 -07:00
|
|
|
WalletCommand::Address => Ok(format!("{}", config.id.pubkey())),
|
2018-09-14 01:58:39 -07:00
|
|
|
// Request an airdrop from Solana Drone;
|
|
|
|
WalletCommand::AirDrop(tokens) => {
|
|
|
|
println!(
|
|
|
|
"Requesting airdrop of {:?} tokens from {}",
|
|
|
|
tokens, config.drone_addr
|
|
|
|
);
|
2018-09-21 14:27:03 -07:00
|
|
|
let params = json!(format!("{}", config.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let previous_balance = match RpcRequest::GetBalance
|
2018-09-20 22:27:06 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_i64()
|
2018-09-21 14:27:03 -07:00
|
|
|
{
|
|
|
|
Some(tokens) => tokens,
|
|
|
|
None => Err(WalletError::RpcRequestError(
|
|
|
|
"Received result of an unexpected type".to_string(),
|
|
|
|
))?,
|
|
|
|
};
|
2018-09-14 01:58:39 -07:00
|
|
|
request_airdrop(&config.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));
|
2018-09-21 14:27:03 -07:00
|
|
|
let params = json!(format!("{}", config.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
current_balance = RpcRequest::GetBalance
|
2018-09-20 22:27:06 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_i64()
|
2018-09-14 01:58:39 -07:00
|
|
|
.unwrap_or(previous_balance);
|
|
|
|
|
|
|
|
if previous_balance != current_balance {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
println!(".");
|
|
|
|
}
|
|
|
|
if current_balance - previous_balance != tokens {
|
|
|
|
Err("Airdrop failed!")?;
|
|
|
|
}
|
2018-09-17 12:15:26 -07:00
|
|
|
Ok(format!("Your balance is: {:?}", current_balance))
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
2018-09-18 14:42:32 -07:00
|
|
|
// Check client balance
|
2018-09-20 22:27:06 -07:00
|
|
|
WalletCommand::Balance => {
|
|
|
|
println!("Balance requested...");
|
2018-09-21 14:27:03 -07:00
|
|
|
let params = json!(format!("{}", config.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let balance = RpcRequest::GetBalance
|
2018-09-20 22:27:06 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_i64();
|
|
|
|
match balance {
|
|
|
|
Some(0) => Ok("No account found! Request an airdrop to get started.".to_string()),
|
|
|
|
Some(tokens) => Ok(format!("Your balance is: {:?}", tokens)),
|
|
|
|
None => Err(WalletError::RpcRequestError(
|
|
|
|
"Received result of an unexpected type".to_string(),
|
|
|
|
))?,
|
|
|
|
}
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
2018-09-18 14:42:32 -07:00
|
|
|
// Cancel a contract by contract Pubkey
|
2018-09-19 12:44:16 -07:00
|
|
|
WalletCommand::Cancel(pubkey) => {
|
2018-09-22 16:51:21 -07:00
|
|
|
let last_id = get_last_id(&config)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
|
|
|
|
let tx =
|
|
|
|
Transaction::budget_new_signature(&config.id, pubkey, config.id.pubkey(), last_id);
|
2018-09-22 16:51:21 -07:00
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
|
2018-09-21 20:48:46 -07:00
|
|
|
Ok(signature_str.to_string())
|
2018-09-19 12:44:16 -07:00
|
|
|
}
|
2018-09-14 01:58:39 -07:00
|
|
|
// Confirm the last client transaction by signature
|
|
|
|
WalletCommand::Confirm(signature) => {
|
2018-09-21 14:27:03 -07:00
|
|
|
let params = json!(format!("{}", signature));
|
2018-10-08 11:10:47 -07:00
|
|
|
let confirmation = RpcRequest::ConfirmTransaction
|
2018-09-20 22:27:06 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_bool();
|
|
|
|
match confirmation {
|
|
|
|
Some(b) => {
|
|
|
|
if b {
|
|
|
|
Ok("Confirmed".to_string())
|
|
|
|
} else {
|
|
|
|
Ok("Not found".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(WalletError::RpcRequestError(
|
|
|
|
"Received result of an unexpected type".to_string(),
|
|
|
|
))?,
|
|
|
|
}
|
|
|
|
}
|
2018-09-21 14:27:03 -07:00
|
|
|
// If client has positive balance, pay tokens to another address
|
2018-09-19 16:44:03 -07:00
|
|
|
WalletCommand::Pay(tokens, to, timestamp, timestamp_pubkey, ref witnesses, cancelable) => {
|
2018-09-22 10:33:06 -07:00
|
|
|
let last_id = get_last_id(&config)?;
|
2018-09-20 22:27:06 -07:00
|
|
|
|
2018-09-19 16:44:03 -07:00
|
|
|
if timestamp == None && *witnesses == None {
|
2018-09-26 08:51:51 -07:00
|
|
|
let tx = Transaction::system_new(&config.id, to, tokens, last_id);
|
2018-09-22 16:51:21 -07:00
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
2018-09-21 20:48:46 -07:00
|
|
|
Ok(signature_str.to_string())
|
2018-09-19 16:44:03 -07:00
|
|
|
} else if *witnesses == None {
|
|
|
|
let dt = timestamp.unwrap();
|
|
|
|
let dt_pubkey = match timestamp_pubkey {
|
|
|
|
Some(pubkey) => pubkey,
|
|
|
|
None => config.id.pubkey(),
|
|
|
|
};
|
2018-09-22 16:51:21 -07:00
|
|
|
|
|
|
|
let contract_funds = Keypair::new();
|
|
|
|
let contract_state = Keypair::new();
|
|
|
|
let budget_program_id = BudgetState::id();
|
|
|
|
|
|
|
|
// Create account for contract funds
|
|
|
|
let tx = Transaction::system_create(
|
|
|
|
&config.id,
|
|
|
|
contract_funds.pubkey(),
|
|
|
|
last_id,
|
|
|
|
tokens,
|
|
|
|
0,
|
|
|
|
budget_program_id,
|
2018-09-24 09:23:16 -07:00
|
|
|
0,
|
2018-09-22 16:51:21 -07:00
|
|
|
);
|
|
|
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
|
|
|
// Create account for contract state
|
|
|
|
let tx = Transaction::system_create(
|
2018-09-21 18:51:42 -07:00
|
|
|
&config.id,
|
2018-09-22 16:51:21 -07:00
|
|
|
contract_state.pubkey(),
|
|
|
|
last_id,
|
|
|
|
1,
|
|
|
|
196,
|
|
|
|
budget_program_id,
|
2018-09-24 09:23:16 -07:00
|
|
|
0,
|
2018-09-22 16:51:21 -07:00
|
|
|
);
|
|
|
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
|
|
|
// Initializing contract
|
|
|
|
let tx = Transaction::budget_new_on_date(
|
|
|
|
&contract_funds,
|
2018-09-21 18:51:42 -07:00
|
|
|
to,
|
2018-09-22 16:51:21 -07:00
|
|
|
contract_state.pubkey(),
|
2018-09-21 18:51:42 -07:00
|
|
|
dt,
|
|
|
|
dt_pubkey,
|
2018-09-22 16:51:21 -07:00
|
|
|
cancelable,
|
2018-09-21 18:51:42 -07:00
|
|
|
tokens,
|
|
|
|
last_id,
|
2018-09-19 16:44:03 -07:00
|
|
|
);
|
2018-09-22 16:51:21 -07:00
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
2018-09-19 16:44:03 -07:00
|
|
|
Ok(json!({
|
|
|
|
"signature": signature_str,
|
2018-09-22 16:51:21 -07:00
|
|
|
"processId": format!("{}", contract_state.pubkey()),
|
2018-09-19 16:44:03 -07:00
|
|
|
}).to_string())
|
|
|
|
} else if timestamp == None {
|
2018-09-24 09:23:16 -07:00
|
|
|
let last_id = get_last_id(&config)?;
|
|
|
|
|
|
|
|
let witness = if let Some(ref witness_vec) = *witnesses {
|
|
|
|
witness_vec[0]
|
|
|
|
} else {
|
|
|
|
Err(WalletError::BadParameter(
|
|
|
|
"Could not parse required signature pubkey(s)".to_string(),
|
|
|
|
))?
|
|
|
|
};
|
|
|
|
|
|
|
|
let contract_funds = Keypair::new();
|
|
|
|
let contract_state = Keypair::new();
|
|
|
|
let budget_program_id = BudgetState::id();
|
|
|
|
|
|
|
|
// Create account for contract funds
|
|
|
|
let tx = Transaction::system_create(
|
|
|
|
&config.id,
|
|
|
|
contract_funds.pubkey(),
|
|
|
|
last_id,
|
|
|
|
tokens,
|
|
|
|
0,
|
|
|
|
budget_program_id,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
|
|
|
// Create account for contract state
|
|
|
|
let tx = Transaction::system_create(
|
|
|
|
&config.id,
|
|
|
|
contract_state.pubkey(),
|
|
|
|
last_id,
|
|
|
|
1,
|
|
|
|
196,
|
|
|
|
budget_program_id,
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
let _signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
|
|
|
// Initializing contract
|
|
|
|
let tx = Transaction::budget_new_when_signed(
|
|
|
|
&contract_funds,
|
|
|
|
to,
|
|
|
|
contract_state.pubkey(),
|
|
|
|
witness,
|
|
|
|
cancelable,
|
|
|
|
tokens,
|
|
|
|
last_id,
|
|
|
|
);
|
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
|
|
|
|
|
|
|
Ok(json!({
|
|
|
|
"signature": signature_str,
|
|
|
|
"processId": format!("{}", contract_state.pubkey()),
|
|
|
|
}).to_string())
|
2018-09-19 16:44:03 -07:00
|
|
|
} else {
|
2018-09-24 09:40:23 -07:00
|
|
|
Ok("Combo transactions not yet handled".to_string())
|
2018-09-19 16:44:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Apply time elapsed to contract
|
2018-09-22 16:51:21 -07:00
|
|
|
WalletCommand::TimeElapsed(to, pubkey, dt) => {
|
2018-09-24 09:23:16 -07:00
|
|
|
let params = json!(format!("{}", config.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_i64();
|
|
|
|
if let Some(0) = balance {
|
|
|
|
request_airdrop(&config.drone_addr, &config.id.pubkey(), 1)?;
|
|
|
|
}
|
|
|
|
|
2018-09-22 10:33:06 -07:00
|
|
|
let last_id = get_last_id(&config)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
|
2018-09-24 09:23:16 -07:00
|
|
|
let tx = Transaction::budget_new_timestamp(&config.id, pubkey, to, dt, last_id);
|
2018-09-22 16:51:21 -07:00
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
2018-09-20 22:27:06 -07:00
|
|
|
|
2018-09-23 14:38:17 -07:00
|
|
|
Ok(signature_str.to_string())
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
2018-09-18 14:42:32 -07:00
|
|
|
// Apply witness signature to contract
|
2018-09-24 09:23:16 -07:00
|
|
|
WalletCommand::Witness(to, pubkey) => {
|
2018-09-22 10:33:06 -07:00
|
|
|
let last_id = get_last_id(&config)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
|
2018-09-24 09:23:16 -07:00
|
|
|
let params = json!(format!("{}", config.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config.rpc_addr, 1, Some(params))?
|
|
|
|
.as_i64();
|
|
|
|
if let Some(0) = balance {
|
|
|
|
request_airdrop(&config.drone_addr, &config.id.pubkey(), 1)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
}
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let tx = Transaction::budget_new_signature(&config.id, pubkey, to, last_id);
|
|
|
|
let signature_str = serialize_and_send_tx(&config, &tx)?;
|
2018-09-19 16:44:03 -07:00
|
|
|
|
2018-09-21 20:48:46 -07:00
|
|
|
Ok(signature_str.to_string())
|
2018-09-19 16:44:03 -07:00
|
|
|
}
|
2018-09-14 01:58:39 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read_leader(path: &str) -> Result<Config, WalletError> {
|
|
|
|
let file = File::open(path.to_string()).or_else(|err| {
|
|
|
|
Err(WalletError::BadParameter(format!(
|
|
|
|
"{}: Unable to open leader file: {}",
|
|
|
|
err, path
|
|
|
|
)))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
serde_json::from_reader(file).or_else(|err| {
|
|
|
|
Err(WalletError::BadParameter(format!(
|
|
|
|
"{}: Failed to parse leader file: {}",
|
|
|
|
err, path
|
|
|
|
)))
|
|
|
|
})
|
|
|
|
}
|
2018-07-31 22:07:53 -07:00
|
|
|
|
|
|
|
pub fn request_airdrop(
|
|
|
|
drone_addr: &SocketAddr,
|
2018-08-09 08:13:57 -07:00
|
|
|
id: &Pubkey,
|
2018-07-31 22:07:53 -07:00
|
|
|
tokens: u64,
|
2018-08-24 08:38:09 -07:00
|
|
|
) -> Result<Signature, Error> {
|
2018-08-26 21:14:20 -07:00
|
|
|
// TODO: make this async tokio client
|
2018-07-31 22:07:53 -07:00
|
|
|
let mut stream = TcpStream::connect(drone_addr)?;
|
|
|
|
let req = DroneRequest::GetAirdrop {
|
|
|
|
airdrop_request_amount: tokens,
|
2018-08-09 08:03:08 -07:00
|
|
|
client_pubkey: *id,
|
2018-07-31 22:07:53 -07:00
|
|
|
};
|
|
|
|
let tx = serialize(&req).expect("serialize drone request");
|
2018-08-24 08:38:09 -07:00
|
|
|
stream.write_all(&tx)?;
|
2018-08-26 21:14:20 -07:00
|
|
|
let mut buffer = [0; size_of::<Signature>()];
|
|
|
|
stream
|
|
|
|
.read_exact(&mut buffer)
|
|
|
|
.or_else(|_| 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),
|
|
|
|
))
|
|
|
|
})?;
|
2018-07-31 22:07:53 -07:00
|
|
|
// TODO: add timeout to this function, in case of unresponsive drone
|
2018-08-26 21:14:20 -07:00
|
|
|
Ok(signature)
|
2018-07-31 22:07:53 -07:00
|
|
|
}
|
2018-09-14 01:58:39 -07:00
|
|
|
|
2018-09-17 09:48:04 -07:00
|
|
|
pub fn gen_keypair_file(outfile: String) -> Result<String, Box<error::Error>> {
|
|
|
|
let rnd = SystemRandom::new();
|
|
|
|
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rnd)?;
|
|
|
|
let serialized = serde_json::to_string(&pkcs8_bytes.to_vec())?;
|
|
|
|
|
|
|
|
if outfile != "-" {
|
|
|
|
if let Some(outdir) = Path::new(&outfile).parent() {
|
|
|
|
fs::create_dir_all(outdir)?;
|
|
|
|
}
|
|
|
|
let mut f = File::create(outfile)?;
|
|
|
|
f.write_all(&serialized.clone().into_bytes())?;
|
|
|
|
}
|
|
|
|
Ok(serialized)
|
|
|
|
}
|
|
|
|
|
2018-09-22 10:33:06 -07:00
|
|
|
fn get_last_id(config: &WalletConfig) -> Result<Hash, Box<error::Error>> {
|
2018-10-08 11:10:47 -07:00
|
|
|
let result = RpcRequest::GetLastId.make_rpc_request(&config.rpc_addr, 1, None)?;
|
2018-09-22 10:33:06 -07:00
|
|
|
if result.as_str().is_none() {
|
|
|
|
Err(WalletError::RpcRequestError(
|
|
|
|
"Received bad last_id".to_string(),
|
|
|
|
))?
|
|
|
|
}
|
|
|
|
let last_id_str = result.as_str().unwrap();
|
|
|
|
let last_id_vec = bs58::decode(last_id_str)
|
|
|
|
.into_vec()
|
|
|
|
.map_err(|_| WalletError::RpcRequestError("Received bad last_id".to_string()))?;
|
|
|
|
Ok(Hash::new(&last_id_vec))
|
|
|
|
}
|
|
|
|
|
2018-09-24 09:23:16 -07:00
|
|
|
fn serialize_and_send_tx(
|
|
|
|
config: &WalletConfig,
|
|
|
|
tx: &Transaction,
|
|
|
|
) -> Result<String, Box<error::Error>> {
|
2018-09-22 16:51:21 -07:00
|
|
|
let serialized = serialize(tx).unwrap();
|
|
|
|
let params = json!(serialized);
|
2018-09-24 09:23:16 -07:00
|
|
|
let signature =
|
2018-10-08 11:10:47 -07:00
|
|
|
RpcRequest::SendTransaction.make_rpc_request(&config.rpc_addr, 2, Some(params))?;
|
2018-09-22 16:51:21 -07:00
|
|
|
if signature.as_str().is_none() {
|
|
|
|
Err(WalletError::RpcRequestError(
|
|
|
|
"Received result of an unexpected type".to_string(),
|
|
|
|
))?
|
|
|
|
}
|
|
|
|
Ok(signature.as_str().unwrap().to_string())
|
|
|
|
}
|
|
|
|
|
2018-09-14 01:58:39 -07:00
|
|
|
#[cfg(test)]
|
2018-09-14 01:59:09 -07:00
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use bank::Bank;
|
|
|
|
use clap::{App, Arg, SubCommand};
|
2018-10-08 19:55:54 -07:00
|
|
|
use cluster_info::Node;
|
2018-09-14 01:59:09 -07:00
|
|
|
use drone::run_local_drone;
|
|
|
|
use fullnode::Fullnode;
|
|
|
|
use ledger::LedgerWriter;
|
|
|
|
use mint::Mint;
|
2018-10-08 11:10:47 -07:00
|
|
|
use serde_json::Value;
|
2018-09-17 09:48:04 -07:00
|
|
|
use signature::{read_keypair, read_pkcs8, Keypair, KeypairUtil};
|
2018-09-25 11:45:25 -07:00
|
|
|
use std::fs::remove_dir_all;
|
2018-09-14 01:59:09 -07:00
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
|
|
|
|
fn tmp_ledger(name: &str, mint: &Mint) -> String {
|
|
|
|
use std::env;
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
2018-10-08 16:15:17 -07:00
|
|
|
let path = format!("{}/tmp/ledger-{}-{}", out_dir, name, keypair.pubkey());
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
let mut writer = LedgerWriter::open(&path, true).unwrap();
|
|
|
|
writer.write_entries(mint.create_entries()).unwrap();
|
|
|
|
|
|
|
|
path
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-09-26 10:17:45 -07:00
|
|
|
fn test_wallet_parse_command() {
|
2018-09-14 01:59:09 -07:00
|
|
|
let test_commands = App::new("test")
|
2018-09-19 12:44:16 -07:00
|
|
|
.subcommand(SubCommand::with_name("address").about("Get your public key"))
|
2018-09-14 01:59:09 -07:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("airdrop")
|
|
|
|
.about("Request a batch of tokens")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("tokens")
|
2018-09-19 12:44:16 -07:00
|
|
|
.index(1)
|
|
|
|
.value_name("NUM")
|
2018-09-14 01:59:09 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The number of tokens to request"),
|
|
|
|
),
|
2018-09-19 12:44:16 -07:00
|
|
|
).subcommand(SubCommand::with_name("balance").about("Get your balance"))
|
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("cancel")
|
|
|
|
.about("Cancel a transfer")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("process-id")
|
|
|
|
.index(1)
|
|
|
|
.value_name("PROCESS_ID")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The process id of the transfer to cancel"),
|
|
|
|
),
|
|
|
|
).subcommand(
|
|
|
|
SubCommand::with_name("confirm")
|
|
|
|
.about("Confirm transaction by signature")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("signature")
|
|
|
|
.index(1)
|
|
|
|
.value_name("SIGNATURE")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The transaction signature to confirm"),
|
|
|
|
),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(
|
2018-09-14 01:59:09 -07:00
|
|
|
SubCommand::with_name("pay")
|
|
|
|
.about("Send a payment")
|
|
|
|
.arg(
|
2018-09-19 12:44:16 -07:00
|
|
|
Arg::with_name("to")
|
|
|
|
.index(1)
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The pubkey of recipient"),
|
|
|
|
).arg(
|
2018-09-14 01:59:09 -07:00
|
|
|
Arg::with_name("tokens")
|
2018-09-19 12:44:16 -07:00
|
|
|
.index(2)
|
|
|
|
.value_name("NUM")
|
2018-09-14 01:59:09 -07:00
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The number of tokens to send"),
|
2018-09-14 16:25:14 -07:00
|
|
|
).arg(
|
2018-09-19 12:44:16 -07:00
|
|
|
Arg::with_name("timestamp")
|
|
|
|
.long("after")
|
|
|
|
.value_name("DATETIME")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("A timestamp after which transaction will execute"),
|
|
|
|
).arg(
|
|
|
|
Arg::with_name("timestamp-pubkey")
|
|
|
|
.long("require-timestamp-from")
|
2018-09-14 01:59:09 -07:00
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
2018-09-19 12:44:16 -07:00
|
|
|
.requires("timestamp")
|
|
|
|
.help("Require timestamp from this third party"),
|
|
|
|
).arg(
|
|
|
|
Arg::with_name("witness")
|
|
|
|
.long("require-signature-from")
|
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.multiple(true)
|
|
|
|
.use_delimiter(true)
|
|
|
|
.help("Any third party signatures required to unlock the tokens"),
|
|
|
|
).arg(
|
2018-09-25 11:45:25 -07:00
|
|
|
Arg::with_name("cancelable")
|
|
|
|
.long("cancelable")
|
|
|
|
.takes_value(false),
|
2018-09-14 01:59:09 -07:00
|
|
|
),
|
2018-09-14 16:25:14 -07:00
|
|
|
).subcommand(
|
2018-09-19 12:44:16 -07:00
|
|
|
SubCommand::with_name("send-signature")
|
|
|
|
.about("Send a signature to authorize a transfer")
|
2018-09-14 01:59:09 -07:00
|
|
|
.arg(
|
2018-09-24 09:23:16 -07:00
|
|
|
Arg::with_name("to")
|
2018-09-14 01:59:09 -07:00
|
|
|
.index(1)
|
2018-09-24 09:23:16 -07:00
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The pubkey of recipient"),
|
|
|
|
).arg(
|
|
|
|
Arg::with_name("process-id")
|
|
|
|
.index(2)
|
2018-09-19 12:44:16 -07:00
|
|
|
.value_name("PROCESS_ID")
|
|
|
|
.takes_value(true)
|
2018-09-14 01:59:09 -07:00
|
|
|
.required(true)
|
2018-09-19 12:44:16 -07:00
|
|
|
.help("The process id of the transfer to authorize"),
|
2018-09-14 01:59:09 -07:00
|
|
|
),
|
2018-09-19 12:44:16 -07:00
|
|
|
).subcommand(
|
|
|
|
SubCommand::with_name("send-timestamp")
|
|
|
|
.about("Send a timestamp to unlock a transfer")
|
|
|
|
.arg(
|
2018-09-24 09:23:16 -07:00
|
|
|
Arg::with_name("to")
|
2018-09-19 12:44:16 -07:00
|
|
|
.index(1)
|
2018-09-24 09:23:16 -07:00
|
|
|
.value_name("PUBKEY")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The pubkey of recipient"),
|
|
|
|
).arg(
|
|
|
|
Arg::with_name("process-id")
|
|
|
|
.index(2)
|
2018-09-19 12:44:16 -07:00
|
|
|
.value_name("PROCESS_ID")
|
|
|
|
.takes_value(true)
|
|
|
|
.required(true)
|
|
|
|
.help("The process id of the transfer to unlock"),
|
|
|
|
).arg(
|
|
|
|
Arg::with_name("datetime")
|
|
|
|
.long("date")
|
|
|
|
.value_name("DATETIME")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Optional arbitrary timestamp to apply"),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
let pubkey = Keypair::new().pubkey();
|
|
|
|
let pubkey_string = format!("{}", pubkey);
|
|
|
|
let witness0 = Keypair::new().pubkey();
|
|
|
|
let witness0_string = format!("{}", witness0);
|
|
|
|
let witness1 = Keypair::new().pubkey();
|
|
|
|
let witness1_string = format!("{}", witness1);
|
|
|
|
let dt = Utc.ymd(2018, 9, 19).and_hms(17, 30, 59);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
// Test Airdrop Subcommand
|
2018-09-14 01:59:09 -07:00
|
|
|
let test_airdrop = test_commands
|
|
|
|
.clone()
|
2018-09-19 12:44:16 -07:00
|
|
|
.get_matches_from(vec!["test", "airdrop", "50"]);
|
2018-09-14 01:59:09 -07:00
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_airdrop).unwrap(),
|
|
|
|
WalletCommand::AirDrop(50)
|
|
|
|
);
|
|
|
|
let test_bad_airdrop = test_commands
|
|
|
|
.clone()
|
2018-09-19 12:44:16 -07:00
|
|
|
.get_matches_from(vec!["test", "airdrop", "notint"]);
|
2018-09-14 01:59:09 -07:00
|
|
|
assert!(parse_command(pubkey, &test_bad_airdrop).is_err());
|
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
// Test Cancel Subcommand
|
|
|
|
let test_cancel =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "cancel", &pubkey_string]);
|
2018-09-14 01:59:09 -07:00
|
|
|
assert_eq!(
|
2018-09-19 12:44:16 -07:00
|
|
|
parse_command(pubkey, &test_cancel).unwrap(),
|
|
|
|
WalletCommand::Cancel(pubkey)
|
2018-09-14 01:59:09 -07:00
|
|
|
);
|
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
// Test Confirm Subcommand
|
2018-09-14 01:59:09 -07:00
|
|
|
let signature = Signature::new(&vec![1; 64]);
|
|
|
|
let signature_string = format!("{:?}", signature);
|
|
|
|
let test_confirm =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "confirm", &signature_string]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_confirm).unwrap(),
|
|
|
|
WalletCommand::Confirm(signature)
|
|
|
|
);
|
2018-09-19 12:44:16 -07:00
|
|
|
let test_bad_signature = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "confirm", "deadbeef"]);
|
2018-09-14 01:59:09 -07:00
|
|
|
assert!(parse_command(pubkey, &test_bad_signature).is_err());
|
2018-09-19 12:44:16 -07:00
|
|
|
|
|
|
|
// Test Simple Pay Subcommand
|
|
|
|
let test_pay =
|
|
|
|
test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "pay", &pubkey_string, "50"]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_pay).unwrap(),
|
|
|
|
WalletCommand::Pay(50, pubkey, None, None, None, None)
|
|
|
|
);
|
|
|
|
let test_bad_pubkey = test_commands
|
|
|
|
.clone()
|
|
|
|
.get_matches_from(vec!["test", "pay", "deadbeef", "50"]);
|
|
|
|
assert!(parse_command(pubkey, &test_bad_pubkey).is_err());
|
|
|
|
|
|
|
|
// Test Pay Subcommand w/ Witness
|
|
|
|
let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"pay",
|
|
|
|
&pubkey_string,
|
|
|
|
"50",
|
|
|
|
"--require-signature-from",
|
|
|
|
&witness0_string,
|
|
|
|
"--require-signature-from",
|
|
|
|
&witness1_string,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_pay_multiple_witnesses).unwrap(),
|
|
|
|
WalletCommand::Pay(50, pubkey, None, None, Some(vec![witness0, witness1]), None)
|
|
|
|
);
|
|
|
|
let test_pay_single_witness = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"pay",
|
|
|
|
&pubkey_string,
|
|
|
|
"50",
|
|
|
|
"--require-signature-from",
|
|
|
|
&witness0_string,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_pay_single_witness).unwrap(),
|
|
|
|
WalletCommand::Pay(50, pubkey, None, None, Some(vec![witness0]), None)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Pay Subcommand w/ Timestamp
|
|
|
|
let test_pay_timestamp = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"pay",
|
|
|
|
&pubkey_string,
|
|
|
|
"50",
|
|
|
|
"--after",
|
|
|
|
"2018-09-19T17:30:59",
|
|
|
|
"--require-timestamp-from",
|
|
|
|
&witness0_string,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_pay_timestamp).unwrap(),
|
|
|
|
WalletCommand::Pay(50, pubkey, Some(dt), Some(witness0), None, None)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Send-Signature Subcommand
|
2018-09-24 09:23:16 -07:00
|
|
|
let test_send_signature = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"send-signature",
|
|
|
|
&pubkey_string,
|
|
|
|
&pubkey_string,
|
|
|
|
]);
|
2018-09-19 12:44:16 -07:00
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_send_signature).unwrap(),
|
2018-09-24 09:23:16 -07:00
|
|
|
WalletCommand::Witness(pubkey, pubkey)
|
2018-09-19 12:44:16 -07:00
|
|
|
);
|
|
|
|
let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"pay",
|
|
|
|
&pubkey_string,
|
|
|
|
"50",
|
|
|
|
"--after",
|
|
|
|
"2018-09-19T17:30:59",
|
|
|
|
"--require-signature-from",
|
|
|
|
&witness0_string,
|
|
|
|
"--require-timestamp-from",
|
|
|
|
&witness0_string,
|
|
|
|
"--require-signature-from",
|
|
|
|
&witness1_string,
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_pay_multiple_witnesses).unwrap(),
|
|
|
|
WalletCommand::Pay(
|
|
|
|
50,
|
|
|
|
pubkey,
|
|
|
|
Some(dt),
|
|
|
|
Some(witness0),
|
|
|
|
Some(vec![witness0, witness1]),
|
|
|
|
None
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test Send-Timestamp Subcommand
|
|
|
|
let test_send_timestamp = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"send-timestamp",
|
|
|
|
&pubkey_string,
|
2018-09-24 09:23:16 -07:00
|
|
|
&pubkey_string,
|
2018-09-19 12:44:16 -07:00
|
|
|
"--date",
|
|
|
|
"2018-09-19T17:30:59",
|
|
|
|
]);
|
|
|
|
assert_eq!(
|
|
|
|
parse_command(pubkey, &test_send_timestamp).unwrap(),
|
2018-09-24 09:23:16 -07:00
|
|
|
WalletCommand::TimeElapsed(pubkey, pubkey, dt)
|
2018-09-19 12:44:16 -07:00
|
|
|
);
|
|
|
|
let test_bad_timestamp = test_commands.clone().get_matches_from(vec![
|
|
|
|
"test",
|
|
|
|
"send-timestamp",
|
|
|
|
&pubkey_string,
|
2018-09-24 09:23:16 -07:00
|
|
|
&pubkey_string,
|
2018-09-19 12:44:16 -07:00
|
|
|
"--date",
|
|
|
|
"20180919T17:30:59",
|
|
|
|
]);
|
|
|
|
assert!(parse_command(pubkey, &test_bad_timestamp).is_err());
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|
|
|
|
#[test]
|
2018-09-26 10:17:45 -07:00
|
|
|
#[ignore]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_process_command() {
|
2018-09-14 01:59:09 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
|
|
|
|
|
|
|
let alice = Mint::new(10_000_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let leader_data = leader.info.clone();
|
|
|
|
let leader_data1 = leader.info.clone();
|
2018-09-25 11:45:25 -07:00
|
|
|
let ledger_path = tmp_ledger("wallet_process_command", &alice);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
let mut config = WalletConfig::default();
|
2018-09-24 09:23:16 -07:00
|
|
|
let rpc_port = 12345; // Needs to be distinct known number to not conflict with other tests
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2018-09-25 11:45:25 -07:00
|
|
|
let server = Fullnode::new_with_bank(
|
2018-09-14 01:59:09 -07:00
|
|
|
leader_keypair,
|
|
|
|
bank,
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
leader,
|
|
|
|
None,
|
2018-09-14 01:53:18 -07:00
|
|
|
&ledger_path,
|
2018-09-14 01:59:09 -07:00
|
|
|
false,
|
2018-09-17 15:43:23 -07:00
|
|
|
None,
|
2018-09-20 22:38:59 -07:00
|
|
|
Some(rpc_port),
|
2018-09-14 01:59:09 -07:00
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
sleep(Duration::from_millis(900));
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
let (sender, receiver) = channel();
|
|
|
|
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
|
|
|
|
config.drone_addr = receiver.recv().unwrap();
|
|
|
|
config.leader = leader_data1;
|
|
|
|
|
2018-09-20 22:27:06 -07:00
|
|
|
let mut rpc_addr = leader_data.contact_info.ncp;
|
2018-09-20 22:38:59 -07:00
|
|
|
rpc_addr.set_port(rpc_port);
|
2018-09-21 14:27:03 -07:00
|
|
|
config.rpc_addr = format!("http://{}", rpc_addr.to_string());
|
2018-09-20 22:27:06 -07:00
|
|
|
|
2018-09-17 12:15:26 -07:00
|
|
|
let tokens = 50;
|
|
|
|
config.command = WalletCommand::AirDrop(tokens);
|
|
|
|
assert_eq!(
|
2018-09-20 22:27:06 -07:00
|
|
|
process_command(&config).unwrap(),
|
2018-09-17 12:15:26 -07:00
|
|
|
format!("Your balance is: {:?}", tokens)
|
|
|
|
);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
config.command = WalletCommand::Balance;
|
2018-09-17 12:15:26 -07:00
|
|
|
assert_eq!(
|
2018-09-20 22:27:06 -07:00
|
|
|
process_command(&config).unwrap(),
|
2018-09-17 12:15:26 -07:00
|
|
|
format!("Your balance is: {:?}", tokens)
|
|
|
|
);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
config.command = WalletCommand::Address;
|
2018-09-17 12:15:26 -07:00
|
|
|
assert_eq!(
|
2018-09-20 22:27:06 -07:00
|
|
|
process_command(&config).unwrap(),
|
2018-09-17 12:15:26 -07:00
|
|
|
format!("{}", config.id.pubkey())
|
|
|
|
);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2018-09-19 12:44:16 -07:00
|
|
|
config.command = WalletCommand::Pay(10, bob_pubkey, None, None, None, None);
|
2018-09-20 22:27:06 -07:00
|
|
|
let sig_response = process_command(&config);
|
2018-09-17 12:15:26 -07:00
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let signatures = bs58::decode(sig_response.unwrap())
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded signature");
|
|
|
|
let signature = Signature::new(&signatures);
|
|
|
|
config.command = WalletCommand::Confirm(signature);
|
2018-09-20 22:27:06 -07:00
|
|
|
assert_eq!(process_command(&config).unwrap(), "Confirmed");
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
config.command = WalletCommand::Balance;
|
2018-09-17 12:15:26 -07:00
|
|
|
assert_eq!(
|
2018-09-20 22:27:06 -07:00
|
|
|
process_command(&config).unwrap(),
|
2018-09-17 12:15:26 -07:00
|
|
|
format!("Your balance is: {:?}", tokens - 10)
|
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
|
|
|
|
server.close().unwrap();
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|
|
|
|
#[test]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_request_airdrop() {
|
2018-09-14 01:59:09 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
|
|
|
|
|
|
|
let alice = Mint::new(10_000_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let leader_data = leader.info.clone();
|
2018-09-25 11:45:25 -07:00
|
|
|
let ledger_path = tmp_ledger("wallet_request_airdrop", &alice);
|
2018-09-14 01:59:09 -07:00
|
|
|
|
2018-09-24 09:23:16 -07:00
|
|
|
let rpc_port = 11111; // Needs to be distinct known number to not conflict with other tests
|
2018-09-20 22:38:59 -07:00
|
|
|
|
2018-09-25 11:45:25 -07:00
|
|
|
let server = Fullnode::new_with_bank(
|
2018-09-14 01:59:09 -07:00
|
|
|
leader_keypair,
|
|
|
|
bank,
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
leader,
|
|
|
|
None,
|
2018-09-14 01:53:18 -07:00
|
|
|
&ledger_path,
|
2018-09-14 01:59:09 -07:00
|
|
|
false,
|
2018-09-17 15:43:23 -07:00
|
|
|
None,
|
2018-09-20 22:38:59 -07:00
|
|
|
Some(rpc_port),
|
2018-09-14 01:59:09 -07:00
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
sleep(Duration::from_millis(900));
|
2018-09-14 01:59:09 -07:00
|
|
|
|
|
|
|
let (sender, receiver) = channel();
|
|
|
|
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
|
|
|
|
let drone_addr = receiver.recv().unwrap();
|
|
|
|
|
2018-09-21 14:27:03 -07:00
|
|
|
let mut addr = leader_data.contact_info.ncp;
|
|
|
|
addr.set_port(rpc_port);
|
|
|
|
let rpc_addr = format!("http://{}", addr.to_string());
|
2018-09-20 22:27:06 -07:00
|
|
|
|
2018-09-14 01:59:09 -07:00
|
|
|
let signature = request_airdrop(&drone_addr, &bob_pubkey, 50);
|
|
|
|
assert!(signature.is_ok());
|
2018-09-21 14:27:03 -07:00
|
|
|
let params = json!(format!("{}", signature.unwrap()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let confirmation = RpcRequest::ConfirmTransaction
|
2018-09-20 22:27:06 -07:00
|
|
|
.make_rpc_request(&rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_bool()
|
|
|
|
.unwrap();
|
|
|
|
assert!(confirmation);
|
2018-09-25 11:45:25 -07:00
|
|
|
|
|
|
|
server.close().unwrap();
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|
2018-10-08 16:15:17 -07:00
|
|
|
fn tmp_file_path(name: &str) -> String {
|
|
|
|
use std::env;
|
|
|
|
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
|
|
|
|
format!("{}/tmp/{}-{}", out_dir, name, keypair.pubkey()).to_string()
|
|
|
|
}
|
|
|
|
|
2018-09-17 09:48:04 -07:00
|
|
|
#[test]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_gen_keypair_file() {
|
2018-10-08 16:15:17 -07:00
|
|
|
let outfile = tmp_file_path("test_gen_keypair_file.json");
|
2018-09-17 09:48:04 -07:00
|
|
|
let serialized_keypair = gen_keypair_file(outfile.to_string()).unwrap();
|
|
|
|
let keypair_vec: Vec<u8> = serde_json::from_str(&serialized_keypair).unwrap();
|
2018-10-08 16:15:17 -07:00
|
|
|
assert!(Path::new(&outfile).exists());
|
2018-09-17 09:48:04 -07:00
|
|
|
assert_eq!(keypair_vec, read_pkcs8(&outfile).unwrap());
|
|
|
|
assert!(read_keypair(&outfile).is_ok());
|
|
|
|
assert_eq!(
|
|
|
|
read_keypair(&outfile).unwrap().pubkey().as_ref().len(),
|
|
|
|
mem::size_of::<Pubkey>()
|
|
|
|
);
|
2018-10-08 16:15:17 -07:00
|
|
|
fs::remove_file(&outfile).unwrap();
|
|
|
|
assert!(!Path::new(&outfile).exists());
|
2018-09-17 09:48:04 -07:00
|
|
|
}
|
2018-09-24 09:23:16 -07:00
|
|
|
#[test]
|
2018-09-26 10:17:45 -07:00
|
|
|
#[ignore]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_timestamp_tx() {
|
2018-09-24 09:23:16 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
|
|
|
|
|
|
|
let alice = Mint::new(10_000_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let leader_data = leader.info.clone();
|
|
|
|
let leader_data1 = leader.info.clone();
|
|
|
|
let leader_data2 = leader.info.clone();
|
2018-09-25 11:45:25 -07:00
|
|
|
let ledger_path = tmp_ledger("wallet_timestamp_tx", &alice);
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let mut config_payer = WalletConfig::default();
|
|
|
|
let mut config_witness = WalletConfig::default();
|
|
|
|
let rpc_port = 13579; // Needs to be distinct known number to not conflict with other tests
|
|
|
|
|
2018-09-25 11:45:25 -07:00
|
|
|
let server = Fullnode::new_with_bank(
|
2018-09-24 09:23:16 -07:00
|
|
|
leader_keypair,
|
|
|
|
bank,
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
leader,
|
|
|
|
None,
|
|
|
|
&ledger_path,
|
|
|
|
false,
|
|
|
|
None,
|
|
|
|
Some(rpc_port),
|
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
sleep(Duration::from_millis(900));
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let (sender, receiver) = channel();
|
|
|
|
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
|
|
|
|
config_payer.drone_addr = receiver.recv().unwrap();
|
|
|
|
config_witness.drone_addr = config_payer.drone_addr.clone();
|
|
|
|
config_payer.leader = leader_data1;
|
|
|
|
config_witness.leader = leader_data2;
|
|
|
|
|
|
|
|
let mut rpc_addr = leader_data.contact_info.ncp;
|
|
|
|
rpc_addr.set_port(rpc_port);
|
|
|
|
config_payer.rpc_addr = format!("http://{}", rpc_addr.to_string());
|
|
|
|
config_witness.rpc_addr = config_payer.rpc_addr.clone();
|
|
|
|
|
|
|
|
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
|
|
|
|
|
|
|
|
let _signature = request_airdrop(&config_payer.drone_addr, &config_payer.id.pubkey(), 50);
|
|
|
|
|
|
|
|
// Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness
|
|
|
|
let date_string = "\"2018-09-19T17:30:59Z\"";
|
|
|
|
let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap();
|
|
|
|
config_payer.command = WalletCommand::Pay(
|
|
|
|
10,
|
|
|
|
bob_pubkey,
|
|
|
|
Some(dt),
|
|
|
|
Some(config_witness.id.pubkey()),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
let sig_response = process_command(&config_payer);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
|
|
|
|
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
|
|
|
let process_id_vec = bs58::decode(process_id_str)
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
let process_id = Pubkey::new(&process_id_vec);
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 39);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 11);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 0);
|
|
|
|
|
|
|
|
// Sign transaction by config_witness
|
|
|
|
config_witness.command = WalletCommand::TimeElapsed(bob_pubkey, process_id, dt);
|
|
|
|
let sig_response = process_command(&config_witness);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 39);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 1);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 10);
|
2018-09-25 11:45:25 -07:00
|
|
|
|
|
|
|
server.close().unwrap();
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
2018-09-24 09:23:16 -07:00
|
|
|
}
|
|
|
|
#[test]
|
2018-09-26 10:17:45 -07:00
|
|
|
#[ignore]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_witness_tx() {
|
2018-09-24 09:23:16 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
|
|
|
|
|
|
|
let alice = Mint::new(10_000_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let leader_data = leader.info.clone();
|
|
|
|
let leader_data1 = leader.info.clone();
|
|
|
|
let leader_data2 = leader.info.clone();
|
2018-09-25 11:45:25 -07:00
|
|
|
let ledger_path = tmp_ledger("wallet_witness_tx", &alice);
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let mut config_payer = WalletConfig::default();
|
|
|
|
let mut config_witness = WalletConfig::default();
|
|
|
|
let rpc_port = 11223; // Needs to be distinct known number to not conflict with other tests
|
|
|
|
|
2018-09-25 11:45:25 -07:00
|
|
|
let server = Fullnode::new_with_bank(
|
2018-09-24 09:23:16 -07:00
|
|
|
leader_keypair,
|
|
|
|
bank,
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
leader,
|
|
|
|
None,
|
|
|
|
&ledger_path,
|
|
|
|
false,
|
|
|
|
None,
|
|
|
|
Some(rpc_port),
|
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
sleep(Duration::from_millis(900));
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let (sender, receiver) = channel();
|
|
|
|
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
|
|
|
|
config_payer.drone_addr = receiver.recv().unwrap();
|
|
|
|
config_witness.drone_addr = config_payer.drone_addr.clone();
|
|
|
|
config_payer.leader = leader_data1;
|
|
|
|
config_witness.leader = leader_data2;
|
|
|
|
|
|
|
|
let mut rpc_addr = leader_data.contact_info.ncp;
|
|
|
|
rpc_addr.set_port(rpc_port);
|
|
|
|
config_payer.rpc_addr = format!("http://{}", rpc_addr.to_string());
|
|
|
|
config_witness.rpc_addr = config_payer.rpc_addr.clone();
|
|
|
|
|
|
|
|
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
|
|
|
|
|
|
|
|
let _signature = request_airdrop(&config_payer.drone_addr, &config_payer.id.pubkey(), 50);
|
|
|
|
|
|
|
|
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
|
|
|
|
config_payer.command = WalletCommand::Pay(
|
|
|
|
10,
|
|
|
|
bob_pubkey,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some(vec![config_witness.id.pubkey()]),
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
let sig_response = process_command(&config_payer);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
|
|
|
|
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
|
|
|
let process_id_vec = bs58::decode(process_id_str)
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
let process_id = Pubkey::new(&process_id_vec);
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 39);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 11);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 0);
|
|
|
|
|
|
|
|
// Sign transaction by config_witness
|
|
|
|
config_witness.command = WalletCommand::Witness(bob_pubkey, process_id);
|
|
|
|
let sig_response = process_command(&config_witness);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 39);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 1);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 10);
|
2018-09-25 11:45:25 -07:00
|
|
|
|
|
|
|
server.close().unwrap();
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
2018-09-24 09:23:16 -07:00
|
|
|
}
|
|
|
|
#[test]
|
2018-09-26 10:17:45 -07:00
|
|
|
#[ignore]
|
2018-09-25 11:45:25 -07:00
|
|
|
fn test_wallet_cancel_tx() {
|
2018-09-24 09:23:16 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
|
|
|
|
|
|
|
|
let alice = Mint::new(10_000_000);
|
|
|
|
let bank = Bank::new(&alice);
|
|
|
|
let bob_pubkey = Keypair::new().pubkey();
|
|
|
|
let leader_data = leader.info.clone();
|
|
|
|
let leader_data1 = leader.info.clone();
|
|
|
|
let leader_data2 = leader.info.clone();
|
2018-09-25 11:45:25 -07:00
|
|
|
let ledger_path = tmp_ledger("wallet_cancel_tx", &alice);
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let mut config_payer = WalletConfig::default();
|
|
|
|
let mut config_witness = WalletConfig::default();
|
|
|
|
let rpc_port = 13456; // Needs to be distinct known number to not conflict with other tests
|
|
|
|
|
2018-09-25 11:45:25 -07:00
|
|
|
let server = Fullnode::new_with_bank(
|
2018-09-24 09:23:16 -07:00
|
|
|
leader_keypair,
|
|
|
|
bank,
|
|
|
|
0,
|
|
|
|
&[],
|
|
|
|
leader,
|
|
|
|
None,
|
|
|
|
&ledger_path,
|
|
|
|
false,
|
|
|
|
None,
|
|
|
|
Some(rpc_port),
|
|
|
|
);
|
2018-09-25 11:45:25 -07:00
|
|
|
sleep(Duration::from_millis(900));
|
2018-09-24 09:23:16 -07:00
|
|
|
|
|
|
|
let (sender, receiver) = channel();
|
|
|
|
run_local_drone(alice.keypair(), leader_data.contact_info.ncp, sender);
|
|
|
|
config_payer.drone_addr = receiver.recv().unwrap();
|
|
|
|
config_witness.drone_addr = config_payer.drone_addr.clone();
|
|
|
|
config_payer.leader = leader_data1;
|
|
|
|
config_witness.leader = leader_data2;
|
|
|
|
|
|
|
|
let mut rpc_addr = leader_data.contact_info.ncp;
|
|
|
|
rpc_addr.set_port(rpc_port);
|
|
|
|
config_payer.rpc_addr = format!("http://{}", rpc_addr.to_string());
|
|
|
|
config_witness.rpc_addr = config_payer.rpc_addr.clone();
|
|
|
|
|
|
|
|
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
|
|
|
|
|
|
|
|
let _signature = request_airdrop(&config_payer.drone_addr, &config_payer.id.pubkey(), 50);
|
|
|
|
|
|
|
|
// Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness
|
|
|
|
config_payer.command = WalletCommand::Pay(
|
|
|
|
10,
|
|
|
|
bob_pubkey,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some(vec![config_witness.id.pubkey()]),
|
|
|
|
Some(config_payer.id.pubkey()),
|
|
|
|
);
|
|
|
|
let sig_response = process_command(&config_payer);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap();
|
|
|
|
let process_id_str = object.get("processId").unwrap().as_str().unwrap();
|
|
|
|
let process_id_vec = bs58::decode(process_id_str)
|
|
|
|
.into_vec()
|
|
|
|
.expect("base58-encoded public key");
|
|
|
|
let process_id = Pubkey::new(&process_id_vec);
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 39);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 11);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 0);
|
|
|
|
|
|
|
|
// Sign transaction by config_witness
|
|
|
|
config_payer.command = WalletCommand::Cancel(process_id);
|
|
|
|
let sig_response = process_command(&config_payer);
|
|
|
|
assert!(sig_response.is_ok());
|
|
|
|
|
|
|
|
let params = json!(format!("{}", config_payer.id.pubkey()));
|
2018-10-08 11:10:47 -07:00
|
|
|
let config_payer_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(config_payer_balance, 49);
|
|
|
|
let params = json!(format!("{}", process_id));
|
2018-10-08 11:10:47 -07:00
|
|
|
let contract_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(contract_balance, 1);
|
|
|
|
let params = json!(format!("{}", bob_pubkey));
|
2018-10-08 11:10:47 -07:00
|
|
|
let recipient_balance = RpcRequest::GetBalance
|
2018-09-24 09:23:16 -07:00
|
|
|
.make_rpc_request(&config_payer.rpc_addr, 1, Some(params))
|
|
|
|
.unwrap()
|
|
|
|
.as_i64()
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(recipient_balance, 0);
|
2018-09-25 11:45:25 -07:00
|
|
|
|
|
|
|
server.close().unwrap();
|
|
|
|
remove_dir_all(ledger_path).unwrap();
|
2018-09-24 09:23:16 -07:00
|
|
|
}
|
2018-09-14 01:59:09 -07:00
|
|
|
}
|