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:
parent
de8e9e6850
commit
c207274e11
|
@ -424,6 +424,7 @@ pub struct RpcSimulateTransactionResult {
|
||||||
pub units_consumed: Option<u64>,
|
pub units_consumed: Option<u64>,
|
||||||
pub return_data: Option<UiTransactionReturnData>,
|
pub return_data: Option<UiTransactionReturnData>,
|
||||||
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
|
pub inner_instructions: Option<Vec<UiInnerInstructions>>,
|
||||||
|
pub replacement_blockhash: Option<RpcBlockhash>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
|
|
|
@ -351,6 +351,7 @@ impl RpcSender for MockSender {
|
||||||
units_consumed: None,
|
units_consumed: None,
|
||||||
return_data: None,
|
return_data: None,
|
||||||
inner_instructions: None,
|
inner_instructions: None,
|
||||||
|
replacement_blockhash: None
|
||||||
},
|
},
|
||||||
})?,
|
})?,
|
||||||
"getMinimumBalanceForRentExemption" => json![20],
|
"getMinimumBalanceForRentExemption" => json![20],
|
||||||
|
|
|
@ -14,7 +14,7 @@ use {
|
||||||
solana_rpc_client::rpc_client::RpcClient,
|
solana_rpc_client::rpc_client::RpcClient,
|
||||||
solana_rpc_client_api::{
|
solana_rpc_client_api::{
|
||||||
client_error::{ErrorKind as ClientErrorKind, Result as ClientResult},
|
client_error::{ErrorKind as ClientErrorKind, Result as ClientResult},
|
||||||
config::{RpcAccountInfoConfig, RpcSignatureSubscribeConfig},
|
config::{RpcAccountInfoConfig, RpcSignatureSubscribeConfig, RpcSimulateTransactionConfig},
|
||||||
request::RpcError,
|
request::RpcError,
|
||||||
response::{Response as RpcResponse, RpcSignatureResult, SlotUpdate},
|
response::{Response as RpcResponse, RpcSignatureResult, SlotUpdate},
|
||||||
},
|
},
|
||||||
|
@ -139,6 +139,51 @@ fn test_rpc_send_tx() {
|
||||||
info!("{:?}", json["result"]["value"]);
|
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]
|
#[test]
|
||||||
fn test_rpc_invalid_requests() {
|
fn test_rpc_invalid_requests() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
|
@ -3723,6 +3723,7 @@ pub mod rpc_full {
|
||||||
units_consumed: Some(units_consumed),
|
units_consumed: Some(units_consumed),
|
||||||
return_data: return_data.map(|return_data| return_data.into()),
|
return_data: return_data.map(|return_data| return_data.into()),
|
||||||
inner_instructions: None,
|
inner_instructions: None,
|
||||||
|
replacement_blockhash: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
.into());
|
.into());
|
||||||
|
@ -3768,15 +3769,24 @@ pub mod rpc_full {
|
||||||
commitment,
|
commitment,
|
||||||
min_context_slot,
|
min_context_slot,
|
||||||
})?;
|
})?;
|
||||||
|
let mut blockhash: Option<RpcBlockhash> = None;
|
||||||
if replace_recent_blockhash {
|
if replace_recent_blockhash {
|
||||||
if sig_verify {
|
if sig_verify {
|
||||||
return Err(Error::invalid_params(
|
return Err(Error::invalid_params(
|
||||||
"sigVerify may not be used with replaceRecentBlockhash",
|
"sigVerify may not be used with replaceRecentBlockhash",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let recent_blockhash = bank.last_blockhash();
|
||||||
unsanitized_tx
|
unsanitized_tx
|
||||||
.message
|
.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)?;
|
let transaction = sanitize_transaction(unsanitized_tx, bank)?;
|
||||||
|
@ -3857,6 +3867,7 @@ pub mod rpc_full {
|
||||||
units_consumed: Some(units_consumed),
|
units_consumed: Some(units_consumed),
|
||||||
return_data: return_data.map(|return_data| return_data.into()),
|
return_data: return_data.map(|return_data| return_data.into()),
|
||||||
inner_instructions,
|
inner_instructions,
|
||||||
|
replacement_blockhash: blockhash,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -6009,6 +6020,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":150,
|
"unitsConsumed":150,
|
||||||
}
|
}
|
||||||
|
@ -6094,6 +6106,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":150,
|
"unitsConsumed":150,
|
||||||
}
|
}
|
||||||
|
@ -6123,7 +6136,8 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
"returnData":null,
|
"replacementBlockhash": null,
|
||||||
|
"returnData": null,
|
||||||
"unitsConsumed":150,
|
"unitsConsumed":150,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6173,7 +6187,8 @@ pub mod tests {
|
||||||
"accounts":null,
|
"accounts":null,
|
||||||
"innerInstructions":null,
|
"innerInstructions":null,
|
||||||
"logs":[],
|
"logs":[],
|
||||||
"returnData":null,
|
"replacementBlockhash": null,
|
||||||
|
"returnData": null,
|
||||||
"unitsConsumed":0,
|
"unitsConsumed":0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6191,6 +6206,11 @@ pub mod tests {
|
||||||
r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{tx_invalid_recent_blockhash}", {{"replaceRecentBlockhash": true}}]}}"#,
|
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 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!({
|
let expected = json!({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"result": {
|
"result": {
|
||||||
|
@ -6203,6 +6223,10 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": {
|
||||||
|
"blockhash": latest_blockhash.to_string(),
|
||||||
|
"lastValidBlockHeight": expiry_slot
|
||||||
|
},
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":150,
|
"unitsConsumed":150,
|
||||||
}
|
}
|
||||||
|
@ -6347,6 +6371,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 invoke [1]",
|
"Program 11111111111111111111111111111111 invoke [1]",
|
||||||
"Program 11111111111111111111111111111111 success"
|
"Program 11111111111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData": null,
|
"returnData": null,
|
||||||
"unitsConsumed": 150,
|
"unitsConsumed": 150,
|
||||||
}
|
}
|
||||||
|
@ -6427,6 +6452,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 success",
|
"Program 11111111111111111111111111111111 success",
|
||||||
"Program AddressLookupTab1e1111111111111111111111111 success"
|
"Program AddressLookupTab1e1111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":1200,
|
"unitsConsumed":1200,
|
||||||
}
|
}
|
||||||
|
@ -6470,6 +6496,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 success",
|
"Program 11111111111111111111111111111111 success",
|
||||||
"Program AddressLookupTab1e1111111111111111111111111 success"
|
"Program AddressLookupTab1e1111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":1200,
|
"unitsConsumed":1200,
|
||||||
}
|
}
|
||||||
|
@ -6556,6 +6583,7 @@ pub mod tests {
|
||||||
"Program 11111111111111111111111111111111 success",
|
"Program 11111111111111111111111111111111 success",
|
||||||
"Program AddressLookupTab1e1111111111111111111111111 success"
|
"Program AddressLookupTab1e1111111111111111111111111 success"
|
||||||
],
|
],
|
||||||
|
"replacementBlockhash": null,
|
||||||
"returnData":null,
|
"returnData":null,
|
||||||
"unitsConsumed":1200,
|
"unitsConsumed":1200,
|
||||||
}
|
}
|
||||||
|
@ -6931,7 +6959,7 @@ pub mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
Some(
|
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(),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue