* Add a flag to simulateTransaction to use most recent blockhash (cherry picked from commit96cef5260c
) * rename flag (cherry picked from commite14f3eb529
) * sigVerify conflicts with replace, add tests (cherry picked from commit660d37aadf
) Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
@ -28,6 +28,8 @@ pub struct RpcSendTransactionConfig {
|
|||||||
pub struct RpcSimulateTransactionConfig {
|
pub struct RpcSimulateTransactionConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub sig_verify: bool,
|
pub sig_verify: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub replace_recent_blockhash: bool,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub commitment: Option<CommitmentConfig>,
|
pub commitment: Option<CommitmentConfig>,
|
||||||
pub encoding: Option<UiTransactionEncoding>,
|
pub encoding: Option<UiTransactionEncoding>,
|
||||||
|
@ -3141,15 +3141,24 @@ pub mod rpc_full {
|
|||||||
debug!("simulate_transaction rpc request received");
|
debug!("simulate_transaction rpc request received");
|
||||||
let config = config.unwrap_or_default();
|
let config = config.unwrap_or_default();
|
||||||
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58);
|
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.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) {
|
if let Err(e) = verify_transaction(&transaction) {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bank = &*meta.bank(config.commitment);
|
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);
|
let (result, logs) = bank.simulate_transaction(transaction);
|
||||||
|
|
||||||
Ok(new_response(
|
Ok(new_response(
|
||||||
@ -4854,6 +4863,8 @@ pub mod tests {
|
|||||||
let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
|
let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
|
||||||
tx.signatures[0] = Signature::default();
|
tx.signatures[0] = Signature::default();
|
||||||
let tx_badsig_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
|
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
|
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":["{}"]}}"#,
|
r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}"]}}"#,
|
||||||
tx_serialized_encoded,
|
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 res = io.handle_request_sync(&req, meta);
|
||||||
let expected = json!({
|
let expected = json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
@ -4941,6 +5021,7 @@ pub mod tests {
|
|||||||
},
|
},
|
||||||
"id": 1,
|
"id": 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
let expected: Response =
|
let expected: Response =
|
||||||
serde_json::from_value(expected).expect("expected response deserialization");
|
serde_json::from_value(expected).expect("expected response deserialization");
|
||||||
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
let result: Response = serde_json::from_str(&res.expect("actual response"))
|
||||||
|
@ -3231,9 +3231,11 @@ Simulate sending a transaction
|
|||||||
|
|
||||||
- `<string>` - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed.
|
- `<string>` - Transaction, as an encoded string. The transaction must have a valid blockhash, but is not required to be signed.
|
||||||
- `<object>` - (optional) Configuration object containing the following field:
|
- `<object>` - (optional) Configuration object containing the following field:
|
||||||
- `sigVerify: <bool>` - if true the transaction signatures will be verified (default: false)
|
- `sigVerify: <bool>` - if true the transaction signatures will be verified (default: false, conflicts with `replaceRecentBlockhash`)
|
||||||
- `commitment: <string>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) level to simulate the transaction at (default: `"finalized"`).
|
- `commitment: <string>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) level to simulate the transaction at (default: `"finalized"`).
|
||||||
- `encoding: <string>` - (optional) Encoding used for the transaction data. Either `"base58"` (*slow*, **DEPRECATED**), or `"base64"`. (default: `"base58"`).
|
- `encoding: <string>` - (optional) Encoding used for the transaction data. Either `"base58"` (*slow*, **DEPRECATED**), or `"base64"`. (default: `"base58"`).
|
||||||
|
- `replaceRecentBlockhash: <bool>` - (optional) if true the transaction recent blockhash will be replaced with the most recent blockhash.
|
||||||
|
(default: false, conflicts with `sigVerify`)
|
||||||
|
|
||||||
#### Results:
|
#### Results:
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user