feat: include replaced blockhash in RPC simulation response (#380)

* feat: include replaced blockhash in RPC simulation response

* rename blockhash field to `replacement_blockhash`

* add tests to ensure replacement_blockhash is returning correctly

* fixed tests

* fixed tests again for real this time?
This commit is contained in:
Sammy Harris 2024-04-06 04:27:10 -05:00 committed by GitHub
parent de8e9e6850
commit c207274e11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 80 additions and 5 deletions

View File

@ -424,6 +424,7 @@ pub struct RpcSimulateTransactionResult {
pub units_consumed: Option<u64>,
pub return_data: Option<UiTransactionReturnData>,
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
pub replacement_blockhash: Option<RpcBlockhash>,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]

View File

@ -351,6 +351,7 @@ impl RpcSender for MockSender {
units_consumed: None,
return_data: None,
inner_instructions: None,
replacement_blockhash: None
},
})?,
"getMinimumBalanceForRentExemption" => json![20],

View File

@ -14,7 +14,7 @@ use {
solana_rpc_client::rpc_client::RpcClient,
solana_rpc_client_api::{
client_error::{ErrorKind as ClientErrorKind, Result as ClientResult},
config::{RpcAccountInfoConfig, RpcSignatureSubscribeConfig},
config::{RpcAccountInfoConfig, RpcSignatureSubscribeConfig, RpcSimulateTransactionConfig},
request::RpcError,
response::{Response as RpcResponse, RpcSignatureResult, SlotUpdate},
},
@ -139,6 +139,51 @@ fn test_rpc_send_tx() {
info!("{:?}", json["result"]["value"]);
}
#[test]
fn test_simulation_replaced_blockhash() -> ClientResult<()> {
solana_logger::setup();
let alice = Keypair::new();
let validator = TestValidator::with_no_fees(alice.pubkey(), None, SocketAddrSpace::Unspecified);
let rpc_client = RpcClient::new(validator.rpc_url());
let bob = Keypair::new();
let lamports = 50;
let res = rpc_client.simulate_transaction_with_config(
&system_transaction::transfer(&alice, &bob.pubkey(), lamports, Hash::default()),
RpcSimulateTransactionConfig {
replace_recent_blockhash: true,
..Default::default()
},
)?;
assert!(
res.value.replacement_blockhash.is_some(),
"replaced_blockhash response is None"
);
let blockhash = res.value.replacement_blockhash.unwrap();
// ensure nothing weird is going on
assert_ne!(
blockhash.blockhash,
Hash::default().to_string(),
"replaced_blockhash is default"
);
let res = rpc_client.simulate_transaction_with_config(
&system_transaction::transfer(&alice, &bob.pubkey(), lamports, Hash::default()),
RpcSimulateTransactionConfig {
replace_recent_blockhash: false,
..Default::default()
},
)?;
assert!(
res.value.replacement_blockhash.is_none(),
"replaced_blockhash is Some when nothing should be replaced"
);
Ok(())
}
#[test]
fn test_rpc_invalid_requests() {
solana_logger::setup();

View File

@ -3723,6 +3723,7 @@ pub mod rpc_full {
units_consumed: Some(units_consumed),
return_data: return_data.map(|return_data| return_data.into()),
inner_instructions: None,
replacement_blockhash: None,
},
}
.into());
@ -3768,15 +3769,24 @@ pub mod rpc_full {
commitment,
min_context_slot,
})?;
let mut blockhash: Option<RpcBlockhash> = None;
if replace_recent_blockhash {
if sig_verify {
return Err(Error::invalid_params(
"sigVerify may not be used with replaceRecentBlockhash",
));
}
let recent_blockhash = bank.last_blockhash();
unsanitized_tx
.message
.set_recent_blockhash(bank.last_blockhash());
.set_recent_blockhash(recent_blockhash);
let last_valid_block_height = bank
.get_blockhash_last_valid_block_height(&recent_blockhash)
.expect("bank blockhash queue should contain blockhash");
blockhash.replace(RpcBlockhash {
blockhash: recent_blockhash.to_string(),
last_valid_block_height,
});
}
let transaction = sanitize_transaction(unsanitized_tx, bank)?;
@ -3857,6 +3867,7 @@ pub mod rpc_full {
units_consumed: Some(units_consumed),
return_data: return_data.map(|return_data| return_data.into()),
inner_instructions,
replacement_blockhash: blockhash,
},
))
}
@ -6009,6 +6020,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData":null,
"unitsConsumed":150,
}
@ -6094,6 +6106,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData":null,
"unitsConsumed":150,
}
@ -6123,7 +6136,8 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"returnData":null,
"replacementBlockhash": null,
"returnData": null,
"unitsConsumed":150,
}
},
@ -6173,7 +6187,8 @@ pub mod tests {
"accounts":null,
"innerInstructions":null,
"logs":[],
"returnData":null,
"replacementBlockhash": null,
"returnData": null,
"unitsConsumed":0,
}
},
@ -6191,6 +6206,11 @@ pub mod tests {
r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{tx_invalid_recent_blockhash}", {{"replaceRecentBlockhash": true}}]}}"#,
);
let res = io.handle_request_sync(&req, meta.clone());
let latest_blockhash = bank.confirmed_last_blockhash();
let expiry_slot = bank
.get_blockhash_last_valid_block_height(&latest_blockhash)
.expect("blockhash exists");
let expected = json!({
"jsonrpc": "2.0",
"result": {
@ -6203,6 +6223,10 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"replacementBlockhash": {
"blockhash": latest_blockhash.to_string(),
"lastValidBlockHeight": expiry_slot
},
"returnData":null,
"unitsConsumed":150,
}
@ -6347,6 +6371,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData": null,
"unitsConsumed": 150,
}
@ -6427,6 +6452,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 success",
"Program AddressLookupTab1e1111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData":null,
"unitsConsumed":1200,
}
@ -6470,6 +6496,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 success",
"Program AddressLookupTab1e1111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData":null,
"unitsConsumed":1200,
}
@ -6556,6 +6583,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 success",
"Program AddressLookupTab1e1111111111111111111111111 success"
],
"replacementBlockhash": null,
"returnData":null,
"unitsConsumed":1200,
}
@ -6931,7 +6959,7 @@ pub mod tests {
assert_eq!(
res,
Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","innerInstructions":null,"logs":[],"returnData":null,"unitsConsumed":0}},"id":1}"#.to_string(),
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","innerInstructions":null,"logs":[],"replacementBlockhash":null,"returnData":null,"unitsConsumed":0}},"id":1}"#.to_string(),
)
);