@@ -11,7 +11,7 @@ use solana_client::{
11
11
rpc_client:: RpcClient ,
12
12
rpc_config:: { RpcSendTransactionConfig , RpcTransactionConfig } ,
13
13
} ;
14
- use solana_program:: { clock:: Slot , hash:: Hash , pubkey:: Pubkey } ;
14
+ use solana_program:: { clock:: Slot , hash:: Hash , instruction :: InstructionError , pubkey:: Pubkey } ;
15
15
use solana_sdk:: {
16
16
account:: { Account , AccountSharedData } ,
17
17
bs58,
@@ -20,7 +20,7 @@ use solana_sdk::{
20
20
epoch_info:: EpochInfo ,
21
21
instruction:: Instruction ,
22
22
signature:: { Keypair , Signature } ,
23
- transaction:: Transaction ,
23
+ transaction:: { Transaction , TransactionError } ,
24
24
} ;
25
25
use solana_transaction_status:: {
26
26
option_serializer:: OptionSerializer , TransactionStatus , UiInstruction , UiTransactionEncoding ,
@@ -124,6 +124,19 @@ impl SolanaRpcConnection {
124
124
}
125
125
}
126
126
127
+ async fn should_retry ( & self , error : & RpcError ) -> bool {
128
+ match error {
129
+ RpcError :: TransactionError ( TransactionError :: InstructionError (
130
+ _,
131
+ InstructionError :: Custom ( 6004 ) ,
132
+ ) ) => {
133
+ // Don't retry ForesterNotEligible error
134
+ false
135
+ }
136
+ _ => true ,
137
+ }
138
+ }
139
+
127
140
async fn retry < F , Fut , T > ( & self , operation : F ) -> Result < T , RpcError >
128
141
where
129
142
F : Fn ( ) -> Fut ,
@@ -139,6 +152,10 @@ impl SolanaRpcConnection {
139
152
match operation ( ) . await {
140
153
Ok ( result) => return Ok ( result) ,
141
154
Err ( e) => {
155
+ if !self . should_retry ( & e) . await {
156
+ return Err ( e) ;
157
+ }
158
+
142
159
attempts += 1 ;
143
160
if attempts >= self . retry_config . max_retries
144
161
|| start_time. elapsed ( ) >= self . retry_config . timeout
@@ -170,6 +187,10 @@ impl SolanaRpcConnection {
170
187
match operation ( ) . await {
171
188
Ok ( result) => return Ok ( result) ,
172
189
Err ( e) => {
190
+ if !self . should_retry ( & e) . await {
191
+ return Err ( e) ;
192
+ }
193
+
173
194
attempts += 1 ;
174
195
if attempts >= self . retry_config . max_retries
175
196
|| start_time. elapsed ( ) >= self . retry_config . timeout
@@ -269,14 +290,14 @@ impl RpcConnection for SolanaRpcConnection {
269
290
self . rpc_rate_limiter = Some ( rate_limiter) ;
270
291
}
271
292
272
- fn rpc_rate_limiter ( & self ) -> Option < & RateLimiter > {
273
- self . rpc_rate_limiter . as_ref ( )
274
- }
275
-
276
293
fn set_send_tx_rate_limiter ( & mut self , rate_limiter : RateLimiter ) {
277
294
self . send_tx_rate_limiter = Some ( rate_limiter) ;
278
295
}
279
296
297
+ fn rpc_rate_limiter ( & self ) -> Option < & RateLimiter > {
298
+ self . rpc_rate_limiter . as_ref ( )
299
+ }
300
+
280
301
fn send_tx_rate_limiter ( & self ) -> Option < & RateLimiter > {
281
302
self . send_tx_rate_limiter . as_ref ( )
282
303
}
@@ -434,143 +455,6 @@ impl RpcConnection for SolanaRpcConnection {
434
455
Ok ( result)
435
456
}
436
457
437
- async fn create_and_send_transaction_with_public_event (
438
- & mut self ,
439
- instructions : & [ Instruction ] ,
440
- payer : & Pubkey ,
441
- signers : & [ & Keypair ] ,
442
- transaction_params : Option < TransactionParams > ,
443
- ) -> Result < Option < ( PublicTransactionEvent , Signature , Slot ) > , RpcError > {
444
- let pre_balance = self . client . get_balance ( payer) ?;
445
- let latest_blockhash = self . client . get_latest_blockhash ( ) ?;
446
-
447
- let mut instructions_vec = vec ! [
448
- solana_sdk:: compute_budget:: ComputeBudgetInstruction :: set_compute_unit_limit( 1_000_000 ) ,
449
- ] ;
450
- instructions_vec. extend_from_slice ( instructions) ;
451
-
452
- let transaction = Transaction :: new_signed_with_payer (
453
- instructions_vec. as_slice ( ) ,
454
- Some ( payer) ,
455
- signers,
456
- latest_blockhash,
457
- ) ;
458
-
459
- let ( signature, slot) = self
460
- . process_transaction_with_context ( transaction. clone ( ) )
461
- . await ?;
462
-
463
- let mut vec = Vec :: new ( ) ;
464
- let mut vec_accounts = Vec :: new ( ) ;
465
- instructions_vec. iter ( ) . for_each ( |x| {
466
- vec. push ( x. data . clone ( ) ) ;
467
- vec_accounts. push ( x. accounts . iter ( ) . map ( |x| x. pubkey ) . collect ( ) ) ;
468
- } ) ;
469
- {
470
- let rpc_transaction_config = RpcTransactionConfig {
471
- encoding : Some ( UiTransactionEncoding :: Base64 ) ,
472
- commitment : Some ( self . client . commitment ( ) ) ,
473
- ..Default :: default ( )
474
- } ;
475
- let transaction = self
476
- . client
477
- . get_transaction_with_config ( & signature, rpc_transaction_config)
478
- . map_err ( |e| RpcError :: CustomError ( e. to_string ( ) ) ) ?;
479
- let decoded_transaction = transaction
480
- . transaction
481
- . transaction
482
- . decode ( )
483
- . clone ( )
484
- . unwrap ( ) ;
485
- let account_keys = decoded_transaction. message . static_account_keys ( ) ;
486
- let meta = transaction. transaction . meta . as_ref ( ) . ok_or_else ( || {
487
- RpcError :: CustomError ( "Transaction missing metadata information" . to_string ( ) )
488
- } ) ?;
489
- if meta. status . is_err ( ) {
490
- return Err ( RpcError :: CustomError (
491
- "Transaction status indicates an error" . to_string ( ) ,
492
- ) ) ;
493
- }
494
-
495
- let inner_instructions = match & meta. inner_instructions {
496
- OptionSerializer :: Some ( i) => i,
497
- OptionSerializer :: None => {
498
- return Err ( RpcError :: CustomError (
499
- "No inner instructions found" . to_string ( ) ,
500
- ) ) ;
501
- }
502
- OptionSerializer :: Skip => {
503
- return Err ( RpcError :: CustomError (
504
- "No inner instructions found" . to_string ( ) ,
505
- ) ) ;
506
- }
507
- } ;
508
-
509
- for ix in inner_instructions. iter ( ) {
510
- for ui_instruction in ix. instructions . iter ( ) {
511
- match ui_instruction {
512
- UiInstruction :: Compiled ( ui_compiled_instruction) => {
513
- let accounts = & ui_compiled_instruction. accounts ;
514
- let data = bs58:: decode ( & ui_compiled_instruction. data )
515
- . into_vec ( )
516
- . map_err ( |_| {
517
- RpcError :: CustomError (
518
- "Failed to decode instruction data" . to_string ( ) ,
519
- )
520
- } ) ?;
521
- vec. push ( data) ;
522
- vec_accounts. push (
523
- accounts
524
- . iter ( )
525
- . map ( |x| account_keys[ ( * x) as usize ] )
526
- . collect ( ) ,
527
- ) ;
528
- }
529
- UiInstruction :: Parsed ( _) => {
530
- println ! ( "Parsed instructions are not implemented yet" ) ;
531
- }
532
- }
533
- }
534
- }
535
- }
536
- println ! ( "vec: {:?}" , vec) ;
537
- println ! ( "vec_accounts {:?}" , vec_accounts) ;
538
- let ( parsed_event, _new_addresses) =
539
- event_from_light_transaction ( vec. as_slice ( ) , vec_accounts) . unwrap ( ) ;
540
- println ! ( "event: {:?}" , parsed_event) ;
541
-
542
- if let Some ( transaction_params) = transaction_params {
543
- let mut deduped_signers = signers. to_vec ( ) ;
544
- deduped_signers. dedup ( ) ;
545
- let post_balance = self . get_account ( * payer) . await ?. unwrap ( ) . lamports ;
546
- // a network_fee is charged if there are input compressed accounts or new addresses
547
- let mut network_fee: i64 = 0 ;
548
- if transaction_params. num_input_compressed_accounts != 0
549
- || transaction_params. num_output_compressed_accounts != 0
550
- {
551
- network_fee += transaction_params. fee_config . network_fee as i64 ;
552
- }
553
- if transaction_params. num_new_addresses != 0 {
554
- network_fee += transaction_params. fee_config . address_network_fee as i64 ;
555
- }
556
-
557
- let expected_post_balance = pre_balance as i64
558
- - i64:: from ( transaction_params. num_new_addresses )
559
- * transaction_params. fee_config . address_queue_rollover as i64
560
- - i64:: from ( transaction_params. num_output_compressed_accounts )
561
- * transaction_params. fee_config . state_merkle_tree_rollover as i64
562
- - transaction_params. compress
563
- - transaction_params. fee_config . solana_network_fee * deduped_signers. len ( ) as i64
564
- - network_fee;
565
-
566
- if post_balance as i64 != expected_post_balance {
567
- return Err ( RpcError :: AssertRpcError ( format ! ( "unexpected balance after transaction: expected {expected_post_balance}, got {post_balance}" ) ) ) ;
568
- }
569
- }
570
- let event = parsed_event. map ( |e| ( e, signature, slot) ) ;
571
- Ok ( event)
572
- }
573
-
574
458
async fn confirm_transaction ( & self , signature : Signature ) -> Result < bool , RpcError > {
575
459
self . retry ( || async {
576
460
self . client
@@ -690,6 +574,7 @@ impl RpcConnection for SolanaRpcConnection {
690
574
} )
691
575
. await
692
576
}
577
+
693
578
async fn get_transaction_slot ( & mut self , signature : & Signature ) -> Result < u64 , RpcError > {
694
579
self . retry ( || async {
695
580
Ok ( self
@@ -707,7 +592,6 @@ impl RpcConnection for SolanaRpcConnection {
707
592
} )
708
593
. await
709
594
}
710
-
711
595
async fn get_signature_statuses (
712
596
& self ,
713
597
signatures : & [ Signature ] ,
@@ -717,10 +601,147 @@ impl RpcConnection for SolanaRpcConnection {
717
601
. map ( |response| response. value )
718
602
. map_err ( RpcError :: from)
719
603
}
604
+
720
605
async fn get_block_height ( & mut self ) -> Result < u64 , RpcError > {
721
606
self . retry ( || async { self . client . get_block_height ( ) . map_err ( RpcError :: from) } )
722
607
. await
723
608
}
609
+ async fn create_and_send_transaction_with_public_event (
610
+ & mut self ,
611
+ instructions : & [ Instruction ] ,
612
+ payer : & Pubkey ,
613
+ signers : & [ & Keypair ] ,
614
+ transaction_params : Option < TransactionParams > ,
615
+ ) -> Result < Option < ( PublicTransactionEvent , Signature , Slot ) > , RpcError > {
616
+ let pre_balance = self . client . get_balance ( payer) ?;
617
+ let latest_blockhash = self . client . get_latest_blockhash ( ) ?;
618
+
619
+ let mut instructions_vec = vec ! [
620
+ solana_sdk:: compute_budget:: ComputeBudgetInstruction :: set_compute_unit_limit( 1_000_000 ) ,
621
+ ] ;
622
+ instructions_vec. extend_from_slice ( instructions) ;
623
+
624
+ let transaction = Transaction :: new_signed_with_payer (
625
+ instructions_vec. as_slice ( ) ,
626
+ Some ( payer) ,
627
+ signers,
628
+ latest_blockhash,
629
+ ) ;
630
+
631
+ let ( signature, slot) = self
632
+ . process_transaction_with_context ( transaction. clone ( ) )
633
+ . await ?;
634
+
635
+ let mut vec = Vec :: new ( ) ;
636
+ let mut vec_accounts = Vec :: new ( ) ;
637
+ instructions_vec. iter ( ) . for_each ( |x| {
638
+ vec. push ( x. data . clone ( ) ) ;
639
+ vec_accounts. push ( x. accounts . iter ( ) . map ( |x| x. pubkey ) . collect ( ) ) ;
640
+ } ) ;
641
+ {
642
+ let rpc_transaction_config = RpcTransactionConfig {
643
+ encoding : Some ( UiTransactionEncoding :: Base64 ) ,
644
+ commitment : Some ( self . client . commitment ( ) ) ,
645
+ ..Default :: default ( )
646
+ } ;
647
+ let transaction = self
648
+ . client
649
+ . get_transaction_with_config ( & signature, rpc_transaction_config)
650
+ . map_err ( |e| RpcError :: CustomError ( e. to_string ( ) ) ) ?;
651
+ let decoded_transaction = transaction
652
+ . transaction
653
+ . transaction
654
+ . decode ( )
655
+ . clone ( )
656
+ . unwrap ( ) ;
657
+ let account_keys = decoded_transaction. message . static_account_keys ( ) ;
658
+ let meta = transaction. transaction . meta . as_ref ( ) . ok_or_else ( || {
659
+ RpcError :: CustomError ( "Transaction missing metadata information" . to_string ( ) )
660
+ } ) ?;
661
+ if meta. status . is_err ( ) {
662
+ return Err ( RpcError :: CustomError (
663
+ "Transaction status indicates an error" . to_string ( ) ,
664
+ ) ) ;
665
+ }
666
+
667
+ let inner_instructions = match & meta. inner_instructions {
668
+ OptionSerializer :: Some ( i) => i,
669
+ OptionSerializer :: None => {
670
+ return Err ( RpcError :: CustomError (
671
+ "No inner instructions found" . to_string ( ) ,
672
+ ) ) ;
673
+ }
674
+ OptionSerializer :: Skip => {
675
+ return Err ( RpcError :: CustomError (
676
+ "No inner instructions found" . to_string ( ) ,
677
+ ) ) ;
678
+ }
679
+ } ;
680
+
681
+ for ix in inner_instructions. iter ( ) {
682
+ for ui_instruction in ix. instructions . iter ( ) {
683
+ match ui_instruction {
684
+ UiInstruction :: Compiled ( ui_compiled_instruction) => {
685
+ let accounts = & ui_compiled_instruction. accounts ;
686
+ let data = bs58:: decode ( & ui_compiled_instruction. data )
687
+ . into_vec ( )
688
+ . map_err ( |_| {
689
+ RpcError :: CustomError (
690
+ "Failed to decode instruction data" . to_string ( ) ,
691
+ )
692
+ } ) ?;
693
+ vec. push ( data) ;
694
+ vec_accounts. push (
695
+ accounts
696
+ . iter ( )
697
+ . map ( |x| account_keys[ ( * x) as usize ] )
698
+ . collect ( ) ,
699
+ ) ;
700
+ }
701
+ UiInstruction :: Parsed ( _) => {
702
+ println ! ( "Parsed instructions are not implemented yet" ) ;
703
+ }
704
+ }
705
+ }
706
+ }
707
+ }
708
+ println ! ( "vec: {:?}" , vec) ;
709
+ println ! ( "vec_accounts {:?}" , vec_accounts) ;
710
+ let ( parsed_event, _new_addresses) =
711
+ event_from_light_transaction ( vec. as_slice ( ) , vec_accounts) . unwrap ( ) ;
712
+ println ! ( "event: {:?}" , parsed_event) ;
713
+
714
+ if let Some ( transaction_params) = transaction_params {
715
+ let mut deduped_signers = signers. to_vec ( ) ;
716
+ deduped_signers. dedup ( ) ;
717
+ let post_balance = self . get_account ( * payer) . await ?. unwrap ( ) . lamports ;
718
+ // a network_fee is charged if there are input compressed accounts or new addresses
719
+ let mut network_fee: i64 = 0 ;
720
+ if transaction_params. num_input_compressed_accounts != 0
721
+ || transaction_params. num_output_compressed_accounts != 0
722
+ {
723
+ network_fee += transaction_params. fee_config . network_fee as i64 ;
724
+ }
725
+ if transaction_params. num_new_addresses != 0 {
726
+ network_fee += transaction_params. fee_config . address_network_fee as i64 ;
727
+ }
728
+
729
+ let expected_post_balance = pre_balance as i64
730
+ - i64:: from ( transaction_params. num_new_addresses )
731
+ * transaction_params. fee_config . address_queue_rollover as i64
732
+ - i64:: from ( transaction_params. num_output_compressed_accounts )
733
+ * transaction_params. fee_config . state_merkle_tree_rollover as i64
734
+ - transaction_params. compress
735
+ - transaction_params. fee_config . solana_network_fee * deduped_signers. len ( ) as i64
736
+ - network_fee;
737
+
738
+ if post_balance as i64 != expected_post_balance {
739
+ return Err ( RpcError :: AssertRpcError ( format ! ( "unexpected balance after transaction: expected {expected_post_balance}, got {post_balance}" ) ) ) ;
740
+ }
741
+ }
742
+ let event = parsed_event. map ( |e| ( e, signature, slot) ) ;
743
+ Ok ( event)
744
+ }
724
745
}
725
746
726
747
impl MerkleTreeExt for SolanaRpcConnection { }
0 commit comments