src/Stratis.FederatedPeg.Features.FederationGateway/TargetChain/CrossChainTransferStore.cs
/// <summary>
/// Partial or fully signed transfers should have their source UTXO's recorded by an up-to-date wallet.
/// Sets transfers to <see cref="CrossChainTransferStatus.Suspended"/> if their UTXO's are not reserved
/// within the wallet.
/// </summary>
/// <param name="crossChainTransfers">The transfers to check. If not supplied then all partial and fully signed transfers are checked.</param>
/// <returns>Returns the list of transfers, possible with updated statuses.</returns>
Partial or fully signed transfers should have their source UTXO's recorded by an up-to-date wallet.
Sets transfers to  if their UTXO's are not reserved
within the wallet.
Partial or fully signed transfers should have their source UTXO ' s recorded by an up - to - date wallet . Sets transfers to if their UTXO ' s are not reserved within the wallet .
private ICrossChainTransfer[] ValidateCrossChainTransfers(ICrossChainTransfer[] crossChainTransfers = null)
        {
            if (crossChainTransfers == null)
            {
                crossChainTransfers = Get(
                    this.depositsIdsByStatus[CrossChainTransferStatus.Partial].Union(
                        this.depositsIdsByStatus[CrossChainTransferStatus.FullySigned]).ToArray());
            }
            var tracker = new StatusChangeTracker();
            int newChainATip = this.NextMatureDepositHeight;
            foreach (ICrossChainTransfer partialTransfer in crossChainTransfers)
            {
                if (partialTransfer == null)
                    continue;
                if (partialTransfer.Status != CrossChainTransferStatus.Partial && partialTransfer.Status != CrossChainTransferStatus.FullySigned)
                    continue;
                List<(Transaction, TransactionData, IWithdrawal)> walletData = this.federationWalletManager.FindWithdrawalTransactions(partialTransfer.DepositTransactionId);
                if (walletData.Count == 1 && this.ValidateTransaction(walletData[0].Item1))
                {
                    Transaction walletTran = walletData[0].Item1;
                    if (walletTran.GetHash() == partialTransfer.PartialTransaction.GetHash())
                        continue;
                    if (CrossChainTransfer.TemplatesMatch(this.network, walletTran, partialTransfer.PartialTransaction))
                    {
                        partialTransfer.SetPartialTransaction(walletTran);
                        if (walletData[0].Item2.BlockHeight != null)
                            tracker.SetTransferStatus(partialTransfer, CrossChainTransferStatus.SeenInBlock, walletData[0].Item2.BlockHash, (int)walletData[0].Item2.BlockHeight);
                        else if (this.ValidateTransaction(walletTran, true))
                            tracker.SetTransferStatus(partialTransfer, CrossChainTransferStatus.FullySigned);
                        else
                            tracker.SetTransferStatus(partialTransfer, CrossChainTransferStatus.Partial);
                        continue;
                    }
                }
                foreach (IWithdrawal withdrawal in walletData.Select(d => d.Item3))
                    this.federationWalletManager.RemoveTransientTransactions(withdrawal.DepositId);
                if (partialTransfer.DepositHeight < newChainATip)
                    newChainATip = partialTransfer.DepositHeight ?? newChainATip;
                tracker.SetTransferStatus(partialTransfer, CrossChainTransferStatus.Suspended);
            }
            if (tracker.Count == 0)
            {
                this.logger.LogTrace("(-)[NO_CHANGES_IN_TRACKER]");
                return crossChainTransfers;
            }
            using (DBreeze.Transactions.Transaction dbreezeTransaction = this.DBreeze.GetTransaction())
            {
                dbreezeTransaction.SynchronizeTables(transferTableName, commonTableName);
                int oldChainATip = this.NextMatureDepositHeight;
                try
                {
                    foreach (KeyValuePair<ICrossChainTransfer, CrossChainTransferStatus?> kv in tracker)
                    {
                        this.PutTransfer(dbreezeTransaction, kv.Key);
                    }
                    this.SaveNextMatureHeight(dbreezeTransaction, newChainATip);
                    dbreezeTransaction.Commit();
                    this.UpdateLookups(tracker);
                    foreach (KeyValuePair<ICrossChainTransfer, CrossChainTransferStatus?> kv in tracker)
                    {
                        if (kv.Value == CrossChainTransferStatus.Suspended)
                        {
                            this.federationWalletManager.RemoveTransientTransactions(kv.Key.DepositTransactionId);
                        }
                    }
                    foreach ((Transaction, TransactionData, IWithdrawal) t in this.federationWalletManager.FindWithdrawalTransactions())
                    {
                        if (t.Item3.BlockNumber >= newChainATip)
                        {
                            this.federationWalletManager.RemoveTransientTransactions(t.Item3.DepositId);
                        }
                    }
                    this.federationWalletManager.SaveWallet();
                    return crossChainTransfers;
                }
                catch (Exception err)
                {
                    this.NextMatureDepositHeight = oldChainATip;
                    this.RollbackAndThrowTransactionError(dbreezeTransaction, err, "SANITY_ERROR");
                    return null;
                }
            }
        }
private ICrossChainTransfer [ ] ValidateCrossChainTransfers ( ICrossChainTransfer [ ] crossChainTransfers = null ) { if ( crossChainTransfers == null ) { crossChainTransfers = Get ( this . depositsIdsByStatus [ CrossChainTransferStatus . Partial ] . Union ( this . depositsIdsByStatus [ CrossChainTransferStatus . FullySigned ] ) . ToArray ( ) ) ; } var tracker = new StatusChangeTracker ( ) ; int newChainATip = this . NextMatureDepositHeight ; foreach ( ICrossChainTransfer partialTransfer in crossChainTransfers ) { if ( partialTransfer == null ) continue ; if ( partialTransfer . Status != CrossChainTransferStatus . Partial && partialTransfer . Status != CrossChainTransferStatus . FullySigned ) continue ; List < ( Transaction , TransactionData , IWithdrawal ) > walletData = this . federationWalletManager . FindWithdrawalTransactions ( partialTransfer . DepositTransactionId ) ; if ( walletData . Count == 1 && this . ValidateTransaction ( walletData [ 0 ] . Item1 ) ) { Transaction walletTran = walletData [ 0 ] . Item1 ; if ( walletTran . GetHash ( ) == partialTransfer . PartialTransaction . GetHash ( ) ) continue ; if ( CrossChainTransfer . TemplatesMatch ( this . network , walletTran , partialTransfer . PartialTransaction ) ) { partialTransfer . SetPartialTransaction ( walletTran ) ; if ( walletData [ 0 ] . Item2 . BlockHeight != null ) tracker . SetTransferStatus ( partialTransfer , CrossChainTransferStatus . SeenInBlock , walletData [ 0 ] . Item2 . BlockHash , ( int ) walletData [ 0 ] . Item2 . BlockHeight ) ; else if ( this . ValidateTransaction ( walletTran , true ) ) tracker . SetTransferStatus ( partialTransfer , CrossChainTransferStatus . FullySigned ) ; else tracker . SetTransferStatus ( partialTransfer , CrossChainTransferStatus . Partial ) ; continue ; } } foreach ( IWithdrawal withdrawal in walletData . Select ( d => d . Item3 ) ) this . federationWalletManager . RemoveTransientTransactions ( withdrawal . DepositId ) ; if ( partialTransfer . DepositHeight < newChainATip ) newChainATip = partialTransfer . DepositHeight ?? newChainATip ; tracker . SetTransferStatus ( partialTransfer , CrossChainTransferStatus . Suspended ) ; } if ( tracker . Count == 0 ) { this . logger . LogTrace ( " " ) ; return crossChainTransfers ; } using ( DBreeze . Transactions . Transaction dbreezeTransaction = this . DBreeze . GetTransaction ( ) ) { dbreezeTransaction . SynchronizeTables ( transferTableName , commonTableName ) ; int oldChainATip = this . NextMatureDepositHeight ; try { foreach ( KeyValuePair < ICrossChainTransfer , CrossChainTransferStatus ? > kv in tracker ) { this . PutTransfer ( dbreezeTransaction , kv . Key ) ; } this . SaveNextMatureHeight ( dbreezeTransaction , newChainATip ) ; dbreezeTransaction . Commit ( ) ; this . UpdateLookups ( tracker ) ; foreach ( KeyValuePair < ICrossChainTransfer , CrossChainTransferStatus ? > kv in tracker ) { if ( kv . Value == CrossChainTransferStatus . Suspended ) { this . federationWalletManager . RemoveTransientTransactions ( kv . Key . DepositTransactionId ) ; } } foreach ( ( Transaction , TransactionData , IWithdrawal ) t in this . federationWalletManager . FindWithdrawalTransactions ( ) ) { if ( t . Item3 . BlockNumber >= newChainATip ) { this . federationWalletManager . RemoveTransientTransactions ( t . Item3 . DepositId ) ; } } this . federationWalletManager . SaveWallet ( ) ; return crossChainTransfers ; } catch ( Exception err ) { this . NextMatureDepositHeight = oldChainATip ; this . RollbackAndThrowTransactionError ( dbreezeTransaction , err , " " ) ; return null ; } } }
Partial or fully signed transfers should have their source UTXO's recorded by an up-to-date wallet.
Partial or fully signed transfers should have their source UTXO ' s recorded by an up - to - date wallet .
// Remove any invalid withdrawal transactions. // The chain may have been rewound so that this transaction or its UTXO's have been lost. // Rewind our recorded chain A tip to ensure the transaction is re-built once UTXO's become available. // Remove any remnants of suspended transactions from the wallet. // Remove transient transactions after the next mature deposit height. // Restore expected store state in case the calling code retries / continues using the store. // Dummy return as the above method throws. Avoids compiler error.
Returns the list of transfers, possible with updated statuses.
Returns the list of transfers possible with updated statuses .
crossChainTransfers
ICrossChainTransfer[]
The transfers to check. If not supplied then all partial and fully signed transfers are checked.
The transfers to check . If not supplied then all partial and fully signed transfers are checked .