Example #1
0
        private async Task ProcessMessageAsync(NewWalletMessage arg)
        {
            _log.Info("Got a message about new wallet", arg.ToDetails());

            await _walletsCache.SetItemAsync(new WalletState
            {
                Address      = arg.Address,
                DueDate      = arg.DueDate,
                Blockchain   = Enum.Parse <BlockchainType>(arg.Blockchain.ToString()),
                Transactions = Enumerable.Empty <PaymentBcnTransaction>()
            });
        }
Example #2
0
        private async Task ProcessMessageAsync(NewTransactionMessage arg)
        {
            _log.Info("Got a message about new transaction", arg.ToDetails());

            await _transactionsCache.SetItemAsync(new TransactionState
            {
                Transaction = new BcnTransaction
                {
                    Id            = arg.Id,
                    Amount        = arg.Amount,
                    Confirmations = arg.Confirmations,
                    BlockId       = arg.BlockId,
                    Blockchain    = Enum.Parse <BlockchainType>(arg.Blockchain.ToString()),
                    AssetId       = arg.AssetId
                },
                DueDate = arg.DueDate
            });
        }
Example #3
0
        public async Task ExecuteAsync()
        {
            IEnumerable <TransactionState> cacheState = await _cacheMaintainer.GetAsync();

            foreach (TransactionState cacheTxState in cacheState)
            {
                BcnTransaction cacheTx = cacheTxState.Transaction;

                if (cacheTx.Blockchain != BlockchainType.Bitcoin)
                {
                    continue;
                }

                GetTransactionResponse bcnTransactionState;

                try
                {
                    bcnTransactionState = await _qBitNinjaClient.GetTransaction(new uint256(cacheTx.Id));
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Getting transaction from ninja", cacheTxState.ToDetails());

                    continue;
                }

                // if transaction has not been indexed by ninja yet
                if (bcnTransactionState == null)
                {
                    continue;
                }

                BcnTransaction bcnTx = bcnTransactionState.ToDomain();

                DiffResult <BcnTransaction> diffResult = _diffService.Diff(cacheTx, bcnTx);

                if (diffResult != null)
                {
                    var tx = diffResult.Object;

                    switch (diffResult.CompareState)
                    {
                    case DiffState.New:

                        _log.Warning("New transactions are not supported by watcher", context: tx.ToDetails());

                        break;

                    case DiffState.Updated:

                        UpdateTransactionRequest updateRequest = null;

                        try
                        {
                            updateRequest = tx.ToUpdateRequest(bcnTransactionState.FirstSeen.DateTime);

                            await _log.LogPayInternalExceptionIfAny(() =>
                                                                    _payInternalClient.UpdateTransactionAsync(updateRequest));
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, context: updateRequest.ToDetails());
                        }

                        break;

                    default:
                        throw new Exception("Unknown transactions diff state");
                    }

                    cacheTxState.Transaction = bcnTx;

                    try
                    {
                        await _cacheMaintainer.SetItemAsync(cacheTxState);
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Updating transaction cache", cacheTxState.ToDetails());

                        continue;
                    }
                }
            }
        }
        public async Task ExecuteAsync()
        {
            IEnumerable <WalletState> cacheState = await _cacheMaintainer.GetAsync();

            foreach (var walletState in cacheState)
            {
                if (walletState.Blockchain != BlockchainType.Bitcoin)
                {
                    continue;
                }

                BalanceModel balance;

                try
                {
                    balance = await _qBitNinjaClient.GetBalance(BitcoinAddress.Create(walletState.Address));
                }
                catch (Exception ex)
                {
                    _log.Error(ex, "Getting balance from ninja", walletState.ToDetails());

                    continue;
                }

                IEnumerable <PaymentBcnTransaction> bcnTransactions =
                    GetIncomingPaymentOperations(balance, walletState.Address)?.ToList() ??
                    new List <PaymentBcnTransaction>();

                IEnumerable <PaymentBcnTransaction> cacheTransactions = walletState.Transactions;

                IEnumerable <DiffResult <PaymentBcnTransaction> > diff = _diffService.Diff(cacheTransactions, bcnTransactions);

                // catching case when ninja returns no transactions but should
                if (cacheTransactions.Any() && !bcnTransactions.Any())
                {
                    _log.Info("There are no transactions from ninja, but there are transactions in cache",
                              $"wallet: {walletState.Address}, walletState: {walletState.ToJson()}");
                }

                // catching case when there are any differences between ninja state and local cache state
                if (diff.Any())
                {
                    _log.Info("The difference between cache state and ninja detected",
                              $"wallet: {walletState.Address}, old txs: {walletState.Transactions.ToJson()}, new txs: {bcnTransactions.ToJson()}");
                }

                // approach to exit loop with switch statement inside
                bool isSyncError = false;

                foreach (var diffResult in diff)
                {
                    var tx = diffResult.Object;

                    switch (diffResult.CompareState)
                    {
                    case DiffState.New:

                        CreateTransactionRequest createRequest = null;

                        try
                        {
                            var txDetails = await _qBitNinjaClient.GetTransaction(new uint256(tx.Id));

                            createRequest = tx.ToCreateRequest(txDetails, _bitcoinNetwork);

                            _log.Info("New transaction detected",
                                      $"wallet: {walletState.Address}, Hash: {tx.Id}, FirstSeen: {txDetails.FirstSeen}");

                            await _log.LogPayInternalExceptionIfAny(() =>
                                                                    _payInternalClient.CreatePaymentTransactionAsync(createRequest));
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, context: createRequest.ToDetails());

                            isSyncError = true;
                        }

                        break;

                    case DiffState.Updated:

                        UpdateTransactionRequest updateRequest = null;

                        try
                        {
                            updateRequest = tx.ToUpdateRequest();

                            _log.Info("Transaction update detected",
                                      $"wallet: {walletState.Address}, new tx state: {tx.ToJson()}");

                            await _log.LogPayInternalExceptionIfAny(() =>
                                                                    _payInternalClient.UpdateTransactionAsync(updateRequest));
                        }
                        catch (Exception ex)
                        {
                            _log.Error(ex, context: updateRequest.ToDetails());

                            isSyncError = true;
                        }

                        break;

                    default: throw new Exception("Unknown transactions diff state");
                    }

                    // stop changes processing in case there is PayInternal sync error
                    if (isSyncError)
                    {
                        break;
                    }
                }

                if (bcnTransactions.Any())
                {
                    walletState.Transactions = bcnTransactions;
                }

                // will sync internal cache with blockchain only if there were no errors while processing changes
                // otherwise the changes will be processed again in the next job cycle
                // WARNING: the approach will work only for single transactions update
                if (!isSyncError)
                {
                    try
                    {
                        await _cacheMaintainer.SetItemAsync(walletState);
                    }
                    catch (Exception ex)
                    {
                        _log.Error(ex, "Updating wallets cache", walletState.ToDetails());

                        continue;
                    }
                }
            }
        }