RPC sendTransaction now returns transaction logs on simulation failure
This commit is contained in:
@ -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(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user