2019-01-21 10:48:40 -08:00
|
|
|
use bincode::serialize;
|
2021-02-02 18:53:08 -08:00
|
|
|
use jsonrpc_core::futures::StreamExt;
|
2020-02-25 20:23:54 -08:00
|
|
|
use jsonrpc_core_client::transports::ws;
|
2019-01-29 17:03:32 -08:00
|
|
|
use log::*;
|
2019-09-24 13:10:59 -07:00
|
|
|
use reqwest::{self, header::CONTENT_TYPE};
|
2019-01-21 10:48:40 -08:00
|
|
|
use serde_json::{json, Value};
|
2020-06-30 21:55:11 -07:00
|
|
|
use solana_account_decoder::UiAccount;
|
2020-03-23 17:00:34 -07:00
|
|
|
use solana_client::{
|
2020-11-25 17:00:47 -08:00
|
|
|
rpc_client::RpcClient,
|
2021-03-12 05:44:06 -08:00
|
|
|
rpc_response::{Response, RpcSignatureResult, SlotUpdate},
|
2020-03-23 17:00:34 -07:00
|
|
|
};
|
2020-09-18 22:21:44 -07:00
|
|
|
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, test_validator::TestValidator};
|
2020-03-23 17:00:34 -07:00
|
|
|
use solana_sdk::{
|
2020-12-08 23:18:27 -08:00
|
|
|
commitment_config::CommitmentConfig,
|
|
|
|
hash::Hash,
|
2021-03-12 05:44:06 -08:00
|
|
|
pubkey::Pubkey,
|
2020-12-08 23:18:27 -08:00
|
|
|
signature::{Keypair, Signer},
|
|
|
|
system_transaction,
|
2020-10-19 12:23:14 -07:00
|
|
|
transaction::Transaction,
|
2020-03-23 17:00:34 -07:00
|
|
|
};
|
2020-02-25 20:23:54 -08:00
|
|
|
use std::{
|
|
|
|
collections::HashSet,
|
|
|
|
net::UdpSocket,
|
2021-03-12 05:44:06 -08:00
|
|
|
sync::{mpsc::channel, Arc},
|
2020-02-25 20:23:54 -08:00
|
|
|
thread::sleep,
|
2020-03-23 17:00:34 -07:00
|
|
|
time::{Duration, Instant},
|
2020-02-25 20:23:54 -08:00
|
|
|
};
|
2021-02-04 23:21:53 -08:00
|
|
|
use tokio_02::runtime::Runtime;
|
2019-01-21 10:48:40 -08:00
|
|
|
|
2020-04-09 18:05:56 -07:00
|
|
|
macro_rules! json_req {
|
|
|
|
($method: expr, $params: expr) => {{
|
|
|
|
json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"id": 1,
|
|
|
|
"method": $method,
|
|
|
|
"params": $params,
|
|
|
|
})
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-11-25 17:00:47 -08:00
|
|
|
fn post_rpc(request: Value, rpc_url: &str) -> Value {
|
2020-04-09 18:05:56 -07:00
|
|
|
let client = reqwest::blocking::Client::new();
|
|
|
|
let response = client
|
2020-11-25 17:00:47 -08:00
|
|
|
.post(rpc_url)
|
2020-04-09 18:05:56 -07:00
|
|
|
.header(CONTENT_TYPE, "application/json")
|
|
|
|
.body(request.to_string())
|
|
|
|
.send()
|
|
|
|
.unwrap();
|
|
|
|
serde_json::from_str(&response.text().unwrap()).unwrap()
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:48:40 -08:00
|
|
|
#[test]
|
|
|
|
fn test_rpc_send_tx() {
|
2019-01-29 17:03:32 -08:00
|
|
|
solana_logger::setup();
|
|
|
|
|
2020-12-08 23:18:27 -08:00
|
|
|
let alice = Keypair::new();
|
|
|
|
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
2020-11-25 17:00:47 -08:00
|
|
|
let rpc_url = test_validator.rpc_url();
|
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
2019-01-21 10:48:40 -08:00
|
|
|
|
2020-04-09 18:05:56 -07:00
|
|
|
let req = json_req!("getRecentBlockhash", json!([]));
|
2020-11-25 17:00:47 -08:00
|
|
|
let json = post_rpc(req, &rpc_url);
|
2020-04-09 18:05:56 -07:00
|
|
|
|
2020-01-14 23:25:45 -08:00
|
|
|
let blockhash: Hash = json["result"]["value"]["blockhash"]
|
2019-11-12 11:49:41 -08:00
|
|
|
.as_str()
|
|
|
|
.unwrap()
|
|
|
|
.parse()
|
|
|
|
.unwrap();
|
2019-01-29 17:03:32 -08:00
|
|
|
|
2019-03-02 10:25:16 -08:00
|
|
|
info!("blockhash: {:?}", blockhash);
|
2019-05-20 10:03:19 -07:00
|
|
|
let tx = system_transaction::transfer(&alice, &bob_pubkey, 20, blockhash);
|
2020-01-21 21:16:07 -08:00
|
|
|
let serialized_encoded_tx = bs58::encode(serialize(&tx).unwrap()).into_string();
|
2019-01-21 10:48:40 -08:00
|
|
|
|
2020-04-09 18:05:56 -07:00
|
|
|
let req = json_req!("sendTransaction", json!([serialized_encoded_tx]));
|
2020-11-25 17:00:47 -08:00
|
|
|
let json: Value = post_rpc(req, &rpc_url);
|
2020-04-09 18:05:56 -07:00
|
|
|
|
2019-01-21 10:48:40 -08:00
|
|
|
let signature = &json["result"];
|
|
|
|
|
|
|
|
let mut confirmed_tx = false;
|
|
|
|
|
2020-04-09 18:05:56 -07:00
|
|
|
let request = json_req!("confirmTransaction", [signature]);
|
2019-01-21 10:48:40 -08:00
|
|
|
|
2019-09-06 14:30:56 -07:00
|
|
|
for _ in 0..solana_sdk::clock::DEFAULT_TICKS_PER_SLOT {
|
2020-11-25 17:00:47 -08:00
|
|
|
let json = post_rpc(request.clone(), &rpc_url);
|
2019-01-21 10:48:40 -08:00
|
|
|
|
2019-11-12 11:49:41 -08:00
|
|
|
if true == json["result"]["value"] {
|
2019-01-21 10:48:40 -08:00
|
|
|
confirmed_tx = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-01-29 17:03:32 -08:00
|
|
|
sleep(Duration::from_millis(500));
|
2019-01-21 10:48:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(confirmed_tx, true);
|
|
|
|
|
2020-08-08 22:40:13 -07:00
|
|
|
use solana_account_decoder::UiAccountEncoding;
|
|
|
|
use solana_client::rpc_config::RpcAccountInfoConfig;
|
|
|
|
let config = RpcAccountInfoConfig {
|
2020-08-15 22:06:39 -07:00
|
|
|
encoding: Some(UiAccountEncoding::Base64),
|
2020-08-08 22:40:13 -07:00
|
|
|
commitment: None,
|
2020-08-10 15:35:29 -07:00
|
|
|
data_slice: None,
|
2020-08-08 22:40:13 -07:00
|
|
|
};
|
|
|
|
let req = json_req!(
|
|
|
|
"getAccountInfo",
|
|
|
|
json!([bs58::encode(bob_pubkey).into_string(), config])
|
|
|
|
);
|
2020-11-25 17:00:47 -08:00
|
|
|
let json: Value = post_rpc(req, &rpc_url);
|
2020-08-08 22:40:13 -07:00
|
|
|
info!("{:?}", json["result"]["value"]);
|
2019-01-21 10:48:40 -08:00
|
|
|
}
|
2019-11-14 08:41:26 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rpc_invalid_requests() {
|
|
|
|
solana_logger::setup();
|
|
|
|
|
2020-12-08 23:18:27 -08:00
|
|
|
let alice = Keypair::new();
|
|
|
|
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
2020-11-25 17:00:47 -08:00
|
|
|
let rpc_url = test_validator.rpc_url();
|
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let bob_pubkey = solana_sdk::pubkey::new_rand();
|
2019-11-14 08:41:26 -08:00
|
|
|
|
|
|
|
// test invalid get_balance request
|
2020-04-09 18:05:56 -07:00
|
|
|
let req = json_req!("getBalance", json!(["invalid9999"]));
|
2020-11-25 17:00:47 -08:00
|
|
|
let json = post_rpc(req, &rpc_url);
|
2020-04-09 18:05:56 -07:00
|
|
|
|
2019-11-14 08:41:26 -08:00
|
|
|
let the_error = json["error"]["message"].as_str().unwrap();
|
2020-07-03 00:46:29 -07:00
|
|
|
assert_eq!(the_error, "Invalid param: Invalid");
|
2019-11-14 08:41:26 -08:00
|
|
|
|
|
|
|
// test invalid get_account_info request
|
2020-04-09 18:05:56 -07:00
|
|
|
let req = json_req!("getAccountInfo", json!(["invalid9999"]));
|
2020-11-25 17:00:47 -08:00
|
|
|
let json = post_rpc(req, &rpc_url);
|
2020-04-09 18:05:56 -07:00
|
|
|
|
2019-11-14 08:41:26 -08:00
|
|
|
let the_error = json["error"]["message"].as_str().unwrap();
|
2020-07-03 00:46:29 -07:00
|
|
|
assert_eq!(the_error, "Invalid param: Invalid");
|
2019-11-14 08:41:26 -08:00
|
|
|
|
|
|
|
// test invalid get_account_info request
|
2020-04-09 18:05:56 -07:00
|
|
|
let req = json_req!("getAccountInfo", json!([bob_pubkey.to_string()]));
|
2020-11-25 17:00:47 -08:00
|
|
|
let json = post_rpc(req, &rpc_url);
|
2020-04-09 18:05:56 -07:00
|
|
|
|
2019-11-14 08:41:26 -08:00
|
|
|
let the_value = &json["result"]["value"];
|
|
|
|
assert!(the_value.is_null());
|
|
|
|
}
|
2020-02-25 20:23:54 -08:00
|
|
|
|
2021-03-12 05:44:06 -08:00
|
|
|
#[test]
|
|
|
|
fn test_rpc_slot_updates() {
|
|
|
|
solana_logger::setup();
|
|
|
|
|
|
|
|
let test_validator = TestValidator::with_no_fees(Pubkey::new_unique());
|
|
|
|
|
|
|
|
// Create the pub sub runtime
|
|
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
let rpc_pubsub_url = test_validator.rpc_pubsub_url();
|
|
|
|
let (update_sender, update_receiver) = channel::<Arc<SlotUpdate>>();
|
|
|
|
|
|
|
|
// Subscribe to slot updates
|
|
|
|
rt.spawn(async move {
|
|
|
|
let connect = ws::try_connect::<PubsubClient>(&rpc_pubsub_url).unwrap();
|
|
|
|
let client = connect.await.unwrap();
|
|
|
|
|
|
|
|
tokio_02::spawn(async move {
|
|
|
|
let mut update_sub = client.slots_updates_subscribe().unwrap();
|
|
|
|
loop {
|
|
|
|
let response = update_sub.next().await.unwrap();
|
|
|
|
update_sender.send(response.unwrap()).unwrap();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
let first_update = update_receiver
|
|
|
|
.recv_timeout(Duration::from_secs(2))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Verify that updates are received in order for an upcoming slot
|
|
|
|
let verify_slot = first_update.slot() + 2;
|
|
|
|
let mut expected_update_index = 0;
|
|
|
|
let expected_updates = vec![
|
|
|
|
"CreatedBank",
|
|
|
|
"Completed",
|
|
|
|
"Frozen",
|
|
|
|
"OptimisticConfirmation",
|
|
|
|
"Root",
|
|
|
|
];
|
|
|
|
|
|
|
|
let test_start = Instant::now();
|
|
|
|
loop {
|
|
|
|
assert!(test_start.elapsed() < Duration::from_secs(30));
|
|
|
|
let update = update_receiver
|
|
|
|
.recv_timeout(Duration::from_secs(2))
|
|
|
|
.unwrap();
|
|
|
|
if update.slot() == verify_slot {
|
|
|
|
let update_name = match *update {
|
|
|
|
SlotUpdate::CreatedBank { .. } => "CreatedBank",
|
|
|
|
SlotUpdate::Completed { .. } => "Completed",
|
|
|
|
SlotUpdate::Frozen { .. } => "Frozen",
|
|
|
|
SlotUpdate::OptimisticConfirmation { .. } => "OptimisticConfirmation",
|
|
|
|
SlotUpdate::Root { .. } => "Root",
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
assert_eq!(update_name, expected_updates[expected_update_index]);
|
|
|
|
expected_update_index += 1;
|
|
|
|
if expected_update_index == expected_updates.len() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
#[test]
|
|
|
|
fn test_rpc_subscriptions() {
|
|
|
|
solana_logger::setup();
|
|
|
|
|
2020-12-08 23:18:27 -08:00
|
|
|
let alice = Keypair::new();
|
|
|
|
let test_validator = TestValidator::with_no_fees(alice.pubkey());
|
2020-02-25 20:23:54 -08:00
|
|
|
|
|
|
|
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
2020-11-25 17:00:47 -08:00
|
|
|
transactions_socket.connect(test_validator.tpu()).unwrap();
|
2020-03-28 10:58:51 -07:00
|
|
|
|
2020-12-08 23:18:27 -08:00
|
|
|
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
|
|
|
let recent_blockhash = rpc_client.get_recent_blockhash().unwrap().0;
|
|
|
|
|
2020-03-28 10:58:51 -07:00
|
|
|
// Create transaction signatures to subscribe to
|
|
|
|
let transactions: Vec<Transaction> = (0..1000)
|
2020-10-19 12:23:14 -07:00
|
|
|
.map(|_| {
|
2020-11-25 17:00:47 -08:00
|
|
|
system_transaction::transfer(
|
|
|
|
&alice,
|
|
|
|
&solana_sdk::pubkey::new_rand(),
|
|
|
|
1,
|
2020-12-08 23:18:27 -08:00
|
|
|
recent_blockhash,
|
2020-11-25 17:00:47 -08:00
|
|
|
)
|
2020-10-19 12:23:14 -07:00
|
|
|
})
|
2020-03-23 17:00:34 -07:00
|
|
|
.collect();
|
|
|
|
let mut signature_set: HashSet<String> = transactions
|
|
|
|
.iter()
|
|
|
|
.map(|tx| tx.signatures[0].to_string())
|
2020-02-25 20:23:54 -08:00
|
|
|
.collect();
|
2020-05-06 23:23:06 -07:00
|
|
|
let account_set: HashSet<String> = transactions
|
|
|
|
.iter()
|
|
|
|
.map(|tx| tx.message.account_keys[1].to_string())
|
|
|
|
.collect();
|
2020-02-25 20:23:54 -08:00
|
|
|
|
2020-03-28 10:58:51 -07:00
|
|
|
// Track when subscriptions are ready
|
|
|
|
let (ready_sender, ready_receiver) = channel::<()>();
|
2020-05-06 23:23:06 -07:00
|
|
|
// Track account notifications are received
|
2020-06-30 21:55:11 -07:00
|
|
|
let (account_sender, account_receiver) = channel::<Response<UiAccount>>();
|
2020-03-28 10:58:51 -07:00
|
|
|
// Track when status notifications are received
|
2020-04-04 16:13:26 -07:00
|
|
|
let (status_sender, status_receiver) = channel::<(String, Response<RpcSignatureResult>)>();
|
2020-03-28 10:58:51 -07:00
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
// Create the pub sub runtime
|
2021-02-02 18:53:08 -08:00
|
|
|
let rt = Runtime::new().unwrap();
|
|
|
|
let rpc_pubsub_url = test_validator.rpc_pubsub_url();
|
|
|
|
let signature_set_clone = signature_set.clone();
|
|
|
|
rt.spawn(async move {
|
|
|
|
let connect = ws::try_connect::<PubsubClient>(&rpc_pubsub_url).unwrap();
|
|
|
|
let client = connect.await.unwrap();
|
|
|
|
|
|
|
|
// Subscribe to signature notifications
|
|
|
|
for sig in signature_set_clone {
|
|
|
|
let status_sender = status_sender.clone();
|
|
|
|
let mut sig_sub = client
|
|
|
|
.signature_subscribe(sig.clone(), None)
|
|
|
|
.unwrap_or_else(|err| panic!("sig sub err: {:#?}", err));
|
|
|
|
|
2021-02-04 23:21:53 -08:00
|
|
|
tokio_02::spawn(async move {
|
2021-02-02 18:53:08 -08:00
|
|
|
let response = sig_sub.next().await.unwrap();
|
|
|
|
status_sender
|
|
|
|
.send((sig.clone(), response.unwrap()))
|
|
|
|
.unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subscribe to account notifications
|
|
|
|
for pubkey in account_set {
|
|
|
|
let account_sender = account_sender.clone();
|
|
|
|
let mut client_sub = client
|
|
|
|
.account_subscribe(pubkey, None)
|
|
|
|
.unwrap_or_else(|err| panic!("acct sub err: {:#?}", err));
|
2021-02-04 23:21:53 -08:00
|
|
|
tokio_02::spawn(async move {
|
2021-02-02 18:53:08 -08:00
|
|
|
let response = client_sub.next().await.unwrap();
|
|
|
|
account_sender.send(response.unwrap()).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Signal ready after the next slot notification
|
|
|
|
let mut slot_sub = client
|
|
|
|
.slot_subscribe()
|
|
|
|
.unwrap_or_else(|err| panic!("sig sub err: {:#?}", err));
|
2021-02-04 23:21:53 -08:00
|
|
|
tokio_02::spawn(async move {
|
2021-02-02 18:53:08 -08:00
|
|
|
let _response = slot_sub.next().await.unwrap();
|
|
|
|
ready_sender.send(()).unwrap();
|
|
|
|
});
|
2020-02-25 20:23:54 -08:00
|
|
|
});
|
|
|
|
|
2020-03-23 17:00:34 -07:00
|
|
|
// Wait for signature subscriptions
|
2020-03-28 10:58:51 -07:00
|
|
|
ready_receiver.recv_timeout(Duration::from_secs(2)).unwrap();
|
2020-03-23 17:00:34 -07:00
|
|
|
|
2020-11-25 17:00:47 -08:00
|
|
|
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
2020-03-28 10:58:51 -07:00
|
|
|
let mut mint_balance = rpc_client
|
2021-01-26 11:23:07 -08:00
|
|
|
.get_balance_with_commitment(&alice.pubkey(), CommitmentConfig::processed())
|
2020-03-28 10:58:51 -07:00
|
|
|
.unwrap()
|
|
|
|
.value;
|
|
|
|
assert!(mint_balance >= transactions.len() as u64);
|
2020-03-23 17:00:34 -07:00
|
|
|
|
|
|
|
// Send all transactions to tpu socket for processing
|
|
|
|
transactions.iter().for_each(|tx| {
|
|
|
|
transactions_socket
|
2020-03-28 10:58:51 -07:00
|
|
|
.send(&bincode::serialize(&tx).unwrap())
|
2020-03-23 17:00:34 -07:00
|
|
|
.unwrap();
|
|
|
|
});
|
2020-03-28 10:58:51 -07:00
|
|
|
|
|
|
|
// Track mint balance to know when transactions have completed
|
2020-03-23 17:00:34 -07:00
|
|
|
let now = Instant::now();
|
2020-03-28 10:58:51 -07:00
|
|
|
let expected_mint_balance = mint_balance - transactions.len() as u64;
|
|
|
|
while mint_balance != expected_mint_balance && now.elapsed() < Duration::from_secs(5) {
|
|
|
|
mint_balance = rpc_client
|
2021-01-26 11:23:07 -08:00
|
|
|
.get_balance_with_commitment(&alice.pubkey(), CommitmentConfig::processed())
|
2020-03-28 10:58:51 -07:00
|
|
|
.unwrap()
|
|
|
|
.value;
|
|
|
|
sleep(Duration::from_millis(100));
|
2020-03-23 17:00:34 -07:00
|
|
|
}
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
// Wait for all signature subscriptions
|
2020-05-12 20:05:05 -07:00
|
|
|
let deadline = Instant::now() + Duration::from_secs(7);
|
2020-02-25 20:23:54 -08:00
|
|
|
while !signature_set.is_empty() {
|
2020-03-23 17:00:34 -07:00
|
|
|
let timeout = deadline.saturating_duration_since(Instant::now());
|
|
|
|
match status_receiver.recv_timeout(timeout) {
|
2020-02-25 20:23:54 -08:00
|
|
|
Ok((sig, result)) => {
|
2020-09-03 17:14:45 -07:00
|
|
|
if let RpcSignatureResult::ProcessedSignature(result) = result.value {
|
2020-09-01 22:06:06 -07:00
|
|
|
assert!(result.err.is_none());
|
|
|
|
assert!(signature_set.remove(&sig));
|
|
|
|
} else {
|
|
|
|
panic!("Unexpected result");
|
|
|
|
}
|
2020-02-25 20:23:54 -08:00
|
|
|
}
|
|
|
|
Err(_err) => {
|
2020-12-13 17:26:34 -08:00
|
|
|
panic!(
|
2020-03-23 17:00:34 -07:00
|
|
|
"recv_timeout, {}/{} signatures remaining",
|
|
|
|
signature_set.len(),
|
|
|
|
transactions.len()
|
|
|
|
);
|
2020-02-25 20:23:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 23:23:06 -07:00
|
|
|
let deadline = Instant::now() + Duration::from_secs(5);
|
|
|
|
let mut account_notifications = transactions.len();
|
|
|
|
while account_notifications > 0 {
|
|
|
|
let timeout = deadline.saturating_duration_since(Instant::now());
|
|
|
|
match account_receiver.recv_timeout(timeout) {
|
|
|
|
Ok(result) => {
|
|
|
|
assert_eq!(result.value.lamports, 1);
|
|
|
|
account_notifications -= 1;
|
|
|
|
}
|
|
|
|
Err(_err) => {
|
2020-12-13 17:26:34 -08:00
|
|
|
panic!(
|
2020-05-06 23:23:06 -07:00
|
|
|
"recv_timeout, {}/{} accounts remaining",
|
|
|
|
account_notifications,
|
|
|
|
transactions.len()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-25 20:23:54 -08:00
|
|
|
}
|