Move generic rpc_client functions from wallet/ to client/
This commit is contained in:
parent
3ad019a176
commit
c498775a3d
|
@ -119,7 +119,7 @@ pub fn send_barrier_transaction(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
*blockhash = barrier_client.get_recent_blockhash();
|
*blockhash = barrier_client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
let transaction =
|
let transaction =
|
||||||
SystemTransaction::new_account(&source_keypair, dest_id, 0, *blockhash, 0);
|
SystemTransaction::new_account(&source_keypair, dest_id, 0, *blockhash, 0);
|
||||||
|
@ -164,7 +164,7 @@ pub fn send_barrier_transaction(
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_blockhash = barrier_client.get_recent_blockhash();
|
let new_blockhash = barrier_client.get_recent_blockhash().unwrap();
|
||||||
if new_blockhash == *blockhash {
|
if new_blockhash == *blockhash {
|
||||||
if poll_count > 0 && poll_count % 8 == 0 {
|
if poll_count > 0 && poll_count % 8 == 0 {
|
||||||
println!("blockhash is not advancing, still at {:?}", *blockhash);
|
println!("blockhash is not advancing, still at {:?}", *blockhash);
|
||||||
|
@ -186,7 +186,7 @@ pub fn generate_txs(
|
||||||
contact_info: &ContactInfo,
|
contact_info: &ContactInfo,
|
||||||
) {
|
) {
|
||||||
let client = create_client(contact_info.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(contact_info.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
let tx_count = source.len();
|
let tx_count = source.len();
|
||||||
println!("Signing transactions... {} (reclaim={})", tx_count, reclaim);
|
println!("Signing transactions... {} (reclaim={})", tx_count, reclaim);
|
||||||
let signing_start = Instant::now();
|
let signing_start = Instant::now();
|
||||||
|
@ -376,7 +376,7 @@ pub fn fund_keys(client: &ThinClient, source: &Keypair, dests: &[Keypair], lampo
|
||||||
to_fund_txs.len(),
|
to_fund_txs.len(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
// re-sign retained to_fund_txes with updated blockhash
|
// re-sign retained to_fund_txes with updated blockhash
|
||||||
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
|
to_fund_txs.par_iter_mut().for_each(|(k, tx)| {
|
||||||
|
@ -415,7 +415,7 @@ pub fn airdrop_lamports(client: &ThinClient, drone_addr: &SocketAddr, id: &Keypa
|
||||||
id.pubkey(),
|
id.pubkey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
match request_airdrop_transaction(&drone_addr, &id.pubkey(), airdrop_amount, blockhash) {
|
match request_airdrop_transaction(&drone_addr, &id.pubkey(), airdrop_amount, blockhash) {
|
||||||
Ok(transaction) => {
|
Ok(transaction) => {
|
||||||
let signature = client.transfer_signed(&transaction).unwrap();
|
let signature = client.transfer_signed(&transaction).unwrap();
|
||||||
|
|
|
@ -103,7 +103,7 @@ fn main() {
|
||||||
airdrop_lamports(&barrier_client, &drone_addr, &barrier_source_keypair, 1);
|
airdrop_lamports(&barrier_client, &drone_addr, &barrier_source_keypair, 1);
|
||||||
|
|
||||||
println!("Get last ID...");
|
println!("Get last ID...");
|
||||||
let mut blockhash = client.get_recent_blockhash();
|
let mut blockhash = client.get_recent_blockhash().unwrap();
|
||||||
println!("Got last ID {:?}", blockhash);
|
println!("Got last ID {:?}", blockhash);
|
||||||
|
|
||||||
let first_tx_count = client.get_transaction_count().expect("transaction count");
|
let first_tx_count = client.get_transaction_count().expect("transaction count");
|
||||||
|
|
|
@ -2,16 +2,21 @@ use crate::generic_rpc_client_request::GenericRpcClientRequest;
|
||||||
use crate::mock_rpc_client_request::MockRpcClientRequest;
|
use crate::mock_rpc_client_request::MockRpcClientRequest;
|
||||||
use crate::rpc_client_request::RpcClientRequest;
|
use crate::rpc_client_request::RpcClientRequest;
|
||||||
use crate::rpc_request::RpcRequest;
|
use crate::rpc_request::RpcRequest;
|
||||||
|
use crate::rpc_signature_status::RpcSignatureStatus;
|
||||||
|
use bincode::serialize;
|
||||||
use bs58;
|
use bs58;
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::Signature;
|
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
||||||
|
use solana_sdk::timing::{DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND};
|
||||||
|
use solana_sdk::transaction::Transaction;
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
@ -43,6 +48,175 @@ impl RpcClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_transaction(
|
||||||
|
&self,
|
||||||
|
transaction: &Transaction,
|
||||||
|
) -> Result<String, Box<dyn error::Error>> {
|
||||||
|
let serialized = serialize(transaction).unwrap();
|
||||||
|
let params = json!([serialized]);
|
||||||
|
let signature = self
|
||||||
|
.client
|
||||||
|
.send(&RpcRequest::SendTransaction, Some(params), 5)?;
|
||||||
|
if signature.as_str().is_none() {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Received result of an unexpected type",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(signature.as_str().unwrap().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_signature_status(
|
||||||
|
&self,
|
||||||
|
signature: &str,
|
||||||
|
) -> Result<RpcSignatureStatus, Box<dyn error::Error>> {
|
||||||
|
let params = json!([signature.to_string()]);
|
||||||
|
let signature_status =
|
||||||
|
self.client
|
||||||
|
.send(&RpcRequest::GetSignatureStatus, Some(params), 5)?;
|
||||||
|
if let Some(status) = signature_status.as_str() {
|
||||||
|
let rpc_status = RpcSignatureStatus::from_str(status).map_err(|err| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("Unable to parse signature status: {:?}", err),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(rpc_status)
|
||||||
|
} else {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Received result of an unexpected type",
|
||||||
|
))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_and_confirm_transaction<T: KeypairUtil>(
|
||||||
|
&self,
|
||||||
|
transaction: &mut Transaction,
|
||||||
|
signer: &T,
|
||||||
|
) -> Result<String, Box<dyn error::Error>> {
|
||||||
|
let mut send_retries = 5;
|
||||||
|
loop {
|
||||||
|
let mut status_retries = 4;
|
||||||
|
let signature_str = self.send_transaction(transaction)?;
|
||||||
|
let status = loop {
|
||||||
|
let status = self.get_signature_status(&signature_str)?;
|
||||||
|
if status == RpcSignatureStatus::SignatureNotFound {
|
||||||
|
status_retries -= 1;
|
||||||
|
if status_retries == 0 {
|
||||||
|
break status;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break status;
|
||||||
|
}
|
||||||
|
if cfg!(not(test)) {
|
||||||
|
// Retry ~twice during a slot
|
||||||
|
sleep(Duration::from_millis(
|
||||||
|
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match status {
|
||||||
|
RpcSignatureStatus::AccountInUse | RpcSignatureStatus::SignatureNotFound => {
|
||||||
|
// Fetch a new blockhash and re-sign the transaction before sending it again
|
||||||
|
self.resign_transaction(transaction, signer)?;
|
||||||
|
send_retries -= 1;
|
||||||
|
}
|
||||||
|
RpcSignatureStatus::Confirmed => {
|
||||||
|
return Ok(signature_str);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
send_retries = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if send_retries == 0 {
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("Transaction {:?} failed: {:?}", signature_str, status),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_and_confirm_transactions(
|
||||||
|
&self,
|
||||||
|
mut transactions: Vec<Transaction>,
|
||||||
|
signer: &Keypair,
|
||||||
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
|
let mut send_retries = 5;
|
||||||
|
loop {
|
||||||
|
let mut status_retries = 4;
|
||||||
|
|
||||||
|
// Send all transactions
|
||||||
|
let mut transactions_signatures = vec![];
|
||||||
|
for transaction in transactions {
|
||||||
|
if cfg!(not(test)) {
|
||||||
|
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
|
||||||
|
// when all the write transactions modify the same program account (eg, deploying a
|
||||||
|
// new program)
|
||||||
|
sleep(Duration::from_millis(1000 / NUM_TICKS_PER_SECOND));
|
||||||
|
}
|
||||||
|
|
||||||
|
let signature = self.send_transaction(&transaction).ok();
|
||||||
|
transactions_signatures.push((transaction, signature))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect statuses for all the transactions, drop those that are confirmed
|
||||||
|
while status_retries > 0 {
|
||||||
|
status_retries -= 1;
|
||||||
|
|
||||||
|
if cfg!(not(test)) {
|
||||||
|
// Retry ~twice during a slot
|
||||||
|
sleep(Duration::from_millis(
|
||||||
|
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
transactions_signatures = transactions_signatures
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_transaction, signature)| {
|
||||||
|
if let Some(signature) = signature {
|
||||||
|
if let Ok(status) = self.get_signature_status(&signature) {
|
||||||
|
return status != RpcSignatureStatus::Confirmed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if transactions_signatures.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if send_retries == 0 {
|
||||||
|
Err(io::Error::new(io::ErrorKind::Other, "Transactions failed"))?;
|
||||||
|
}
|
||||||
|
send_retries -= 1;
|
||||||
|
|
||||||
|
// Re-sign any failed transactions with a new blockhash and retry
|
||||||
|
let blockhash =
|
||||||
|
self.get_new_blockhash(&transactions_signatures[0].0.recent_blockhash)?;
|
||||||
|
transactions = transactions_signatures
|
||||||
|
.into_iter()
|
||||||
|
.map(|(mut transaction, _)| {
|
||||||
|
transaction.sign(&[signer], blockhash);
|
||||||
|
transaction
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resign_transaction<T: KeypairUtil>(
|
||||||
|
&self,
|
||||||
|
tx: &mut Transaction,
|
||||||
|
signer_key: &T,
|
||||||
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
|
let blockhash = self.get_new_blockhash(&tx.recent_blockhash)?;
|
||||||
|
tx.sign(&[signer_key], blockhash);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn retry_get_balance(
|
pub fn retry_get_balance(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
|
@ -126,56 +300,50 @@ impl RpcClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request the last Entry ID from the server without blocking.
|
pub fn get_recent_blockhash(&self) -> io::Result<Hash> {
|
||||||
/// Returns the blockhash Hash or None if there was no response from the server.
|
let mut num_retries = 5;
|
||||||
pub fn try_get_recent_blockhash(&self, mut num_retries: u64) -> Option<Hash> {
|
while num_retries > 0 {
|
||||||
loop {
|
match self.client.send(&RpcRequest::GetRecentBlockhash, None, 0) {
|
||||||
let response = self.client.send(&RpcRequest::GetRecentBlockhash, None, 0);
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
let blockhash_str = value.as_str().unwrap();
|
if let Some(blockhash_str) = value.as_str() {
|
||||||
let blockhash_vec = bs58::decode(blockhash_str).into_vec().unwrap();
|
let blockhash_vec = bs58::decode(blockhash_str)
|
||||||
return Some(Hash::new(&blockhash_vec));
|
.into_vec()
|
||||||
}
|
.expect("bs58::decode");
|
||||||
Err(error) => {
|
return Ok(Hash::new(&blockhash_vec));
|
||||||
debug!("thin_client get_recent_blockhash error: {:?}", error);
|
|
||||||
num_retries -= 1;
|
|
||||||
if num_retries == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!("retry_get_recent_blockhash failed: {:?}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
num_retries -= 1;
|
||||||
}
|
}
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Unable to get recent blockhash, too many retries",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request the last Entry ID from the server. This method blocks
|
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<Hash> {
|
||||||
/// until the server sends a response.
|
let mut num_retries = 5;
|
||||||
pub fn get_recent_blockhash(&self) -> Hash {
|
while num_retries > 0 {
|
||||||
loop {
|
if let Ok(new_blockhash) = self.get_recent_blockhash() {
|
||||||
if let Some(hash) = self.try_get_recent_blockhash(10) {
|
if new_blockhash != *blockhash {
|
||||||
return hash;
|
return Ok(new_blockhash);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request a new last Entry ID from the server. This method blocks
|
|
||||||
/// until the server sends a response.
|
|
||||||
pub fn get_next_blockhash(&self, previous_blockhash: &Hash) -> Hash {
|
|
||||||
self.get_next_blockhash_ext(previous_blockhash, &|| {
|
|
||||||
sleep(Duration::from_millis(100));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_next_blockhash_ext(&self, previous_blockhash: &Hash, func: &Fn()) -> Hash {
|
|
||||||
loop {
|
|
||||||
let blockhash = self.get_recent_blockhash();
|
|
||||||
if blockhash != *previous_blockhash {
|
|
||||||
break blockhash;
|
|
||||||
}
|
}
|
||||||
debug!("Got same blockhash ({:?}), will retry...", blockhash);
|
debug!("Got same blockhash ({:?}), will retry...", blockhash);
|
||||||
func()
|
|
||||||
|
// Retry ~twice during a slot
|
||||||
|
sleep(Duration::from_millis(
|
||||||
|
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
||||||
|
));
|
||||||
|
num_retries -= 1;
|
||||||
}
|
}
|
||||||
|
Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"Unable to get next blockhash, too many retries",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_balance_with_timeout(
|
pub fn poll_balance_with_timeout(
|
||||||
|
@ -308,10 +476,13 @@ pub fn get_rpc_request_str(rpc_addr: SocketAddr, tls: bool) -> String {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::mock_rpc_client_request::{PUBKEY, SIGNATURE};
|
||||||
use jsonrpc_core::{Error, IoHandler, Params};
|
use jsonrpc_core::{Error, IoHandler, Params};
|
||||||
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
|
||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use solana_logger;
|
use solana_logger;
|
||||||
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||||
|
use solana_sdk::system_transaction::SystemTransaction;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
@ -409,4 +580,100 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(balance.unwrap().as_u64().unwrap(), 5);
|
assert_eq!(balance.unwrap().as_u64().unwrap(), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_send_transaction() {
|
||||||
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
|
let key = Keypair::new();
|
||||||
|
let to = Keypair::new().pubkey();
|
||||||
|
let blockhash = Hash::default();
|
||||||
|
let tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
||||||
|
|
||||||
|
let signature = rpc_client.send_transaction(&tx);
|
||||||
|
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
|
|
||||||
|
let signature = rpc_client.send_transaction(&tx);
|
||||||
|
assert!(signature.is_err());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_get_recent_blockhash() {
|
||||||
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
|
let vec = bs58::decode(PUBKEY).into_vec().unwrap();
|
||||||
|
let expected_blockhash = Hash::new(&vec);
|
||||||
|
|
||||||
|
let blockhash = dbg!(rpc_client.get_recent_blockhash()).expect("blockhash ok");
|
||||||
|
assert_eq!(blockhash, expected_blockhash);
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
|
|
||||||
|
let blockhash = dbg!(rpc_client.get_recent_blockhash());
|
||||||
|
assert!(blockhash.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_signature_status() {
|
||||||
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
let signature = "good_signature";
|
||||||
|
let status = rpc_client.get_signature_status(&signature);
|
||||||
|
assert_eq!(status.unwrap(), RpcSignatureStatus::Confirmed);
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("bad_sig_status".to_string());
|
||||||
|
let signature = "bad_status";
|
||||||
|
let status = rpc_client.get_signature_status(&signature);
|
||||||
|
assert!(status.is_err());
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
|
let signature = "bad_status_fmt";
|
||||||
|
let status = rpc_client.get_signature_status(&signature);
|
||||||
|
assert!(status.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_send_and_confirm_transaction() {
|
||||||
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
|
let key = Keypair::new();
|
||||||
|
let to = Keypair::new().pubkey();
|
||||||
|
let blockhash = Hash::default();
|
||||||
|
let mut tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
||||||
|
|
||||||
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &key);
|
||||||
|
result.unwrap();
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("account_in_use".to_string());
|
||||||
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &key);
|
||||||
|
assert!(result.is_err());
|
||||||
|
|
||||||
|
let rpc_client = RpcClient::new_mock("fails".to_string());
|
||||||
|
let result = rpc_client.send_and_confirm_transaction(&mut tx, &key);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resign_transaction() {
|
||||||
|
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
||||||
|
|
||||||
|
let key = Keypair::new();
|
||||||
|
let to = Keypair::new().pubkey();
|
||||||
|
let vec = bs58::decode("HUu3LwEzGRsUkuJS121jzkPJW39Kq62pXCTmTa1F9jDL")
|
||||||
|
.into_vec()
|
||||||
|
.unwrap();
|
||||||
|
let blockhash = Hash::new(&vec);
|
||||||
|
let prev_tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
||||||
|
let mut tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
||||||
|
|
||||||
|
rpc_client.resign_transaction(&mut tx, &key).unwrap();
|
||||||
|
|
||||||
|
assert_ne!(prev_tx, tx);
|
||||||
|
assert_ne!(prev_tx.signatures, tx.signatures);
|
||||||
|
assert_ne!(prev_tx.recent_blockhash, tx.recent_blockhash);
|
||||||
|
assert_eq!(prev_tx.fee, tx.fee);
|
||||||
|
assert_eq!(prev_tx.account_keys, tx.account_keys);
|
||||||
|
assert_eq!(prev_tx.instructions, tx.instructions);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl ThinClient {
|
||||||
tries: usize,
|
tries: usize,
|
||||||
) -> io::Result<Signature> {
|
) -> io::Result<Signature> {
|
||||||
for x in 0..tries {
|
for x in 0..tries {
|
||||||
transaction.sign(&[keypair], self.get_recent_blockhash());
|
transaction.sign(&[keypair], self.get_recent_blockhash()?);
|
||||||
let mut buf = vec![0; transaction.serialized_size().unwrap() as usize];
|
let mut buf = vec![0; transaction.serialized_size().unwrap() as usize];
|
||||||
let mut wr = std::io::Cursor::new(&mut buf[..]);
|
let mut wr = std::io::Cursor::new(&mut buf[..]);
|
||||||
serialize_into(&mut wr, &transaction)
|
serialize_into(&mut wr, &transaction)
|
||||||
|
@ -111,16 +111,12 @@ impl ThinClient {
|
||||||
self.rpc_client.get_transaction_count()
|
self.rpc_client.get_transaction_count()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_get_recent_blockhash(&self, num_retries: u64) -> Option<Hash> {
|
pub fn get_recent_blockhash(&self) -> io::Result<Hash> {
|
||||||
self.rpc_client.try_get_recent_blockhash(num_retries)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_recent_blockhash(&self) -> Hash {
|
|
||||||
self.rpc_client.get_recent_blockhash()
|
self.rpc_client.get_recent_blockhash()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_next_blockhash(&self, previous_blockhash: &Hash) -> Hash {
|
pub fn get_new_blockhash(&self, blockhash: &Hash) -> io::Result<Hash> {
|
||||||
self.rpc_client.get_next_blockhash(previous_blockhash)
|
self.rpc_client.get_new_blockhash(blockhash)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_balance_with_timeout(
|
pub fn poll_balance_with_timeout(
|
||||||
|
|
|
@ -37,7 +37,7 @@ pub fn spend_and_verify_all_nodes(
|
||||||
&funding_keypair,
|
&funding_keypair,
|
||||||
&random_keypair.pubkey(),
|
&random_keypair.pubkey(),
|
||||||
1,
|
1,
|
||||||
client.get_recent_blockhash(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
let sig = client
|
let sig = client
|
||||||
|
@ -62,7 +62,7 @@ pub fn send_many_transactions(node: &ContactInfo, funding_keypair: &Keypair, num
|
||||||
&funding_keypair,
|
&funding_keypair,
|
||||||
&random_keypair.pubkey(),
|
&random_keypair.pubkey(),
|
||||||
1,
|
1,
|
||||||
client.get_recent_blockhash(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
|
@ -159,7 +159,7 @@ pub fn kill_entry_and_spend_and_verify_rest(
|
||||||
&funding_keypair,
|
&funding_keypair,
|
||||||
&random_keypair.pubkey(),
|
&random_keypair.pubkey(),
|
||||||
1,
|
1,
|
||||||
client.get_recent_blockhash(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,7 @@ impl LocalCluster {
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
trace!("getting leader blockhash");
|
trace!("getting leader blockhash");
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
let mut tx =
|
let mut tx =
|
||||||
SystemTransaction::new_account(&source_keypair, dest_pubkey, lamports, blockhash, 0);
|
SystemTransaction::new_account(&source_keypair, dest_pubkey, lamports, blockhash, 0);
|
||||||
info!(
|
info!(
|
||||||
|
@ -228,7 +228,7 @@ impl LocalCluster {
|
||||||
let mut transaction = VoteTransaction::new_account(
|
let mut transaction = VoteTransaction::new_account(
|
||||||
from_account,
|
from_account,
|
||||||
&vote_account_pubkey,
|
&vote_account_pubkey,
|
||||||
client.get_recent_blockhash(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
amount,
|
amount,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
@ -243,7 +243,7 @@ impl LocalCluster {
|
||||||
// 2) Set delegate for new vote account
|
// 2) Set delegate for new vote account
|
||||||
let mut transaction = VoteTransaction::delegate_vote_account(
|
let mut transaction = VoteTransaction::delegate_vote_account(
|
||||||
vote_account,
|
vote_account,
|
||||||
client.get_recent_blockhash(),
|
client.get_recent_blockhash().unwrap(),
|
||||||
&delegate_id,
|
&delegate_id,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
|
@ -316,7 +316,7 @@ impl Replicator {
|
||||||
);
|
);
|
||||||
Self::get_airdrop_lamports(&client, &self.keypair, &self.cluster_entrypoint);
|
Self::get_airdrop_lamports(&client, &self.keypair, &self.cluster_entrypoint);
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().expect("blockhash");
|
||||||
let mut tx = StorageTransaction::new_mining_proof(
|
let mut tx = StorageTransaction::new_mining_proof(
|
||||||
&self.keypair,
|
&self.keypair,
|
||||||
self.hash,
|
self.hash,
|
||||||
|
@ -388,7 +388,7 @@ impl Replicator {
|
||||||
|
|
||||||
let airdrop_amount = 1;
|
let airdrop_amount = 1;
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().expect("blockhash");
|
||||||
match request_airdrop_transaction(
|
match request_airdrop_transaction(
|
||||||
&drone_addr,
|
&drone_addr,
|
||||||
&keypair.pubkey(),
|
&keypair.pubkey(),
|
||||||
|
|
|
@ -242,7 +242,7 @@ impl StorageStage {
|
||||||
|
|
||||||
let mut blockhash = None;
|
let mut blockhash = None;
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
if let Some(new_blockhash) = client.try_get_recent_blockhash(1) {
|
if let Ok(new_blockhash) = client.get_recent_blockhash() {
|
||||||
blockhash = Some(new_blockhash);
|
blockhash = Some(new_blockhash);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ fn test_thin_client_basic() {
|
||||||
let transaction_count = client.get_transaction_count().unwrap();
|
let transaction_count = client.get_transaction_count().unwrap();
|
||||||
assert_eq!(transaction_count, 0);
|
assert_eq!(transaction_count, 0);
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
info!("test_thin_client blockhash: {:?}", blockhash);
|
info!("test_thin_client blockhash: {:?}", blockhash);
|
||||||
|
|
||||||
let signature = transfer(&client, 500, &alice, &bob_pubkey, &blockhash).unwrap();
|
let signature = transfer(&client, 500, &alice, &bob_pubkey, &blockhash).unwrap();
|
||||||
|
@ -74,13 +74,13 @@ fn test_bad_sig() {
|
||||||
|
|
||||||
let client = create_client(leader_data.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(leader_data.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
let tx = SystemTransaction::new_account(&alice, &bob_pubkey, 500, blockhash, 0);
|
let tx = SystemTransaction::new_account(&alice, &bob_pubkey, 500, blockhash, 0);
|
||||||
|
|
||||||
let _sig = client.transfer_signed(&tx).unwrap();
|
let _sig = client.transfer_signed(&tx).unwrap();
|
||||||
|
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
let mut tr2 = SystemTransaction::new_account(&alice, &bob_pubkey, 501, blockhash, 0);
|
let mut tr2 = SystemTransaction::new_account(&alice, &bob_pubkey, 501, blockhash, 0);
|
||||||
let mut instruction2 = deserialize(tr2.data(0)).unwrap();
|
let mut instruction2 = deserialize(tr2.data(0)).unwrap();
|
||||||
|
@ -107,7 +107,7 @@ fn test_register_vote_account() {
|
||||||
|
|
||||||
// Create the validator account, transfer some lamports to that account
|
// Create the validator account, transfer some lamports to that account
|
||||||
let validator_keypair = Keypair::new();
|
let validator_keypair = Keypair::new();
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
let signature = transfer(
|
let signature = transfer(
|
||||||
&client,
|
&client,
|
||||||
500,
|
500,
|
||||||
|
@ -122,7 +122,7 @@ fn test_register_vote_account() {
|
||||||
// Create and register the vote account
|
// Create and register the vote account
|
||||||
let validator_vote_account_keypair = Keypair::new();
|
let validator_vote_account_keypair = Keypair::new();
|
||||||
let vote_account_id = validator_vote_account_keypair.pubkey();
|
let vote_account_id = validator_vote_account_keypair.pubkey();
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
|
|
||||||
let transaction =
|
let transaction =
|
||||||
VoteTransaction::new_account(&validator_keypair, &vote_account_id, blockhash, 1, 1);
|
VoteTransaction::new_account(&validator_keypair, &vote_account_id, blockhash, 1, 1);
|
||||||
|
@ -179,7 +179,7 @@ fn test_zero_balance_after_nonzero() {
|
||||||
discover(&leader_data.gossip, 1).unwrap();
|
discover(&leader_data.gossip, 1).unwrap();
|
||||||
|
|
||||||
let client = create_client(leader_data.client_facing_addr(), FULLNODE_PORT_RANGE);
|
let client = create_client(leader_data.client_facing_addr(), FULLNODE_PORT_RANGE);
|
||||||
let blockhash = client.get_recent_blockhash();
|
let blockhash = client.get_recent_blockhash().unwrap();
|
||||||
info!("test_thin_client blockhash: {:?}", blockhash);
|
info!("test_thin_client blockhash: {:?}", blockhash);
|
||||||
|
|
||||||
let starting_alice_balance = client.poll_get_balance(&alice.pubkey()).unwrap();
|
let starting_alice_balance = client.poll_get_balance(&alice.pubkey()).unwrap();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use bincode::serialize;
|
|
||||||
use bs58;
|
use bs58;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
@ -9,7 +8,6 @@ use solana_budget_api;
|
||||||
use solana_budget_api::budget_transaction::BudgetTransaction;
|
use solana_budget_api::budget_transaction::BudgetTransaction;
|
||||||
use solana_client::rpc_client::{get_rpc_request_str, RpcClient};
|
use solana_client::rpc_client::{get_rpc_request_str, RpcClient};
|
||||||
use solana_client::rpc_request::RpcRequest;
|
use solana_client::rpc_request::RpcRequest;
|
||||||
use solana_client::rpc_signature_status::RpcSignatureStatus;
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use solana_drone::drone::request_airdrop_transaction;
|
use solana_drone::drone::request_airdrop_transaction;
|
||||||
use solana_drone::drone::DRONE_PORT;
|
use solana_drone::drone::DRONE_PORT;
|
||||||
|
@ -22,16 +20,12 @@ use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::rpc_port::DEFAULT_RPC_PORT;
|
use solana_sdk::rpc_port::DEFAULT_RPC_PORT;
|
||||||
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
||||||
use solana_sdk::system_transaction::SystemTransaction;
|
use solana_sdk::system_transaction::SystemTransaction;
|
||||||
use solana_sdk::timing::{DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND};
|
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::Transaction;
|
||||||
use solana_vote_api::vote_instruction::VoteInstruction;
|
use solana_vote_api::vote_instruction::VoteInstruction;
|
||||||
use solana_vote_api::vote_transaction::VoteTransaction;
|
use solana_vote_api::vote_transaction::VoteTransaction;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
use std::str::FromStr;
|
|
||||||
use std::thread::sleep;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::{error, fmt, mem};
|
use std::{error, fmt, mem};
|
||||||
|
|
||||||
const USERDATA_CHUNK_SIZE: usize = 256;
|
const USERDATA_CHUNK_SIZE: usize = 256;
|
||||||
|
@ -439,7 +433,7 @@ fn process_configure_staking(
|
||||||
delegate_option: Option<Pubkey>,
|
delegate_option: Option<Pubkey>,
|
||||||
authorized_voter_option: Option<Pubkey>,
|
authorized_voter_option: Option<Pubkey>,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let recent_blockhash = get_recent_blockhash(&rpc_client)?;
|
let recent_blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let mut ixs = vec![];
|
let mut ixs = vec![];
|
||||||
if let Some(delegate_id) = delegate_option {
|
if let Some(delegate_id) = delegate_option {
|
||||||
ixs.push(VoteInstruction::new_delegate_stake(
|
ixs.push(VoteInstruction::new_delegate_stake(
|
||||||
|
@ -455,7 +449,7 @@ fn process_configure_staking(
|
||||||
}
|
}
|
||||||
let mut tx = Transaction::new(ixs);
|
let mut tx = Transaction::new(ixs);
|
||||||
tx.sign(&[&config.id], recent_blockhash);
|
tx.sign(&[&config.id], recent_blockhash);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -465,10 +459,10 @@ fn process_create_staking(
|
||||||
voting_account_id: &Pubkey,
|
voting_account_id: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let recent_blockhash = get_recent_blockhash(&rpc_client)?;
|
let recent_blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let mut tx =
|
let mut tx =
|
||||||
VoteTransaction::new_account(&config.id, voting_account_id, recent_blockhash, lamports, 0);
|
VoteTransaction::new_account(&config.id, voting_account_id, recent_blockhash, lamports, 0);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,7 +480,7 @@ fn process_deploy(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let program_id = Keypair::new();
|
let program_id = Keypair::new();
|
||||||
let mut file = File::open(program_location).map_err(|err| {
|
let mut file = File::open(program_location).map_err(|err| {
|
||||||
WalletError::DynamicProgramError(
|
WalletError::DynamicProgramError(
|
||||||
|
@ -510,9 +504,11 @@ fn process_deploy(
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
trace!("Creating program account");
|
trace!("Creating program account");
|
||||||
send_and_confirm_transaction(&rpc_client, &mut tx, &config.id).map_err(|_| {
|
rpc_client
|
||||||
WalletError::DynamicProgramError("Program allocate space failed".to_string())
|
.send_and_confirm_transaction(&mut tx, &config.id)
|
||||||
})?;
|
.map_err(|_| {
|
||||||
|
WalletError::DynamicProgramError("Program allocate space failed".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
trace!("Writing program data");
|
trace!("Writing program data");
|
||||||
let write_transactions: Vec<_> = program_data
|
let write_transactions: Vec<_> = program_data
|
||||||
|
@ -529,13 +525,15 @@ fn process_deploy(
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
send_and_confirm_transactions(&rpc_client, write_transactions, &program_id)?;
|
rpc_client.send_and_confirm_transactions(write_transactions, &program_id)?;
|
||||||
|
|
||||||
trace!("Finalizing program account");
|
trace!("Finalizing program account");
|
||||||
let mut tx = LoaderTransaction::new_finalize(&program_id, &bpf_loader::id(), blockhash, 0);
|
let mut tx = LoaderTransaction::new_finalize(&program_id, &bpf_loader::id(), blockhash, 0);
|
||||||
send_and_confirm_transaction(&rpc_client, &mut tx, &program_id).map_err(|_| {
|
rpc_client
|
||||||
WalletError::DynamicProgramError("Program finalize transaction failed".to_string())
|
.send_and_confirm_transaction(&mut tx, &program_id)
|
||||||
})?;
|
.map_err(|_| {
|
||||||
|
WalletError::DynamicProgramError("Program finalize transaction failed".to_string())
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"programId": format!("{}", program_id.pubkey()),
|
"programId": format!("{}", program_id.pubkey()),
|
||||||
|
@ -553,11 +551,11 @@ fn process_pay(
|
||||||
witnesses: &Option<Vec<Pubkey>>,
|
witnesses: &Option<Vec<Pubkey>>,
|
||||||
cancelable: Option<Pubkey>,
|
cancelable: Option<Pubkey>,
|
||||||
) -> ProcessResult {
|
) -> ProcessResult {
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
if timestamp == None && *witnesses == None {
|
if timestamp == None && *witnesses == None {
|
||||||
let mut tx = SystemTransaction::new_move(&config.id, to, lamports, blockhash, 0);
|
let mut tx = SystemTransaction::new_move(&config.id, to, lamports, blockhash, 0);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
} else if *witnesses == None {
|
} else if *witnesses == None {
|
||||||
let dt = timestamp.unwrap();
|
let dt = timestamp.unwrap();
|
||||||
|
@ -579,7 +577,7 @@ fn process_pay(
|
||||||
lamports,
|
lamports,
|
||||||
blockhash,
|
blockhash,
|
||||||
);
|
);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"signature": signature_str,
|
"signature": signature_str,
|
||||||
|
@ -587,7 +585,7 @@ fn process_pay(
|
||||||
})
|
})
|
||||||
.to_string())
|
.to_string())
|
||||||
} else if timestamp == None {
|
} else if timestamp == None {
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let witness = if let Some(ref witness_vec) = *witnesses {
|
let witness = if let Some(ref witness_vec) = *witnesses {
|
||||||
witness_vec[0]
|
witness_vec[0]
|
||||||
|
@ -609,7 +607,7 @@ fn process_pay(
|
||||||
lamports,
|
lamports,
|
||||||
blockhash,
|
blockhash,
|
||||||
);
|
);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"signature": signature_str,
|
"signature": signature_str,
|
||||||
|
@ -622,10 +620,10 @@ fn process_pay(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_cancel(rpc_client: &RpcClient, config: &WalletConfig, pubkey: &Pubkey) -> ProcessResult {
|
fn process_cancel(rpc_client: &RpcClient, config: &WalletConfig, pubkey: &Pubkey) -> ProcessResult {
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let mut tx =
|
let mut tx =
|
||||||
BudgetTransaction::new_signature(&config.id, pubkey, &config.id.pubkey(), blockhash);
|
BudgetTransaction::new_signature(&config.id, pubkey, &config.id.pubkey(), blockhash);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -655,10 +653,10 @@ fn process_time_elapsed(
|
||||||
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config.id.pubkey(), 1)?;
|
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config.id.pubkey(), 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
|
|
||||||
let mut tx = BudgetTransaction::new_timestamp(&config.id, pubkey, to, dt, blockhash);
|
let mut tx = BudgetTransaction::new_timestamp(&config.id, pubkey, to, dt, blockhash);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
@ -676,9 +674,9 @@ fn process_witness(
|
||||||
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config.id.pubkey(), 1)?;
|
request_and_confirm_airdrop(&rpc_client, &drone_addr, &config.id.pubkey(), 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blockhash = get_recent_blockhash(&rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let mut tx = BudgetTransaction::new_signature(&config.id, pubkey, to, blockhash);
|
let mut tx = BudgetTransaction::new_signature(&config.id, pubkey, to, blockhash);
|
||||||
let signature_str = send_and_confirm_transaction(&rpc_client, &mut tx, &config.id)?;
|
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &config.id)?;
|
||||||
|
|
||||||
Ok(signature_str.to_string())
|
Ok(signature_str.to_string())
|
||||||
}
|
}
|
||||||
|
@ -772,215 +770,6 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_recent_blockhash(rpc_client: &RpcClient) -> Result<Hash, Box<dyn error::Error>> {
|
|
||||||
let result = rpc_client.retry_make_rpc_request(&RpcRequest::GetRecentBlockhash, None, 5)?;
|
|
||||||
if result.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received bad blockhash".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
let blockhash_str = result.as_str().unwrap();
|
|
||||||
let blockhash_vec = bs58::decode(blockhash_str)
|
|
||||||
.into_vec()
|
|
||||||
.map_err(|_| WalletError::RpcRequestError("Received bad blockhash".to_string()))?;
|
|
||||||
Ok(Hash::new(&blockhash_vec))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_next_blockhash(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
previous_blockhash: &Hash,
|
|
||||||
) -> Result<Hash, Box<dyn error::Error>> {
|
|
||||||
let mut next_blockhash_retries = 3;
|
|
||||||
loop {
|
|
||||||
let next_blockhash = get_recent_blockhash(rpc_client)?;
|
|
||||||
if cfg!(not(test)) {
|
|
||||||
if next_blockhash != *previous_blockhash {
|
|
||||||
return Ok(next_blockhash);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When using MockRpcClient, get_recent_blockhash() returns a constant value
|
|
||||||
return Ok(next_blockhash);
|
|
||||||
}
|
|
||||||
if next_blockhash_retries == 0 {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
format!(
|
|
||||||
"Unable to fetch new blockhash, blockhash stuck at {:?}",
|
|
||||||
next_blockhash
|
|
||||||
)
|
|
||||||
.to_string(),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
next_blockhash_retries -= 1;
|
|
||||||
// Retry ~twice during a slot
|
|
||||||
sleep(Duration::from_millis(
|
|
||||||
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_transaction(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
transaction: &Transaction,
|
|
||||||
) -> Result<String, Box<dyn error::Error>> {
|
|
||||||
let serialized = serialize(transaction).unwrap();
|
|
||||||
let params = json!([serialized]);
|
|
||||||
let signature =
|
|
||||||
rpc_client.retry_make_rpc_request(&RpcRequest::SendTransaction, Some(params), 5)?;
|
|
||||||
if signature.as_str().is_none() {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
Ok(signature.as_str().unwrap().to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn confirm_transaction(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
signature: &str,
|
|
||||||
) -> Result<RpcSignatureStatus, Box<dyn error::Error>> {
|
|
||||||
let params = json!([signature.to_string()]);
|
|
||||||
let signature_status =
|
|
||||||
rpc_client.retry_make_rpc_request(&RpcRequest::GetSignatureStatus, Some(params), 5)?;
|
|
||||||
if let Some(status) = signature_status.as_str() {
|
|
||||||
let rpc_status = RpcSignatureStatus::from_str(status).map_err(|_| {
|
|
||||||
WalletError::RpcRequestError("Unable to parse signature status".to_string())
|
|
||||||
})?;
|
|
||||||
Ok(rpc_status)
|
|
||||||
} else {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Received result of an unexpected type".to_string(),
|
|
||||||
))?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_and_confirm_transaction<T: KeypairUtil>(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
transaction: &mut Transaction,
|
|
||||||
signer: &T,
|
|
||||||
) -> Result<String, Box<dyn error::Error>> {
|
|
||||||
let mut send_retries = 5;
|
|
||||||
loop {
|
|
||||||
let mut status_retries = 4;
|
|
||||||
let signature_str = send_transaction(rpc_client, transaction)?;
|
|
||||||
let status = loop {
|
|
||||||
let status = confirm_transaction(rpc_client, &signature_str)?;
|
|
||||||
if status == RpcSignatureStatus::SignatureNotFound {
|
|
||||||
status_retries -= 1;
|
|
||||||
if status_retries == 0 {
|
|
||||||
break status;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break status;
|
|
||||||
}
|
|
||||||
if cfg!(not(test)) {
|
|
||||||
// Retry ~twice during a slot
|
|
||||||
sleep(Duration::from_millis(
|
|
||||||
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match status {
|
|
||||||
RpcSignatureStatus::AccountInUse | RpcSignatureStatus::SignatureNotFound => {
|
|
||||||
// Fetch a new blockhash and re-sign the transaction before sending it again
|
|
||||||
resign_transaction(rpc_client, transaction, signer)?;
|
|
||||||
send_retries -= 1;
|
|
||||||
}
|
|
||||||
RpcSignatureStatus::Confirmed => {
|
|
||||||
return Ok(signature_str);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
send_retries = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if send_retries == 0 {
|
|
||||||
Err(WalletError::RpcRequestError(format!(
|
|
||||||
"Transaction {:?} failed: {:?}",
|
|
||||||
signature_str, status
|
|
||||||
)))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_and_confirm_transactions(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
mut transactions: Vec<Transaction>,
|
|
||||||
signer: &Keypair,
|
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
|
||||||
let mut send_retries = 5;
|
|
||||||
loop {
|
|
||||||
let mut status_retries = 4;
|
|
||||||
|
|
||||||
// Send all transactions
|
|
||||||
let mut transactions_signatures = vec![];
|
|
||||||
for transaction in transactions {
|
|
||||||
if cfg!(not(test)) {
|
|
||||||
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
|
|
||||||
// since all the write transactions modify the same program account
|
|
||||||
sleep(Duration::from_millis(1000 / NUM_TICKS_PER_SECOND));
|
|
||||||
}
|
|
||||||
|
|
||||||
let signature = send_transaction(&rpc_client, &transaction).ok();
|
|
||||||
transactions_signatures.push((transaction, signature))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect statuses for all the transactions, drop those that are confirmed
|
|
||||||
while status_retries > 0 {
|
|
||||||
status_retries -= 1;
|
|
||||||
|
|
||||||
if cfg!(not(test)) {
|
|
||||||
// Retry ~twice during a slot
|
|
||||||
sleep(Duration::from_millis(
|
|
||||||
500 * DEFAULT_TICKS_PER_SLOT / NUM_TICKS_PER_SECOND,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
transactions_signatures = transactions_signatures
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(_transaction, signature)| {
|
|
||||||
if let Some(signature) = signature {
|
|
||||||
if let Ok(status) = confirm_transaction(rpc_client, &signature) {
|
|
||||||
return status != RpcSignatureStatus::Confirmed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if transactions_signatures.is_empty() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if send_retries == 0 {
|
|
||||||
Err(WalletError::RpcRequestError(
|
|
||||||
"Transactions failed".to_string(),
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
send_retries -= 1;
|
|
||||||
|
|
||||||
// Re-sign any failed transactions with a new blockhash and retry
|
|
||||||
let blockhash =
|
|
||||||
get_next_blockhash(rpc_client, &transactions_signatures[0].0.recent_blockhash)?;
|
|
||||||
transactions = transactions_signatures
|
|
||||||
.into_iter()
|
|
||||||
.map(|(mut transaction, _)| {
|
|
||||||
transaction.sign(&[signer], blockhash);
|
|
||||||
transaction
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resign_transaction<T: KeypairUtil>(
|
|
||||||
rpc_client: &RpcClient,
|
|
||||||
tx: &mut Transaction,
|
|
||||||
signer_key: &T,
|
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
|
||||||
let blockhash = get_next_blockhash(rpc_client, &tx.recent_blockhash)?;
|
|
||||||
tx.sign(&[signer_key], blockhash);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quick and dirty Keypair that assumes the client will do retries but not update the
|
// Quick and dirty Keypair that assumes the client will do retries but not update the
|
||||||
// blockhash. If the client updates the blockhash, the signature will be invalid.
|
// blockhash. If the client updates the blockhash, the signature will be invalid.
|
||||||
// TODO: Parse `msg` and use that data to make a new airdrop request.
|
// TODO: Parse `msg` and use that data to make a new airdrop request.
|
||||||
|
@ -1025,10 +814,10 @@ pub fn request_and_confirm_airdrop(
|
||||||
to_pubkey: &Pubkey,
|
to_pubkey: &Pubkey,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
) -> Result<(), Box<dyn error::Error>> {
|
) -> Result<(), Box<dyn error::Error>> {
|
||||||
let blockhash = get_recent_blockhash(rpc_client)?;
|
let blockhash = rpc_client.get_recent_blockhash()?;
|
||||||
let keypair = DroneKeypair::new_keypair(drone_addr, to_pubkey, lamports, blockhash)?;
|
let keypair = DroneKeypair::new_keypair(drone_addr, to_pubkey, lamports, blockhash)?;
|
||||||
let mut tx = keypair.airdrop_transaction();
|
let mut tx = keypair.airdrop_transaction();
|
||||||
send_and_confirm_transaction(rpc_client, &mut tx, &keypair)?;
|
rpc_client.send_and_confirm_transaction(&mut tx, &keypair)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,8 +826,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use clap::{App, Arg, ArgGroup, SubCommand};
|
use clap::{App, Arg, ArgGroup, SubCommand};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use solana::socketaddr;
|
use solana_client::mock_rpc_client_request::SIGNATURE;
|
||||||
use solana_client::mock_rpc_client_request::{PUBKEY, SIGNATURE};
|
|
||||||
use solana_sdk::signature::{gen_keypair_file, read_keypair, read_pkcs8, Keypair, KeypairUtil};
|
use solana_sdk::signature::{gen_keypair_file, read_keypair, read_pkcs8, Keypair, KeypairUtil};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::net::{Ipv4Addr, SocketAddr};
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
@ -1705,115 +1493,4 @@ mod tests {
|
||||||
fs::remove_file(&outfile).unwrap();
|
fs::remove_file(&outfile).unwrap();
|
||||||
assert!(!Path::new(&outfile).exists());
|
assert!(!Path::new(&outfile).exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wallet_get_recent_blockhash() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
|
|
||||||
let vec = bs58::decode(PUBKEY).into_vec().unwrap();
|
|
||||||
let expected_blockhash = Hash::new(&vec);
|
|
||||||
|
|
||||||
let blockhash = get_recent_blockhash(&rpc_client);
|
|
||||||
assert_eq!(blockhash.unwrap(), expected_blockhash);
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
|
||||||
|
|
||||||
let blockhash = get_recent_blockhash(&rpc_client);
|
|
||||||
assert!(blockhash.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wallet_send_transaction() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
|
|
||||||
let key = Keypair::new();
|
|
||||||
let to = Keypair::new().pubkey();
|
|
||||||
let blockhash = Hash::default();
|
|
||||||
let tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
|
||||||
|
|
||||||
let signature = send_transaction(&rpc_client, &tx);
|
|
||||||
assert_eq!(signature.unwrap(), SIGNATURE.to_string());
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
|
||||||
|
|
||||||
let signature = send_transaction(&rpc_client, &tx);
|
|
||||||
assert!(signature.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wallet_confirm_transaction() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
let signature = "good_signature";
|
|
||||||
let status = confirm_transaction(&rpc_client, &signature);
|
|
||||||
assert_eq!(status.unwrap(), RpcSignatureStatus::Confirmed);
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("bad_sig_status".to_string());
|
|
||||||
let signature = "bad_status";
|
|
||||||
let status = confirm_transaction(&rpc_client, &signature);
|
|
||||||
assert!(status.is_err());
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
|
||||||
let signature = "bad_status_fmt";
|
|
||||||
let status = confirm_transaction(&rpc_client, &signature);
|
|
||||||
assert!(status.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wallet_send_and_confirm_transaction() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
|
|
||||||
let key = Keypair::new();
|
|
||||||
let to = Keypair::new().pubkey();
|
|
||||||
let blockhash = Hash::default();
|
|
||||||
let mut tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
|
||||||
|
|
||||||
let result = send_and_confirm_transaction(&rpc_client, &mut tx, &key);
|
|
||||||
result.unwrap();
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("account_in_use".to_string());
|
|
||||||
let result = send_and_confirm_transaction(&rpc_client, &mut tx, &key);
|
|
||||||
assert!(result.is_err());
|
|
||||||
|
|
||||||
let rpc_client = RpcClient::new_mock("fails".to_string());
|
|
||||||
let result = send_and_confirm_transaction(&rpc_client, &mut tx, &key);
|
|
||||||
assert!(result.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_wallet_resign_transaction() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
|
|
||||||
let key = Keypair::new();
|
|
||||||
let to = Keypair::new().pubkey();
|
|
||||||
let vec = bs58::decode("HUu3LwEzGRsUkuJS121jzkPJW39Kq62pXCTmTa1F9jDL")
|
|
||||||
.into_vec()
|
|
||||||
.unwrap();
|
|
||||||
let blockhash = Hash::new(&vec);
|
|
||||||
let prev_tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
|
||||||
let mut tx = SystemTransaction::new_account(&key, &to, 50, blockhash, 0);
|
|
||||||
|
|
||||||
resign_transaction(&rpc_client, &mut tx, &key).unwrap();
|
|
||||||
|
|
||||||
assert_ne!(prev_tx, tx);
|
|
||||||
assert_ne!(prev_tx.signatures, tx.signatures);
|
|
||||||
assert_ne!(prev_tx.recent_blockhash, tx.recent_blockhash);
|
|
||||||
assert_eq!(prev_tx.fee, tx.fee);
|
|
||||||
assert_eq!(prev_tx.account_keys, tx.account_keys);
|
|
||||||
assert_eq!(prev_tx.instructions, tx.instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_request_and_confirm_airdrop() {
|
|
||||||
let rpc_client = RpcClient::new_mock("succeeds".to_string());
|
|
||||||
let drone_addr = socketaddr!(0, 0);
|
|
||||||
let pubkey = Keypair::new().pubkey();
|
|
||||||
let lamports = 50;
|
|
||||||
assert_eq!(
|
|
||||||
request_and_confirm_airdrop(&rpc_client, &drone_addr, &pubkey, lamports).unwrap(),
|
|
||||||
()
|
|
||||||
);
|
|
||||||
|
|
||||||
let lamports = 0;
|
|
||||||
assert!(request_and_confirm_airdrop(&rpc_client, &drone_addr, &pubkey, lamports).is_err());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue