RpcClient: include signature check in send_transaction, bump send retries in get_num_blocks_since_signature_confirmation (#9341)

* Bump rpc send retries

* Add signature check to send_transaction and update mocks to test
This commit is contained in:
Tyera Eulberg 2020-04-06 19:27:37 -06:00 committed by GitHub
parent 4677cdb4c2
commit 6b988155e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 57 deletions

View File

@ -3182,7 +3182,7 @@ mod tests {
let process_id = Pubkey::new_rand(); let process_id = Pubkey::new_rand();
config.command = CliCommand::Cancel(process_id); config.command = CliCommand::Cancel(process_id);
assert_eq!(process_command(&config).unwrap(), SIGNATURE); assert!(process_command(&config).is_ok());
let good_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap()); let good_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap());
config.command = CliCommand::Confirm(good_signature); config.command = CliCommand::Confirm(good_signature);
@ -3199,8 +3199,8 @@ mod tests {
commission: 0, commission: 0,
}; };
config.signers = vec![&keypair, &bob_keypair, &identity_keypair]; config.signers = vec![&keypair, &bob_keypair, &identity_keypair];
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let new_authorized_pubkey = Pubkey::new_rand(); let new_authorized_pubkey = Pubkey::new_rand();
config.signers = vec![&bob_keypair]; config.signers = vec![&bob_keypair];
@ -3209,8 +3209,8 @@ mod tests {
new_authorized_pubkey, new_authorized_pubkey,
vote_authorize: VoteAuthorize::Voter, vote_authorize: VoteAuthorize::Voter,
}; };
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let new_identity_keypair = Keypair::new(); let new_identity_keypair = Keypair::new();
config.signers = vec![&keypair, &bob_keypair, &new_identity_keypair]; config.signers = vec![&keypair, &bob_keypair, &new_identity_keypair];
@ -3218,8 +3218,8 @@ mod tests {
vote_account_pubkey: bob_pubkey, vote_account_pubkey: bob_pubkey,
new_identity_account: 2, new_identity_account: 2,
}; };
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let bob_keypair = Keypair::new(); let bob_keypair = Keypair::new();
let bob_pubkey = bob_keypair.pubkey(); let bob_pubkey = bob_keypair.pubkey();
@ -3243,8 +3243,8 @@ mod tests {
from: 0, from: 0,
}; };
config.signers = vec![&keypair, &bob_keypair]; config.signers = vec![&keypair, &bob_keypair];
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let stake_pubkey = Pubkey::new_rand(); let stake_pubkey = Pubkey::new_rand();
let to_pubkey = Pubkey::new_rand(); let to_pubkey = Pubkey::new_rand();
@ -3260,8 +3260,8 @@ mod tests {
fee_payer: 0, fee_payer: 0,
}; };
config.signers = vec![&keypair]; config.signers = vec![&keypair];
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let stake_pubkey = Pubkey::new_rand(); let stake_pubkey = Pubkey::new_rand();
config.command = CliCommand::DeactivateStake { config.command = CliCommand::DeactivateStake {
@ -3273,8 +3273,8 @@ mod tests {
nonce_authority: 0, nonce_authority: 0,
fee_payer: 0, fee_payer: 0,
}; };
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let stake_pubkey = Pubkey::new_rand(); let stake_pubkey = Pubkey::new_rand();
let split_stake_account = Keypair::new(); let split_stake_account = Keypair::new();
@ -3291,8 +3291,8 @@ mod tests {
fee_payer: 0, fee_payer: 0,
}; };
config.signers = vec![&keypair, &split_stake_account]; config.signers = vec![&keypair, &split_stake_account];
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
config.command = CliCommand::GetSlot { config.command = CliCommand::GetSlot {
commitment_config: CommitmentConfig::default(), commitment_config: CommitmentConfig::default(),
@ -3310,8 +3310,8 @@ mod tests {
to: bob_pubkey, to: bob_pubkey,
..PayCommand::default() ..PayCommand::default()
}); });
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let date_string = "\"2018-09-19T17:30:59Z\""; let date_string = "\"2018-09-19T17:30:59Z\"";
let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap(); let dt: DateTime<Utc> = serde_json::from_str(&date_string).unwrap();
@ -3323,16 +3323,7 @@ mod tests {
..PayCommand::default() ..PayCommand::default()
}); });
let result = process_command(&config); let result = process_command(&config);
let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); assert!(result.is_ok());
assert_eq!(
json.as_object()
.unwrap()
.get("signature")
.unwrap()
.as_str()
.unwrap(),
SIGNATURE.to_string()
);
let witness = Pubkey::new_rand(); let witness = Pubkey::new_rand();
config.command = CliCommand::Pay(PayCommand { config.command = CliCommand::Pay(PayCommand {
@ -3343,27 +3334,18 @@ mod tests {
..PayCommand::default() ..PayCommand::default()
}); });
let result = process_command(&config); let result = process_command(&config);
let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); assert!(result.is_ok());
assert_eq!(
json.as_object()
.unwrap()
.get("signature")
.unwrap()
.as_str()
.unwrap(),
SIGNATURE.to_string()
);
let process_id = Pubkey::new_rand(); let process_id = Pubkey::new_rand();
config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
config.signers = vec![&keypair]; config.signers = vec![&keypair];
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let witness = Pubkey::new_rand(); let witness = Pubkey::new_rand();
config.command = CliCommand::Witness(bob_pubkey, witness); config.command = CliCommand::Witness(bob_pubkey, witness);
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
// CreateAddressWithSeed // CreateAddressWithSeed
let from_pubkey = Pubkey::new_rand(); let from_pubkey = Pubkey::new_rand();
@ -3390,13 +3372,13 @@ mod tests {
assert!(process_command(&config).is_ok()); assert!(process_command(&config).is_ok());
config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt);
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
let witness = Pubkey::new_rand(); let witness = Pubkey::new_rand();
config.command = CliCommand::Witness(bob_pubkey, witness); config.command = CliCommand::Witness(bob_pubkey, witness);
let signature = process_command(&config); let result = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert!(result.is_ok());
// sig_not_found case // sig_not_found case
config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string())); config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string()));

View File

@ -8,7 +8,8 @@ use serde_json::{Number, Value};
use solana_sdk::{ use solana_sdk::{
fee_calculator::{FeeCalculator, FeeRateGovernor}, fee_calculator::{FeeCalculator, FeeRateGovernor},
instruction::InstructionError, instruction::InstructionError,
transaction::{self, TransactionError}, signature::Signature,
transaction::{self, Transaction, TransactionError},
}; };
use solana_transaction_status::TransactionStatus; use solana_transaction_status::TransactionStatus;
use std::{collections::HashMap, sync::RwLock}; use std::{collections::HashMap, sync::RwLock};
@ -40,7 +41,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
fn send( fn send(
&self, &self,
request: &RpcRequest, request: &RpcRequest,
_params: serde_json::Value, params: serde_json::Value,
_retries: usize, _retries: usize,
) -> Result<serde_json::Value> { ) -> Result<serde_json::Value> {
if let Some(value) = self.mocks.write().unwrap().remove(request) { if let Some(value) = self.mocks.write().unwrap().remove(request) {
@ -105,7 +106,17 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
} }
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::GetSlot => Value::Number(Number::from(0)), RpcRequest::GetSlot => Value::Number(Number::from(0)),
RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), RpcRequest::SendTransaction => {
let signature = if self.url == "malicious" {
Signature::new(&[8; 64]).to_string()
} else {
let tx_str = params.as_array().unwrap()[0].as_str().unwrap().to_string();
let data = bs58::decode(tx_str).into_vec().unwrap();
let tx: Transaction = bincode::deserialize(&data).unwrap();
tx.signatures[0].to_string()
};
Value::String(signature)
}
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)), RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)),
_ => Value::Null, _ => Value::Null,
}; };

View File

@ -99,9 +99,24 @@ impl RpcClient {
None => { None => {
Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into()) Err(RpcError::ForUser("Received result of an unexpected type".to_string()).into())
} }
Some(signature_base58_str) => signature_base58_str Some(signature_base58_str) => {
.parse::<Signature>() let signature = signature_base58_str.parse::<Signature>().map_err(|err| {
.map_err(|err| RpcError::ParseError(err.to_string()).into()), Into::<ClientError>::into(RpcError::ParseError(err.to_string()))
})?;
// A mismatching RPC response signature indicates an issue with the RPC node, and
// should not be passed along to confirmation methods. The transaction may or may
// not have been submitted to the cluster, so callers should verify the success of
// the correct transaction signature independently.
if signature != transaction.signatures[0] {
Err(RpcError::RpcRequestError(format!(
"RPC node returned mismatched signature {:?}, expected {:?}",
signature, transaction.signatures[0]
))
.into())
} else {
Ok(transaction.signatures[0])
}
}
} }
} }
@ -956,7 +971,7 @@ impl RpcClient {
.send( .send(
&RpcRequest::GetSignatureStatuses, &RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()]]), json!([[signature.to_string()]]),
1, 5,
) )
.map_err(|err| err.into_with_command("GetSignatureStatuses"))?; .map_err(|err| err.into_with_command("GetSignatureStatuses"))?;
let result: Response<Vec<Option<TransactionStatus>>> = serde_json::from_value(response) let result: Response<Vec<Option<TransactionStatus>>> = serde_json::from_value(response)
@ -1094,10 +1109,7 @@ pub fn get_rpc_request_str(rpc_addr: SocketAddr, tls: bool) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{client_error::ClientErrorKind, mock_rpc_client_request::PUBKEY};
client_error::ClientErrorKind,
mock_rpc_client_request::{PUBKEY, SIGNATURE},
};
use assert_matches::assert_matches; use assert_matches::assert_matches;
use jsonrpc_core::{Error, IoHandler, Params}; use jsonrpc_core::{Error, IoHandler, Params};
use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder};
@ -1210,12 +1222,17 @@ mod tests {
let tx = system_transaction::transfer(&key, &to, 50, blockhash); let tx = system_transaction::transfer(&key, &to, 50, blockhash);
let signature = rpc_client.send_transaction(&tx); let signature = rpc_client.send_transaction(&tx);
assert_eq!(signature.unwrap(), SIGNATURE.parse().unwrap()); assert_eq!(signature.unwrap(), tx.signatures[0]);
let rpc_client = RpcClient::new_mock("fails".to_string()); let rpc_client = RpcClient::new_mock("fails".to_string());
let signature = rpc_client.send_transaction(&tx); let signature = rpc_client.send_transaction(&tx);
assert!(signature.is_err()); assert!(signature.is_err());
// Test bad signature returned from rpc node
let rpc_client = RpcClient::new_mock("malicious".to_string());
let signature = rpc_client.send_transaction(&tx);
assert!(signature.is_err());
} }
#[test] #[test]
fn test_get_recent_blockhash() { fn test_get_recent_blockhash() {