Plumb TransactionError through Rpc

This commit is contained in:
Tyera Eulberg
2019-04-05 19:07:30 -06:00
committed by Tyera Eulberg
parent 569a289a6f
commit 90c1300bb6
3 changed files with 70 additions and 48 deletions

View File

@ -8,13 +8,12 @@ use bincode::{deserialize, serialize};
use bs58; use bs58;
use jsonrpc_core::{Error, Metadata, Result}; use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc; use jsonrpc_derive::rpc;
use solana_client::rpc_signature_status::RpcSignatureStatus;
use solana_drone::drone::request_airdrop_transaction; use solana_drone::drone::request_airdrop_transaction;
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, Transaction, TransactionError}; use solana_sdk::transaction::{self, Transaction};
use std::mem; use std::mem;
use std::net::{SocketAddr, UdpSocket}; use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@ -189,7 +188,11 @@ pub trait RpcSol {
fn get_recent_blockhash(&self, _: Self::Metadata) -> Result<String>; fn get_recent_blockhash(&self, _: Self::Metadata) -> Result<String>;
#[rpc(meta, name = "getSignatureStatus")] #[rpc(meta, name = "getSignatureStatus")]
fn get_signature_status(&self, _: Self::Metadata, _: String) -> Result<RpcSignatureStatus>; fn get_signature_status(
&self,
_: Self::Metadata,
_: String,
) -> Result<Option<transaction::Result<()>>>;
#[rpc(meta, name = "getTransactionCount")] #[rpc(meta, name = "getTransactionCount")]
fn get_transaction_count(&self, _: Self::Metadata) -> Result<u64>; fn get_transaction_count(&self, _: Self::Metadata) -> Result<u64>;
@ -221,14 +224,14 @@ pub trait RpcSol {
&self, &self,
_: Self::Metadata, _: Self::Metadata,
_: String, _: String,
) -> Result<usize>; ) -> Result<Option<usize>>;
#[rpc(meta, name = "getSignatureConfirmation")] #[rpc(meta, name = "getSignatureConfirmation")]
fn get_signature_confirmation( fn get_signature_confirmation(
&self, &self,
_: Self::Metadata, _: Self::Metadata,
_: String, _: String,
) -> Result<(usize, RpcSignatureStatus)>; ) -> Result<Option<(usize, transaction::Result<()>)>>;
} }
pub struct RpcSolImpl; pub struct RpcSolImpl;
@ -237,8 +240,12 @@ impl RpcSol for RpcSolImpl {
fn confirm_transaction(&self, meta: Self::Metadata, id: String) -> Result<bool> { fn confirm_transaction(&self, meta: Self::Metadata, id: String) -> Result<bool> {
debug!("confirm_transaction rpc request received: {:?}", id); debug!("confirm_transaction rpc request received: {:?}", id);
self.get_signature_status(meta, id) self.get_signature_status(meta, id).map(|status_option| {
.map(|status| status == RpcSignatureStatus::Confirmed) if status_option.is_none() {
return false;
}
status_option.unwrap().is_ok()
})
} }
fn get_account_info(&self, meta: Self::Metadata, id: String) -> Result<Account> { fn get_account_info(&self, meta: Self::Metadata, id: String) -> Result<Account> {
@ -265,57 +272,36 @@ impl RpcSol for RpcSolImpl {
.get_recent_blockhash()) .get_recent_blockhash())
} }
fn get_signature_status(&self, meta: Self::Metadata, id: String) -> Result<RpcSignatureStatus> { fn get_signature_status(
self.get_signature_confirmation(meta, id).map(|x| x.1) &self,
meta: Self::Metadata,
id: String,
) -> Result<Option<transaction::Result<()>>> {
self.get_signature_confirmation(meta, id)
.map(|res| res.map(|x| x.1))
} }
fn get_num_blocks_since_signature_confirmation( fn get_num_blocks_since_signature_confirmation(
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
id: String, id: String,
) -> Result<usize> { ) -> Result<Option<usize>> {
self.get_signature_confirmation(meta, id).map(|x| x.0) self.get_signature_confirmation(meta, id)
.map(|res| res.map(|x| x.0))
} }
fn get_signature_confirmation( fn get_signature_confirmation(
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
id: String, id: String,
) -> Result<(usize, RpcSignatureStatus)> { ) -> Result<Option<(usize, transaction::Result<()>)>> {
debug!("get_signature_confirmation rpc request received: {:?}", id); debug!("get_signature_confirmation rpc request received: {:?}", id);
let signature = verify_signature(&id)?; let signature = verify_signature(&id)?;
let res = meta Ok(meta
.request_processor .request_processor
.read() .read()
.unwrap() .unwrap()
.get_signature_confirmation_status(signature); .get_signature_confirmation_status(signature))
let status = {
if let Some((count, res)) = res {
let res = match res {
Ok(_) => RpcSignatureStatus::Confirmed,
Err(TransactionError::AccountInUse) => RpcSignatureStatus::AccountInUse,
Err(TransactionError::AccountLoadedTwice) => {
RpcSignatureStatus::AccountLoadedTwice
}
Err(TransactionError::InstructionError(_, _)) => {
RpcSignatureStatus::ProgramRuntimeError
}
Err(err) => {
trace!("mapping {:?} to GenericFailure", err);
RpcSignatureStatus::GenericFailure
}
};
(count, res)
} else {
(0, RpcSignatureStatus::SignatureNotFound)
}
};
debug!(
"get_signature_confirmation rpc request status: {:?}",
status
);
Ok(status)
} }
fn get_transaction_count(&self, meta: Self::Metadata) -> Result<u64> { fn get_transaction_count(&self, meta: Self::Metadata) -> Result<u64> {
@ -453,8 +439,10 @@ mod tests {
use jsonrpc_core::{MetaIoHandler, Response}; use jsonrpc_core::{MetaIoHandler, Response};
use solana_sdk::genesis_block::GenesisBlock; use solana_sdk::genesis_block::GenesisBlock;
use solana_sdk::hash::{hash, Hash}; use solana_sdk::hash::{hash, Hash};
use solana_sdk::instruction::InstructionError;
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::thread; use std::thread;
fn start_rpc_handler_with_tx(pubkey: &Pubkey) -> (MetaIoHandler<Meta>, Meta, Hash, Keypair) { fn start_rpc_handler_with_tx(pubkey: &Pubkey) -> (MetaIoHandler<Meta>, Meta, Hash, Keypair) {
@ -466,6 +454,9 @@ mod tests {
let tx = system_transaction::transfer(&alice, pubkey, 20, blockhash, 0); let tx = system_transaction::transfer(&alice, pubkey, 20, blockhash, 0);
bank.process_transaction(&tx).expect("process transaction"); bank.process_transaction(&tx).expect("process transaction");
let tx = system_transaction::transfer(&alice, &alice.pubkey(), 20, blockhash, 0);
let _ = bank.process_transaction(&tx);
let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new( let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new(
StorageState::default(), StorageState::default(),
JsonRpcConfig::default(), JsonRpcConfig::default(),
@ -601,9 +592,14 @@ mod tests {
tx.signatures[0] tx.signatures[0]
); );
let res = io.handle_request_sync(&req, meta.clone()); let res = io.handle_request_sync(&req, meta.clone());
let expected = format!(r#"{{"jsonrpc":"2.0","result":"Confirmed","id":1}}"#); let expected_res: Option<transaction::Result<()>> = Some(Ok(()));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response = let expected: Response =
serde_json::from_str(&expected).expect("expected response deserialization"); serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response")) let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization"); .expect("actual response deserialization");
assert_eq!(expected, result); assert_eq!(expected, result);
@ -614,10 +610,36 @@ mod tests {
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#, r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0] tx.signatures[0]
); );
let res = io.handle_request_sync(&req, meta); let res = io.handle_request_sync(&req, meta.clone());
let expected = format!(r#"{{"jsonrpc":"2.0","result":"SignatureNotFound","id":1}}"#); let expected_res: Option<String> = None;
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response = let expected: Response =
serde_json::from_str(&expected).expect("expected response deserialization"); serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
assert_eq!(expected, result);
// Test getSignatureStatus request on a TransactionError
let tx = system_transaction::transfer(&alice, &alice.pubkey(), 20, blockhash, 0);
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatus","params":["{}"]}}"#,
tx.signatures[0]
);
let res = io.handle_request_sync(&req, meta);
let expected_res: Option<transaction::Result<()>> = Some(Err(
TransactionError::InstructionError(0, InstructionError::DuplicateAccountIndex),
));
let expected = json!({
"jsonrpc": "2.0",
"result": expected_res,
"id": 1
});
let expected: Response =
serde_json::from_value(expected).expect("expected response deserialization");
let result: Response = serde_json::from_str(&res.expect("actual response")) let result: Response = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization"); .expect("actual response deserialization");
assert_eq!(expected, result); assert_eq!(expected, result);

View File

@ -7,7 +7,7 @@ use bincode::serialize;
use serde::Serialize; use serde::Serialize;
/// Reasons the runtime might have rejected an instruction. /// Reasons the runtime might have rejected an instruction.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum InstructionError { pub enum InstructionError {
/// Deprecated! Use CustomError instead! /// Deprecated! Use CustomError instead!
/// The program instruction returned an error /// The program instruction returned an error

View File

@ -10,7 +10,7 @@ use bincode::serialize;
use std::result; use std::result;
/// Reasons a transaction might be rejected. /// Reasons a transaction might be rejected.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum TransactionError { pub enum TransactionError {
/// This Pubkey is being processed in another transaction /// This Pubkey is being processed in another transaction
AccountInUse, AccountInUse,