@@ -9,7 +9,7 @@ pub(crate) mod remainder;
9
9
pub ( crate ) mod requirement;
10
10
pub ( crate ) mod transition;
11
11
12
- use alloc:: collections:: BTreeMap ;
12
+ use alloc:: collections:: { BTreeMap , VecDeque } ;
13
13
use std:: collections:: { HashMap , HashSet } ;
14
14
15
15
use crypto:: keys:: bip44:: Bip44 ;
@@ -180,7 +180,7 @@ impl Client {
180
180
pub struct TransactionBuilder {
181
181
available_inputs : Vec < InputSigningData > ,
182
182
required_inputs : HashSet < OutputId > ,
183
- selected_inputs : Vec < InputSigningData > ,
183
+ selected_inputs : OrderedInputs ,
184
184
bic_context_inputs : HashSet < BlockIssuanceCreditContextInput > ,
185
185
commitment_context_input : Option < CommitmentContextInput > ,
186
186
reward_context_inputs : HashSet < OutputId > ,
@@ -252,7 +252,7 @@ impl TransactionBuilder {
252
252
Self {
253
253
available_inputs,
254
254
required_inputs : HashSet :: new ( ) ,
255
- selected_inputs : Vec :: new ( ) ,
255
+ selected_inputs : Default :: default ( ) ,
256
256
bic_context_inputs : HashSet :: new ( ) ,
257
257
commitment_context_input : None ,
258
258
reward_context_inputs : HashSet :: new ( ) ,
@@ -456,7 +456,7 @@ impl TransactionBuilder {
456
456
457
457
let data = PreparedTransactionData {
458
458
transaction,
459
- inputs_data : self . selected_inputs ,
459
+ inputs_data : self . selected_inputs . into_iter ( ) . collect ( ) ,
460
460
remainders : self . remainders . data ,
461
461
mana_rewards : self . mana_rewards . into_iter ( ) . collect ( ) ,
462
462
} ;
@@ -485,7 +485,16 @@ impl TransactionBuilder {
485
485
self . requirements . push ( requirement) ;
486
486
}
487
487
488
- self . insert_input_sorted ( input) ;
488
+ let required_address = input
489
+ . output
490
+ . required_address (
491
+ self . latest_slot_commitment_id . slot_index ( ) ,
492
+ self . protocol_parameters . committable_age_range ( ) ,
493
+ )
494
+ // PANIC: safe to unwrap as non basic/account/foundry/nft/delegation outputs are already filtered out.
495
+ . unwrap ( )
496
+ . expect ( "expiration unlockable outputs already filtered out" ) ;
497
+ self . selected_inputs . insert ( input, required_address) ;
489
498
490
499
// New inputs/outputs may need context inputs
491
500
if !self . requirements . contains ( & Requirement :: ContextInputs ) {
@@ -671,102 +680,119 @@ impl TransactionBuilder {
671
680
}
672
681
} )
673
682
}
683
+ }
674
684
675
- fn insert_input_sorted ( & mut self , input : InputSigningData ) {
676
- let input_required_address = input
677
- . output
678
- . required_address (
679
- self . latest_slot_commitment_id . slot_index ( ) ,
680
- self . protocol_parameters . committable_age_range ( ) ,
681
- )
682
- // PANIC: safe to unwrap as non basic/account/foundry/nft/delegation outputs are already filtered out.
683
- . unwrap ( )
684
- . expect ( "expiration unlockable outputs already filtered out" ) ;
685
- if input_required_address. is_ed25519 ( ) {
686
- if let Some ( position) = self
687
- . selected_inputs
688
- . iter ( )
689
- . position ( |i| i. output_id ( ) > input. output_id ( ) )
690
- {
691
- self . selected_inputs . insert ( position, input) ;
692
- } else {
693
- self . selected_inputs . push ( input) ;
694
- }
685
+ #[ derive( Clone , Debug , Default ) ]
686
+ pub ( crate ) struct OrderedInputs {
687
+ ed25519 : VecDeque < InputSigningData > ,
688
+ other : BTreeMap < Address , Vec < InputSigningData > > ,
689
+ len : usize ,
690
+ }
691
+
692
+ impl OrderedInputs {
693
+ pub ( crate ) fn iter ( & self ) -> <& Self as IntoIterator >:: IntoIter {
694
+ self . into_iter ( )
695
+ }
696
+
697
+ pub ( crate ) fn insert ( & mut self , input : InputSigningData , required_address : Address ) {
698
+ if required_address. is_ed25519 ( ) {
699
+ self . ed25519 . push_back ( input) ;
695
700
} else {
696
- match self . selected_inputs . iter ( ) . position ( |input_signing_data| {
697
- let required_address = input_signing_data
698
- . output
699
- . required_address (
700
- self . latest_slot_commitment_id . slot_index ( ) ,
701
- self . protocol_parameters . committable_age_range ( ) ,
702
- )
703
- // PANIC: safe to unwrap as non basic/account/foundry/nft/delegation outputs are already filtered
704
- // out.
705
- . unwrap ( )
706
- . expect ( "expiration unlockable outputs already filtered out" ) ;
707
- match required_address {
708
- Address :: Account ( unlock_address) => {
709
- if let Output :: Account ( account_output) = & input_signing_data. output {
710
- * unlock_address. account_id ( )
711
- == account_output. account_id_non_null ( input_signing_data. output_id ( ) )
712
- } else {
713
- false
714
- }
715
- }
716
- Address :: Nft ( unlock_address) => {
717
- if let Output :: Nft ( nft_output) = & input_signing_data. output {
718
- * unlock_address. nft_id ( ) == nft_output. nft_id_non_null ( input_signing_data. output_id ( ) )
719
- } else {
720
- false
721
- }
722
- }
723
- _ => false ,
701
+ self . other . entry ( required_address) . or_default ( ) . push ( input) ;
702
+ }
703
+ self . len += 1 ;
704
+ }
705
+
706
+ pub ( crate ) fn len ( & self ) -> usize {
707
+ self . len
708
+ }
709
+
710
+ pub ( crate ) fn is_empty ( & self ) -> bool {
711
+ self . len ( ) == 0
712
+ }
713
+ }
714
+
715
+ impl < ' a > IntoIterator for & ' a OrderedInputs {
716
+ type Item = & ' a InputSigningData ;
717
+ type IntoIter = alloc:: vec:: IntoIter < Self :: Item > ;
718
+
719
+ fn into_iter ( self ) -> Self :: IntoIter {
720
+ let mut other = self
721
+ . other
722
+ . iter ( )
723
+ . map ( |( k, v) | ( k, v. iter ( ) . collect :: < VecDeque < _ > > ( ) ) )
724
+ . collect :: < BTreeMap < _ , _ > > ( ) ;
725
+ let mut inputs = Vec :: new ( ) ;
726
+ let mut queue = self . ed25519 . iter ( ) . collect :: < VecDeque < _ > > ( ) ;
727
+ while let Some ( input) = queue. pop_front ( ) {
728
+ // Add associated inputs to the front of the queue
729
+ match & input. output {
730
+ Output :: Account ( account_output) => {
731
+ queue = other
732
+ . remove ( & Address :: Account ( AccountAddress :: new (
733
+ account_output. account_id_non_null ( input. output_id ( ) ) ,
734
+ ) ) )
735
+ . into_iter ( )
736
+ . flatten ( )
737
+ . chain ( queue)
738
+ . collect ( )
724
739
}
725
- } ) {
726
- Some ( position) => {
727
- // Insert after the output we need
728
- self . selected_inputs . insert ( position + 1 , input) ;
740
+ Output :: Nft ( nft_output) => {
741
+ queue = other
742
+ . remove ( & Address :: Nft ( NftAddress :: new (
743
+ nft_output. nft_id_non_null ( input. output_id ( ) ) ,
744
+ ) ) )
745
+ . into_iter ( )
746
+ . flatten ( )
747
+ . chain ( queue)
748
+ . collect ( )
729
749
}
730
- None => {
731
- // insert before address
732
- let account_or_nft_address = match & input. output {
733
- Output :: Account ( account_output) => Some ( Address :: Account ( AccountAddress :: new (
750
+ _ => ( ) ,
751
+ } ;
752
+ inputs. push ( input) ;
753
+ }
754
+ inputs. extend ( other. into_values ( ) . flatten ( ) ) ;
755
+ inputs. into_iter ( )
756
+ }
757
+ }
758
+
759
+ impl IntoIterator for OrderedInputs {
760
+ type Item = InputSigningData ;
761
+ type IntoIter = alloc:: vec:: IntoIter < Self :: Item > ;
762
+
763
+ fn into_iter ( mut self ) -> Self :: IntoIter {
764
+ let mut inputs = Vec :: new ( ) ;
765
+ let mut queue = self . ed25519 ;
766
+ while let Some ( input) = queue. pop_front ( ) {
767
+ // Add associated inputs to the front of the queue
768
+ match & input. output {
769
+ Output :: Account ( account_output) => {
770
+ queue = self
771
+ . other
772
+ . remove ( & Address :: Account ( AccountAddress :: new (
734
773
account_output. account_id_non_null ( input. output_id ( ) ) ,
735
- ) ) ) ,
736
- Output :: Nft ( nft_output) => Some ( Address :: Nft ( NftAddress :: new (
774
+ ) ) )
775
+ . into_iter ( )
776
+ . flatten ( )
777
+ . chain ( queue)
778
+ . collect ( )
779
+ }
780
+ Output :: Nft ( nft_output) => {
781
+ queue = self
782
+ . other
783
+ . remove ( & Address :: Nft ( NftAddress :: new (
737
784
nft_output. nft_id_non_null ( input. output_id ( ) ) ,
738
- ) ) ) ,
739
- _ => None ,
740
- } ;
741
-
742
- if let Some ( account_or_nft_address) = account_or_nft_address {
743
- // Check for existing outputs for this address, and insert before
744
- match self . selected_inputs . iter ( ) . position ( |input_signing_data| {
745
- let required_address = input_signing_data
746
- . output
747
- . required_address (
748
- self . latest_slot_commitment_id . slot_index ( ) ,
749
- self . protocol_parameters . committable_age_range ( ) ,
750
- )
751
- // PANIC: safe to unwrap as non basic/account/foundry/nft/delegation outputs are already
752
- // filtered out.
753
- . unwrap ( )
754
- . expect ( "expiration unlockable outputs already filtered out" ) ;
755
- required_address == account_or_nft_address
756
- } ) {
757
- Some ( position) => {
758
- // Insert before the output with this address required for unlocking
759
- self . selected_inputs . insert ( position, input) ;
760
- }
761
- // just push output
762
- None => self . selected_inputs . push ( input) ,
763
- }
764
- } else {
765
- // just push basic or foundry output
766
- self . selected_inputs . push ( input) ;
767
- }
785
+ ) ) )
786
+ . into_iter ( )
787
+ . flatten ( )
788
+ . chain ( queue)
789
+ . collect ( )
768
790
}
769
- }
791
+ _ => ( ) ,
792
+ } ;
793
+ inputs. push ( input) ;
770
794
}
795
+ inputs. extend ( self . other . into_values ( ) . flatten ( ) ) ;
796
+ inputs. into_iter ( )
771
797
}
772
798
}
0 commit comments