diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index f0b0c8708d..e245749950 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -1,6 +1,7 @@ use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::rpc_request::RpcRequest; use serde_json::{Number, Value}; +use solana_sdk::transaction::{self, TransactionError}; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; pub const SIGNATURE: &str = @@ -44,14 +45,14 @@ impl GenericRpcClientRequest for MockRpcClientRequest { } RpcRequest::GetRecentBlockhash => Value::String(PUBKEY.to_string()), RpcRequest::GetSignatureStatus => { - let str = if self.url == "account_in_use" { - "AccountInUse" - } else if self.url == "bad_sig_status" { - "SignatureNotFound" + let response: Option> = if self.url == "account_in_use" { + Some(Err(TransactionError::AccountInUse)) + } else if self.url == "sig_not_found" { + None } else { - "Confirmed" + Some(Ok(())) }; - Value::String(str.to_string()) + serde_json::to_value(response).unwrap() } RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index fbe67ac6f0..56f425e487 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -2,7 +2,6 @@ use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::mock_rpc_client_request::MockRpcClientRequest; use crate::rpc_client_request::RpcClientRequest; use crate::rpc_request::RpcRequest; -use crate::rpc_signature_status::RpcSignatureStatus; use bincode::serialize; use bs58; use log::*; @@ -12,11 +11,10 @@ use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; 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 solana_sdk::transaction::{self, Transaction, TransactionError}; use std::error; use std::io; use std::net::SocketAddr; -use std::str::FromStr; use std::thread::sleep; use std::time::{Duration, Instant}; @@ -69,25 +67,14 @@ impl RpcClient { pub fn get_signature_status( &self, signature: &str, - ) -> Result> { + ) -> Result>, Box> { 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", - ))? - } + let result: Option> = + serde_json::from_value(signature_status).unwrap(); + Ok(result) } pub fn send_and_confirm_transaction( @@ -101,7 +88,7 @@ impl RpcClient { let signature_str = self.send_transaction(transaction)?; let status = loop { let status = self.get_signature_status(&signature_str)?; - if status == RpcSignatureStatus::SignatureNotFound { + if status.is_none() { status_retries -= 1; if status_retries == 0 { break status; @@ -116,19 +103,19 @@ impl RpcClient { )); } }; - 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; + send_retries = if let Some(result) = status.clone() { + match result { + Ok(_) => return Ok(signature_str), + Err(TransactionError::AccountInUse) => { + // Fetch a new blockhash and re-sign the transaction before sending it again + self.resign_transaction(transaction, signer)?; + send_retries - 1 + } + Err(_) => 0, } - RpcSignatureStatus::Confirmed => { - return Ok(signature_str); - } - _ => { - send_retries = 0; - } - } + } else { + send_retries - 1 + }; if send_retries == 0 { Err(io::Error::new( io::ErrorKind::Other, @@ -177,7 +164,10 @@ impl RpcClient { .filter(|(_transaction, signature)| { if let Some(signature) = signature { if let Ok(status) = self.get_signature_status(&signature) { - return status != RpcSignatureStatus::Confirmed; + if status.is_none() { + return false; + } + return status.unwrap().is_err(); } } true @@ -557,6 +547,7 @@ mod tests { use solana_logger; use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::system_transaction; + use solana_sdk::transaction::TransactionError; use std::sync::mpsc::channel; use std::thread; @@ -692,18 +683,18 @@ mod tests { 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 status = rpc_client.get_signature_status(&signature).unwrap(); + assert_eq!(status, Some(Ok(()))); - let rpc_client = RpcClient::new_mock("bad_sig_status".to_string()); - let signature = "bad_status"; - let status = dbg!(rpc_client.get_signature_status(&signature)); - assert_eq!(status.unwrap(), RpcSignatureStatus::SignatureNotFound); + let rpc_client = RpcClient::new_mock("sig_not_found".to_string()); + let signature = "sig_not_found"; + let status = rpc_client.get_signature_status(&signature).unwrap(); + assert_eq!(status, None); - 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()); + let rpc_client = RpcClient::new_mock("account_in_use".to_string()); + let signature = "account_in_use"; + let status = rpc_client.get_signature_status(&signature).unwrap(); + assert_eq!(status, Some(Err(TransactionError::AccountInUse))); } #[test] diff --git a/core/src/rpc_pubsub.rs b/core/src/rpc_pubsub.rs index b6dc069a47..f8f14a4e9b 100644 --- a/core/src/rpc_pubsub.rs +++ b/core/src/rpc_pubsub.rs @@ -6,10 +6,10 @@ use jsonrpc_core::{Error, ErrorCode, Result}; use jsonrpc_derive::rpc; use jsonrpc_pubsub::typed::Subscriber; use jsonrpc_pubsub::{Session, SubscriptionId}; -use solana_client::rpc_signature_status::RpcSignatureStatus; use solana_sdk::account::Account; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Signature; +use solana_sdk::transaction; use std::mem; use std::sync::{atomic, Arc}; @@ -58,7 +58,12 @@ pub trait RpcSolPubSub { subscribe, name = "signatureSubscribe" )] - fn signature_subscribe(&self, _: Self::Metadata, _: Subscriber, _: String); + fn signature_subscribe( + &self, + _: Self::Metadata, + _: Subscriber>>, + _: String, + ); // Unsubscribe from signature notification subscription. #[pubsub( @@ -178,7 +183,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { fn signature_subscribe( &self, _meta: Self::Metadata, - subscriber: Subscriber, + subscriber: Subscriber>>, signature_str: String, ) { info!("signature_subscribe"); @@ -283,7 +288,10 @@ mod tests { // Test signature confirmation notification let string = receiver.poll(); if let Async::Ready(Some(response)) = string.unwrap() { - let expected = format!(r#"{{"jsonrpc":"2.0","method":"signatureNotification","params":{{"result":"Confirmed","subscription":0}}}}"#); + let expected_res: Option> = Some(Ok(())); + let expected_res_str = + serde_json::to_string(&serde_json::to_value(expected_res).unwrap()).unwrap(); + let expected = format!(r#"{{"jsonrpc":"2.0","method":"signatureNotification","params":{{"result":{},"subscription":0}}}}"#, expected_res_str); assert_eq!(expected, response); } } diff --git a/core/src/rpc_subscriptions.rs b/core/src/rpc_subscriptions.rs index 80d7d3aeab..211f30361c 100644 --- a/core/src/rpc_subscriptions.rs +++ b/core/src/rpc_subscriptions.rs @@ -5,12 +5,11 @@ use core::hash::Hash; use jsonrpc_core::futures::Future; use jsonrpc_pubsub::typed::Sink; use jsonrpc_pubsub::SubscriptionId; -use solana_client::rpc_signature_status::RpcSignatureStatus; use solana_runtime::bank::Bank; use solana_sdk::account::Account; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Signature; -use solana_sdk::transaction::{self, TransactionError}; +use solana_sdk::transaction; use std::collections::HashMap; use std::sync::RwLock; @@ -18,7 +17,7 @@ type RpcAccountSubscriptions = RwLock>>>; type RpcSignatureSubscriptions = - RwLock>>>; + RwLock>>>>>; fn add_subscription( subscriptions: &mut HashMap>>, @@ -97,19 +96,10 @@ impl RpcSubscriptions { } pub fn check_signature(&self, signature: &Signature, bank_error: &transaction::Result<()>) { - let status = match bank_error { - Ok(_) => RpcSignatureStatus::Confirmed, - Err(TransactionError::AccountInUse) => RpcSignatureStatus::AccountInUse, - Err(TransactionError::InstructionError(_, _)) => { - RpcSignatureStatus::ProgramRuntimeError - } - Err(_) => RpcSignatureStatus::GenericFailure, - }; - let mut subscriptions = self.signature_subscriptions.write().unwrap(); if let Some(hashmap) = subscriptions.get(signature) { for (_bank_sub_id, sink) in hashmap.iter() { - sink.notify(Ok(status)).wait().unwrap(); + sink.notify(Ok(Some(bank_error.clone()))).wait().unwrap(); } } subscriptions.remove(&signature); @@ -149,7 +139,7 @@ impl RpcSubscriptions { &self, signature: &Signature, sub_id: &SubscriptionId, - sink: &Sink, + sink: &Sink>>, ) { let mut subscriptions = self.signature_subscriptions.write().unwrap(); add_subscription(&mut subscriptions, signature, sub_id, sink); @@ -322,7 +312,10 @@ mod tests { subscriptions.check_signature(&signature, &Ok(())); let string = transport_receiver.poll(); if let Async::Ready(Some(response)) = string.unwrap() { - let expected = format!(r#"{{"jsonrpc":"2.0","method":"signatureNotification","params":{{"result":"Confirmed","subscription":0}}}}"#); + let expected_res: Option> = Some(Ok(())); + let expected_res_str = + serde_json::to_string(&serde_json::to_value(expected_res).unwrap()).unwrap(); + let expected = format!(r#"{{"jsonrpc":"2.0","method":"signatureNotification","params":{{"result":{},"subscription":0}}}}"#, expected_res_str); assert_eq!(expected, response); } diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index 3a436f1521..ae110fba66 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -311,8 +311,11 @@ fn process_balance(pubkey: &Pubkey, rpc_client: &RpcClient) -> ProcessResult { fn process_confirm(rpc_client: &RpcClient, signature: Signature) -> ProcessResult { match rpc_client.get_signature_status(&signature.to_string()) { Ok(status) => { - if status == solana_client::rpc_signature_status::RpcSignatureStatus::Confirmed { - Ok("Confirmed".to_string()) + if let Some(result) = status { + match result { + Ok(_) => Ok("Confirmed".to_string()), + Err(err) => Ok(format!("Transaction failed with error {:?}", err)), + } } else { Ok("Not found".to_string()) } @@ -724,6 +727,7 @@ mod tests { use serde_json::Value; use solana_client::mock_rpc_client_request::SIGNATURE; use solana_sdk::signature::{gen_keypair_file, read_keypair, read_pkcs8, Keypair, KeypairUtil}; + use solana_sdk::transaction::TransactionError; use std::fs; use std::net::{Ipv4Addr, SocketAddr}; use std::path::{Path, PathBuf}; @@ -1269,12 +1273,24 @@ mod tests { let signature = process_command(&config); assert_eq!(signature.unwrap(), SIGNATURE.to_string()); - // bad_sig_status cases - config.rpc_client = Some(RpcClient::new_mock("bad_sig_status".to_string())); + // sig_not_found case + config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string())); let missing_signature = Signature::new(&bs58::decode("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW").into_vec().unwrap()); config.command = WalletCommand::Confirm(missing_signature); assert_eq!(process_command(&config).unwrap(), "Not found"); + // Tx error case + config.rpc_client = Some(RpcClient::new_mock("account_in_use".to_string())); + let any_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap()); + config.command = WalletCommand::Confirm(any_signature); + assert_eq!( + process_command(&config).unwrap(), + format!( + "Transaction failed with error {:?}", + TransactionError::AccountInUse + ) + ); + // Failure cases config.rpc_client = Some(RpcClient::new_mock("fails".to_string())); @@ -1284,10 +1300,6 @@ mod tests { config.command = WalletCommand::Balance(config.keypair.pubkey()); assert!(process_command(&config).is_err()); - let any_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap()); - config.command = WalletCommand::Confirm(any_signature); - assert!(process_command(&config).is_err()); - config.command = WalletCommand::ConfigureStakingAccount(None, Some(bob_pubkey)); assert!(process_command(&config).is_err());