From 62db7f6562679f982b601d2e6411a71ae841f5d9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2020 02:12:46 +0000 Subject: [PATCH] Surface transaction logs in rpc client (#13376) (cherry picked from commit 6d9ca0ae157a0f109987fd7802fcfcf19e532ed8) Co-authored-by: Michael Vines --- cli/src/stake.rs | 4 ++-- client/src/http_sender.rs | 30 ++++++++++++++++++++++++------ client/src/rpc_client.rs | 29 ++++++++++++++++++++++++++--- client/src/rpc_request.rs | 31 +++++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 13 deletions(-) diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 7909f0d84e..b19f658b60 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -1618,9 +1618,9 @@ pub(crate) fn fetch_epoch_rewards( kind: ClientErrorKind::RpcError(rpc_request::RpcError::RpcResponseError { code: rpc_custom_error::JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE, - message: _, + .. }), - request: _, + .. }) => { // RPC node doesn't have this block break; diff --git a/client/src/http_sender.rs b/client/src/http_sender.rs index d72122b3a6..bc200afbda 100644 --- a/client/src/http_sender.rs +++ b/client/src/http_sender.rs @@ -1,6 +1,8 @@ use crate::{ client_error::Result, - rpc_request::{RpcError, RpcRequest}, + rpc_custom_error, + rpc_request::{RpcError, RpcRequest, RpcResponseErrorData}, + rpc_response::RpcSimulateTransactionResult, rpc_sender::RpcSender, }; use log::*; @@ -31,7 +33,7 @@ impl HttpSender { struct RpcErrorObject { code: i64, message: String, - /*data field omitted*/ + data: serde_json::Value, } impl RpcSender for HttpSender { @@ -72,11 +74,27 @@ impl RpcSender for HttpSender { if json["error"].is_object() { return match serde_json::from_value::(json["error"].clone()) { - Ok(rpc_error_object) => Err(RpcError::RpcResponseError { - code: rpc_error_object.code, - message: rpc_error_object.message, + Ok(rpc_error_object) => { + let data = match rpc_error_object.code { + rpc_custom_error::JSON_RPC_SERVER_ERROR_SEND_TRANSACTION_PREFLIGHT_FAILURE => { + match serde_json::from_value::(json["error"]["data"].clone()) { + Ok(data) => RpcResponseErrorData::SendTransactionPreflightFailure(data), + Err(err) => { + debug!("Failed to deserialize RpcSimulateTransactionResult: {:?}", err); + RpcResponseErrorData::Empty + } + } + }, + _ => RpcResponseErrorData::Empty + }; + + Err(RpcError::RpcResponseError { + code: rpc_error_object.code, + message: rpc_error_object.message, + data, + } + .into()) } - .into()), Err(err) => Err(RpcError::RpcRequestError(format!( "Failed to deserialize RPC error response: {} [{}]", serde_json::to_string(&json["error"]).unwrap(), diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 79a8cd4133..3c256400e7 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -8,7 +8,7 @@ use crate::{ RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig, RpcTokenAccountsFilter, }, - rpc_request::{RpcError, RpcRequest, TokenAccountsFilter}, + rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter}, rpc_response::*, rpc_sender::RpcSender, }; @@ -171,10 +171,33 @@ impl RpcClient { ..config }; let serialized_encoded = serialize_encode_transaction(transaction, encoding)?; - let signature_base58_str: String = self.send( + let signature_base58_str: String = match self.send( RpcRequest::SendTransaction, json!([serialized_encoded, config]), - )?; + ) { + Ok(signature_base58_str) => signature_base58_str, + Err(err) => { + if let ClientErrorKind::RpcError(RpcError::RpcResponseError { + code, + message, + data, + }) = &err.kind + { + debug!("{} {}", code, message); + if let RpcResponseErrorData::SendTransactionPreflightFailure( + RpcSimulateTransactionResult { + logs: Some(logs), .. + }, + ) = data + { + for (i, log) in logs.iter().enumerate() { + debug!("{:>3}: {}", i + 1, log); + } + } + } + return Err(err); + } + }; let signature = signature_base58_str .parse::() diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index ec57f8680d..b8b5fd3200 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -1,3 +1,4 @@ +use crate::rpc_response::RpcSimulateTransactionResult; use serde_json::{json, Value}; use solana_sdk::pubkey::Pubkey; use std::fmt; @@ -138,12 +139,38 @@ impl RpcRequest { } } +#[derive(Debug)] +pub enum RpcResponseErrorData { + Empty, + SendTransactionPreflightFailure(RpcSimulateTransactionResult), +} + +impl fmt::Display for RpcResponseErrorData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + RpcResponseErrorData::SendTransactionPreflightFailure( + RpcSimulateTransactionResult { + logs: Some(logs), .. + }, + ) => { + // Give the user a hint that there is more useful logging information available... + write!(f, "{} log messages", logs.len()) + } + _ => Ok(()), + } + } +} + #[derive(Debug, Error)] pub enum RpcError { #[error("RPC request error: {0}")] RpcRequestError(String), - #[error("RPC response error {code}: {message}")] - RpcResponseError { code: i64, message: String }, + #[error("RPC response error {code}: {message} [{data}]")] + RpcResponseError { + code: i64, + message: String, + data: RpcResponseErrorData, + }, #[error("parse error: expected {0}")] ParseError(String), /* "expected" */ // Anything in a `ForUser` needs to die. The caller should be