15
15
16
16
use std:: {
17
17
cmp:: Reverse ,
18
- collections:: { btree_map:: Entry , BTreeMap , BTreeSet } ,
18
+ collections:: { btree_map:: Entry , BTreeMap , BTreeSet , VecDeque } ,
19
19
ops:: Add ,
20
20
} ;
21
21
@@ -749,7 +749,7 @@ impl OutputCache {
749
749
& mut self ,
750
750
confirmed_tx : & Transaction ,
751
751
block_id : Id < GenBlock > ,
752
- ) -> WalletResult < Vec < Id < Transaction > > > {
752
+ ) -> WalletResult < Vec < ( Id < Transaction > , WalletTx ) > > {
753
753
struct ConflictCheck {
754
754
frozen_token_id : Option < TokenId > ,
755
755
confirmed_account_nonce : Option < ( AccountType , AccountNonce ) > ,
@@ -800,58 +800,67 @@ impl OutputCache {
800
800
for unconfirmed in self . unconfirmed_descendants . keys ( ) {
801
801
let unconfirmed_tx = self . txs . get ( unconfirmed) . expect ( "must be present" ) ;
802
802
803
- if let WalletTx :: Tx ( tx) = unconfirmed_tx {
804
- if let Some ( frozen_token_id) = conflict_check. frozen_token_id {
805
- if self . violates_frozen_token ( unconfirmed_tx, & frozen_token_id) {
806
- conflicting_txs. insert ( tx. get_transaction ( ) . get_id ( ) ) ;
807
- continue ;
803
+ match unconfirmed_tx {
804
+ WalletTx :: Tx ( tx) => {
805
+ if let Some ( frozen_token_id) = conflict_check. frozen_token_id {
806
+ if self . violates_frozen_token ( unconfirmed_tx, & frozen_token_id) {
807
+ conflicting_txs. insert ( tx. get_transaction ( ) . get_id ( ) ) ;
808
+ continue ;
809
+ }
808
810
}
809
- }
810
811
811
- if let Some ( ( confirmed_account, confirmed_account_nonce) ) =
812
- conflict_check. confirmed_account_nonce
813
- {
814
- if confirmed_tx. get_id ( ) != tx. get_transaction ( ) . get_id ( )
815
- && uses_conflicting_nonce (
816
- unconfirmed_tx,
817
- confirmed_account,
818
- confirmed_account_nonce,
819
- )
812
+ if let Some ( ( confirmed_account, confirmed_account_nonce) ) =
813
+ conflict_check. confirmed_account_nonce
820
814
{
821
- conflicting_txs. insert ( tx. get_transaction ( ) . get_id ( ) ) ;
822
- continue ;
815
+ if confirmed_tx. get_id ( ) != tx. get_transaction ( ) . get_id ( )
816
+ && uses_conflicting_nonce (
817
+ unconfirmed_tx,
818
+ confirmed_account,
819
+ confirmed_account_nonce,
820
+ )
821
+ {
822
+ conflicting_txs. insert ( tx. get_transaction ( ) . get_id ( ) ) ;
823
+ continue ;
824
+ }
823
825
}
824
826
}
825
- }
827
+ WalletTx :: Block ( _) => {
828
+ utils:: debug_panic_or_log!( "Cannot be block reward" ) ;
829
+ }
830
+ } ;
826
831
}
827
832
}
828
833
829
834
// Remove all descendants of conflicting txs
835
+ let mut conflicting_tx_ids_with_descendants = vec ! [ ] ;
830
836
let mut conflicting_txs_with_descendants = vec ! [ ] ;
831
837
832
838
for conflicting_tx in conflicting_txs {
833
- let txs_to_rollback = self . remove_from_unconfirmed_descendants ( conflicting_tx) ;
839
+ let tx_ids_to_rollback = self . remove_from_unconfirmed_descendants ( conflicting_tx) ;
834
840
835
841
// Mark conflicting tx and its descendants as Conflicting and update OutputCache data accordingly
836
842
let mut tx_to_rollback_data = vec ! [ ] ;
837
- for tx_id in txs_to_rollback . iter ( ) . rev ( ) . copied ( ) {
843
+ for tx_id in tx_ids_to_rollback . iter ( ) . rev ( ) . copied ( ) {
838
844
match self . txs . entry ( tx_id. into ( ) ) {
839
845
Entry :: Occupied ( mut entry) => match entry. get_mut ( ) {
840
846
WalletTx :: Block ( _) => {
841
847
Err ( WalletError :: TransactionIdCannotMapToBlock ( tx_id) )
842
848
}
843
- WalletTx :: Tx ( tx) => match tx. state ( ) {
844
- TxState :: Inactive ( _) | TxState :: InMempool ( _) => {
845
- tx. set_state ( TxState :: Conflicted ( block_id) ) ;
846
- tx_to_rollback_data. push ( entry. get ( ) . clone ( ) ) ;
847
- Ok ( ( ) )
849
+ WalletTx :: Tx ( tx) => {
850
+ conflicting_txs_with_descendants. push ( WalletTx :: Tx ( tx. clone ( ) ) ) ;
851
+ match tx. state ( ) {
852
+ TxState :: Inactive ( _) | TxState :: InMempool ( _) => {
853
+ tx. set_state ( TxState :: Conflicted ( block_id) ) ;
854
+ tx_to_rollback_data. push ( entry. get ( ) . clone ( ) ) ;
855
+ Ok ( ( ) )
856
+ }
857
+ TxState :: Conflicted ( ..)
858
+ | TxState :: Abandoned
859
+ | TxState :: Confirmed ( ..) => {
860
+ Err ( WalletError :: CannotMarkTxAsConflictedIfInState ( * tx. state ( ) ) )
861
+ }
848
862
}
849
- TxState :: Conflicted ( ..)
850
- | TxState :: Abandoned
851
- | TxState :: Confirmed ( ..) => {
852
- Err ( WalletError :: CannotMarkTxAsConflictedIfInState ( * tx. state ( ) ) )
853
- }
854
- } ,
863
+ }
855
864
} ,
856
865
Entry :: Vacant ( _) => {
857
866
Err ( WalletError :: CannotFindDescendantTransactionWithId ( tx_id) )
@@ -863,10 +872,13 @@ impl OutputCache {
863
872
self . rollback_tx_data ( & tx) ?;
864
873
}
865
874
866
- conflicting_txs_with_descendants . extend ( txs_to_rollback . into_iter ( ) ) ;
875
+ conflicting_tx_ids_with_descendants . extend ( tx_ids_to_rollback . into_iter ( ) ) ;
867
876
}
868
877
869
- Ok ( conflicting_txs_with_descendants)
878
+ Ok ( conflicting_tx_ids_with_descendants
879
+ . into_iter ( )
880
+ . zip_eq ( conflicting_txs_with_descendants. into_iter ( ) )
881
+ . collect ( ) )
870
882
}
871
883
872
884
fn violates_frozen_token ( & self , unconfirmed_tx : & WalletTx , frozen_token_id : & TokenId ) -> bool {
@@ -933,9 +945,9 @@ impl OutputCache {
933
945
TxState :: Confirmed ( _, _, _) => false ,
934
946
} ;
935
947
936
- if is_unconfirmed && !already_present {
937
- self . unconfirmed_descendants . insert ( tx_id. clone ( ) , BTreeSet :: new ( ) ) ;
938
- } else if !is_unconfirmed {
948
+ if is_unconfirmed {
949
+ self . unconfirmed_descendants . entry ( tx_id. clone ( ) ) . or_default ( ) ;
950
+ } else {
939
951
self . unconfirmed_descendants . remove ( & tx_id) ;
940
952
}
941
953
@@ -1438,16 +1450,21 @@ impl OutputCache {
1438
1450
}
1439
1451
1440
1452
// Removes a tx and all its descendants from unconfirmed descendants collection.
1441
- // Returns provided tx and all the descendants in that specific order.
1453
+ // Returns provided tx and all the descendants in such way that parent always comes before its
1454
+ // descendants. Returned collection contains no duplicates.
1442
1455
fn remove_from_unconfirmed_descendants (
1443
1456
& mut self ,
1444
1457
tx_id : Id < Transaction > ,
1445
1458
) -> Vec < Id < Transaction > > {
1446
1459
let mut all_txs = Vec :: new ( ) ;
1447
- let mut to_update = BTreeSet :: from_iter ( [ OutPointSourceId :: from ( tx_id) ] ) ;
1460
+ let mut all_txs_as_set = BTreeSet :: new ( ) ;
1461
+ let mut to_update: VecDeque < OutPointSourceId > = [ tx_id. into ( ) ] . into ( ) ;
1448
1462
1449
- while let Some ( outpoint_source_id) = to_update. pop_first ( ) {
1450
- all_txs. push ( * outpoint_source_id. get_tx_id ( ) . expect ( "must be a transaction" ) ) ;
1463
+ while let Some ( outpoint_source_id) = to_update. pop_front ( ) {
1464
+ let tx_id = * outpoint_source_id. get_tx_id ( ) . expect ( "must be a transaction" ) ;
1465
+ if all_txs_as_set. insert ( tx_id) {
1466
+ all_txs. push ( tx_id) ;
1467
+ }
1451
1468
1452
1469
if let Some ( descendants) = self . unconfirmed_descendants . remove ( & outpoint_source_id) {
1453
1470
to_update. extend ( descendants. into_iter ( ) )
@@ -1501,7 +1518,7 @@ impl OutputCache {
1501
1518
}
1502
1519
}
1503
1520
1504
- for output in tx. outputs ( ) {
1521
+ for output in tx. outputs ( ) . iter ( ) . rev ( ) {
1505
1522
match output {
1506
1523
TxOutput :: CreateStakePool ( pool_id, _) => {
1507
1524
self . pools . remove ( pool_id) ;
@@ -1542,43 +1559,47 @@ impl OutputCache {
1542
1559
}
1543
1560
1544
1561
/// Mark a transaction and its descendants as abandoned
1545
- /// Returns a Vec of the transaction Ids that have been abandoned
1562
+ /// Returns a Vec of the transactions and the Ids that have been abandoned
1546
1563
pub fn abandon_transaction (
1547
1564
& mut self ,
1548
1565
tx_id : Id < Transaction > ,
1549
- ) -> WalletResult < Vec < Id < Transaction > > > {
1566
+ ) -> WalletResult < Vec < ( Id < Transaction > , WalletTx ) > > {
1550
1567
let all_abandoned = self . remove_from_unconfirmed_descendants ( tx_id) ;
1568
+ let mut all_abandoned_txs = Vec :: with_capacity ( all_abandoned. len ( ) ) ;
1551
1569
1552
1570
let mut txs_to_rollback = vec ! [ ] ;
1553
1571
for tx_id in all_abandoned. iter ( ) . rev ( ) . copied ( ) {
1554
1572
match self . txs . entry ( tx_id. into ( ) ) {
1555
1573
Entry :: Occupied ( mut entry) => match entry. get_mut ( ) {
1556
1574
WalletTx :: Block ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1557
- WalletTx :: Tx ( tx) => match tx. state ( ) {
1558
- TxState :: Inactive ( _) => {
1559
- tx. set_state ( TxState :: Abandoned ) ;
1560
- txs_to_rollback. push ( entry. get ( ) . clone ( ) ) ;
1561
- Ok ( ( ) )
1562
- }
1563
- TxState :: Conflicted ( _) => {
1564
- tx. set_state ( TxState :: Abandoned ) ;
1565
- Ok ( ( ) )
1575
+ WalletTx :: Tx ( tx) => {
1576
+ all_abandoned_txs. push ( WalletTx :: Tx ( tx. clone ( ) ) ) ;
1577
+ match tx. state ( ) {
1578
+ TxState :: Inactive ( _) => {
1579
+ tx. set_state ( TxState :: Abandoned ) ;
1580
+ txs_to_rollback. push ( entry. get ( ) . clone ( ) ) ;
1581
+ Ok ( ( ) )
1582
+ }
1583
+ TxState :: Conflicted ( _) => {
1584
+ tx. set_state ( TxState :: Abandoned ) ;
1585
+ Ok ( ( ) )
1586
+ }
1587
+ state => Err ( WalletError :: CannotChangeTransactionState (
1588
+ * state,
1589
+ TxState :: Abandoned ,
1590
+ ) ) ,
1566
1591
}
1567
- state => Err ( WalletError :: CannotChangeTransactionState (
1568
- * state,
1569
- TxState :: Abandoned ,
1570
- ) ) ,
1571
- } ,
1592
+ }
1572
1593
} ,
1573
1594
Entry :: Vacant ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1574
1595
} ?;
1575
1596
}
1576
1597
1577
- for tx in txs_to_rollback {
1578
- self . rollback_tx_data ( & tx) ?;
1598
+ for tx in & txs_to_rollback {
1599
+ self . rollback_tx_data ( tx) ?;
1579
1600
}
1580
1601
1581
- Ok ( all_abandoned)
1602
+ Ok ( all_abandoned. into_iter ( ) . zip_eq ( all_abandoned_txs . into_iter ( ) ) . collect ( ) )
1582
1603
}
1583
1604
1584
1605
pub fn get_transaction ( & self , transaction_id : Id < Transaction > ) -> WalletResult < & TxData > {
@@ -1840,3 +1861,6 @@ fn uses_conflicting_nonce(
1840
1861
}
1841
1862
} )
1842
1863
}
1864
+
1865
+ #[ cfg( test) ]
1866
+ mod tests;
0 commit comments