RPC sendTransaction now returns transaction logs on simulation failure

This commit is contained in:
Michael Vines
2020-09-15 22:30:04 -07:00
parent bb9c04895b
commit 749208fa32
2 changed files with 27 additions and 22 deletions

View File

@ -1750,7 +1750,7 @@ fn _send_transaction(
last_valid_slot: Slot, last_valid_slot: Slot,
) -> Result<String> { ) -> Result<String> {
if transaction.signatures.is_empty() { if transaction.signatures.is_empty() {
return Err(RpcCustomError::SendTransactionIsNotSigned.into()); return Err(RpcCustomError::TransactionSignatureVerificationFailure.into());
} }
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let transaction_info = TransactionInfo::new(signature, wire_transaction, last_valid_slot); let transaction_info = TransactionInfo::new(signature, wire_transaction, last_valid_slot);
@ -2158,26 +2158,18 @@ impl RpcSol for RpcSolImpl {
if !config.skip_preflight { if !config.skip_preflight {
if transaction.verify().is_err() { if transaction.verify().is_err() {
return Err(RpcCustomError::SendTransactionPreflightFailure { return Err(RpcCustomError::TransactionSignatureVerificationFailure.into());
message: "Transaction signature verification failed".into(),
}
.into());
} }
if meta.health.check() != RpcHealthStatus::Ok { if meta.health.check() != RpcHealthStatus::Ok {
return Err(RpcCustomError::SendTransactionPreflightFailure { return Err(RpcCustomError::RpcNodeUnhealthy.into());
message: "RPC node is unhealthy, unable to simulate transaction".into(),
}
.into());
} }
let preflight_commitment = config let preflight_commitment = config
.preflight_commitment .preflight_commitment
.map(|commitment| CommitmentConfig { commitment }); .map(|commitment| CommitmentConfig { commitment });
let preflight_bank = &*meta.bank(preflight_commitment); let preflight_bank = &*meta.bank(preflight_commitment);
if let (Err(err), _log_output) = if let (Err(err), logs) = preflight_bank.simulate_transaction(transaction.clone()) {
preflight_bank.simulate_transaction(transaction.clone())
{
// Note: it's possible that the transaction simulation failed but the actual // Note: it's possible that the transaction simulation failed but the actual
// transaction would succeed, such as when a transaction depends on an earlier // transaction would succeed, such as when a transaction depends on an earlier
// transaction that has yet to reach max confirmations. In these cases the user // transaction that has yet to reach max confirmations. In these cases the user
@ -2185,6 +2177,10 @@ impl RpcSol for RpcSolImpl {
// additional controls over what bank is used for preflight should be exposed. // additional controls over what bank is used for preflight should be exposed.
return Err(RpcCustomError::SendTransactionPreflightFailure { return Err(RpcCustomError::SendTransactionPreflightFailure {
message: format!("Transaction simulation failed: {}", err), message: format!("Transaction simulation failed: {}", err),
result: RpcSimulateTransactionResult {
err: Some(err),
logs: Some(logs),
},
} }
.into()); .into());
} }
@ -4060,7 +4056,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found"},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"err":"BlockhashNotFound","logs":[]}},"id":1}"#.to_string(),
) )
); );
@ -4076,7 +4072,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Transaction failed to sanitize accounts offsets correctly"},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Transaction failed to sanitize accounts offsets correctly","data":{"err":"SanitizeFailure","logs":[]}},"id":1}"#.to_string(),
) )
); );
let mut bad_transaction = let mut bad_transaction =
@ -4092,7 +4088,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"RPC node is unhealthy, unable to simulate transaction"},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32005,"message":"RPC node is unhealthy"},"id":1}"#.to_string(),
) )
); );
health.stub_set_health_status(None); health.stub_set_health_status(None);
@ -4108,7 +4104,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction signature verification failed"},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Transaction signature verification failure"},"id":1}"#.to_string(),
) )
); );
@ -4137,7 +4133,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Transaction is not signed"},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32003,"message":"Transaction signature verification failure"},"id":1}"#.to_string(),
) )
); );
} }

View File

@ -1,10 +1,12 @@
use jsonrpc_core::{Error, ErrorCode}; use jsonrpc_core::{Error, ErrorCode};
use solana_client::rpc_response::RpcSimulateTransactionResult;
use solana_sdk::clock::Slot; use solana_sdk::clock::Slot;
const JSON_RPC_SERVER_ERROR_1: i64 = -32001; const JSON_RPC_SERVER_ERROR_1: i64 = -32001;
const JSON_RPC_SERVER_ERROR_2: i64 = -32002; const JSON_RPC_SERVER_ERROR_2: i64 = -32002;
const JSON_RPC_SERVER_ERROR_3: i64 = -32003; const JSON_RPC_SERVER_ERROR_3: i64 = -32003;
const JSON_RPC_SERVER_ERROR_4: i64 = -32004; const JSON_RPC_SERVER_ERROR_4: i64 = -32004;
const JSON_RPC_SERVER_ERROR_5: i64 = -32005;
pub enum RpcCustomError { pub enum RpcCustomError {
BlockCleanedUp { BlockCleanedUp {
@ -13,11 +15,13 @@ pub enum RpcCustomError {
}, },
SendTransactionPreflightFailure { SendTransactionPreflightFailure {
message: String, message: String,
result: RpcSimulateTransactionResult,
}, },
SendTransactionIsNotSigned, TransactionSignatureVerificationFailure,
BlockNotAvailable { BlockNotAvailable {
slot: Slot, slot: Slot,
}, },
RpcNodeUnhealthy,
} }
impl From<RpcCustomError> for Error { impl From<RpcCustomError> for Error {
@ -34,14 +38,14 @@ impl From<RpcCustomError> for Error {
), ),
data: None, data: None,
}, },
RpcCustomError::SendTransactionPreflightFailure { message } => Self { RpcCustomError::SendTransactionPreflightFailure { message, result } => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_2), code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_2),
message, message,
data: None, data: Some(serde_json::json!(result)),
}, },
RpcCustomError::SendTransactionIsNotSigned => Self { RpcCustomError::TransactionSignatureVerificationFailure => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_3), code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_3),
message: "Transaction is not signed".to_string(), message: "Transaction signature verification failure".to_string(),
data: None, data: None,
}, },
RpcCustomError::BlockNotAvailable { slot } => Self { RpcCustomError::BlockNotAvailable { slot } => Self {
@ -49,6 +53,11 @@ impl From<RpcCustomError> for Error {
message: format!("Block not available for slot {}", slot), message: format!("Block not available for slot {}", slot),
data: None, data: None,
}, },
RpcCustomError::RpcNodeUnhealthy => Self {
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_5),
message: "RPC node is unhealthy".to_string(),
data: None,
},
} }
} }
} }