Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit 2da0dfb

Browse files
committed
RPC simulateTransaction endpoint now returns program log output
1 parent 718244f commit 2da0dfb

File tree

13 files changed

+295
-120
lines changed

13 files changed

+295
-120
lines changed

client/src/rpc_client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl RpcClient {
129129
&self,
130130
transaction: &Transaction,
131131
sig_verify: bool,
132-
) -> RpcResult<TransactionStatus> {
132+
) -> RpcResult<RpcSimulateTransactionResult> {
133133
let serialized_encoded = bs58::encode(serialize(transaction).unwrap()).into_string();
134134
self.send(
135135
RpcRequest::SimulateTransaction,

client/src/rpc_response.rs

+7
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ pub struct RpcSignatureConfirmation {
211211
pub status: Result<()>,
212212
}
213213

214+
#[derive(Serialize, Deserialize, Clone, Debug)]
215+
#[serde(rename_all = "camelCase")]
216+
pub struct RpcSimulateTransactionResult {
217+
pub err: Option<TransactionError>,
218+
pub logs: Option<Vec<String>>,
219+
}
220+
214221
#[derive(Serialize, Deserialize, Clone, Debug)]
215222
#[serde(rename_all = "camelCase")]
216223
pub struct RpcStorageTurn {

core/src/banking_stage.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ impl BankingStage {
514514
vec![]
515515
};
516516
let (mut loaded_accounts, results, mut retryable_txs, tx_count, signature_count) =
517-
bank.load_and_execute_transactions(batch, MAX_PROCESSING_AGE);
517+
bank.load_and_execute_transactions(batch, MAX_PROCESSING_AGE, None);
518518
load_execute_time.stop();
519519

520520
let freeze_lock = bank.freeze_lock();

core/src/rpc.rs

+28-19
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use solana_ledger::{
2525
bank_forks::BankForks, blockstore::Blockstore, blockstore_db::BlockstoreError,
2626
};
2727
use solana_perf::packet::PACKET_DATA_SIZE;
28-
use solana_runtime::{accounts::AccountAddressFilter, bank::Bank};
28+
use solana_runtime::{accounts::AccountAddressFilter, bank::Bank, log_collector::LogCollector};
2929
use solana_sdk::{
3030
clock::{Epoch, Slot, UnixTimestamp},
3131
commitment_config::{CommitmentConfig, CommitmentLevel},
@@ -718,14 +718,22 @@ fn verify_signature(input: &str) -> Result<Signature> {
718718
}
719719

720720
/// Run transactions against a frozen bank without committing the results
721-
fn run_transaction_simulation(bank: &Bank, transaction: Transaction) -> transaction::Result<()> {
721+
fn run_transaction_simulation(
722+
bank: &Bank,
723+
transaction: Transaction,
724+
) -> (transaction::Result<()>, Vec<String>) {
722725
assert!(bank.is_frozen(), "simulation bank must be frozen");
723726

724727
let txs = &[transaction];
725728
let batch = bank.prepare_simulation_batch(txs);
729+
let log_collector = LogCollector::default();
726730
let (_loaded_accounts, executed, _retryable_transactions, _transaction_count, _signature_count) =
727-
bank.load_and_execute_transactions(&batch, solana_sdk::clock::MAX_PROCESSING_AGE);
728-
executed[0].0.clone().map(|_| ())
731+
bank.load_and_execute_transactions(
732+
&batch,
733+
solana_sdk::clock::MAX_PROCESSING_AGE,
734+
Some(&log_collector),
735+
);
736+
(executed[0].0.clone().map(|_| ()), log_collector.output())
729737
}
730738

731739
#[derive(Clone)]
@@ -932,7 +940,7 @@ pub trait RpcSol {
932940
meta: Self::Metadata,
933941
data: String,
934942
config: Option<RpcSimulateTransactionConfig>,
935-
) -> RpcResponse<TransactionStatus>;
943+
) -> RpcResponse<RpcSimulateTransactionResult>;
936944

937945
#[rpc(meta, name = "getSlotLeader")]
938946
fn get_slot_leader(
@@ -1452,7 +1460,7 @@ impl RpcSol for RpcSolImpl {
14521460
}
14531461

14541462
let bank = &*meta.request_processor.read().unwrap().bank(None)?;
1455-
if let Err(err) = run_transaction_simulation(&bank, transaction) {
1463+
if let (Err(err), _log_output) = run_transaction_simulation(&bank, transaction) {
14561464
// Note: it's possible that the transaction simulation failed but the actual
14571465
// transaction would succeed, such as when a transaction depends on an earlier
14581466
// transaction that has yet to reach max confirmations. In these cases the user
@@ -1486,7 +1494,7 @@ impl RpcSol for RpcSolImpl {
14861494
meta: Self::Metadata,
14871495
data: String,
14881496
config: Option<RpcSimulateTransactionConfig>,
1489-
) -> RpcResponse<TransactionStatus> {
1497+
) -> RpcResponse<RpcSimulateTransactionResult> {
14901498
let (_, transaction) = deserialize_bs58_transaction(data)?;
14911499
let config = config.unwrap_or_default();
14921500

@@ -1497,18 +1505,19 @@ impl RpcSol for RpcSolImpl {
14971505
};
14981506

14991507
let bank = &*meta.request_processor.read().unwrap().bank(None)?;
1500-
1501-
if result.is_ok() {
1502-
result = run_transaction_simulation(&bank, transaction);
1503-
}
1508+
let logs = if result.is_ok() {
1509+
let sim_result = run_transaction_simulation(&bank, transaction);
1510+
result = sim_result.0;
1511+
Some(sim_result.1)
1512+
} else {
1513+
None
1514+
};
15041515

15051516
new_response(
15061517
&bank,
1507-
TransactionStatus {
1508-
slot: bank.slot(),
1509-
confirmations: Some(0),
1510-
status: result.clone(),
1518+
RpcSimulateTransactionResult {
15111519
err: result.err(),
1520+
logs,
15121521
},
15131522
)
15141523
}
@@ -2400,7 +2409,7 @@ pub mod tests {
24002409
"jsonrpc": "2.0",
24012410
"result": {
24022411
"context":{"slot":0},
2403-
"value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null}
2412+
"value":{"err":null, "logs":[]}
24042413
},
24052414
"id": 1,
24062415
});
@@ -2420,7 +2429,7 @@ pub mod tests {
24202429
"jsonrpc": "2.0",
24212430
"result": {
24222431
"context":{"slot":0},
2423-
"value":{"confirmations":0,"slot":0,"status":{"Err":"SignatureFailure"},"err":"SignatureFailure"}
2432+
"value":{"err":"SignatureFailure", "logs":null}
24242433
},
24252434
"id": 1,
24262435
});
@@ -2440,7 +2449,7 @@ pub mod tests {
24402449
"jsonrpc": "2.0",
24412450
"result": {
24422451
"context":{"slot":0},
2443-
"value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null}
2452+
"value":{"err":null, "logs":[]}
24442453
},
24452454
"id": 1,
24462455
});
@@ -2460,7 +2469,7 @@ pub mod tests {
24602469
"jsonrpc": "2.0",
24612470
"result": {
24622471
"context":{"slot":0},
2463-
"value":{"confirmations":0,"slot": 0,"status":{"Ok":null},"err":null}
2472+
"value":{"err":null, "logs":[]}
24642473
},
24652474
"id": 1,
24662475
});

docs/src/apps/jsonrpc-api.md

+4
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,10 @@ Simulate sending a transaction
11071107
#### Results:
11081108
11091109
An RpcResponse containing a TransactionStatus object
1110+
The result will be an RpcResponse JSON object with `value` set to a JSON object with the following fields:
1111+
1112+
* `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L14)
1113+
* `logs: <array | null>` - 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)
11101114
11111115
#### Example:
11121116

programs/bpf/benches/bpf_loader.rs

+4
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,8 @@ impl InvokeContext for MockInvokeContext {
155155
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
156156
&[]
157157
}
158+
fn log_enabled(&self) -> bool {
159+
false
160+
}
161+
fn log(&mut self, _message: &str) {}
158162
}

programs/bpf_loader/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ mod tests {
267267
#[derive(Debug, Default)]
268268
pub struct MockInvokeContext {
269269
key: Pubkey,
270+
pub log: Vec<String>,
270271
}
271272
impl InvokeContext for MockInvokeContext {
272273
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
@@ -287,6 +288,13 @@ mod tests {
287288
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
288289
&[]
289290
}
291+
fn log_enabled(&self) -> bool {
292+
true
293+
}
294+
fn log(&mut self, message: &str) {
295+
info!("[MockInvokeContext::log] {}", message);
296+
self.log.push(message.to_string());
297+
}
290298
}
291299

292300
#[test]

0 commit comments

Comments
 (0)