From b06bfeec8d4bba6b2bb0e19d82cfe67db0164ff6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 01:49:52 +0000 Subject: [PATCH] Add a flag to simulateTransaction to use most recent blockhash (backport #17485) (#17497) * Add a flag to simulateTransaction to use most recent blockhash (cherry picked from commit 96cef5260c4ed0d1b1fec9ba648f8eeea24e619e) * rename flag (cherry picked from commit e14f3eb529d7155df5e6f0a6aa4493c013242fec) * sigVerify conflicts with replace, add tests (cherry picked from commit 660d37aadfee27a83a5986ecef98c63904b0274e) Co-authored-by: Justin Starry --- client/src/rpc_config.rs | 2 + core/src/rpc.rs | 83 +++++++++++++++++++++- docs/src/developing/clients/jsonrpc-api.md | 4 +- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/client/src/rpc_config.rs b/client/src/rpc_config.rs index bf97d1de55..1061f30e5e 100644 --- a/client/src/rpc_config.rs +++ b/client/src/rpc_config.rs @@ -28,6 +28,8 @@ pub struct RpcSendTransactionConfig { pub struct RpcSimulateTransactionConfig { #[serde(default)] pub sig_verify: bool, + #[serde(default)] + pub replace_recent_blockhash: bool, #[serde(flatten)] pub commitment: Option, pub encoding: Option, diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a81f162023..7d90bbba16 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -3141,15 +3141,24 @@ pub mod rpc_full { debug!("simulate_transaction rpc request received"); let config = config.unwrap_or_default(); let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58); - let (_, transaction) = deserialize_transaction(data, encoding)?; + let (_, mut transaction) = deserialize_transaction(data, encoding)?; if config.sig_verify { + if config.replace_recent_blockhash { + return Err(Error::invalid_params( + "sigVerify may not be used with replaceRecentBlockhash", + )); + } + if let Err(e) = verify_transaction(&transaction) { return Err(e); } } let bank = &*meta.bank(config.commitment); + if config.replace_recent_blockhash { + transaction.message.recent_blockhash = bank.last_blockhash(); + } let (result, logs) = bank.simulate_transaction(transaction); Ok(new_response( @@ -4854,6 +4863,8 @@ pub mod tests { let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string(); tx.signatures[0] = Signature::default(); let tx_badsig_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string(); + tx.message.recent_blockhash = Hash::default(); + let tx_invalid_recent_blockhash = bs58::encode(serialize(&tx).unwrap()).into_string(); bank.freeze(); // Ensure the root bank is frozen, `start_rpc_handler_with_tx()` doesn't do this @@ -4929,6 +4940,75 @@ pub mod tests { r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}"]}}"#, tx_serialized_encoded, ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "context":{"slot":0}, + "value":{"err":null, "logs":[ + "Program 11111111111111111111111111111111 invoke [1]", + "Program 11111111111111111111111111111111 success" + ]} + }, + "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!(expected, result); + + // Enabled both sigVerify=true and replaceRecentBlockhash=true + let req = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {}]}}"#, + tx_serialized_encoded, + json!({ + "sigVerify": true, + "replaceRecentBlockhash": true, + }) + .to_string() + ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc":"2.0", + "error": { + "code": ErrorCode::InvalidParams, + "message": "sigVerify may not be used with replaceRecentBlockhash" + }, + "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!(expected, result); + + // Bad recent blockhash with replaceRecentBlockhash=false + let req = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {{"replaceRecentBlockhash": false}}]}}"#, + tx_invalid_recent_blockhash, + ); + let res = io.handle_request_sync(&req, meta.clone()); + let expected = json!({ + "jsonrpc":"2.0", + "result": { + "context":{"slot":0}, + "value":{"err": "BlockhashNotFound", "logs":[]} + }, + "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!(expected, result); + + // Bad recent blockhash with replaceRecentBlockhash=true + let req = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {{"replaceRecentBlockhash": true}}]}}"#, + tx_invalid_recent_blockhash, + ); let res = io.handle_request_sync(&req, meta); let expected = json!({ "jsonrpc": "2.0", @@ -4941,6 +5021,7 @@ pub mod tests { }, "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")) diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 65556b5bf0..40d1e971d3 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -3231,9 +3231,11 @@ Simulate sending a transaction - `` - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed. - `` - (optional) Configuration object containing the following field: - - `sigVerify: ` - if true the transaction signatures will be verified (default: false) + - `sigVerify: ` - if true the transaction signatures will be verified (default: false, conflicts with `replaceRecentBlockhash`) - `commitment: ` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) level to simulate the transaction at (default: `"finalized"`). - `encoding: ` - (optional) Encoding used for the transaction data. Either `"base58"` (*slow*, **DEPRECATED**), or `"base64"`. (default: `"base58"`). + - `replaceRecentBlockhash: ` - (optional) if true the transaction recent blockhash will be replaced with the most recent blockhash. + (default: false, conflicts with `sigVerify`) #### Results: