diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 0cc4a3254b..5de4353219 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -1767,7 +1767,7 @@ fn _send_transaction( last_valid_slot: Slot, ) -> Result { if transaction.signatures.is_empty() { - return Err(RpcCustomError::SendTransactionIsNotSigned.into()); + return Err(RpcCustomError::TransactionSignatureVerificationFailure.into()); } let signature = transaction.signatures[0]; let transaction_info = TransactionInfo::new(signature, wire_transaction, last_valid_slot); @@ -2175,33 +2175,24 @@ impl RpcSol for RpcSolImpl { if !config.skip_preflight { if transaction.verify().is_err() { - return Err(RpcCustomError::SendTransactionPreflightFailure { - message: "Transaction signature verification failed".into(), - } - .into()); + return Err(RpcCustomError::TransactionSignatureVerificationFailure.into()); } if meta.health.check() != RpcHealthStatus::Ok { - return Err(RpcCustomError::SendTransactionPreflightFailure { - message: "RPC node is unhealthy, unable to simulate transaction".into(), - } - .into()); + return Err(RpcCustomError::RpcNodeUnhealthy.into()); } let preflight_commitment = config .preflight_commitment .map(|commitment| CommitmentConfig { commitment }); let preflight_bank = &*meta.bank(preflight_commitment); - if let (Err(err), _log_output) = - preflight_bank.simulate_transaction(transaction.clone()) - { - // 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 that has yet to reach max confirmations. In these cases the user - // should use the config.skip_preflight flag, and potentially in the future - // additional controls over what bank is used for preflight should be exposed. + if let (Err(err), logs) = preflight_bank.simulate_transaction(transaction.clone()) { return Err(RpcCustomError::SendTransactionPreflightFailure { message: format!("Transaction simulation failed: {}", err), + result: RpcSimulateTransactionResult { + err: Some(err), + logs: Some(logs), + }, } .into()); } @@ -4076,7 +4067,7 @@ pub mod tests { assert_eq!( res, 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(), ) ); @@ -4092,7 +4083,7 @@ pub mod tests { assert_eq!( res, 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 = @@ -4108,7 +4099,7 @@ pub mod tests { assert_eq!( res, 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); @@ -4124,7 +4115,7 @@ pub mod tests { assert_eq!( res, 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(), ) ); @@ -4153,7 +4144,7 @@ pub mod tests { assert_eq!( res, 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(), ) ); } diff --git a/core/src/rpc_error.rs b/core/src/rpc_error.rs index f9c0f15c81..6f31295309 100644 --- a/core/src/rpc_error.rs +++ b/core/src/rpc_error.rs @@ -1,10 +1,12 @@ use jsonrpc_core::{Error, ErrorCode}; +use solana_client::rpc_response::RpcSimulateTransactionResult; use solana_sdk::clock::Slot; const JSON_RPC_SERVER_ERROR_1: i64 = -32001; const JSON_RPC_SERVER_ERROR_2: i64 = -32002; const JSON_RPC_SERVER_ERROR_3: i64 = -32003; const JSON_RPC_SERVER_ERROR_4: i64 = -32004; +const JSON_RPC_SERVER_ERROR_5: i64 = -32005; pub enum RpcCustomError { BlockCleanedUp { @@ -13,11 +15,13 @@ pub enum RpcCustomError { }, SendTransactionPreflightFailure { message: String, + result: RpcSimulateTransactionResult, }, - SendTransactionIsNotSigned, + TransactionSignatureVerificationFailure, BlockNotAvailable { slot: Slot, }, + RpcNodeUnhealthy, } impl From for Error { @@ -34,14 +38,14 @@ impl From for Error { ), data: None, }, - RpcCustomError::SendTransactionPreflightFailure { message } => Self { + RpcCustomError::SendTransactionPreflightFailure { message, result } => Self { code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_2), message, - data: None, + data: Some(serde_json::json!(result)), }, - RpcCustomError::SendTransactionIsNotSigned => Self { + RpcCustomError::TransactionSignatureVerificationFailure => Self { code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_3), - message: "Transaction is not signed".to_string(), + message: "Transaction signature verification failure".to_string(), data: None, }, RpcCustomError::BlockNotAvailable { slot } => Self { @@ -49,6 +53,11 @@ impl From for Error { message: format!("Block not available for slot {}", slot,), data: None, }, + RpcCustomError::RpcNodeUnhealthy => Self { + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_5), + message: "RPC node is unhealthy".to_string(), + data: None, + }, } } }