@@ -813,8 +813,40 @@ impl OutputCache {
813
813
let mut conflicting_txs_with_descendants = vec ! [ ] ;
814
814
815
815
for conflicting_tx in conflicting_txs {
816
- let descendants =
817
- self . remove_descendants_and_mark_as ( conflicting_tx, TxState :: Conflicted ( block_id) ) ?;
816
+ let descendants = self . remove_descendants ( conflicting_tx) ;
817
+
818
+ // Mark conflicting tx and its descendants as Conflicting and update OutputCache data accordingly
819
+ for tx_id in descendants. iter ( ) . rev ( ) . copied ( ) {
820
+ match self . txs . entry ( tx_id. into ( ) ) {
821
+ Entry :: Occupied ( mut entry) => match entry. get_mut ( ) {
822
+ WalletTx :: Block ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
823
+ WalletTx :: Tx ( tx) => match tx. state ( ) {
824
+ TxState :: Inactive ( _) => {
825
+ tx. set_state ( TxState :: Conflicted ( block_id) ) ;
826
+ OutputCache :: rollback_tx_data (
827
+ tx,
828
+ & self . unconfirmed_descendants ,
829
+ & mut self . consumed ,
830
+ & mut self . delegations ,
831
+ & mut self . token_issuance ,
832
+ & mut self . orders ,
833
+ ) ;
834
+ Ok ( ( ) )
835
+ }
836
+ TxState :: Abandoned
837
+ | TxState :: Confirmed ( ..)
838
+ | TxState :: InMempool ( ..)
839
+ | TxState :: Conflicted ( ..) => {
840
+ Err ( WalletError :: CannotChangeTransactionState (
841
+ * tx. state ( ) ,
842
+ TxState :: Conflicted ( block_id) ,
843
+ ) )
844
+ }
845
+ } ,
846
+ } ,
847
+ Entry :: Vacant ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
848
+ } ?;
849
+ }
818
850
819
851
conflicting_txs_with_descendants. extend ( descendants. into_iter ( ) ) ;
820
852
}
@@ -1446,11 +1478,7 @@ impl OutputCache {
1446
1478
} )
1447
1479
}
1448
1480
1449
- fn remove_descendants_and_mark_as (
1450
- & mut self ,
1451
- tx_id : Id < Transaction > ,
1452
- new_state : TxState ,
1453
- ) -> WalletResult < Vec < Id < Transaction > > > {
1481
+ fn remove_descendants ( & mut self , tx_id : Id < Transaction > ) -> Vec < Id < Transaction > > {
1454
1482
let mut all_txs = Vec :: new ( ) ;
1455
1483
let mut to_update = BTreeSet :: from_iter ( [ OutPointSourceId :: from ( tx_id) ] ) ;
1456
1484
@@ -1462,85 +1490,56 @@ impl OutputCache {
1462
1490
}
1463
1491
}
1464
1492
1465
- for tx_id in all_txs. iter ( ) . rev ( ) . copied ( ) {
1466
- match self . txs . entry ( tx_id. into ( ) ) {
1467
- Entry :: Occupied ( mut entry) => match entry. get_mut ( ) {
1468
- WalletTx :: Block ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1469
- WalletTx :: Tx ( tx) => {
1470
- ensure ! (
1471
- tx. state( ) != & new_state,
1472
- WalletError :: CannotChangeTransactionState ( * tx. state( ) , new_state)
1473
- ) ;
1493
+ all_txs
1494
+ }
1474
1495
1475
- match tx. state ( ) {
1476
- TxState :: Inactive ( _) | TxState :: Conflicted ( _) | TxState :: Abandoned => {
1477
- tx. set_state ( new_state) ;
1478
- for input in tx. get_transaction ( ) . inputs ( ) {
1479
- match input {
1480
- TxInput :: Utxo ( outpoint) => {
1481
- self . consumed . insert ( outpoint. clone ( ) , * tx. state ( ) ) ;
1482
- }
1483
- TxInput :: Account ( outpoint) => match outpoint. account ( ) {
1484
- AccountSpending :: DelegationBalance (
1485
- delegation_id,
1486
- _,
1487
- ) => {
1488
- if let Some ( data) =
1489
- self . delegations . get_mut ( delegation_id)
1490
- {
1491
- data. last_nonce = outpoint. nonce ( ) . decrement ( ) ;
1492
- data. last_parent = find_parent (
1493
- & self . unconfirmed_descendants ,
1494
- tx_id. into ( ) ,
1495
- ) ;
1496
- }
1497
- }
1498
- } ,
1499
- TxInput :: AccountCommand ( nonce, op) => match op {
1500
- AccountCommand :: MintTokens ( token_id, _)
1501
- | AccountCommand :: UnmintTokens ( token_id)
1502
- | AccountCommand :: LockTokenSupply ( token_id)
1503
- | AccountCommand :: FreezeToken ( token_id, _)
1504
- | AccountCommand :: UnfreezeToken ( token_id)
1505
- | AccountCommand :: ChangeTokenMetadataUri ( token_id, _)
1506
- | AccountCommand :: ChangeTokenAuthority ( token_id, _) => {
1507
- if let Some ( data) =
1508
- self . token_issuance . get_mut ( token_id)
1509
- {
1510
- data. last_nonce = nonce. decrement ( ) ;
1511
- data. last_parent = find_parent (
1512
- & self . unconfirmed_descendants ,
1513
- tx_id. into ( ) ,
1514
- ) ;
1515
- data. unconfirmed_txs . remove ( & tx_id. into ( ) ) ;
1516
- }
1517
- }
1518
- AccountCommand :: ConcludeOrder ( order_id)
1519
- | AccountCommand :: FillOrder ( order_id, _, _) => {
1520
- if let Some ( data) = self . orders . get_mut ( order_id) {
1521
- data. last_nonce = nonce. decrement ( ) ;
1522
- data. last_parent = find_parent (
1523
- & self . unconfirmed_descendants ,
1524
- tx_id. into ( ) ,
1525
- ) ;
1526
- }
1527
- }
1528
- } ,
1529
- }
1530
- }
1531
- Ok ( ( ) )
1532
- }
1533
- TxState :: Confirmed ( ..) | TxState :: InMempool ( ..) => Err (
1534
- WalletError :: CannotChangeTransactionState ( * tx. state ( ) , new_state) ,
1535
- ) ,
1496
+ // After tx is abandoned or marked as conflicted its effect on OutputCache should be rolled back
1497
+ fn rollback_tx_data (
1498
+ tx : & TxData ,
1499
+ unconfirmed_descendants : & BTreeMap < OutPointSourceId , BTreeSet < OutPointSourceId > > ,
1500
+ consumed : & mut BTreeMap < UtxoOutPoint , TxState > ,
1501
+ delegations : & mut BTreeMap < DelegationId , DelegationData > ,
1502
+ token_issuance : & mut BTreeMap < TokenId , TokenIssuanceData > ,
1503
+ orders : & mut BTreeMap < OrderId , OrderData > ,
1504
+ ) {
1505
+ let tx_id = tx. get_transaction ( ) . get_id ( ) ;
1506
+ for input in tx. get_transaction ( ) . inputs ( ) {
1507
+ match input {
1508
+ TxInput :: Utxo ( outpoint) => {
1509
+ consumed. insert ( outpoint. clone ( ) , * tx. state ( ) ) ;
1510
+ }
1511
+ TxInput :: Account ( outpoint) => match outpoint. account ( ) {
1512
+ AccountSpending :: DelegationBalance ( delegation_id, _) => {
1513
+ if let Some ( data) = delegations. get_mut ( delegation_id) {
1514
+ data. last_nonce = outpoint. nonce ( ) . decrement ( ) ;
1515
+ data. last_parent = find_parent ( unconfirmed_descendants, tx_id. into ( ) ) ;
1536
1516
}
1537
1517
}
1538
1518
} ,
1539
- Entry :: Vacant ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1540
- } ?;
1519
+ TxInput :: AccountCommand ( nonce, op) => match op {
1520
+ AccountCommand :: MintTokens ( token_id, _)
1521
+ | AccountCommand :: UnmintTokens ( token_id)
1522
+ | AccountCommand :: LockTokenSupply ( token_id)
1523
+ | AccountCommand :: FreezeToken ( token_id, _)
1524
+ | AccountCommand :: UnfreezeToken ( token_id)
1525
+ | AccountCommand :: ChangeTokenMetadataUri ( token_id, _)
1526
+ | AccountCommand :: ChangeTokenAuthority ( token_id, _) => {
1527
+ if let Some ( data) = token_issuance. get_mut ( token_id) {
1528
+ data. last_nonce = nonce. decrement ( ) ;
1529
+ data. last_parent = find_parent ( unconfirmed_descendants, tx_id. into ( ) ) ;
1530
+ data. unconfirmed_txs . remove ( & tx_id. into ( ) ) ;
1531
+ }
1532
+ }
1533
+ AccountCommand :: ConcludeOrder ( order_id)
1534
+ | AccountCommand :: FillOrder ( order_id, _, _) => {
1535
+ if let Some ( data) = orders. get_mut ( order_id) {
1536
+ data. last_nonce = nonce. decrement ( ) ;
1537
+ data. last_parent = find_parent ( unconfirmed_descendants, tx_id. into ( ) ) ;
1538
+ }
1539
+ }
1540
+ } ,
1541
+ }
1541
1542
}
1542
-
1543
- Ok ( all_txs)
1544
1543
}
1545
1544
1546
1545
/// Mark a transaction and its descendants as abandoned
@@ -1549,7 +1548,40 @@ impl OutputCache {
1549
1548
& mut self ,
1550
1549
tx_id : Id < Transaction > ,
1551
1550
) -> WalletResult < Vec < Id < Transaction > > > {
1552
- self . remove_descendants_and_mark_as ( tx_id, TxState :: Abandoned )
1551
+ let all_abandoned = self . remove_descendants ( tx_id) ;
1552
+
1553
+ for tx_id in all_abandoned. iter ( ) . rev ( ) . copied ( ) {
1554
+ match self . txs . entry ( tx_id. into ( ) ) {
1555
+ Entry :: Occupied ( mut entry) => match entry. get_mut ( ) {
1556
+ WalletTx :: Block ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1557
+ WalletTx :: Tx ( tx) => match tx. state ( ) {
1558
+ TxState :: Inactive ( _) => {
1559
+ tx. set_state ( TxState :: Abandoned ) ;
1560
+ OutputCache :: rollback_tx_data (
1561
+ tx,
1562
+ & self . unconfirmed_descendants ,
1563
+ & mut self . consumed ,
1564
+ & mut self . delegations ,
1565
+ & mut self . token_issuance ,
1566
+ & mut self . orders ,
1567
+ ) ;
1568
+ Ok ( ( ) )
1569
+ }
1570
+ TxState :: Conflicted ( _) => {
1571
+ tx. set_state ( TxState :: Abandoned ) ;
1572
+ Ok ( ( ) )
1573
+ }
1574
+ state => Err ( WalletError :: CannotChangeTransactionState (
1575
+ * state,
1576
+ TxState :: Abandoned ,
1577
+ ) ) ,
1578
+ } ,
1579
+ } ,
1580
+ Entry :: Vacant ( _) => Err ( WalletError :: CannotFindTransactionWithId ( tx_id) ) ,
1581
+ } ?;
1582
+ }
1583
+
1584
+ Ok ( all_abandoned)
1553
1585
}
1554
1586
1555
1587
pub fn get_transaction ( & self , transaction_id : Id < Transaction > ) -> WalletResult < & TxData > {
0 commit comments