Improve Wallet coverage (#2385)

* Add trait for RpcRequestHandler trait for RpcClient and add MockRpcClient for unit tests

* Add request_airdrop integration test

* Add timestamp_tx, witness_tx, and cancel_tx to wallet integration tests; add wallet integration tests to test-stable

* Add test cases

* Ignore plentiful sleeps in unit tests
This commit is contained in:
Tyera Eulberg 2019-01-14 00:10:03 -07:00 committed by GitHub
parent 780360834d
commit 8af61f561b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 894 additions and 612 deletions

View File

@ -28,10 +28,10 @@ for program in programs/native/*; do
done
# Run integration tests serially
for test in tests/*.rs; do
for test in tests/*.rs wallet/tests/*.rs; do
test=${test##*/} # basename x
test=${test%.rs} # basename x .rs
_ cargo test --verbose --features="$FEATURES" --test="$test" -- --test-threads=1 --nocapture
_ cargo test --all --verbose --features="$FEATURES" --test="$test" -- --test-threads=1 --nocapture
done
echo --- ci/localnet-sanity.sh

View File

@ -53,6 +53,7 @@ pub mod replicator;
pub mod result;
pub mod retransmit_stage;
pub mod rpc;
pub mod rpc_mock;
pub mod rpc_pubsub;
pub mod rpc_request;
pub mod runtime;

View File

@ -7,7 +7,7 @@ use crate::db_ledger::DbLedger;
use crate::gossip_service::GossipService;
use crate::leader_scheduler::LeaderScheduler;
use crate::result::Result;
use crate::rpc_request::{RpcClient, RpcRequest};
use crate::rpc_request::{RpcClient, RpcRequest, RpcRequestHandler};
use crate::service::Service;
use crate::storage_stage::ENTRIES_PER_SEGMENT;
use crate::streamer::BlobReceiver;
@ -144,12 +144,12 @@ impl Replicator {
RpcClient::new_from_socket(rpc_peers[node_idx].rpc)
};
storage_last_id = RpcRequest::GetStorageMiningLastId
.make_rpc_request(&rpc_client, 2, None)
storage_last_id = rpc_client
.make_rpc_request(2, RpcRequest::GetStorageMiningLastId, None)
.expect("rpc request")
.to_string();
storage_entry_height = RpcRequest::GetStorageMiningEntryHeight
.make_rpc_request(&rpc_client, 2, None)
storage_entry_height = rpc_client
.make_rpc_request(2, RpcRequest::GetStorageMiningEntryHeight, None)
.expect("rpc request")
.as_u64()
.unwrap();

88
src/rpc_mock.rs Normal file
View File

@ -0,0 +1,88 @@
// Implementation of RpcRequestHandler trait for testing Rpc requests without i/o
use crate::rpc_request::{RpcRequest, RpcRequestHandler};
use serde_json::{self, Number, Value};
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_transaction::SystemTransaction;
use solana_sdk::transaction::Transaction;
use std::error;
use std::io::{Error, ErrorKind};
use std::net::SocketAddr;
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
pub const SIGNATURE: &str =
"43yNSFC6fYTuPgTNFFhF4axw7AfWxB2BPdurme8yrsWEYwm8299xh8n6TAHjGymiSub1XtyxTNyd9GBfY2hxoBw8";
#[derive(Clone)]
pub struct MockRpcClient {
pub addr: String,
}
impl MockRpcClient {
pub fn new(addr: String) -> Self {
MockRpcClient { addr }
}
}
impl RpcRequestHandler for MockRpcClient {
fn make_rpc_request(
&self,
_id: u64,
request: RpcRequest,
params: Option<Value>,
) -> Result<Value, Box<dyn error::Error>> {
if self.addr == "fails" {
return Ok(Value::Null);
}
let val = match request {
RpcRequest::ConfirmTransaction => {
if let Some(Value::Array(param_array)) = params {
if let Value::String(param_string) = &param_array[0] {
Value::Bool(param_string == SIGNATURE)
} else {
Value::Null
}
} else {
Value::Null
}
}
RpcRequest::GetBalance => {
let n = if self.addr == "airdrop" { 0 } else { 50 };
Value::Number(Number::from(n))
}
RpcRequest::GetLastId => Value::String(PUBKEY.to_string()),
RpcRequest::GetSignatureStatus => {
let str = if self.addr == "account_in_use" {
"AccountInUse"
} else if self.addr == "bad_sig_status" {
"Nonexistent"
} else {
"Confirmed"
};
Value::String(str.to_string())
}
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()),
_ => Value::Null,
};
Ok(val)
}
}
pub fn request_airdrop_transaction(
_drone_addr: &SocketAddr,
_id: &Pubkey,
tokens: u64,
_last_id: Hash,
) -> Result<Transaction, Error> {
if tokens == 0 {
Err(Error::new(ErrorKind::Other, "Airdrop failed"))?
}
let key = Keypair::new();
let to = Keypair::new().pubkey();
let last_id = Hash::default();
let tx = Transaction::system_new(&key, to, 50, last_id);
Ok(tx)
}

View File

@ -6,6 +6,7 @@ use std::thread::sleep;
use std::time::Duration;
use std::{error, fmt};
#[derive(Clone)]
pub struct RpcClient {
pub client: reqwest::Client,
pub addr: String,
@ -35,55 +36,22 @@ impl RpcClient {
addr,
}
}
}
pub fn get_rpc_request_str(rpc_addr: SocketAddr) -> String {
format!("http://{}", rpc_addr)
}
pub enum RpcRequest {
ConfirmTransaction,
GetAccountInfo,
GetBalance,
GetConfirmationTime,
GetLastId,
GetSignatureStatus,
GetTransactionCount,
RequestAirdrop,
SendTransaction,
RegisterNode,
SignVote,
DeregisterNode,
GetStorageMiningLastId,
GetStorageMiningEntryHeight,
GetStoragePubkeysForEntryHeight,
}
impl RpcRequest {
pub fn make_rpc_request(
&self,
client: &RpcClient,
id: u64,
params: Option<Value>,
) -> Result<Value, Box<dyn error::Error>> {
self.retry_make_rpc_request(client, id, params, 0)
}
pub fn retry_make_rpc_request(
&self,
client: &RpcClient,
id: u64,
request: &RpcRequest,
params: Option<Value>,
mut retries: usize,
) -> Result<Value, Box<dyn error::Error>> {
let request = self.build_request_json(id, params);
let request_json = request.build_request_json(id, params);
loop {
match client
match self
.client
.post(&client.addr)
.post(&self.addr)
.header(CONTENT_TYPE, "application/json")
.body(request.to_string())
.body(request_json.to_string())
.send()
{
Ok(mut response) => {
@ -111,7 +79,52 @@ impl RpcRequest {
}
}
}
}
pub fn get_rpc_request_str(rpc_addr: SocketAddr) -> String {
format!("http://{}", rpc_addr)
}
pub trait RpcRequestHandler {
fn make_rpc_request(
&self,
id: u64,
request: RpcRequest,
params: Option<Value>,
) -> Result<Value, Box<dyn error::Error>>;
}
impl RpcRequestHandler for RpcClient {
fn make_rpc_request(
&self,
id: u64,
request: RpcRequest,
params: Option<Value>,
) -> Result<Value, Box<dyn error::Error>> {
self.retry_make_rpc_request(id, &request, params, 0)
}
}
#[derive(Debug, PartialEq)]
pub enum RpcRequest {
ConfirmTransaction,
GetAccountInfo,
GetBalance,
GetConfirmationTime,
GetLastId,
GetSignatureStatus,
GetTransactionCount,
RequestAirdrop,
SendTransaction,
RegisterNode,
SignVote,
DeregisterNode,
GetStorageMiningLastId,
GetStorageMiningEntryHeight,
GetStoragePubkeysForEntryHeight,
}
impl RpcRequest {
fn build_request_json(&self, id: u64, params: Option<Value>) -> Value {
let jsonrpc = "2.0";
let method = match self {
@ -251,15 +264,15 @@ mod tests {
let rpc_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_from_socket(rpc_addr);
let balance = RpcRequest::GetBalance.make_rpc_request(
&rpc_client,
let balance = rpc_client.make_rpc_request(
1,
RpcRequest::GetBalance,
Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])),
);
assert!(balance.is_ok());
assert_eq!(balance.unwrap().as_u64().unwrap(), 50);
let last_id = RpcRequest::GetLastId.make_rpc_request(&rpc_client, 2, None);
let last_id = rpc_client.make_rpc_request(2, RpcRequest::GetLastId, None);
assert!(last_id.is_ok());
assert_eq!(
last_id.unwrap().as_str().unwrap(),
@ -268,7 +281,7 @@ mod tests {
// Send erroneous parameter
let last_id =
RpcRequest::GetLastId.make_rpc_request(&rpc_client, 3, Some(json!("paramter")));
rpc_client.make_rpc_request(3, RpcRequest::GetLastId, Some(json!("paramter")));
assert_eq!(last_id.is_err(), true);
}
@ -302,9 +315,9 @@ mod tests {
let rpc_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_from_socket(rpc_addr);
let balance = RpcRequest::GetBalance.retry_make_rpc_request(
&rpc_client,
let balance = rpc_client.retry_make_rpc_request(
1,
&RpcRequest::GetBalance,
Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhw"])),
10,
);

View File

@ -8,7 +8,7 @@ use crate::cluster_info::{ClusterInfo, ClusterInfoError, NodeInfo};
use crate::gossip_service::GossipService;
use crate::packet::PACKET_DATA_SIZE;
use crate::result::{Error, Result};
use crate::rpc_request::{RpcClient, RpcRequest};
use crate::rpc_request::{RpcClient, RpcRequest, RpcRequestHandler};
use bincode::serialize;
use bs58;
use hashbrown::HashMap;
@ -149,7 +149,9 @@ impl ThinClient {
pub fn get_account_userdata(&mut self, pubkey: &Pubkey) -> io::Result<Option<Vec<u8>>> {
let params = json!([format!("{}", pubkey)]);
let resp = RpcRequest::GetAccountInfo.make_rpc_request(&self.rpc_client, 1, Some(params));
let resp = self
.rpc_client
.make_rpc_request(1, RpcRequest::GetAccountInfo, Some(params));
if let Ok(account_json) = resp {
let account: Account =
serde_json::from_value(account_json).expect("deserialize account");
@ -167,7 +169,9 @@ impl ThinClient {
pub fn get_balance(&mut self, pubkey: &Pubkey) -> io::Result<u64> {
trace!("get_balance sending request to {}", self.rpc_addr);
let params = json!([format!("{}", pubkey)]);
let resp = RpcRequest::GetAccountInfo.make_rpc_request(&self.rpc_client, 1, Some(params));
let resp = self
.rpc_client
.make_rpc_request(1, RpcRequest::GetAccountInfo, Some(params));
if let Ok(account_json) = resp {
let account: Account =
serde_json::from_value(account_json).expect("deserialize account");
@ -193,7 +197,9 @@ impl ThinClient {
let mut done = false;
while !done {
debug!("get_confirmation_time send_to {}", &self.rpc_addr);
let resp = RpcRequest::GetConfirmationTime.make_rpc_request(&self.rpc_client, 1, None);
let resp = self
.rpc_client
.make_rpc_request(1, RpcRequest::GetConfirmationTime, None);
if let Ok(value) = resp {
done = true;
@ -212,7 +218,9 @@ impl ThinClient {
debug!("transaction_count");
let mut tries_left = 5;
while tries_left > 0 {
let resp = RpcRequest::GetTransactionCount.make_rpc_request(&self.rpc_client, 1, None);
let resp = self
.rpc_client
.make_rpc_request(1, RpcRequest::GetTransactionCount, None);
if let Ok(value) = resp {
debug!("transaction_count recv_response: {:?}", value);
@ -233,7 +241,9 @@ impl ThinClient {
let mut done = false;
while !done {
debug!("get_last_id send_to {}", &self.rpc_addr);
let resp = RpcRequest::GetLastId.make_rpc_request(&self.rpc_client, 1, None);
let resp = self
.rpc_client
.make_rpc_request(1, RpcRequest::GetLastId, None);
if let Ok(value) = resp {
done = true;
@ -309,9 +319,9 @@ impl ThinClient {
let now = Instant::now();
let mut done = false;
while !done {
let resp = RpcRequest::ConfirmTransaction.make_rpc_request(
&self.rpc_client,
let resp = self.rpc_client.make_rpc_request(
1,
RpcRequest::ConfirmTransaction,
Some(params.clone()),
);

View File

@ -48,24 +48,27 @@ impl VoteSigner for RemoteVoteSigner {
msg: &[u8],
) -> jsonrpc_core::Result<Pubkey> {
let params = json!([pubkey, sig, msg]);
let resp = RpcRequest::RegisterNode
.retry_make_rpc_request(&self.rpc_client, 1, Some(params), 5)
let resp = self
.rpc_client
.retry_make_rpc_request(1, &RpcRequest::RegisterNode, Some(params), 5)
.unwrap();
let vote_account: Pubkey = serde_json::from_value(resp).unwrap();
Ok(vote_account)
}
fn sign(&self, pubkey: Pubkey, sig: &Signature, msg: &[u8]) -> jsonrpc_core::Result<Signature> {
let params = json!([pubkey, sig, msg]);
let resp = RpcRequest::SignVote
.make_rpc_request(&self.rpc_client, 1, Some(params))
let resp = self
.rpc_client
.retry_make_rpc_request(1, &RpcRequest::SignVote, Some(params), 0)
.unwrap();
let vote_signature: Signature = serde_json::from_value(resp).unwrap();
Ok(vote_signature)
}
fn deregister(&self, pubkey: Pubkey, sig: &Signature, msg: &[u8]) -> jsonrpc_core::Result<()> {
let params = json!([pubkey, sig, msg]);
let _resp = RpcRequest::DeregisterNode
.retry_make_rpc_request(&self.rpc_client, 1, Some(params), 5)
let _resp = self
.rpc_client
.retry_make_rpc_request(1, &RpcRequest::DeregisterNode, Some(params), 5)
.unwrap();
Ok(())
}

View File

@ -156,15 +156,15 @@ fn test_replicator_startup() {
// chacha is not enabled
#[cfg(feature = "chacha")]
{
use solana::rpc_request::{RpcClient, RpcRequest};
use solana::rpc_request::{RpcClient, RpcRequest, RpcRequestHandler};
use std::thread::sleep;
let rpc_client = RpcClient::new_from_socket(validator_node_info.rpc);
let mut non_zero_pubkeys = false;
for _ in 0..30 {
let params = json!([0]);
let pubkeys = RpcRequest::GetStoragePubkeysForEntryHeight
.make_rpc_request(&rpc_client, 1, Some(params))
let pubkeys = rpc_client
.make_rpc_request(1, RpcRequest::GetStoragePubkeysForEntryHeight, Some(params))
.unwrap();
info!("pubkeys: {:?}", pubkeys);
if pubkeys.as_array().unwrap().len() != 0 {

1
wallet/src/lib.rs Normal file
View File

@ -0,0 +1 @@
pub mod wallet;

View File

@ -1,9 +1,7 @@
mod wallet;
use crate::wallet::{parse_command, process_command, WalletConfig, WalletError};
use clap::{crate_version, App, Arg, ArgMatches, SubCommand};
use solana::socketaddr;
use solana_sdk::signature::{gen_keypair_file, read_keypair, KeypairUtil};
use solana_wallet::wallet::{parse_command, process_command, WalletConfig, WalletError};
use std::error;
use std::net::SocketAddr;
@ -53,6 +51,7 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<WalletConfig, Box<dyn erro
timeout,
proxy,
drone_port: None,
rpc_client: None,
})
}

File diff suppressed because it is too large Load Diff

320
wallet/tests/pay.rs Normal file
View File

@ -0,0 +1,320 @@
use chrono::prelude::*;
use serde_json::{json, Value};
use solana::bank::Bank;
use solana::cluster_info::Node;
use solana::db_ledger::create_tmp_ledger_with_mint;
use solana::fullnode::Fullnode;
use solana::leader_scheduler::LeaderScheduler;
use solana::mint::Mint;
use solana::rpc_request::{RpcClient, RpcRequest, RpcRequestHandler};
use solana::vote_signer_proxy::VoteSignerProxy;
use solana_drone::drone::run_local_drone;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_vote_signer::rpc::LocalVoteSigner;
use solana_wallet::wallet::{
process_command, request_and_confirm_airdrop, WalletCommand, WalletConfig,
};
use std::fs::remove_dir_all;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
fn check_balance(expected_balance: u64, client: &RpcClient, params: Value) {
let balance = client
.make_rpc_request(1, RpcRequest::GetBalance, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(balance, expected_balance);
}
#[test]
fn test_wallet_timestamp_tx() {
let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone();
let alice = Mint::new(10_000);
let mut bank = Bank::new(&alice);
let bob_pubkey = Keypair::new().pubkey();
let ledger_path = create_tmp_ledger_with_mint("thin_client", &alice);
let entry_height = alice.create_entries().len() as u64;
let leader_scheduler = Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
leader_data.id,
)));
bank.leader_scheduler = leader_scheduler;
let vote_account_keypair = Arc::new(Keypair::new());
let vote_signer =
VoteSignerProxy::new(&vote_account_keypair, Box::new(LocalVoteSigner::default()));
let last_id = bank.last_id();
let server = Fullnode::new_with_bank(
leader_keypair,
Arc::new(vote_signer),
bank,
None,
entry_height,
&last_id,
leader,
None,
&ledger_path,
false,
None,
);
sleep(Duration::from_millis(900));
let (sender, receiver) = channel();
run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_from_socket(leader_data.rpc);
let mut config_payer = WalletConfig::default();
config_payer.network = leader_data.gossip;
config_payer.drone_port = Some(drone_addr.port());
let mut config_witness = WalletConfig::default();
config_witness.network = leader_data.gossip;
config_witness.drone_port = Some(drone_addr.port());
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50).unwrap();
let params = json!([format!("{}", config_payer.id.pubkey())]);
check_balance(50, &rpc_client, params);
// 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())]);
check_balance(39, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(11, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(0, &rpc_client, params); // recipient balance
// 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())]);
check_balance(39, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(1, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(10, &rpc_client, params); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_wallet_witness_tx() {
let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone();
let alice = Mint::new(10_000);
let mut bank = Bank::new(&alice);
let bob_pubkey = Keypair::new().pubkey();
let ledger_path = create_tmp_ledger_with_mint("thin_client", &alice);
let entry_height = alice.create_entries().len() as u64;
let leader_scheduler = Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
leader_data.id,
)));
bank.leader_scheduler = leader_scheduler;
let vote_account_keypair = Arc::new(Keypair::new());
let vote_signer =
VoteSignerProxy::new(&vote_account_keypair, Box::new(LocalVoteSigner::default()));
let last_id = bank.last_id();
let server = Fullnode::new_with_bank(
leader_keypair,
Arc::new(vote_signer),
bank,
None,
entry_height,
&last_id,
leader,
None,
&ledger_path,
false,
None,
);
sleep(Duration::from_millis(900));
let (sender, receiver) = channel();
run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_from_socket(leader_data.rpc);
let mut config_payer = WalletConfig::default();
config_payer.network = leader_data.gossip;
config_payer.drone_port = Some(drone_addr.port());
let mut config_witness = WalletConfig::default();
config_witness.network = leader_data.gossip;
config_witness.drone_port = Some(drone_addr.port());
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50).unwrap();
// 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())]);
check_balance(39, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(11, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(0, &rpc_client, params); // recipient balance
// 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())]);
check_balance(39, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(1, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(10, &rpc_client, params); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_wallet_cancel_tx() {
let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone();
let alice = Mint::new(10_000);
let mut bank = Bank::new(&alice);
let bob_pubkey = Keypair::new().pubkey();
let ledger_path = create_tmp_ledger_with_mint("thin_client", &alice);
let entry_height = alice.create_entries().len() as u64;
let leader_scheduler = Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
leader_data.id,
)));
bank.leader_scheduler = leader_scheduler;
let vote_account_keypair = Arc::new(Keypair::new());
let vote_signer =
VoteSignerProxy::new(&vote_account_keypair, Box::new(LocalVoteSigner::default()));
let last_id = bank.last_id();
let server = Fullnode::new_with_bank(
leader_keypair,
Arc::new(vote_signer),
bank,
None,
entry_height,
&last_id,
leader,
None,
&ledger_path,
false,
None,
);
sleep(Duration::from_millis(900));
let (sender, receiver) = channel();
run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_from_socket(leader_data.rpc);
let mut config_payer = WalletConfig::default();
config_payer.network = leader_data.gossip;
config_payer.drone_port = Some(drone_addr.port());
let mut config_witness = WalletConfig::default();
config_witness.network = leader_data.gossip;
config_witness.drone_port = Some(drone_addr.port());
assert_ne!(config_payer.id.pubkey(), config_witness.id.pubkey());
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config_payer.id.pubkey(), 50).unwrap();
// 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())]);
check_balance(39, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(11, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(0, &rpc_client, params); // recipient balance
// 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())]);
check_balance(49, &rpc_client, params); // config_payer balance
let params = json!([format!("{}", process_id)]);
check_balance(1, &rpc_client, params); // contract balance
let params = json!([format!("{}", bob_pubkey)]);
check_balance(0, &rpc_client, params); // recipient balance
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}

View File

@ -0,0 +1,78 @@
use serde_json::json;
use solana::bank::Bank;
use solana::cluster_info::Node;
use solana::db_ledger::create_tmp_ledger_with_mint;
use solana::fullnode::Fullnode;
use solana::leader_scheduler::LeaderScheduler;
use solana::mint::Mint;
use solana::rpc_request::{RpcClient, RpcRequest, RpcRequestHandler};
use solana::vote_signer_proxy::VoteSignerProxy;
use solana_drone::drone::run_local_drone;
use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_vote_signer::rpc::LocalVoteSigner;
use solana_wallet::wallet::{process_command, WalletCommand, WalletConfig};
use std::fs::remove_dir_all;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::Duration;
#[test]
fn test_wallet_request_airdrop() {
let leader_keypair = Arc::new(Keypair::new());
let leader = Node::new_localhost_with_pubkey(leader_keypair.pubkey());
let leader_data = leader.info.clone();
let alice = Mint::new(10_000);
let mut bank = Bank::new(&alice);
let ledger_path = create_tmp_ledger_with_mint("thin_client", &alice);
let entry_height = alice.create_entries().len() as u64;
let leader_scheduler = Arc::new(RwLock::new(LeaderScheduler::from_bootstrap_leader(
leader_data.id,
)));
bank.leader_scheduler = leader_scheduler;
let vote_account_keypair = Arc::new(Keypair::new());
let vote_signer =
VoteSignerProxy::new(&vote_account_keypair, Box::new(LocalVoteSigner::default()));
let last_id = bank.last_id();
let server = Fullnode::new_with_bank(
leader_keypair,
Arc::new(vote_signer),
bank,
None,
entry_height,
&last_id,
leader,
None,
&ledger_path,
false,
None,
);
sleep(Duration::from_millis(900));
let (sender, receiver) = channel();
run_local_drone(alice.keypair(), sender);
let drone_addr = receiver.recv().unwrap();
let mut bob_config = WalletConfig::default();
bob_config.network = leader_data.gossip;
bob_config.drone_port = Some(drone_addr.port());
bob_config.command = WalletCommand::Airdrop(50);
let sig_response = process_command(&bob_config);
assert!(sig_response.is_ok());
let rpc_client = RpcClient::new_from_socket(leader_data.rpc);
let params = json!([format!("{}", bob_config.id.pubkey())]);
let balance = rpc_client
.make_rpc_request(1, RpcRequest::GetBalance, Some(params))
.unwrap()
.as_u64()
.unwrap();
assert_eq!(balance, 50);
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}