Get everything off RpcSignatureStatus

This commit is contained in:
Tyera Eulberg
2019-04-05 20:56:17 -06:00
committed by Tyera Eulberg
parent 90c1300bb6
commit f349c1f0dc
5 changed files with 80 additions and 75 deletions

View File

@ -1,6 +1,7 @@
use crate::generic_rpc_client_request::GenericRpcClientRequest; use crate::generic_rpc_client_request::GenericRpcClientRequest;
use crate::rpc_request::RpcRequest; use crate::rpc_request::RpcRequest;
use serde_json::{Number, Value}; use serde_json::{Number, Value};
use solana_sdk::transaction::{self, TransactionError};
pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8";
pub const SIGNATURE: &str = pub const SIGNATURE: &str =
@ -44,14 +45,14 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
} }
RpcRequest::GetRecentBlockhash => Value::String(PUBKEY.to_string()), RpcRequest::GetRecentBlockhash => Value::String(PUBKEY.to_string()),
RpcRequest::GetSignatureStatus => { RpcRequest::GetSignatureStatus => {
let str = if self.url == "account_in_use" { let response: Option<transaction::Result<()>> = if self.url == "account_in_use" {
"AccountInUse" Some(Err(TransactionError::AccountInUse))
} else if self.url == "bad_sig_status" { } else if self.url == "sig_not_found" {
"SignatureNotFound" None
} else { } else {
"Confirmed" Some(Ok(()))
}; };
Value::String(str.to_string()) serde_json::to_value(response).unwrap()
} }
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()),

View File

@ -2,7 +2,6 @@ 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 bincode::serialize;
use bs58; use bs58;
use log::*; use log::*;
@ -12,11 +11,10 @@ use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
use solana_sdk::timing::{DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND}; 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::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};
@ -69,25 +67,14 @@ impl RpcClient {
pub fn get_signature_status( pub fn get_signature_status(
&self, &self,
signature: &str, signature: &str,
) -> Result<RpcSignatureStatus, Box<dyn error::Error>> { ) -> Result<Option<transaction::Result<()>>, Box<dyn error::Error>> {
let params = json!([signature.to_string()]); let params = json!([signature.to_string()]);
let signature_status = let signature_status =
self.client self.client
.send(&RpcRequest::GetSignatureStatus, Some(params), 5)?; .send(&RpcRequest::GetSignatureStatus, Some(params), 5)?;
if let Some(status) = signature_status.as_str() { let result: Option<transaction::Result<()>> =
let rpc_status = RpcSignatureStatus::from_str(status).map_err(|err| { serde_json::from_value(signature_status).unwrap();
io::Error::new( Ok(result)
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>( pub fn send_and_confirm_transaction<T: KeypairUtil>(
@ -101,7 +88,7 @@ impl RpcClient {
let signature_str = self.send_transaction(transaction)?; let signature_str = self.send_transaction(transaction)?;
let status = loop { let status = loop {
let status = self.get_signature_status(&signature_str)?; let status = self.get_signature_status(&signature_str)?;
if status == RpcSignatureStatus::SignatureNotFound { if status.is_none() {
status_retries -= 1; status_retries -= 1;
if status_retries == 0 { if status_retries == 0 {
break status; break status;
@ -116,19 +103,19 @@ impl RpcClient {
)); ));
} }
}; };
match status { send_retries = if let Some(result) = status.clone() {
RpcSignatureStatus::AccountInUse | RpcSignatureStatus::SignatureNotFound => { match result {
Ok(_) => return Ok(signature_str),
Err(TransactionError::AccountInUse) => {
// Fetch a new blockhash and re-sign the transaction before sending it again // Fetch a new blockhash and re-sign the transaction before sending it again
self.resign_transaction(transaction, signer)?; self.resign_transaction(transaction, signer)?;
send_retries -= 1; send_retries - 1
}
RpcSignatureStatus::Confirmed => {
return Ok(signature_str);
}
_ => {
send_retries = 0;
} }
Err(_) => 0,
} }
} else {
send_retries - 1
};
if send_retries == 0 { if send_retries == 0 {
Err(io::Error::new( Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
@ -177,7 +164,10 @@ impl RpcClient {
.filter(|(_transaction, signature)| { .filter(|(_transaction, signature)| {
if let Some(signature) = signature { if let Some(signature) = signature {
if let Ok(status) = self.get_signature_status(&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 true
@ -557,6 +547,7 @@ mod tests {
use solana_logger; use solana_logger;
use solana_sdk::signature::{Keypair, KeypairUtil}; use solana_sdk::signature::{Keypair, KeypairUtil};
use solana_sdk::system_transaction; use solana_sdk::system_transaction;
use solana_sdk::transaction::TransactionError;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::thread; use std::thread;
@ -692,18 +683,18 @@ mod tests {
fn test_get_signature_status() { fn test_get_signature_status() {
let rpc_client = RpcClient::new_mock("succeeds".to_string()); let rpc_client = RpcClient::new_mock("succeeds".to_string());
let signature = "good_signature"; let signature = "good_signature";
let status = rpc_client.get_signature_status(&signature); let status = rpc_client.get_signature_status(&signature).unwrap();
assert_eq!(status.unwrap(), RpcSignatureStatus::Confirmed); assert_eq!(status, Some(Ok(())));
let rpc_client = RpcClient::new_mock("bad_sig_status".to_string()); let rpc_client = RpcClient::new_mock("sig_not_found".to_string());
let signature = "bad_status"; let signature = "sig_not_found";
let status = dbg!(rpc_client.get_signature_status(&signature)); let status = rpc_client.get_signature_status(&signature).unwrap();
assert_eq!(status.unwrap(), RpcSignatureStatus::SignatureNotFound); assert_eq!(status, None);
let rpc_client = RpcClient::new_mock("fails".to_string()); let rpc_client = RpcClient::new_mock("account_in_use".to_string());
let signature = "bad_status_fmt"; let signature = "account_in_use";
let status = rpc_client.get_signature_status(&signature); let status = rpc_client.get_signature_status(&signature).unwrap();
assert!(status.is_err()); assert_eq!(status, Some(Err(TransactionError::AccountInUse)));
} }
#[test] #[test]

View File

@ -6,10 +6,10 @@ use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc; use jsonrpc_derive::rpc;
use jsonrpc_pubsub::typed::Subscriber; use jsonrpc_pubsub::typed::Subscriber;
use jsonrpc_pubsub::{Session, SubscriptionId}; use jsonrpc_pubsub::{Session, SubscriptionId};
use solana_client::rpc_signature_status::RpcSignatureStatus;
use solana_sdk::account::Account; use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature; use solana_sdk::signature::Signature;
use solana_sdk::transaction;
use std::mem; use std::mem;
use std::sync::{atomic, Arc}; use std::sync::{atomic, Arc};
@ -58,7 +58,12 @@ pub trait RpcSolPubSub {
subscribe, subscribe,
name = "signatureSubscribe" name = "signatureSubscribe"
)] )]
fn signature_subscribe(&self, _: Self::Metadata, _: Subscriber<RpcSignatureStatus>, _: String); fn signature_subscribe(
&self,
_: Self::Metadata,
_: Subscriber<Option<transaction::Result<()>>>,
_: String,
);
// Unsubscribe from signature notification subscription. // Unsubscribe from signature notification subscription.
#[pubsub( #[pubsub(
@ -178,7 +183,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
fn signature_subscribe( fn signature_subscribe(
&self, &self,
_meta: Self::Metadata, _meta: Self::Metadata,
subscriber: Subscriber<RpcSignatureStatus>, subscriber: Subscriber<Option<transaction::Result<()>>>,
signature_str: String, signature_str: String,
) { ) {
info!("signature_subscribe"); info!("signature_subscribe");
@ -283,7 +288,10 @@ mod tests {
// Test signature confirmation notification // Test signature confirmation notification
let string = receiver.poll(); let string = receiver.poll();
if let Async::Ready(Some(response)) = string.unwrap() { 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<transaction::Result<()>> = 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); assert_eq!(expected, response);
} }
} }

View File

@ -5,12 +5,11 @@ use core::hash::Hash;
use jsonrpc_core::futures::Future; use jsonrpc_core::futures::Future;
use jsonrpc_pubsub::typed::Sink; use jsonrpc_pubsub::typed::Sink;
use jsonrpc_pubsub::SubscriptionId; use jsonrpc_pubsub::SubscriptionId;
use solana_client::rpc_signature_status::RpcSignatureStatus;
use solana_runtime::bank::Bank; use solana_runtime::bank::Bank;
use solana_sdk::account::Account; use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature; use solana_sdk::signature::Signature;
use solana_sdk::transaction::{self, TransactionError}; use solana_sdk::transaction;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock; use std::sync::RwLock;
@ -18,7 +17,7 @@ type RpcAccountSubscriptions = RwLock<HashMap<Pubkey, HashMap<SubscriptionId, Si
type RpcProgramSubscriptions = type RpcProgramSubscriptions =
RwLock<HashMap<Pubkey, HashMap<SubscriptionId, Sink<(String, Account)>>>>; RwLock<HashMap<Pubkey, HashMap<SubscriptionId, Sink<(String, Account)>>>>;
type RpcSignatureSubscriptions = type RpcSignatureSubscriptions =
RwLock<HashMap<Signature, HashMap<SubscriptionId, Sink<RpcSignatureStatus>>>>; RwLock<HashMap<Signature, HashMap<SubscriptionId, Sink<Option<transaction::Result<()>>>>>>;
fn add_subscription<K, S>( fn add_subscription<K, S>(
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, Sink<S>>>, subscriptions: &mut HashMap<K, HashMap<SubscriptionId, Sink<S>>>,
@ -97,19 +96,10 @@ impl RpcSubscriptions {
} }
pub fn check_signature(&self, signature: &Signature, bank_error: &transaction::Result<()>) { 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(); let mut subscriptions = self.signature_subscriptions.write().unwrap();
if let Some(hashmap) = subscriptions.get(signature) { if let Some(hashmap) = subscriptions.get(signature) {
for (_bank_sub_id, sink) in hashmap.iter() { 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); subscriptions.remove(&signature);
@ -149,7 +139,7 @@ impl RpcSubscriptions {
&self, &self,
signature: &Signature, signature: &Signature,
sub_id: &SubscriptionId, sub_id: &SubscriptionId,
sink: &Sink<RpcSignatureStatus>, sink: &Sink<Option<transaction::Result<()>>>,
) { ) {
let mut subscriptions = self.signature_subscriptions.write().unwrap(); let mut subscriptions = self.signature_subscriptions.write().unwrap();
add_subscription(&mut subscriptions, signature, sub_id, sink); add_subscription(&mut subscriptions, signature, sub_id, sink);
@ -322,7 +312,10 @@ mod tests {
subscriptions.check_signature(&signature, &Ok(())); subscriptions.check_signature(&signature, &Ok(()));
let string = transport_receiver.poll(); let string = transport_receiver.poll();
if let Async::Ready(Some(response)) = string.unwrap() { 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<transaction::Result<()>> = 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); assert_eq!(expected, response);
} }

View File

@ -311,8 +311,11 @@ fn process_balance(pubkey: &Pubkey, rpc_client: &RpcClient) -> ProcessResult {
fn process_confirm(rpc_client: &RpcClient, signature: Signature) -> ProcessResult { fn process_confirm(rpc_client: &RpcClient, signature: Signature) -> ProcessResult {
match rpc_client.get_signature_status(&signature.to_string()) { match rpc_client.get_signature_status(&signature.to_string()) {
Ok(status) => { Ok(status) => {
if status == solana_client::rpc_signature_status::RpcSignatureStatus::Confirmed { if let Some(result) = status {
Ok("Confirmed".to_string()) match result {
Ok(_) => Ok("Confirmed".to_string()),
Err(err) => Ok(format!("Transaction failed with error {:?}", err)),
}
} else { } else {
Ok("Not found".to_string()) Ok("Not found".to_string())
} }
@ -724,6 +727,7 @@ mod tests {
use serde_json::Value; use serde_json::Value;
use solana_client::mock_rpc_client_request::SIGNATURE; use solana_client::mock_rpc_client_request::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 solana_sdk::transaction::TransactionError;
use std::fs; use std::fs;
use std::net::{Ipv4Addr, SocketAddr}; use std::net::{Ipv4Addr, SocketAddr};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -1269,12 +1273,24 @@ mod tests {
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
// bad_sig_status cases // sig_not_found case
config.rpc_client = Some(RpcClient::new_mock("bad_sig_status".to_string())); config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string()));
let missing_signature = Signature::new(&bs58::decode("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW").into_vec().unwrap()); let missing_signature = Signature::new(&bs58::decode("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW").into_vec().unwrap());
config.command = WalletCommand::Confirm(missing_signature); config.command = WalletCommand::Confirm(missing_signature);
assert_eq!(process_command(&config).unwrap(), "Not found"); 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 // Failure cases
config.rpc_client = Some(RpcClient::new_mock("fails".to_string())); config.rpc_client = Some(RpcClient::new_mock("fails".to_string()));
@ -1284,10 +1300,6 @@ mod tests {
config.command = WalletCommand::Balance(config.keypair.pubkey()); config.command = WalletCommand::Balance(config.keypair.pubkey());
assert!(process_command(&config).is_err()); 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)); config.command = WalletCommand::ConfigureStakingAccount(None, Some(bob_pubkey));
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());