simulateTransaction can now return accounts modified by the simulation

This commit is contained in:
Michael Vines
2021-05-25 16:44:18 -07:00
parent 54f0fc9f0f
commit cbce440af4
11 changed files with 184 additions and 49 deletions

View File

@ -341,7 +341,7 @@ impl JsonRpcRequestProcessor {
for pubkey in pubkeys {
let response_account =
get_encoded_account(&bank, &pubkey, encoding.clone(), config.data_slice)?;
get_encoded_account(&bank, &pubkey, encoding, config.data_slice)?;
accounts.push(response_account)
}
Ok(new_response(&bank, accounts))
@ -387,8 +387,8 @@ impl JsonRpcRequestProcessor {
pubkey: pubkey.to_string(),
account: UiAccount::encode(
&pubkey,
account,
encoding.clone(),
&account,
encoding,
None,
data_slice_config,
),
@ -1647,8 +1647,8 @@ impl JsonRpcRequestProcessor {
pubkey: pubkey.to_string(),
account: UiAccount::encode(
&pubkey,
account,
encoding.clone(),
&account,
encoding,
None,
data_slice_config,
),
@ -1706,8 +1706,8 @@ impl JsonRpcRequestProcessor {
pubkey: pubkey.to_string(),
account: UiAccount::encode(
&pubkey,
account,
encoding.clone(),
&account,
encoding,
None,
data_slice_config,
),
@ -1957,7 +1957,7 @@ fn get_encoded_account(
});
} else {
response = Some(UiAccount::encode(
pubkey, account, encoding, None, data_slice,
pubkey, &account, encoding, None, data_slice,
));
}
}
@ -2970,12 +2970,13 @@ pub mod rpc_full {
}
}
if let (Err(err), logs) = preflight_bank.simulate_transaction(transaction.clone()) {
if let (Err(err), logs, _) = preflight_bank.simulate_transaction(&transaction) {
return Err(RpcCustomError::SendTransactionPreflightFailure {
message: format!("Transaction simulation failed: {}", err),
result: RpcSimulateTransactionResult {
err: Some(err),
logs: Some(logs),
accounts: None,
},
}
.into());
@ -3013,18 +3014,59 @@ pub mod rpc_full {
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);
let (result, logs, post_simulation_accounts) = bank.simulate_transaction(&transaction);
let accounts = if let Some(config_accounts) = config.accounts {
let accounts_encoding = config_accounts
.encoding
.unwrap_or(UiAccountEncoding::Base64);
if accounts_encoding == UiAccountEncoding::Binary
|| accounts_encoding == UiAccountEncoding::Base58
{
return Err(Error::invalid_params("base58 encoding not supported"));
}
if config_accounts.addresses.len() > post_simulation_accounts.len() {
return Err(Error::invalid_params(format!(
"Too many accounts provided; max {}",
post_simulation_accounts.len()
)));
}
let mut accounts = vec![];
for address_str in config_accounts.addresses {
let address = verify_pubkey(&address_str)?;
accounts.push(if result.is_err() {
None
} else {
transaction
.message
.account_keys
.iter()
.position(|pubkey| *pubkey == address)
.map(|i| post_simulation_accounts.get(i))
.flatten()
.map(|account| {
UiAccount::encode(&address, account, accounts_encoding, None, None)
})
});
}
Some(accounts)
} else {
None
};
Ok(new_response(
&bank,
RpcSimulateTransactionResult {
err: result.err(),
logs: Some(logs),
accounts,
},
))
}
@ -4928,7 +4970,6 @@ pub mod tests {
#[test]
fn test_rpc_simulate_transaction() {
let bob_pubkey = solana_sdk::pubkey::new_rand();
let RpcHandler {
io,
meta,
@ -4936,8 +4977,9 @@ pub mod tests {
alice,
bank,
..
} = start_rpc_handler_with_tx(&bob_pubkey);
} = start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand());
let bob_pubkey = solana_sdk::pubkey::new_rand();
let mut tx = system_transaction::transfer(&alice, &bob_pubkey, 1234, blockhash);
let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string();
tx.signatures[0] = Signature::default();
@ -4949,18 +4991,46 @@ pub mod tests {
// Good signature with sigVerify=true
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {{"sigVerify": true}}]}}"#,
r#"{{"jsonrpc":"2.0",
"id":1,
"method":"simulateTransaction",
"params":[
"{}",
{{
"sigVerify": true,
"accounts": {{
"encoding": "jsonParsed",
"addresses": ["{}", "{}"]
}}
}}
]
}}"#,
tx_serialized_encoded,
solana_sdk::pubkey::new_rand(),
bob_pubkey,
);
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"
]}
"value":{
"accounts": [
null,
{
"data": ["", "base64"],
"executable": false,
"owner": "11111111111111111111111111111111",
"lamports": 1234,
"rentEpoch": 0
}
],
"err":null,
"logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
]
}
},
"id": 1,
});
@ -4970,6 +5040,43 @@ pub mod tests {
.expect("actual response deserialization");
assert_eq!(expected, result);
// Too many input accounts...
let req = format!(
r#"{{"jsonrpc":"2.0",
"id":1,
"method":"simulateTransaction",
"params":[
"{}",
{{
"sigVerify": true,
"accounts": {{
"addresses": [
"11111111111111111111111111111111",
"11111111111111111111111111111111",
"11111111111111111111111111111111",
"11111111111111111111111111111111"
]
}}
}}
]
}}"#,
tx_serialized_encoded,
);
let res = io.handle_request_sync(&req, meta.clone());
let expected = json!({
"jsonrpc":"2.0",
"error": {
"code": error::ErrorCode::InvalidParams.code(),
"message": "Too many accounts provided; max 3"
},
"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 signature with sigVerify=true
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {{"sigVerify": true}}]}}"#,
@ -5001,7 +5108,7 @@ pub mod tests {
"jsonrpc": "2.0",
"result": {
"context":{"slot":0},
"value":{"err":null, "logs":[
"value":{"accounts": null, "err":null, "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
]}
@ -5024,7 +5131,7 @@ pub mod tests {
"jsonrpc": "2.0",
"result": {
"context":{"slot":0},
"value":{"err":null, "logs":[
"value":{"accounts": null, "err":null, "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
]}
@ -5072,7 +5179,7 @@ pub mod tests {
"jsonrpc":"2.0",
"result": {
"context":{"slot":0},
"value":{"err": "BlockhashNotFound", "logs":[]}
"value":{"err": "BlockhashNotFound", "accounts": null, "logs":[]}
},
"id":1
});
@ -5093,7 +5200,7 @@ pub mod tests {
"jsonrpc": "2.0",
"result": {
"context":{"slot":0},
"value":{"err":null, "logs":[
"value":{"accounts": null, "err":null, "logs":[
"Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success"
]}
@ -5437,7 +5544,7 @@ pub mod tests {
assert_eq!(
res,
Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"err":"BlockhashNotFound","logs":[]}},"id":1}"#.to_string(),
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[]}},"id":1}"#.to_string(),
)
);