diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index d111b27a1c..0313605ce5 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -131,7 +131,7 @@ impl RpcClient { &self, transaction: &Transaction, sig_verify: bool, - ) -> RpcResult { + ) -> RpcResult { let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string(); self.send( RpcRequest::SimulateTransaction, diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index abdccdaf06..a8be5ca652 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -228,6 +228,13 @@ pub struct RpcSignatureConfirmation { pub status: Result<()>, } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcSimulateTransactionResult { + pub err: Option, + pub logs: Option>, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct RpcStorageTurn { diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 49c2f81fe6..9f19ae9331 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -752,14 +752,17 @@ fn verify_signature(input: &str) -> Result { } /// Run transactions against a frozen bank without committing the results -fn run_transaction_simulation(bank: &Bank, transaction: Transaction) -> transaction::Result<()> { +fn run_transaction_simulation( + bank: &Bank, + transaction: Transaction, +) -> (transaction::Result<()>, Vec) { assert!(bank.is_frozen(), "simulation bank must be frozen"); let txs = &[transaction]; let batch = bank.prepare_simulation_batch(txs); let (_loaded_accounts, executed, _retryable_transactions, _transaction_count, _signature_count) = bank.load_and_execute_transactions(&batch, solana_sdk::clock::MAX_PROCESSING_AGE); - executed[0].0.clone().map(|_| ()) + (executed[0].0.clone().map(|_| ()), vec![]) } #[derive(Clone)] @@ -966,7 +969,7 @@ pub trait RpcSol { meta: Self::Metadata, data: String, config: Option, - ) -> RpcResponse; + ) -> RpcResponse; #[rpc(meta, name = "getSlotLeader")] fn get_slot_leader( @@ -1511,7 +1514,7 @@ impl RpcSol for RpcSolImpl { } let bank = &*meta.request_processor.read().unwrap().bank(None)?; - if let Err(err) = run_transaction_simulation(&bank, transaction) { + if let (Err(err), _log_output) = run_transaction_simulation(&bank, transaction) { // 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 @@ -1546,7 +1549,7 @@ impl RpcSol for RpcSolImpl { meta: Self::Metadata, data: String, config: Option, - ) -> RpcResponse { + ) -> RpcResponse { let (_, transaction) = deserialize_bs58_transaction(data)?; let config = config.unwrap_or_default(); @@ -1557,18 +1560,19 @@ impl RpcSol for RpcSolImpl { }; let bank = &*meta.request_processor.read().unwrap().bank(None)?; - - if result.is_ok() { - result = run_transaction_simulation(&bank, transaction); - } + let logs = if result.is_ok() { + let sim_result = run_transaction_simulation(&bank, transaction); + result = sim_result.0; + Some(sim_result.1) + } else { + None + }; new_response( &bank, - TransactionStatus { - slot: bank.slot(), - confirmations: Some(0), - status: result.clone(), + RpcSimulateTransactionResult { err: result.err(), + logs, }, ) } @@ -2504,7 +2508,7 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null} + "value":{"err":null, "logs":[]} }, "id": 1, }); @@ -2524,7 +2528,7 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"confirmations":0,"slot":0,"status":{"Err":"SignatureFailure"},"err":"SignatureFailure"} + "value":{"err":"SignatureFailure", "logs":null} }, "id": 1, }); @@ -2544,7 +2548,7 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null} + "value":{"err":null, "logs":[]} }, "id": 1, }); @@ -2564,7 +2568,7 @@ pub mod tests { "jsonrpc": "2.0", "result": { "context":{"slot":0}, - "value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null} + "value":{"err":null, "logs":[]} }, "id": 1, }); diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 765de97de5..7411bf45f5 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -1198,6 +1198,10 @@ Simulate sending a transaction #### Results: An RpcResponse containing a TransactionStatus object +The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields: + +* `err: ` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14) +* `logs: ` - Array of log messages the transaction instructions output during execution, null if simulation failed before the transaction was able to execute (for example due to an invalid blockhash or signature verification failure) #### Example: