From 9caf9e8f17a1767091b73e862a60bc8d3ee593a0 Mon Sep 17 00:00:00 2001 From: Joe C Date: Mon, 22 Jan 2024 19:27:27 -0500 Subject: [PATCH] rpc: add tests for simulate transaction inner instructions (#34495) * rpc: add tests for simulate transaction inner instructions * update encoding * take out extra test case --- rpc/src/rpc.rs | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 8bdee0df6..5cc5b8234 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -6294,6 +6294,215 @@ pub mod tests { assert_eq!(result, expected); } + #[test] + fn test_rpc_simulate_transaction_with_inner_instructions() { + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { + ref meta, ref io, .. + } = rpc; + + let recent_slot = 123; + let mut slot_hashes = SlotHashes::default(); + slot_hashes.add(recent_slot, Hash::new_unique()); + bank.set_sysvar_for_tests(&slot_hashes); + + let lookup_table_authority = Keypair::new(); + let lookup_table_space = solana_sdk::address_lookup_table::state::LOOKUP_TABLE_META_SIZE; + let lookup_table_lamports = bank.get_minimum_balance_for_rent_exemption(lookup_table_space); + + let (instruction, lookup_table_address) = + solana_sdk::address_lookup_table::instruction::create_lookup_table( + lookup_table_authority.pubkey(), + rpc.mint_keypair.pubkey(), + recent_slot, + ); + let tx = Transaction::new_signed_with_payer( + &[instruction], + Some(&rpc.mint_keypair.pubkey()), + &[&rpc.mint_keypair], + recent_blockhash, + ); + let tx_serialized_encoded = + base64::prelude::BASE64_STANDARD.encode(serialize(&tx).unwrap()); + + // Simulation bank must be frozen + bank.freeze(); + + // `innerInstructions` not provided, should not be in response + let req = format!( + r#"{{"jsonrpc":"2.0", + "id":1, + "method":"simulateTransaction", + "params":[ + "{}", + {{ "encoding": "base64" }} + ] + }}"#, + tx_serialized_encoded, + ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, + "value":{ + "accounts": null, + "err":null, + "innerInstructions": null, + "logs":[ + "Program AddressLookupTab1e1111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program AddressLookupTab1e1111111111111111111111111 success" + ], + "returnData":null, + "unitsConsumed":1200, + } + }, + "id": 1, + }); + let expected: Response = + serde_json::from_value(expected).expect("expected response deserialization"); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(result, expected); + + // `innerInstructions` provided as `false`, should not be in response + let req = format!( + r#"{{"jsonrpc":"2.0", + "id":1, + "method":"simulateTransaction", + "params":[ + "{}", + {{ "innerInstructions": false, "encoding": "base64" }} + ] + }}"#, + tx_serialized_encoded, + ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, + "value":{ + "accounts": null, + "err":null, + "innerInstructions": null, + "logs":[ + "Program AddressLookupTab1e1111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program AddressLookupTab1e1111111111111111111111111 success" + ], + "returnData":null, + "unitsConsumed":1200, + } + }, + "id": 1, + }); + let expected: Response = + serde_json::from_value(expected).expect("expected response deserialization"); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(result, expected); + + // `innerInstructions` provided as `true`, should have parsed inner instructions + let req = format!( + r#"{{"jsonrpc":"2.0", + "id":1, + "method":"simulateTransaction", + "params":[ + "{}", + {{ "innerInstructions": true, "encoding": "base64" }} + ] + }}"#, + tx_serialized_encoded, + ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, + "value":{ + "accounts": null, + "err":null, + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "parsed": { + "info": { + "destination": lookup_table_address.to_string(), + "lamports": lookup_table_lamports, + "source": rpc.mint_keypair.pubkey().to_string() + }, + "type": "transfer" + }, + "program": "system", + "programId": "11111111111111111111111111111111", + "stackHeight": 2 + }, + { + "parsed": { + "info": { + "account": lookup_table_address.to_string(), + "space": lookup_table_space + }, + "type": "allocate" + }, + "program": "system", + "programId": "11111111111111111111111111111111", + "stackHeight": 2 + }, + { + "parsed": { + "info": { + "account": lookup_table_address.to_string(), + "owner": "AddressLookupTab1e1111111111111111111111111" + }, + "type": "assign" + }, + "program": "system", + "programId": "11111111111111111111111111111111", + "stackHeight": 2 + } + ] + } + ], + "logs":[ + "Program AddressLookupTab1e1111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [2]", + "Program 11111111111111111111111111111111 success", + "Program AddressLookupTab1e1111111111111111111111111 success" + ], + "returnData":null, + "unitsConsumed":1200, + } + }, + "id": 1, + }); + let expected: Response = + serde_json::from_value(expected).expect("expected response deserialization"); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(result, expected); + } + #[test] #[should_panic(expected = "simulation bank must be frozen")] fn test_rpc_simulate_transaction_panic_on_unfrozen_bank() {