@@ -132,34 +132,21 @@ impl AssetsInHolding {
132132
133133 /// Mutate `self` to contain all given `assets`, saturating if necessary.
134134 ///
135- /// NOTE: [`AssetsInHolding`] are always sorted, allowing us to optimize this function from
136- /// `O(n^2)` to `O(n)`.
135+ /// NOTE: [`AssetsInHolding`] are always sorted
137136 pub fn subsume_assets ( & mut self , mut assets : AssetsInHolding ) {
138- let mut f_iter = assets. fungible . iter_mut ( ) ;
139- let mut g_iter = self . fungible . iter_mut ( ) ;
140- if let ( Some ( mut f) , Some ( mut g) ) = ( f_iter. next ( ) , g_iter. next ( ) ) {
141- loop {
142- if f. 0 == g. 0 {
143- // keys are equal. in this case, we add `self`'s balance for the asset onto
144- // `assets`, balance, knowing that the `append` operation which follows will
145- // clobber `self`'s value and only use `assets`'s.
146- ( * f. 1 ) . saturating_accrue ( * g. 1 ) ;
147- }
148- if f. 0 <= g. 0 {
149- f = match f_iter. next ( ) {
150- Some ( x) => x,
151- None => break ,
152- } ;
153- }
154- if f. 0 >= g. 0 {
155- g = match g_iter. next ( ) {
156- Some ( x) => x,
157- None => break ,
158- } ;
159- }
160- }
137+ // for fungibles, find matching fungibles and sum their amounts so we end-up having just
138+ // single such fungible but with increased amount inside
139+ for ( asset_id, asset_amount) in assets. fungible {
140+ self . fungible
141+ . entry ( asset_id)
142+ . and_modify ( |current_asset_amount| {
143+ current_asset_amount. saturating_accrue ( asset_amount)
144+ } )
145+ . or_insert ( asset_amount) ;
161146 }
162- self . fungible . append ( & mut assets. fungible ) ;
147+ // for non-fungibles, every entry is unique so there is no notion of amount to sum-up
148+ // together if there is the same non-fungible in both holdings (same instance_id) these
149+ // will be collapsed into just single one
163150 self . non_fungible . append ( & mut assets. non_fungible ) ;
164151 }
165152
@@ -530,6 +517,21 @@ mod tests {
530517 ( Here , amount) . into ( )
531518 }
532519 #[ allow( non_snake_case) ]
520+ /// Concrete fungible constructor with index for GeneralIndex
521+ fn CFG ( index : u128 , amount : u128 ) -> Asset {
522+ ( GeneralIndex ( index) , amount) . into ( )
523+ }
524+ #[ allow( non_snake_case) ]
525+ /// Concrete fungible constructor (parent=1)
526+ fn CFP ( amount : u128 ) -> Asset {
527+ ( Parent , amount) . into ( )
528+ }
529+ #[ allow( non_snake_case) ]
530+ /// Concrete fungible constructor (parent=2)
531+ fn CFPP ( amount : u128 ) -> Asset {
532+ ( ( Parent , Parent ) , amount) . into ( )
533+ }
534+ #[ allow( non_snake_case) ]
533535 /// Concrete non-fungible constructor
534536 fn CNF ( instance_id : u8 ) -> Asset {
535537 ( Here , [ instance_id; 4 ] ) . into ( )
@@ -543,18 +545,156 @@ mod tests {
543545 }
544546
545547 #[ test]
546- fn subsume_assets_works ( ) {
547- let t1 = test_assets ( ) ;
548+ fn assets_in_holding_order_works ( ) {
549+ // populate assets in non-ordered fashion
550+ let mut assets = AssetsInHolding :: new ( ) ;
551+ assets. subsume ( CFPP ( 300 ) ) ;
552+ assets. subsume ( CFP ( 200 ) ) ;
553+ assets. subsume ( CNF ( 2 ) ) ;
554+ assets. subsume ( CF ( 100 ) ) ;
555+ assets. subsume ( CNF ( 1 ) ) ;
556+ assets. subsume ( CFG ( 10 , 400 ) ) ;
557+ assets. subsume ( CFG ( 15 , 500 ) ) ;
558+
559+ // following is the order we expect from AssetsInHolding
560+ // - fungibles before non-fungibles
561+ // - for fungibles, sort by parent first, if parents match, then by other components like
562+ // general index
563+ // - for non-fungibles, sort by instance_id
564+ let mut iter = assets. clone ( ) . into_assets_iter ( ) ;
565+ // fungible, order by parent, parent=0
566+ assert_eq ! ( Some ( CF ( 100 ) ) , iter. next( ) ) ;
567+ // fungible, order by parent then by general index, parent=0, general index=10
568+ assert_eq ! ( Some ( CFG ( 10 , 400 ) ) , iter. next( ) ) ;
569+ // fungible, order by parent then by general index, parent=0, general index=15
570+ assert_eq ! ( Some ( CFG ( 15 , 500 ) ) , iter. next( ) ) ;
571+ // fungible, order by parent, parent=1
572+ assert_eq ! ( Some ( CFP ( 200 ) ) , iter. next( ) ) ;
573+ // fungible, order by parent, parent=2
574+ assert_eq ! ( Some ( CFPP ( 300 ) ) , iter. next( ) ) ;
575+ // non-fungible, after fungibles, order by instance id, id=1
576+ assert_eq ! ( Some ( CNF ( 1 ) ) , iter. next( ) ) ;
577+ // non-fungible, after fungibles, order by instance id, id=2
578+ assert_eq ! ( Some ( CNF ( 2 ) ) , iter. next( ) ) ;
579+ // nothing else in the assets
580+ assert_eq ! ( None , iter. next( ) ) ;
581+
582+ // lets add copy of the assets to the assets itself, just to check if order stays the same
583+ // we also expect 2x amount for every fungible and collapsed non-fungibles
584+ let assets_same = assets. clone ( ) ;
585+ assets. subsume_assets ( assets_same) ;
586+
587+ let mut iter = assets. into_assets_iter ( ) ;
588+ assert_eq ! ( Some ( CF ( 200 ) ) , iter. next( ) ) ;
589+ assert_eq ! ( Some ( CFG ( 10 , 800 ) ) , iter. next( ) ) ;
590+ assert_eq ! ( Some ( CFG ( 15 , 1000 ) ) , iter. next( ) ) ;
591+ assert_eq ! ( Some ( CFP ( 400 ) ) , iter. next( ) ) ;
592+ assert_eq ! ( Some ( CFPP ( 600 ) ) , iter. next( ) ) ;
593+ assert_eq ! ( Some ( CNF ( 1 ) ) , iter. next( ) ) ;
594+ assert_eq ! ( Some ( CNF ( 2 ) ) , iter. next( ) ) ;
595+ assert_eq ! ( None , iter. next( ) ) ;
596+ }
597+
598+ #[ test]
599+ fn subsume_assets_equal_length_holdings ( ) {
600+ let mut t1 = test_assets ( ) ;
548601 let mut t2 = AssetsInHolding :: new ( ) ;
549602 t2. subsume ( CF ( 300 ) ) ;
550603 t2. subsume ( CNF ( 50 ) ) ;
551- let mut r1 = t1. clone ( ) ;
552- r1. subsume_assets ( t2. clone ( ) ) ;
553- let mut r2 = t1. clone ( ) ;
554- for a in t2. assets_iter ( ) {
555- r2. subsume ( a)
556- }
557- assert_eq ! ( r1, r2) ;
604+
605+ let t1_clone = t1. clone ( ) ;
606+ let mut t2_clone = t2. clone ( ) ;
607+
608+ // ensure values for same fungibles are summed up together
609+ // and order is also ok (see assets_in_holding_order_works())
610+ t1. subsume_assets ( t2. clone ( ) ) ;
611+ let mut iter = t1. into_assets_iter ( ) ;
612+ assert_eq ! ( Some ( CF ( 600 ) ) , iter. next( ) ) ;
613+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
614+ assert_eq ! ( Some ( CNF ( 50 ) ) , iter. next( ) ) ;
615+ assert_eq ! ( None , iter. next( ) ) ;
616+
617+ // try the same initial holdings but other way around
618+ // expecting same exact result as above
619+ t2_clone. subsume_assets ( t1_clone. clone ( ) ) ;
620+ let mut iter = t2_clone. into_assets_iter ( ) ;
621+ assert_eq ! ( Some ( CF ( 600 ) ) , iter. next( ) ) ;
622+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
623+ assert_eq ! ( Some ( CNF ( 50 ) ) , iter. next( ) ) ;
624+ assert_eq ! ( None , iter. next( ) ) ;
625+ }
626+
627+ #[ test]
628+ fn subsume_assets_different_length_holdings ( ) {
629+ let mut t1 = AssetsInHolding :: new ( ) ;
630+ t1. subsume ( CFP ( 400 ) ) ;
631+ t1. subsume ( CFPP ( 100 ) ) ;
632+
633+ let mut t2 = AssetsInHolding :: new ( ) ;
634+ t2. subsume ( CF ( 100 ) ) ;
635+ t2. subsume ( CNF ( 50 ) ) ;
636+ t2. subsume ( CNF ( 40 ) ) ;
637+ t2. subsume ( CFP ( 100 ) ) ;
638+ t2. subsume ( CFPP ( 100 ) ) ;
639+
640+ let t1_clone = t1. clone ( ) ;
641+ let mut t2_clone = t2. clone ( ) ;
642+
643+ // ensure values for same fungibles are summed up together
644+ // and order is also ok (see assets_in_holding_order_works())
645+ t1. subsume_assets ( t2) ;
646+ let mut iter = t1. into_assets_iter ( ) ;
647+ assert_eq ! ( Some ( CF ( 100 ) ) , iter. next( ) ) ;
648+ assert_eq ! ( Some ( CFP ( 500 ) ) , iter. next( ) ) ;
649+ assert_eq ! ( Some ( CFPP ( 200 ) ) , iter. next( ) ) ;
650+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
651+ assert_eq ! ( Some ( CNF ( 50 ) ) , iter. next( ) ) ;
652+ assert_eq ! ( None , iter. next( ) ) ;
653+
654+ // try the same initial holdings but other way around
655+ // expecting same exact result as above
656+ t2_clone. subsume_assets ( t1_clone) ;
657+ let mut iter = t2_clone. into_assets_iter ( ) ;
658+ assert_eq ! ( Some ( CF ( 100 ) ) , iter. next( ) ) ;
659+ assert_eq ! ( Some ( CFP ( 500 ) ) , iter. next( ) ) ;
660+ assert_eq ! ( Some ( CFPP ( 200 ) ) , iter. next( ) ) ;
661+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
662+ assert_eq ! ( Some ( CNF ( 50 ) ) , iter. next( ) ) ;
663+ assert_eq ! ( None , iter. next( ) ) ;
664+ }
665+
666+ #[ test]
667+ fn subsume_assets_empty_holding ( ) {
668+ let mut t1 = AssetsInHolding :: new ( ) ;
669+ let t2 = AssetsInHolding :: new ( ) ;
670+ t1. subsume_assets ( t2. clone ( ) ) ;
671+ let mut iter = t1. clone ( ) . into_assets_iter ( ) ;
672+ assert_eq ! ( None , iter. next( ) ) ;
673+
674+ t1. subsume ( CFP ( 400 ) ) ;
675+ t1. subsume ( CNF ( 40 ) ) ;
676+ t1. subsume ( CFPP ( 100 ) ) ;
677+
678+ let t1_clone = t1. clone ( ) ;
679+ let mut t2_clone = t2. clone ( ) ;
680+
681+ // ensure values for same fungibles are summed up together
682+ // and order is also ok (see assets_in_holding_order_works())
683+ t1. subsume_assets ( t2. clone ( ) ) ;
684+ let mut iter = t1. into_assets_iter ( ) ;
685+ assert_eq ! ( Some ( CFP ( 400 ) ) , iter. next( ) ) ;
686+ assert_eq ! ( Some ( CFPP ( 100 ) ) , iter. next( ) ) ;
687+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
688+ assert_eq ! ( None , iter. next( ) ) ;
689+
690+ // try the same initial holdings but other way around
691+ // expecting same exact result as above
692+ t2_clone. subsume_assets ( t1_clone. clone ( ) ) ;
693+ let mut iter = t2_clone. into_assets_iter ( ) ;
694+ assert_eq ! ( Some ( CFP ( 400 ) ) , iter. next( ) ) ;
695+ assert_eq ! ( Some ( CFPP ( 100 ) ) , iter. next( ) ) ;
696+ assert_eq ! ( Some ( CNF ( 40 ) ) , iter. next( ) ) ;
697+ assert_eq ! ( None , iter. next( ) ) ;
558698 }
559699
560700 #[ test]
0 commit comments