diff --git a/src/rpc.rs b/src/rpc.rs index 45abbf3912..ba660d5a85 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -14,6 +14,7 @@ use solana_program_interface::pubkey::Pubkey; use std::mem; use std::net::{SocketAddr, UdpSocket}; use std::result; +use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread::{self, sleep, Builder, JoinHandle}; @@ -96,6 +97,20 @@ pub enum RpcSignatureStatus { ProgramRuntimeError, SignatureNotFound, } +impl FromStr for RpcSignatureStatus { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "AccountInUse" => Ok(RpcSignatureStatus::AccountInUse), + "Confirmed" => Ok(RpcSignatureStatus::Confirmed), + "GenericFailure" => Ok(RpcSignatureStatus::GenericFailure), + "ProgramRuntimeError" => Ok(RpcSignatureStatus::ProgramRuntimeError), + "SignatureNotFound" => Ok(RpcSignatureStatus::SignatureNotFound), + _ => Err(Error::parse_error()), + } + } +} build_rpc_trait! { pub trait RpcSol { diff --git a/src/rpc_request.rs b/src/rpc_request.rs index 48e8d8f06d..5ccfdd126d 100644 --- a/src/rpc_request.rs +++ b/src/rpc_request.rs @@ -9,6 +9,7 @@ pub enum RpcRequest { GetBalance, GetFinality, GetLastId, + GetSignatureStatus, GetTransactionCount, RequestAirdrop, SendTransaction, @@ -44,6 +45,7 @@ impl RpcRequest { RpcRequest::GetBalance => "getBalance", RpcRequest::GetFinality => "getFinality", RpcRequest::GetLastId => "getLastId", + RpcRequest::GetSignatureStatus => "getSignatureStatus", RpcRequest::GetTransactionCount => "getTransactionCount", RpcRequest::RequestAirdrop => "requestAirdrop", RpcRequest::SendTransaction => "sendTransaction", diff --git a/src/wallet.rs b/src/wallet.rs index d3f5ed77eb..732a63834c 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -13,6 +13,7 @@ use hash::Hash; use loader_transaction::LoaderTransaction; use ring::rand::SystemRandom; use ring::signature::Ed25519KeyPair; +use rpc::RpcSignatureStatus; use rpc_request::RpcRequest; use serde_json; use signature::{Keypair, KeypairUtil, Signature}; @@ -23,6 +24,7 @@ use std::io::{Error, ErrorKind, Write}; use std::mem::size_of; use std::net::{Ipv4Addr, SocketAddr, TcpStream}; use std::path::Path; +use std::str::FromStr; use std::thread::sleep; use std::time::Duration; use std::{error, fmt, mem}; @@ -412,57 +414,43 @@ pub fn process_command(config: &WalletConfig) -> Result { @@ -709,16 +697,60 @@ fn serialize_and_send_tx( Ok(signature.as_str().unwrap().to_string()) } -fn confirm_tx(config: &WalletConfig, signature: &str) -> Result> { +fn confirm_tx( + config: &WalletConfig, + signature: &str, +) -> Result> { let params = json!(signature.to_string()); - let status = - RpcRequest::ConfirmTransaction.make_rpc_request(&config.rpc_addr, 1, Some(params))?; - if status.as_bool().is_none() { + let signature_status = + RpcRequest::GetSignatureStatus.make_rpc_request(&config.rpc_addr, 1, Some(params))?; + 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(), ))? } - Ok(status.as_bool().unwrap()) +} + +fn send_and_confirm_tx(config: &WalletConfig, tx: &Transaction) -> Result<(), Box> { + let mut send_retries = 3; + while send_retries > 0 { + let mut status_retries = 4; + let signature_str = serialize_and_send_tx(config, tx)?; + let status = loop { + let status = confirm_tx(config, &signature_str)?; + if status == RpcSignatureStatus::SignatureNotFound { + status_retries -= 1; + if status_retries == 0 { + break status; + } + } else { + break status; + } + }; + match status { + RpcSignatureStatus::AccountInUse => { + send_retries -= 1; + } + RpcSignatureStatus::Confirmed => { + return Ok(()); + } + _ => { + return Err(WalletError::RpcRequestError(format!( + "Transaction {:?} failed: {:?}", + signature_str, status + )))?; + } + } + } + Err(WalletError::RpcRequestError(format!( + "AccountInUse after 3 retries: {:?}", + tx.account_keys[0] + )))? } #[cfg(test)]