2019-11-06 13:15:00 -08:00
use crate ::{
2020-03-17 16:58:02 -07:00
client_error ::{ ClientError , ClientErrorKind , Result as ClientResult } ,
2019-11-06 13:15:00 -08:00
generic_rpc_client_request ::GenericRpcClientRequest ,
2019-12-27 12:35:49 -08:00
mock_rpc_client_request ::{ MockRpcClientRequest , Mocks } ,
2019-11-06 13:15:00 -08:00
rpc_client_request ::RpcClientRequest ,
2020-03-12 23:20:49 -07:00
rpc_request ::{ RpcError , RpcRequest } ,
2020-03-24 05:05:38 -07:00
rpc_response ::* ,
2019-11-06 13:15:00 -08:00
} ;
2019-03-16 17:17:44 -07:00
use bincode ::serialize ;
2020-03-17 16:58:02 -07:00
use indicatif ::{ ProgressBar , ProgressStyle } ;
2019-03-16 22:37:20 -07:00
use log ::* ;
use serde_json ::{ json , Value } ;
2019-09-27 22:00:30 -07:00
use solana_sdk ::{
account ::Account ,
2019-11-25 23:40:36 -08:00
clock ::{ Slot , UnixTimestamp , DEFAULT_TICKS_PER_SECOND , DEFAULT_TICKS_PER_SLOT } ,
2019-11-06 13:15:00 -08:00
commitment_config ::CommitmentConfig ,
2019-10-22 13:41:18 -07:00
epoch_schedule ::EpochSchedule ,
2020-02-28 12:27:01 -08:00
fee_calculator ::{ FeeCalculator , FeeRateGovernor } ,
2019-09-27 22:00:30 -07:00
hash ::Hash ,
inflation ::Inflation ,
pubkey ::Pubkey ,
2020-02-20 12:13:23 -08:00
signature ::Signature ,
signers ::Signers ,
2019-09-27 22:00:30 -07:00
transaction ::{ self , Transaction , TransactionError } ,
} ;
2020-03-26 13:29:30 -07:00
use solana_transaction_status ::{ ConfirmedBlock , TransactionEncoding , TransactionStatus } ;
2020-03-17 16:58:02 -07:00
use solana_vote_program ::vote_state ::MAX_LOCKOUT_HISTORY ;
2019-09-27 22:00:30 -07:00
use std ::{
2020-03-12 23:20:49 -07:00
error ,
2019-09-27 22:00:30 -07:00
net ::SocketAddr ,
2020-03-17 16:58:02 -07:00
str ::FromStr ,
2019-09-27 22:00:30 -07:00
thread ::sleep ,
time ::{ Duration , Instant } ,
} ;
2019-03-16 22:37:20 -07:00
pub struct RpcClient {
2019-08-15 13:00:09 -07:00
client : Box < dyn GenericRpcClientRequest + Send + Sync > ,
2019-03-16 22:37:20 -07:00
}
impl RpcClient {
pub fn new ( url : String ) -> Self {
Self {
client : Box ::new ( RpcClientRequest ::new ( url ) ) ,
}
}
pub fn new_mock ( url : String ) -> Self {
Self {
client : Box ::new ( MockRpcClientRequest ::new ( url ) ) ,
}
}
2019-12-27 12:35:49 -08:00
pub fn new_mock_with_mocks ( url : String , mocks : Mocks ) -> Self {
Self {
client : Box ::new ( MockRpcClientRequest ::new_with_mocks ( url , mocks ) ) ,
}
}
2019-03-16 22:37:20 -07:00
pub fn new_socket ( addr : SocketAddr ) -> Self {
Self ::new ( get_rpc_request_str ( addr , false ) )
}
pub fn new_socket_with_timeout ( addr : SocketAddr , timeout : Duration ) -> Self {
let url = get_rpc_request_str ( addr , false ) ;
Self {
client : Box ::new ( RpcClientRequest ::new_with_timeout ( url , timeout ) ) ,
}
}
2020-03-12 23:20:49 -07:00
pub fn confirm_transaction ( & self , signature : & str ) -> ClientResult < bool > {
2019-11-12 11:49:41 -08:00
Ok ( self
. confirm_transaction_with_commitment ( signature , CommitmentConfig ::default ( ) ) ?
. value )
2019-11-06 19:08:03 -08:00
}
pub fn confirm_transaction_with_commitment (
& self ,
signature : & str ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> RpcResult < bool > {
2019-11-12 11:49:41 -08:00
let response = self
. client
. send (
& RpcRequest ::ConfirmTransaction ,
2019-12-18 21:26:11 -08:00
json! ( [ signature , commitment_config ] ) ,
2019-11-12 11:49:41 -08:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " ConfirmTransaction " ) ) ? ;
2019-11-12 11:49:41 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ::< Response < bool > > ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " ConfirmTransaction " ) )
2019-11-06 19:08:03 -08:00
}
2020-03-12 23:20:49 -07:00
pub fn send_transaction ( & self , transaction : & Transaction ) -> ClientResult < String > {
2020-01-21 21:16:07 -08:00
let serialized_encoded = bs58 ::encode ( serialize ( transaction ) . unwrap ( ) ) . into_string ( ) ;
let signature =
self . client
. send ( & RpcRequest ::SendTransaction , json! ( [ serialized_encoded ] ) , 5 ) ? ;
2019-03-16 17:17:44 -07:00
if signature . as_str ( ) . is_none ( ) {
2020-03-12 23:20:49 -07:00
Err ( RpcError ::ForUser ( " Received result of an unexpected type " . to_string ( ) ) . into ( ) )
2019-10-02 18:04:18 -07:00
} else {
Ok ( signature . as_str ( ) . unwrap ( ) . to_string ( ) )
2019-03-16 17:17:44 -07:00
}
}
pub fn get_signature_status (
& self ,
signature : & str ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Option < transaction ::Result < ( ) > > > {
2019-11-06 13:15:00 -08:00
self . get_signature_status_with_commitment ( signature , CommitmentConfig ::default ( ) )
}
pub fn get_signature_status_with_commitment (
& self ,
signature : & str ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Option < transaction ::Result < ( ) > > > {
2019-11-06 13:15:00 -08:00
let signature_status = self . client . send (
& RpcRequest ::GetSignatureStatus ,
2020-03-23 10:25:39 -07:00
json! ( [ [ signature . to_string ( ) ] , commitment_config ] ) ,
2019-11-06 13:15:00 -08:00
5 ,
) ? ;
2020-03-26 13:29:30 -07:00
let result : Vec < Option < TransactionStatus > > =
2019-04-05 19:56:17 -07:00
serde_json ::from_value ( signature_status ) . unwrap ( ) ;
2020-03-23 10:25:39 -07:00
Ok ( result [ 0 ] . clone ( ) . map ( | status_meta | status_meta . status ) )
2019-03-16 17:17:44 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_slot ( & self ) -> ClientResult < Slot > {
2019-11-06 13:15:00 -08:00
self . get_slot_with_commitment ( CommitmentConfig ::default ( ) )
}
pub fn get_slot_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Slot > {
2019-06-12 16:43:05 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetSlot , json! ( [ commitment_config ] ) , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetSlot " ) ) ? ;
2019-06-12 16:43:05 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetSlot " ) )
2019-08-27 15:17:03 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn total_supply ( & self ) -> ClientResult < u64 > {
2020-03-09 01:28:44 -07:00
self . total_supply_with_commitment ( CommitmentConfig ::default ( ) )
}
pub fn total_supply_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < u64 > {
2020-03-09 01:28:44 -07:00
let response = self
. client
. send ( & RpcRequest ::GetTotalSupply , json! ( [ commitment_config ] ) , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetTotalSupply " ) ) ? ;
2020-03-09 01:28:44 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetTotalSupply " ) )
2020-03-09 01:28:44 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_vote_accounts ( & self ) -> ClientResult < RpcVoteAccountStatus > {
2020-03-03 16:53:30 -08:00
self . get_vote_accounts_with_commitment ( CommitmentConfig ::default ( ) )
}
pub fn get_vote_accounts_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < RpcVoteAccountStatus > {
2019-10-06 20:30:22 -07:00
let response = self
. client
2020-03-03 16:53:30 -08:00
. send ( & RpcRequest ::GetVoteAccounts , json! ( [ commitment_config ] ) , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetVoteAccounts " ) ) ? ;
2019-10-06 20:30:22 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetVoteAccounts " ) )
2019-10-06 20:30:22 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_cluster_nodes ( & self ) -> ClientResult < Vec < RpcContactInfo > > {
2019-11-13 14:58:14 -08:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetClusterNodes , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetClusterNodes " ) ) ? ;
2019-11-13 14:58:14 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetClusterNodes " ) )
2019-11-13 14:58:14 -08:00
}
2020-03-26 13:29:30 -07:00
pub fn get_confirmed_block ( & self , slot : Slot ) -> ClientResult < ConfirmedBlock > {
self . get_confirmed_block_with_encoding ( slot , TransactionEncoding ::Json )
2020-03-24 05:05:38 -07:00
}
pub fn get_confirmed_block_with_encoding (
& self ,
slot : Slot ,
2020-03-26 13:29:30 -07:00
encoding : TransactionEncoding ,
) -> ClientResult < ConfirmedBlock > {
2019-12-18 21:31:38 -08:00
let response = self
. client
2020-03-24 05:05:38 -07:00
. send ( & RpcRequest ::GetConfirmedBlock , json! ( [ slot , encoding ] ) , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetConfirmedBlock " ) ) ? ;
2019-12-18 21:31:38 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetConfirmedBlock " ) )
2019-12-18 21:31:38 -08:00
}
pub fn get_confirmed_blocks (
& self ,
start_slot : Slot ,
end_slot : Option < Slot > ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Vec < Slot > > {
2019-12-18 21:31:38 -08:00
let response = self
. client
. send (
& RpcRequest ::GetConfirmedBlocks ,
json! ( [ start_slot , end_slot ] ) ,
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetConfirmedBlocks " ) ) ? ;
2019-12-18 21:31:38 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetConfirmedBlocks " ) )
2019-12-18 21:31:38 -08:00
}
2020-03-12 23:20:49 -07:00
pub fn get_block_time ( & self , slot : Slot ) -> ClientResult < UnixTimestamp > {
2019-11-25 23:40:36 -08:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetBlockTime , json! ( [ slot ] ) , 0 ) ;
2019-11-25 23:40:36 -08:00
response
. map ( | result_json | {
if result_json . is_null ( ) {
2020-03-12 23:20:49 -07:00
return Err ( RpcError ::ForUser ( format! ( " Block Not Found: slot= {} " , slot ) ) . into ( ) ) ;
2019-11-25 23:40:36 -08:00
}
2020-03-12 23:20:49 -07:00
let result = serde_json ::from_value ( result_json )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetBlockTime " ) ) ? ;
2019-11-25 23:40:36 -08:00
trace! ( " Response block timestamp {:?} {:?} " , slot , result ) ;
Ok ( result )
} )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetBlockTime " ) ) ?
2019-11-25 23:40:36 -08:00
}
2020-03-12 23:20:49 -07:00
pub fn get_epoch_info ( & self ) -> ClientResult < RpcEpochInfo > {
2019-11-24 16:34:18 -08:00
self . get_epoch_info_with_commitment ( CommitmentConfig ::default ( ) )
}
pub fn get_epoch_info_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < RpcEpochInfo > {
2019-09-27 22:00:30 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetEpochInfo , json! ( [ commitment_config ] ) , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetEpochInfo " ) ) ? ;
2019-09-27 22:00:30 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetEpochInfo " ) )
2019-12-17 15:26:31 -08:00
}
2020-03-12 23:20:49 -07:00
pub fn get_leader_schedule (
& self ,
slot : Option < Slot > ,
) -> ClientResult < Option < RpcLeaderSchedule > > {
2019-12-17 15:26:31 -08:00
self . get_leader_schedule_with_commitment ( slot , CommitmentConfig ::default ( ) )
}
pub fn get_leader_schedule_with_commitment (
& self ,
slot : Option < Slot > ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Option < RpcLeaderSchedule > > {
2019-12-17 15:26:31 -08:00
let response = self
. client
. send (
& RpcRequest ::GetLeaderSchedule ,
2019-12-18 21:26:11 -08:00
json! ( [ slot , commitment_config ] ) ,
2019-12-17 15:26:31 -08:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetLeaderSchedule " ) ) ? ;
2019-12-17 15:26:31 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetLeaderSchedule " ) )
2019-09-27 22:00:30 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_epoch_schedule ( & self ) -> ClientResult < EpochSchedule > {
2019-10-22 13:41:18 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetEpochSchedule , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetEpochSchedule " ) ) ? ;
2019-10-22 13:41:18 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetEpochSchedule " ) )
2019-10-22 13:41:18 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_identity ( & self ) -> ClientResult < Pubkey > {
2020-03-04 14:44:21 -08:00
let response = self
. client
. send ( & RpcRequest ::GetIdentity , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetIdentity " ) ) ? ;
2020-03-04 14:44:21 -08:00
serde_json ::from_value ( response )
2020-03-12 23:20:49 -07:00
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetIdentity " ) )
2020-03-04 14:44:21 -08:00
. and_then ( | rpc_identity : RpcIdentity | {
2020-03-12 23:20:49 -07:00
rpc_identity . identity . parse ::< Pubkey > ( ) . map_err ( | _ | {
ClientError ::new_with_command (
RpcError ::ParseError ( " Pubkey " . to_string ( ) ) . into ( ) ,
" GetIdentity " ,
2020-03-04 14:44:21 -08:00
)
} )
} )
}
2020-03-12 23:20:49 -07:00
pub fn get_inflation ( & self ) -> ClientResult < Inflation > {
2019-08-27 15:17:03 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetInflation , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetInflation " ) ) ? ;
2019-08-27 15:17:03 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetInflation " ) )
2019-06-12 16:43:05 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_version ( & self ) -> ClientResult < RpcVersionInfo > {
2019-08-08 10:13:06 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetVersion , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetVersion " ) ) ? ;
2019-08-08 10:13:06 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetVersion " ) )
2019-08-08 10:13:06 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn minimum_ledger_slot ( & self ) -> ClientResult < Slot > {
2020-01-21 12:05:04 -08:00
let response = self
. client
. send ( & RpcRequest ::MinimumLedgerSlot , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " MinimumLedgerSlot " ) ) ? ;
2020-01-21 12:05:04 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " MinimumLedgerSlot " ) )
2020-01-21 12:05:04 -08:00
}
2020-02-20 12:13:23 -08:00
pub fn send_and_confirm_transaction < T : Signers > (
2019-03-16 17:17:44 -07:00
& self ,
transaction : & mut Transaction ,
2020-02-20 12:13:23 -08:00
signer_keys : & T ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < String > {
2019-10-08 13:04:27 -07:00
let mut send_retries = 20 ;
2019-03-16 17:17:44 -07:00
loop {
2019-11-06 13:15:00 -08:00
let mut status_retries = 15 ;
2019-03-16 17:17:44 -07:00
let signature_str = self . send_transaction ( transaction ) ? ;
let status = loop {
let status = self . get_signature_status ( & signature_str ) ? ;
2019-04-05 19:56:17 -07:00
if status . is_none ( ) {
2019-03-16 17:17:44 -07:00
status_retries - = 1 ;
if status_retries = = 0 {
break status ;
}
} else {
break status ;
}
if cfg! ( not ( test ) ) {
2019-11-06 13:15:00 -08:00
// Retry twice a second
sleep ( Duration ::from_millis ( 500 ) ) ;
2019-03-16 17:17:44 -07:00
}
} ;
2019-04-05 19:56:17 -07:00
send_retries = if let Some ( result ) = status . clone ( ) {
match result {
Ok ( _ ) = > return Ok ( signature_str ) ,
Err ( TransactionError ::AccountInUse ) = > {
// Fetch a new blockhash and re-sign the transaction before sending it again
2019-05-07 15:00:54 -07:00
self . resign_transaction ( transaction , signer_keys ) ? ;
2019-04-05 19:56:17 -07:00
send_retries - 1
}
Err ( _ ) = > 0 ,
2019-03-16 17:17:44 -07:00
}
2019-04-05 19:56:17 -07:00
} else {
send_retries - 1
} ;
2019-03-16 17:17:44 -07:00
if send_retries = = 0 {
2019-12-09 19:35:34 -08:00
if let Some ( err ) = status {
return Err ( err . unwrap_err ( ) . into ( ) ) ;
} else {
2020-03-12 23:20:49 -07:00
return Err (
RpcError ::ForUser ( " unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds " . to_string ( ) ) . into ( ) ,
) ;
2019-12-09 19:35:34 -08:00
}
2019-03-16 17:17:44 -07:00
}
}
}
2020-02-20 12:13:23 -08:00
pub fn send_and_confirm_transactions < T : Signers > (
2019-03-16 17:17:44 -07:00
& self ,
mut transactions : Vec < Transaction > ,
2020-02-20 12:13:23 -08:00
signer_keys : & T ,
2019-03-16 17:17:44 -07:00
) -> Result < ( ) , Box < dyn error ::Error > > {
let mut send_retries = 5 ;
loop {
2019-11-06 13:15:00 -08:00
let mut status_retries = 15 ;
2019-03-16 17:17:44 -07:00
// Send all transactions
let mut transactions_signatures = vec! [ ] ;
for transaction in transactions {
if cfg! ( not ( test ) ) {
// Delay ~1 tick between write transactions in an attempt to reduce AccountInUse errors
// when all the write transactions modify the same program account (eg, deploying a
// new program)
2019-08-19 23:22:56 -07:00
sleep ( Duration ::from_millis ( 1000 / DEFAULT_TICKS_PER_SECOND ) ) ;
2019-03-16 17:17:44 -07:00
}
let signature = self . send_transaction ( & transaction ) . ok ( ) ;
transactions_signatures . push ( ( transaction , signature ) )
}
// Collect statuses for all the transactions, drop those that are confirmed
while status_retries > 0 {
status_retries - = 1 ;
if cfg! ( not ( test ) ) {
2019-11-06 13:15:00 -08:00
// Retry twice a second
sleep ( Duration ::from_millis ( 500 ) ) ;
2019-03-16 17:17:44 -07:00
}
transactions_signatures = transactions_signatures
. into_iter ( )
. filter ( | ( _transaction , signature ) | {
if let Some ( signature ) = signature {
if let Ok ( status ) = self . get_signature_status ( & signature ) {
2019-04-05 19:56:17 -07:00
if status . is_none ( ) {
return false ;
}
return status . unwrap ( ) . is_err ( ) ;
2019-03-16 17:17:44 -07:00
}
}
true
} )
. collect ( ) ;
if transactions_signatures . is_empty ( ) {
return Ok ( ( ) ) ;
}
}
if send_retries = = 0 {
2020-03-12 23:20:49 -07:00
return Err ( RpcError ::ForUser ( " Transactions failed " . to_string ( ) ) . into ( ) ) ;
2019-03-16 17:17:44 -07:00
}
send_retries - = 1 ;
// Re-sign any failed transactions with a new blockhash and retry
2019-05-13 12:49:37 -07:00
let ( blockhash , _fee_calculator ) =
2019-03-29 09:05:06 -07:00
self . get_new_blockhash ( & transactions_signatures [ 0 ] . 0. message ( ) . recent_blockhash ) ? ;
2020-02-20 19:04:53 -08:00
transactions = vec! [ ] ;
for ( mut transaction , _ ) in transactions_signatures . into_iter ( ) {
transaction . try_sign ( signer_keys , blockhash ) ? ;
transactions . push ( transaction ) ;
}
2019-03-16 17:17:44 -07:00
}
}
2020-02-20 12:13:23 -08:00
pub fn resign_transaction < T : Signers > (
2019-03-16 17:17:44 -07:00
& self ,
tx : & mut Transaction ,
2020-02-20 12:13:23 -08:00
signer_keys : & T ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < ( ) > {
2019-05-13 12:49:37 -07:00
let ( blockhash , _fee_calculator ) =
self . get_new_blockhash ( & tx . message ( ) . recent_blockhash ) ? ;
2020-02-20 19:04:53 -08:00
tx . try_sign ( signer_keys , blockhash ) ? ;
2019-03-16 17:17:44 -07:00
Ok ( ( ) )
}
2019-03-16 22:37:20 -07:00
pub fn retry_get_balance (
& self ,
pubkey : & Pubkey ,
retries : usize ,
) -> Result < Option < u64 > , Box < dyn error ::Error > > {
2019-11-12 11:49:41 -08:00
let balance_json = self
2019-03-16 22:37:20 -07:00
. client
2019-12-18 21:26:11 -08:00
. send (
& RpcRequest ::GetBalance ,
json! ( [ pubkey . to_string ( ) ] ) ,
retries ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " RetryGetBalance " ) ) ? ;
2019-11-12 11:49:41 -08:00
Ok ( Some (
serde_json ::from_value ::< Response < u64 > > ( balance_json )
2020-03-12 23:20:49 -07:00
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " RetryGetBalance " ) ) ?
2019-11-12 11:49:41 -08:00
. value ,
) )
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_account ( & self , pubkey : & Pubkey ) -> ClientResult < Account > {
2019-11-13 13:41:54 -08:00
self . get_account_with_commitment ( pubkey , CommitmentConfig ::default ( ) ) ?
. value
2020-03-12 23:20:49 -07:00
. ok_or_else ( | | RpcError ::ForUser ( format! ( " AccountNotFound: pubkey= {} " , pubkey ) ) . into ( ) )
2019-11-06 13:15:00 -08:00
}
pub fn get_account_with_commitment (
& self ,
pubkey : & Pubkey ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> RpcResult < Option < Account > > {
2019-11-06 13:15:00 -08:00
let response = self . client . send (
& RpcRequest ::GetAccountInfo ,
2019-12-18 21:26:11 -08:00
json! ( [ pubkey . to_string ( ) , commitment_config ] ) ,
2019-11-06 13:15:00 -08:00
0 ,
) ;
2019-03-16 22:37:20 -07:00
response
2019-11-12 11:49:41 -08:00
. map ( | result_json | {
if result_json . is_null ( ) {
2020-03-12 23:20:49 -07:00
return Err (
RpcError ::ForUser ( format! ( " AccountNotFound: pubkey= {} " , pubkey ) ) . into ( ) ,
) ;
2019-11-12 11:49:41 -08:00
}
2020-01-15 14:33:53 -08:00
let Response {
context ,
value : rpc_account ,
} = serde_json ::from_value ::< Response < Option < RpcAccount > > > ( result_json ) ? ;
trace! ( " Response account {:?} {:?} " , pubkey , rpc_account ) ;
let account = rpc_account . and_then ( | rpc_account | rpc_account . decode ( ) . ok ( ) ) ;
Ok ( Response {
context ,
value : account ,
} )
2019-03-16 22:37:20 -07:00
} )
2019-05-13 12:49:37 -07:00
. map_err ( | err | {
2020-03-12 23:20:49 -07:00
Into ::< ClientError > ::into ( RpcError ::ForUser ( format! (
" AccountNotFound: pubkey={}: {} " ,
pubkey , err
) ) )
2019-11-12 11:49:41 -08:00
} ) ?
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_account_data ( & self , pubkey : & Pubkey ) -> ClientResult < Vec < u8 > > {
2019-11-13 13:41:54 -08:00
Ok ( self . get_account ( pubkey ) ? . data )
2019-05-08 15:50:23 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_minimum_balance_for_rent_exemption ( & self , data_len : usize ) -> ClientResult < u64 > {
2019-09-26 10:57:13 -07:00
let minimum_balance_json = self
. client
. send (
& RpcRequest ::GetMinimumBalanceForRentExemption ,
2019-12-18 21:26:11 -08:00
json! ( [ data_len ] ) ,
2019-09-26 10:57:13 -07:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetMinimumBalanceForRentExemption " ) ) ? ;
2019-09-26 10:57:13 -07:00
2019-09-30 12:44:49 -07:00
let minimum_balance : u64 = serde_json ::from_value ( minimum_balance_json ) . map_err ( | err | {
2020-03-12 23:20:49 -07:00
ClientError ::new_with_command ( err . into ( ) , " GetMinimumBalanceForRentExemption " )
2019-09-30 12:44:49 -07:00
} ) ? ;
2019-09-26 10:57:13 -07:00
trace! (
" Response minimum balance {:?} {:?} " ,
data_len ,
minimum_balance
) ;
Ok ( minimum_balance )
}
2019-11-06 13:15:00 -08:00
/// Request the balance of the account `pubkey`.
2020-03-12 23:20:49 -07:00
pub fn get_balance ( & self , pubkey : & Pubkey ) -> ClientResult < u64 > {
2019-11-12 11:49:41 -08:00
Ok ( self
. get_balance_with_commitment ( pubkey , CommitmentConfig ::default ( ) ) ?
. value )
2019-11-06 13:15:00 -08:00
}
pub fn get_balance_with_commitment (
& self ,
pubkey : & Pubkey ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> RpcResult < u64 > {
2019-11-06 19:08:03 -08:00
let balance_json = self
. client
. send (
& RpcRequest ::GetBalance ,
2019-12-18 21:26:11 -08:00
json! ( [ pubkey . to_string ( ) , commitment_config ] ) ,
2019-11-06 19:08:03 -08:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetBalance " ) ) ? ;
2019-11-12 11:49:41 -08:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ::< Response < u64 > > ( balance_json )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetBalance " ) )
2019-05-08 15:50:23 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_program_accounts ( & self , pubkey : & Pubkey ) -> ClientResult < Vec < ( Pubkey , Account ) > > {
2019-07-11 11:38:52 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send (
& RpcRequest ::GetProgramAccounts ,
json! ( [ pubkey . to_string ( ) ] ) ,
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetProgramAccounts " ) ) ? ;
2019-07-11 11:38:52 -07:00
2020-01-15 14:33:53 -08:00
let accounts : Vec < RpcKeyedAccount > =
2020-03-12 23:20:49 -07:00
serde_json ::from_value ::< Vec < RpcKeyedAccount > > ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetProgramAccounts " ) ) ? ;
2019-07-11 11:38:52 -07:00
let mut pubkey_accounts : Vec < ( Pubkey , Account ) > = Vec ::new ( ) ;
2020-01-15 14:33:53 -08:00
for RpcKeyedAccount { pubkey , account } in accounts . into_iter ( ) {
2020-03-12 23:20:49 -07:00
let pubkey = pubkey . parse ( ) . map_err ( | _ | {
ClientError ::new_with_command (
RpcError ::ParseError ( " Pubkey " . to_string ( ) ) . into ( ) ,
" GetProgramAccounts " ,
2019-07-11 11:38:52 -07:00
)
} ) ? ;
2020-01-15 14:33:53 -08:00
pubkey_accounts . push ( ( pubkey , account . decode ( ) . unwrap ( ) ) ) ;
2019-07-11 11:38:52 -07:00
}
Ok ( pubkey_accounts )
}
2019-11-06 13:15:00 -08:00
/// Request the transaction count.
2020-03-12 23:20:49 -07:00
pub fn get_transaction_count ( & self ) -> ClientResult < u64 > {
2019-11-06 13:15:00 -08:00
self . get_transaction_count_with_commitment ( CommitmentConfig ::default ( ) )
}
pub fn get_transaction_count_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < u64 > {
2019-05-08 16:20:37 -07:00
let response = self
. client
2019-11-06 13:15:00 -08:00
. send (
& RpcRequest ::GetTransactionCount ,
2019-12-18 21:26:11 -08:00
json! ( [ commitment_config ] ) ,
2019-11-06 13:15:00 -08:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetTransactionCount " ) ) ? ;
2019-03-16 22:37:20 -07:00
2020-03-12 23:20:49 -07:00
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetTransactionCount " ) )
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_recent_blockhash ( & self ) -> ClientResult < ( Hash , FeeCalculator ) > {
2019-11-12 11:49:41 -08:00
Ok ( self
. get_recent_blockhash_with_commitment ( CommitmentConfig ::default ( ) ) ?
. value )
2019-11-06 13:15:00 -08:00
}
pub fn get_recent_blockhash_with_commitment (
& self ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> RpcResult < ( Hash , FeeCalculator ) > {
2019-05-08 16:20:37 -07:00
let response = self
. client
2019-11-06 13:15:00 -08:00
. send (
& RpcRequest ::GetRecentBlockhash ,
2019-12-18 21:26:11 -08:00
json! ( [ commitment_config ] ) ,
2019-11-06 13:15:00 -08:00
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetRecentBlockhash " ) ) ? ;
2019-05-08 16:20:37 -07:00
2019-11-12 11:49:41 -08:00
let Response {
context ,
2020-01-14 23:25:45 -08:00
value :
RpcBlockhashFeeCalculator {
blockhash ,
fee_calculator ,
} ,
2020-03-12 23:20:49 -07:00
} = serde_json ::from_value ::< Response < RpcBlockhashFeeCalculator > > ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetRecentBlockhash " ) ) ? ;
let blockhash = blockhash . parse ( ) . map_err ( | _ | {
ClientError ::new_with_command (
RpcError ::ParseError ( " Hash " . to_string ( ) ) . into ( ) ,
" GetRecentBlockhash " ,
2019-05-13 12:49:37 -07:00
)
} ) ? ;
2019-11-12 11:49:41 -08:00
Ok ( Response {
context ,
value : ( blockhash , fee_calculator ) ,
} )
2019-03-16 22:37:20 -07:00
}
2020-03-06 16:01:31 -08:00
pub fn get_fee_calculator_for_blockhash (
& self ,
blockhash : & Hash ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < Option < FeeCalculator > > {
2020-03-06 16:01:31 -08:00
let response = self
. client
. send (
& RpcRequest ::GetFeeCalculatorForBlockhash ,
json! ( [ blockhash . to_string ( ) ] ) ,
0 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetFeeCalculatorForBlockhash " ) ) ? ;
2020-03-06 16:01:31 -08:00
let Response { value , .. } = serde_json ::from_value ::< Response < Option < RpcFeeCalculator > > > (
response ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | e | ClientError ::new_with_command ( e . into ( ) , " GetFeeCalculatorForBlockhash " ) ) ? ;
2020-03-06 16:01:31 -08:00
Ok ( value . map ( | rf | rf . fee_calculator ) )
}
2020-03-12 23:20:49 -07:00
pub fn get_fee_rate_governor ( & self ) -> RpcResult < FeeRateGovernor > {
2020-02-28 12:27:01 -08:00
let response = self
. client
. send ( & RpcRequest ::GetFeeRateGovernor , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetFeeRateGovernor " ) ) ? ;
2020-02-28 12:27:01 -08:00
let Response {
context ,
value : RpcFeeRateGovernor { fee_rate_governor } ,
2020-03-12 23:20:49 -07:00
} = serde_json ::from_value ::< Response < RpcFeeRateGovernor > > ( response )
. map_err ( | e | ClientError ::new_with_command ( e . into ( ) , " GetFeeRateGovernor " ) ) ? ;
2020-02-28 12:27:01 -08:00
Ok ( Response {
context ,
value : fee_rate_governor ,
} )
}
2020-03-12 23:20:49 -07:00
pub fn get_new_blockhash ( & self , blockhash : & Hash ) -> ClientResult < ( Hash , FeeCalculator ) > {
2019-09-27 10:36:38 -07:00
let mut num_retries = 0 ;
2019-09-20 15:50:43 -07:00
let start = Instant ::now ( ) ;
2019-09-27 10:36:38 -07:00
while start . elapsed ( ) . as_secs ( ) < 5 {
2019-05-13 12:49:37 -07:00
if let Ok ( ( new_blockhash , fee_calculator ) ) = self . get_recent_blockhash ( ) {
2019-03-16 17:17:44 -07:00
if new_blockhash ! = * blockhash {
2019-05-13 12:49:37 -07:00
return Ok ( ( new_blockhash , fee_calculator ) ) ;
2019-03-16 17:17:44 -07:00
}
2019-03-16 22:37:20 -07:00
}
debug! ( " Got same blockhash ({:?}), will retry... " , blockhash ) ;
2019-03-16 17:17:44 -07:00
// Retry ~twice during a slot
sleep ( Duration ::from_millis (
2019-08-19 23:22:56 -07:00
500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND ,
2019-03-16 17:17:44 -07:00
) ) ;
2019-09-27 10:36:38 -07:00
num_retries + = 1 ;
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
Err ( RpcError ::ForUser ( format! (
" Unable to get new blockhash after {}ms (retried {} times), stuck at {} " ,
start . elapsed ( ) . as_millis ( ) ,
num_retries ,
blockhash
2019-03-16 17:17:44 -07:00
) )
2020-03-12 23:20:49 -07:00
. into ( ) )
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn get_genesis_hash ( & self ) -> ClientResult < Hash > {
2019-08-21 18:16:40 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::GetGenesisHash , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetGenesisHash " ) ) ? ;
2019-08-21 18:16:40 -07:00
2020-03-12 23:20:49 -07:00
let hash = serde_json ::from_value ::< String > ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " GetGenesisHash " ) ) ? ;
2019-08-21 18:16:40 -07:00
2020-03-12 23:20:49 -07:00
let hash = hash . parse ( ) . map_err ( | _ | {
ClientError ::new_with_command (
RpcError ::ParseError ( " Hash " . to_string ( ) ) . into ( ) ,
" GetGenesisHash " ,
2019-08-21 18:16:40 -07:00
)
} ) ? ;
2019-11-08 20:56:57 -08:00
Ok ( hash )
2019-08-21 18:16:40 -07:00
}
2019-11-06 13:15:00 -08:00
pub fn poll_balance_with_timeout_and_commitment (
2019-03-16 22:37:20 -07:00
& self ,
pubkey : & Pubkey ,
polling_frequency : & Duration ,
timeout : & Duration ,
2019-11-06 13:15:00 -08:00
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < u64 > {
2019-03-16 22:37:20 -07:00
let now = Instant ::now ( ) ;
loop {
2019-11-06 13:15:00 -08:00
match self . get_balance_with_commitment ( & pubkey , commitment_config . clone ( ) ) {
2019-03-16 22:37:20 -07:00
Ok ( bal ) = > {
2019-11-12 11:49:41 -08:00
return Ok ( bal . value ) ;
2019-03-16 22:37:20 -07:00
}
Err ( e ) = > {
sleep ( * polling_frequency ) ;
if now . elapsed ( ) > * timeout {
return Err ( e ) ;
}
}
} ;
}
}
2019-11-06 13:15:00 -08:00
pub fn poll_get_balance_with_commitment (
& self ,
pubkey : & Pubkey ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < u64 > {
2019-11-06 13:15:00 -08:00
self . poll_balance_with_timeout_and_commitment (
pubkey ,
& Duration ::from_millis ( 100 ) ,
& Duration ::from_secs ( 1 ) ,
commitment_config ,
)
2019-03-16 22:37:20 -07:00
}
2019-11-06 13:15:00 -08:00
pub fn wait_for_balance_with_commitment (
& self ,
pubkey : & Pubkey ,
expected_balance : Option < u64 > ,
commitment_config : CommitmentConfig ,
) -> Option < u64 > {
2019-03-16 23:29:11 -07:00
const LAST : usize = 30 ;
for run in 0 .. LAST {
2019-11-06 13:15:00 -08:00
let balance_result =
self . poll_get_balance_with_commitment ( pubkey , commitment_config . clone ( ) ) ;
2019-03-16 23:29:11 -07:00
if expected_balance . is_none ( ) {
return balance_result . ok ( ) ;
}
trace! (
" retry_get_balance[{}] {:?} {:?} " ,
run ,
balance_result ,
expected_balance
) ;
if let ( Some ( expected_balance ) , Ok ( balance_result ) ) = ( expected_balance , balance_result )
{
if expected_balance = = balance_result {
return Some ( balance_result ) ;
}
}
}
None
}
2019-03-16 22:37:20 -07:00
/// Poll the server to confirm a transaction.
2020-03-12 23:20:49 -07:00
pub fn poll_for_signature ( & self , signature : & Signature ) -> ClientResult < ( ) > {
2019-11-06 13:15:00 -08:00
self . poll_for_signature_with_commitment ( signature , CommitmentConfig ::default ( ) )
}
/// Poll the server to confirm a transaction.
pub fn poll_for_signature_with_commitment (
& self ,
signature : & Signature ,
commitment_config : CommitmentConfig ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < ( ) > {
2019-03-16 22:37:20 -07:00
let now = Instant ::now ( ) ;
2019-11-06 13:15:00 -08:00
loop {
if let Ok ( Some ( _ ) ) = self . get_signature_status_with_commitment (
& signature . to_string ( ) ,
commitment_config . clone ( ) ,
) {
break ;
}
2019-03-16 22:37:20 -07:00
if now . elapsed ( ) . as_secs ( ) > 15 {
2020-03-12 23:20:49 -07:00
return Err ( RpcError ::ForUser ( format! (
" signature not found after {} seconds " ,
now . elapsed ( ) . as_secs ( )
) )
. into ( ) ) ;
2019-03-16 22:37:20 -07:00
}
sleep ( Duration ::from_millis ( 250 ) ) ;
}
Ok ( ( ) )
}
2019-04-08 10:32:22 -07:00
/// Check a signature in the bank.
2019-03-16 22:37:20 -07:00
pub fn check_signature ( & self , signature : & Signature ) -> bool {
trace! ( " check_signature: {:?} " , signature ) ;
2019-04-08 10:32:22 -07:00
for _ in 0 .. 30 {
2019-11-06 13:15:00 -08:00
let response = self . client . send (
& RpcRequest ::ConfirmTransaction ,
2019-12-18 21:26:11 -08:00
json! ( [ signature . to_string ( ) , CommitmentConfig ::recent ( ) ] ) ,
2019-11-06 13:15:00 -08:00
0 ,
) ;
2019-03-16 22:37:20 -07:00
match response {
2019-11-13 13:41:54 -08:00
Ok ( Value ::Bool ( signature_status ) ) = > {
2019-03-16 22:37:20 -07:00
if signature_status {
trace! ( " Response found signature " ) ;
} else {
trace! ( " Response signature not found " ) ;
}
return signature_status ;
}
2019-11-13 13:41:54 -08:00
Ok ( other ) = > {
debug! (
" check_signature request failed, expected bool, got: {:?} " ,
other
) ;
}
2019-03-16 22:37:20 -07:00
Err ( err ) = > {
debug! ( " check_signature request failed: {:?} " , err ) ;
}
} ;
2019-04-08 10:32:22 -07:00
sleep ( Duration ::from_millis ( 250 ) ) ;
2019-03-16 22:37:20 -07:00
}
2019-04-08 10:32:22 -07:00
panic! ( " Couldn't check signature of {} " , signature ) ;
2019-03-16 22:37:20 -07:00
}
2019-03-21 07:43:21 -07:00
/// Poll the server to confirm a transaction.
pub fn poll_for_signature_confirmation (
& self ,
signature : & Signature ,
min_confirmed_blocks : usize ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < usize > {
2019-03-21 07:43:21 -07:00
let mut now = Instant ::now ( ) ;
let mut confirmed_blocks = 0 ;
loop {
let response = self . get_num_blocks_since_signature_confirmation ( signature ) ;
match response {
Ok ( count ) = > {
if confirmed_blocks ! = count {
info! (
2019-07-01 13:21:00 -07:00
" signature {} confirmed {} out of {} after {} ms " ,
signature ,
count ,
min_confirmed_blocks ,
now . elapsed ( ) . as_millis ( )
2019-03-21 07:43:21 -07:00
) ;
now = Instant ::now ( ) ;
confirmed_blocks = count ;
}
if count > = min_confirmed_blocks {
break ;
}
}
Err ( err ) = > {
debug! ( " check_confirmations request failed: {:?} " , err ) ;
}
} ;
2019-11-06 13:15:00 -08:00
if now . elapsed ( ) . as_secs ( ) > 20 {
2019-07-01 13:21:00 -07:00
info! (
" signature {} confirmed {} out of {} failed after {} ms " ,
signature ,
confirmed_blocks ,
min_confirmed_blocks ,
now . elapsed ( ) . as_millis ( )
) ;
2019-07-02 20:56:10 -07:00
if confirmed_blocks > 0 {
return Ok ( confirmed_blocks ) ;
} else {
2020-03-12 23:20:49 -07:00
return Err ( RpcError ::ForUser ( format! (
" signature not found after {} seconds " ,
now . elapsed ( ) . as_secs ( )
) )
. into ( ) ) ;
2019-07-02 20:56:10 -07:00
}
2019-03-21 07:43:21 -07:00
}
2019-09-24 16:01:18 -07:00
sleep ( Duration ::from_millis ( 250 ) ) ;
2019-03-21 07:43:21 -07:00
}
2019-07-02 20:56:10 -07:00
Ok ( confirmed_blocks )
2019-03-21 07:43:21 -07:00
}
pub fn get_num_blocks_since_signature_confirmation (
& self ,
2019-12-18 21:26:11 -08:00
signature : & Signature ,
2020-03-12 23:20:49 -07:00
) -> ClientResult < usize > {
2019-03-21 07:43:21 -07:00
let response = self
. client
. send (
& RpcRequest ::GetNumBlocksSinceSignatureConfirmation ,
2019-12-18 21:26:11 -08:00
json! ( [ signature . to_string ( ) , CommitmentConfig ::recent ( ) . ok ( ) ] ) ,
2019-03-21 07:43:21 -07:00
1 ,
)
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " GetNumBlocksSinceSignatureConfirmation " ) ) ? ;
2019-05-13 12:49:37 -07:00
serde_json ::from_value ( response ) . map_err ( | err | {
2020-03-12 23:20:49 -07:00
ClientError ::new_with_command ( err . into ( ) , " GetNumBlocksSinceSignatureConfirmation " )
2019-03-21 07:43:21 -07:00
} )
}
2020-03-17 16:58:02 -07:00
pub fn send_and_confirm_transaction_with_spinner < T : Signers > (
& self ,
transaction : & mut Transaction ,
signer_keys : & T ,
) -> ClientResult < String > {
2020-03-19 11:10:35 -07:00
let mut confirmations = 0 ;
let progress_bar = new_spinner_progress_bar ( ) ;
progress_bar . set_message ( & format! (
" [{}/{}] Waiting for confirmations " ,
confirmations ,
MAX_LOCKOUT_HISTORY + 1 ,
) ) ;
2020-03-17 16:58:02 -07:00
let mut send_retries = 20 ;
let signature_str = loop {
let mut status_retries = 15 ;
let ( signature_str , status ) = loop {
let signature_str = self . send_transaction ( transaction ) ? ;
// Get recent commitment in order to count confirmations for successful transactions
let status = self . get_signature_status_with_commitment (
& signature_str ,
CommitmentConfig ::recent ( ) ,
) ? ;
if status . is_none ( ) {
status_retries - = 1 ;
if status_retries = = 0 {
break ( signature_str , status ) ;
}
} else {
break ( signature_str , status ) ;
}
if cfg! ( not ( test ) ) {
sleep ( Duration ::from_millis ( 500 ) ) ;
}
} ;
send_retries = if let Some ( result ) = status . clone ( ) {
match result {
Ok ( _ ) = > 0 ,
Err ( TransactionError ::AccountInUse ) = > {
// Fetch a new blockhash and re-sign the transaction before sending it again
self . resign_transaction ( transaction , signer_keys ) ? ;
send_retries - 1
}
// If transaction errors, return right away; no point in counting confirmations
Err ( _ ) = > 0 ,
}
} else {
send_retries - 1
} ;
if send_retries = = 0 {
if let Some ( result ) = status {
match result {
Ok ( _ ) = > {
break signature_str ;
}
Err ( err ) = > {
return Err ( err . into ( ) ) ;
}
}
} else {
return Err (
RpcError ::ForUser ( " unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds " . to_string ( ) ) . into ( ) ,
) ;
}
}
} ;
let signature = Signature ::from_str ( & signature_str ) . map_err ( | _ | {
ClientError ::from ( ClientErrorKind ::Custom ( format! (
" Returned string {} cannot be parsed as a signature " ,
signature_str
) ) )
} ) ? ;
loop {
// Return when default (max) commitment is reached
// Failed transactions have already been eliminated, `is_some` check is sufficient
if self . get_signature_status ( & signature_str ) ? . is_some ( ) {
progress_bar . set_message ( " Transaction confirmed " ) ;
progress_bar . finish_and_clear ( ) ;
return Ok ( signature_str ) ;
}
progress_bar . set_message ( & format! (
" [{}/{}] Waiting for confirmations " ,
2020-03-19 11:10:35 -07:00
confirmations + 1 ,
MAX_LOCKOUT_HISTORY + 1 ,
2020-03-17 16:58:02 -07:00
) ) ;
sleep ( Duration ::from_millis ( 500 ) ) ;
confirmations = self . get_num_blocks_since_signature_confirmation ( & signature ) ? ;
}
}
2020-03-12 23:20:49 -07:00
pub fn validator_exit ( & self ) -> ClientResult < bool > {
2019-03-16 22:37:20 -07:00
let response = self
. client
2019-12-18 21:26:11 -08:00
. send ( & RpcRequest ::ValidatorExit , Value ::Null , 0 )
2020-03-12 23:20:49 -07:00
. map_err ( | err | err . into_with_command ( " ValidatorExit " ) ) ? ;
serde_json ::from_value ( response )
. map_err ( | err | ClientError ::new_with_command ( err . into ( ) , " ValidatorExit " ) )
2019-03-16 22:37:20 -07:00
}
2020-03-12 23:20:49 -07:00
pub fn send ( & self , request : & RpcRequest , params : Value , retries : usize ) -> ClientResult < Value > {
2019-12-18 21:26:11 -08:00
assert! ( params . is_array ( ) | | params . is_null ( ) ) ;
self . client . send ( request , params , retries )
2019-03-16 22:37:20 -07:00
}
}
2020-03-17 16:58:02 -07:00
fn new_spinner_progress_bar ( ) -> ProgressBar {
let progress_bar = ProgressBar ::new ( 42 ) ;
progress_bar
. set_style ( ProgressStyle ::default_spinner ( ) . template ( " {spinner:.green} {wide_msg} " ) ) ;
progress_bar . enable_steady_tick ( 100 ) ;
progress_bar
}
2019-03-16 22:37:20 -07:00
pub fn get_rpc_request_str ( rpc_addr : SocketAddr , tls : bool ) -> String {
if tls {
format! ( " https:// {} " , rpc_addr )
} else {
format! ( " http:// {} " , rpc_addr )
}
}
#[ cfg(test) ]
mod tests {
use super ::* ;
2020-03-12 23:20:49 -07:00
use crate ::{
client_error ::ClientErrorKind ,
mock_rpc_client_request ::{ PUBKEY , SIGNATURE } ,
} ;
2019-12-09 19:35:34 -08:00
use assert_matches ::assert_matches ;
2019-03-16 22:37:20 -07:00
use jsonrpc_core ::{ Error , IoHandler , Params } ;
use jsonrpc_http_server ::{ AccessControlAllowOrigin , DomainsValidation , ServerBuilder } ;
use serde_json ::Number ;
use solana_logger ;
2019-11-06 13:15:00 -08:00
use solana_sdk ::{
2020-02-12 13:15:12 -08:00
instruction ::InstructionError , signature ::Keypair , system_transaction ,
2019-11-06 13:15:00 -08:00
transaction ::TransactionError ,
} ;
2020-03-12 23:20:49 -07:00
use std ::{ io , sync ::mpsc ::channel , thread } ;
2019-03-16 22:37:20 -07:00
#[ test ]
2019-11-13 08:43:15 -08:00
fn test_send ( ) {
2019-03-16 22:37:20 -07:00
let ( sender , receiver ) = channel ( ) ;
thread ::spawn ( move | | {
let rpc_addr = " 0.0.0.0:0 " . parse ( ) . unwrap ( ) ;
let mut io = IoHandler ::default ( ) ;
// Successful request
io . add_method ( " getBalance " , | _params : Params | {
Ok ( Value ::Number ( Number ::from ( 50 ) ) )
} ) ;
// Failed request
io . add_method ( " getRecentBlockhash " , | params : Params | {
if params ! = Params ::None {
Err ( Error ::invalid_request ( ) )
} else {
Ok ( Value ::String (
" deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx " . to_string ( ) ,
) )
}
} ) ;
let server = ServerBuilder ::new ( io )
. threads ( 1 )
. cors ( DomainsValidation ::AllowOnly ( vec! [
AccessControlAllowOrigin ::Any ,
] ) )
. start_http ( & rpc_addr )
. expect ( " Unable to start RPC server " ) ;
sender . send ( * server . address ( ) ) . unwrap ( ) ;
server . wait ( ) ;
} ) ;
let rpc_addr = receiver . recv ( ) . unwrap ( ) ;
let rpc_client = RpcClient ::new_socket ( rpc_addr ) ;
2019-11-13 08:43:15 -08:00
let balance = rpc_client . send (
2019-03-16 22:37:20 -07:00
& RpcRequest ::GetBalance ,
2019-12-18 21:26:11 -08:00
json! ( [ " deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx " ] ) ,
2019-03-16 22:37:20 -07:00
0 ,
) ;
assert_eq! ( balance . unwrap ( ) . as_u64 ( ) . unwrap ( ) , 50 ) ;
2019-12-18 21:26:11 -08:00
let blockhash = rpc_client . send ( & RpcRequest ::GetRecentBlockhash , Value ::Null , 0 ) ;
2019-03-16 22:37:20 -07:00
assert_eq! (
blockhash . unwrap ( ) . as_str ( ) . unwrap ( ) ,
" deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx "
) ;
// Send erroneous parameter
2019-12-18 21:26:11 -08:00
let blockhash = rpc_client . send ( & RpcRequest ::GetRecentBlockhash , json! ( [ " parameter " ] ) , 0 ) ;
2019-03-16 22:37:20 -07:00
assert_eq! ( blockhash . is_err ( ) , true ) ;
}
#[ test ]
2019-11-13 08:43:15 -08:00
fn test_retry_send ( ) {
2019-03-16 22:37:20 -07:00
solana_logger ::setup ( ) ;
let ( sender , receiver ) = channel ( ) ;
thread ::spawn ( move | | {
// 1. Pick a random port
// 2. Tell the client to start using it
// 3. Delay for 1.5 seconds before starting the server to ensure the client will fail
// and need to retry
let rpc_addr : SocketAddr = " 0.0.0.0:4242 " . parse ( ) . unwrap ( ) ;
sender . send ( rpc_addr . clone ( ) ) . unwrap ( ) ;
sleep ( Duration ::from_millis ( 1500 ) ) ;
let mut io = IoHandler ::default ( ) ;
io . add_method ( " getBalance " , move | _params : Params | {
Ok ( Value ::Number ( Number ::from ( 5 ) ) )
} ) ;
let server = ServerBuilder ::new ( io )
. threads ( 1 )
. cors ( DomainsValidation ::AllowOnly ( vec! [
AccessControlAllowOrigin ::Any ,
] ) )
. start_http ( & rpc_addr )
. expect ( " Unable to start RPC server " ) ;
server . wait ( ) ;
} ) ;
let rpc_addr = receiver . recv ( ) . unwrap ( ) ;
let rpc_client = RpcClient ::new_socket ( rpc_addr ) ;
2019-11-13 08:43:15 -08:00
let balance = rpc_client . send (
2019-03-16 22:37:20 -07:00
& RpcRequest ::GetBalance ,
2019-12-18 21:26:11 -08:00
json! ( [ " deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhw " ] ) ,
2019-03-16 22:37:20 -07:00
10 ,
) ;
assert_eq! ( balance . unwrap ( ) . as_u64 ( ) . unwrap ( ) , 5 ) ;
}
2019-03-16 17:17:44 -07:00
#[ test ]
fn test_send_transaction ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
let key = Keypair ::new ( ) ;
2019-03-30 20:37:33 -07:00
let to = Pubkey ::new_rand ( ) ;
2019-03-16 17:17:44 -07:00
let blockhash = Hash ::default ( ) ;
2019-10-23 22:01:22 -07:00
let tx = system_transaction ::transfer ( & key , & to , 50 , blockhash ) ;
2019-03-16 17:17:44 -07:00
let signature = rpc_client . send_transaction ( & tx ) ;
assert_eq! ( signature . unwrap ( ) , SIGNATURE . to_string ( ) ) ;
let rpc_client = RpcClient ::new_mock ( " fails " . to_string ( ) ) ;
let signature = rpc_client . send_transaction ( & tx ) ;
assert! ( signature . is_err ( ) ) ;
}
#[ test ]
fn test_get_recent_blockhash ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
2019-05-16 21:43:18 -07:00
let expected_blockhash : Hash = PUBKEY . parse ( ) . unwrap ( ) ;
2019-03-16 17:17:44 -07:00
2019-05-13 12:49:37 -07:00
let ( blockhash , _fee_calculator ) = rpc_client . get_recent_blockhash ( ) . expect ( " blockhash ok " ) ;
2019-03-16 17:17:44 -07:00
assert_eq! ( blockhash , expected_blockhash ) ;
let rpc_client = RpcClient ::new_mock ( " fails " . to_string ( ) ) ;
2019-05-13 12:49:37 -07:00
assert! ( rpc_client . get_recent_blockhash ( ) . is_err ( ) ) ;
2019-03-16 17:17:44 -07:00
}
#[ test ]
fn test_get_signature_status ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
let signature = " good_signature " ;
2019-04-05 19:56:17 -07:00
let status = rpc_client . get_signature_status ( & signature ) . unwrap ( ) ;
assert_eq! ( status , Some ( Ok ( ( ) ) ) ) ;
2019-03-16 17:17:44 -07:00
2019-04-05 19:56:17 -07:00
let rpc_client = RpcClient ::new_mock ( " sig_not_found " . to_string ( ) ) ;
let signature = " sig_not_found " ;
let status = rpc_client . get_signature_status ( & signature ) . unwrap ( ) ;
assert_eq! ( status , None ) ;
2019-03-16 17:17:44 -07:00
2019-04-05 19:56:17 -07:00
let rpc_client = RpcClient ::new_mock ( " account_in_use " . to_string ( ) ) ;
let signature = " account_in_use " ;
let status = rpc_client . get_signature_status ( & signature ) . unwrap ( ) ;
assert_eq! ( status , Some ( Err ( TransactionError ::AccountInUse ) ) ) ;
2019-03-16 17:17:44 -07:00
}
#[ test ]
fn test_send_and_confirm_transaction ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
let key = Keypair ::new ( ) ;
2019-03-30 20:37:33 -07:00
let to = Pubkey ::new_rand ( ) ;
2019-03-16 17:17:44 -07:00
let blockhash = Hash ::default ( ) ;
2019-10-23 22:01:22 -07:00
let mut tx = system_transaction ::transfer ( & key , & to , 50 , blockhash ) ;
2019-03-16 17:17:44 -07:00
2019-05-07 15:00:54 -07:00
let result = rpc_client . send_and_confirm_transaction ( & mut tx , & [ & key ] ) ;
2019-03-16 17:17:44 -07:00
result . unwrap ( ) ;
let rpc_client = RpcClient ::new_mock ( " account_in_use " . to_string ( ) ) ;
2019-05-07 15:00:54 -07:00
let result = rpc_client . send_and_confirm_transaction ( & mut tx , & [ & key ] ) ;
2019-03-16 17:17:44 -07:00
assert! ( result . is_err ( ) ) ;
2019-12-09 19:35:34 -08:00
let rpc_client = RpcClient ::new_mock ( " instruction_error " . to_string ( ) ) ;
2019-05-07 15:00:54 -07:00
let result = rpc_client . send_and_confirm_transaction ( & mut tx , & [ & key ] ) ;
2019-12-09 19:35:34 -08:00
assert_matches! (
2020-03-12 23:20:49 -07:00
result . unwrap_err ( ) . kind ( ) ,
ClientErrorKind ::TransactionError ( TransactionError ::InstructionError (
2019-12-09 19:35:34 -08:00
0 ,
InstructionError ::UninitializedAccount
) )
) ;
let rpc_client = RpcClient ::new_mock ( " sig_not_found " . to_string ( ) ) ;
let result = rpc_client . send_and_confirm_transaction ( & mut tx , & [ & key ] ) ;
2020-03-12 23:20:49 -07:00
if let ClientErrorKind ::Io ( err ) = result . unwrap_err ( ) . kind ( ) {
2019-12-09 19:35:34 -08:00
assert_eq! ( err . kind ( ) , io ::ErrorKind ::Other ) ;
}
2019-03-16 17:17:44 -07:00
}
#[ test ]
fn test_resign_transaction ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
let key = Keypair ::new ( ) ;
2019-03-30 20:37:33 -07:00
let to = Pubkey ::new_rand ( ) ;
2019-05-16 21:43:18 -07:00
let blockhash : Hash = " HUu3LwEzGRsUkuJS121jzkPJW39Kq62pXCTmTa1F9jDL "
. parse ( )
2019-03-16 17:17:44 -07:00
. unwrap ( ) ;
2019-10-23 22:01:22 -07:00
let prev_tx = system_transaction ::transfer ( & key , & to , 50 , blockhash ) ;
let mut tx = system_transaction ::transfer ( & key , & to , 50 , blockhash ) ;
2019-03-16 17:17:44 -07:00
2019-05-07 15:00:54 -07:00
rpc_client . resign_transaction ( & mut tx , & [ & key ] ) . unwrap ( ) ;
2019-03-16 17:17:44 -07:00
assert_ne! ( prev_tx , tx ) ;
assert_ne! ( prev_tx . signatures , tx . signatures ) ;
2019-03-29 09:05:06 -07:00
assert_ne! (
prev_tx . message ( ) . recent_blockhash ,
tx . message ( ) . recent_blockhash
) ;
2019-03-16 17:17:44 -07:00
}
2019-04-19 07:54:21 -07:00
#[ test ]
fn test_rpc_client_thread ( ) {
let rpc_client = RpcClient ::new_mock ( " succeeds " . to_string ( ) ) ;
thread ::spawn ( move | | rpc_client ) ;
}
2019-03-16 22:37:20 -07:00
}