public async Task MonitoringCoinTransactionJobUnitTest_TransactionIsInBlockchainSenhdEvent()
        {
            string operationId             = "OpId-2";
            QueueTriggeringContext context = new QueueTriggeringContext(DateTimeOffset.UtcNow);
            CoinTransactionMessage coinTransactionMessage = new CoinTransactionMessage()
            {
                PutDateTime     = DateTime.UtcNow,
                TransactionHash = _commitedTransactionHash,
                OperationId     = operationId
            };

            #region ArrangeMocks
            ICoinTransaction coinTransaction = new CoinTransaction()
            {
                ConfirmationLevel = 1,
                TransactionHash   = _commitedTransactionHash
            };
            ICoinEvent coinEvent = new CoinEvent(operationId,
                                                 _commitedTransactionHash, "from", "to", "1000000000000000000", CoinEventType.TransferStarted, "contractAddress", true);
            _ethereumTransactionService.Setup(x => x.IsTransactionInPool(_commitedTransactionHash)).Returns(Task.FromResult(false));
            _coinTransactionService.Setup(x => x.ProcessTransaction(coinTransactionMessage)).Returns(Task.FromResult <ICoinTransaction>(coinTransaction));
            _coinEventService.Setup(x => x.GetCoinEvent(_commitedTransactionHash)).Returns(Task.FromResult <ICoinEvent>(coinEvent));
            _coinEventService.Setup(x => x.PublishEvent(coinEvent, It.IsAny <bool>())).Returns(Task.FromResult(0)).Verifiable();

            #endregion

            MonitoringCoinTransactionJob job = GetJob();
            await job.Execute(coinTransactionMessage, context);

            _coinEventService.Verify(x => x.PublishEvent(coinEvent, It.IsAny <bool>()), Times.Once);
        }
        public async Task MonitoringCoinTransactionJobUnitTest_LostTransactionProcessing()
        {
            string operationId             = "OpId-3";
            QueueTriggeringContext context = new QueueTriggeringContext(DateTimeOffset.UtcNow);
            CoinTransactionMessage coinTransactionMessage = new CoinTransactionMessage()
            {
                PutDateTime     = DateTime.UtcNow,
                TransactionHash = _lostTransactionHash,
                OperationId     = operationId
            };

            #region ArrangeMocks

            ICoinEvent coinEvent = new CoinEvent(operationId,
                                                 _lostTransactionHash, "from", "to", "1000000000000000000", CoinEventType.TransferStarted, "contractAddress", true);
            _ethereumTransactionService.Setup(x => x.IsTransactionInPool(_lostTransactionHash)).Returns(Task.FromResult(false));
            _coinTransactionService.Setup(x => x.ProcessTransaction(coinTransactionMessage)).Returns(Task.FromResult <ICoinTransaction>(null));
            _coinEventService.Setup(x => x.GetCoinEvent(_lostTransactionHash)).Returns(Task.FromResult <ICoinEvent>(coinEvent));
            _coinEventService.Setup(x => x.PublishEvent(coinEvent, It.IsAny <bool>())).Returns(Task.FromResult(0)).Verifiable();
            _pendingOperationService.Setup(x => x.RefreshOperationByIdAsync(operationId)).Returns(Task.FromResult(0)).Verifiable();

            #endregion

            MonitoringCoinTransactionJob job = GetJob();
            await job.Execute(coinTransactionMessage, context);

            _coinEventService.Verify(x => x.PublishEvent(coinEvent, It.IsAny <bool>()), Times.Never);
            _pendingOperationService.Verify(x => x.RefreshOperationByIdAsync(operationId), Times.Once);
        }
コード例 #3
0
 private void SendMessageToTheQueueEnd(QueueTriggeringContext context, CoinTransactionMessage transaction, int delay, string error = "")
 {
     transaction.DequeueCount++;
     transaction.LastError = string.IsNullOrEmpty(error) ? transaction.LastError : error;
     context.MoveMessageToEnd(transaction.ToJson());
     context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, delay);
 }
コード例 #4
0
        private async Task RepeatOperationTillWin(CoinTransactionMessage message)
        {
            var operation = await GetOperationAsync(message?.TransactionHash, message?.OperationId);

            if (operation == null)
            {
                return;
            }

            switch (operation.OperationType)
            {
            case HotWalletOperationType.Cashout:
                break;

            case HotWalletOperationType.Cashin:
                var retryMessage = new LykkePayErc20TransferMessage()
                {
                    OperationId = operation.OperationId
                };

                await _transferStartQueue.PutRawMessageAsync(retryMessage.ToJson());

                break;

            default:
                return;
            }
        }
コード例 #5
0
        public async Task <ICoinTransaction> ProcessTransaction(CoinTransactionMessage transaction)
        {
            var receipt = await _transactionService.GetTransactionReceipt(transaction.TransactionHash);

            if (receipt == null)
            {
                return(null);
            }

            ICoinTransaction coinDbTransaction = await _coinTransactionRepository.GetTransaction(transaction.TransactionHash)
                                                 ?? new CoinTransaction()
            {
                ConfirmationLevel = 0,
                TransactionHash   = transaction.TransactionHash
            };
            bool error = coinDbTransaction?.Error == true || !await _transactionService.IsTransactionExecuted(transaction.TransactionHash);

            var confimations = await _contractService.GetCurrentBlock() - receipt.BlockNumber;

            var coinTransaction = new CoinTransaction
            {
                TransactionHash   = transaction.TransactionHash,
                Error             = error,
                ConfirmationLevel = GetTransactionConfirmationLevel(confimations)
            };

            await _coinTransactionRepository.InsertOrReplaceAsync(coinTransaction);

            return(coinTransaction);
        }
コード例 #6
0
        public async Task <string> StartCashinAsync(IHotWalletOperation operation)
        {
            await SaveOperationAsync(operation);

            var transactionHash = await _erc20DepositContractService.RecievePaymentFromDepositContract(operation.FromAddress,
                                                                                                       operation.TokenAddress,
                                                                                                       operation.ToAddress);

            await RetryPolicy.ExecuteUnlimitedAsync(async() =>
            {
                await _hotWalletCashoutTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
                {
                    OperationId     = operation.OperationId,
                    TransactionHash = transactionHash
                });
            }, TimeSpan.FromMinutes(1).Milliseconds, _log);

            CoinTransactionMessage message = new CoinTransactionMessage()
            {
                OperationId     = operation.OperationId,
                TransactionHash = transactionHash
            };

            return(transactionHash);
        }
コード例 #7
0
        private async Task RepeatOperationTillWin(CoinTransactionMessage message)
        {
            var operation = await GetOperationAsync(message?.TransactionHash, message?.OperationId);

            switch (operation.OperationType)
            {
            case HotWalletOperationType.Cashout:
                await _hotWalletService.RetryCashoutAsync(operation);

                break;

            case HotWalletOperationType.Cashin:
                await _hotWalletService.RemoveCashinLockAsync(operation.TokenAddress, operation.FromAddress);

                break;

            default:
                return;
            }
        }
コード例 #8
0
        public async Task <string> StartCashinAsync(IHotWalletOperation operation)
        {
            await SaveOperationAsync(operation);

            var transactionHash = await _erc20DepositContractService.RecievePaymentFromDepositContract(operation.FromAddress,
                                                                                                       operation.TokenAddress,
                                                                                                       operation.ToAddress);

            await _hotWalletCashoutTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
            {
                OperationId     = operation.OperationId,
                TransactionHash = transactionHash
            });

            CoinTransactionMessage message = new CoinTransactionMessage()
            {
                OperationId     = operation.OperationId,
                TransactionHash = transactionHash
            };

            return(transactionHash);
        }
        public async Task MonitoringCoinTransactionJobUnitTest_SkipTransactionThatIsStillInMemoryPool()
        {
            string operationId             = "OpId-1";
            QueueTriggeringContext context = new QueueTriggeringContext(DateTimeOffset.UtcNow);
            CoinTransactionMessage coinTransactionMessage = new CoinTransactionMessage()
            {
                PutDateTime     = DateTime.UtcNow,
                TransactionHash = _memoryPoolTransactionHash,
                OperationId     = operationId
            };

            #region ArrangeMocks

            _ethereumTransactionService.Setup(x => x.IsTransactionInPool(_memoryPoolTransactionHash)).Returns(Task.FromResult(true));
            _coinTransactionService.Setup(x => x.ProcessTransaction(coinTransactionMessage)).Verifiable();

            #endregion

            MonitoringCoinTransactionJob job = GetJob();
            await job.Execute(coinTransactionMessage, context);

            _coinTransactionService.Verify(x => x.ProcessTransaction(coinTransactionMessage), Times.Never);
        }
コード例 #10
0
        private async Task RepeatOperationTillWin(CoinTransactionMessage message)
        {
            ICoinEvent coinEvent = await GetCoinEvent(message.TransactionHash, message.OperationId, true);

            if (coinEvent == null)
            {
                await _eventTraceRepository.InsertAsync(new EventTrace()
                {
                    Note        = $"Operation processing is over. Put it in the garbage. With hash {message.TransactionHash}",
                    OperationId = message.OperationId,
                    TraceDate   = DateTime.UtcNow
                });

                return;
            }
            switch (coinEvent.CoinEventType)
            {
            case CoinEventType.CashinStarted:
            case CoinEventType.CashinCompleted:
                await UpdateUserTransferWallet(coinEvent.FromAddress, coinEvent.ToAddress);

                break;

            default:
                break;
            }

            await _eventTraceRepository.InsertAsync(new EventTrace()
            {
                Note        = $"Operation With Id {coinEvent.OperationId} hash {message.TransactionHash} goes to {Constants.PendingOperationsQueue}",
                OperationId = message.OperationId,
                TraceDate   = DateTime.UtcNow
            });

            await _pendingOperationService.RefreshOperationByIdAsync(coinEvent.OperationId);
        }
コード例 #11
0
        public async Task Execute(LykkePayErc20TransferMessage transaction, QueueTriggeringContext context)
        {
            IHotWalletOperation operation = null;

            if (string.IsNullOrEmpty(transaction?.OperationId))
            {
                await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                "Execute", "", "Empty message skipped");

                return;
            }

            try
            {
                operation = await _operationsRepository.GetAsync(transaction.OperationId);

                if (operation == null)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"No operation for id {transaction?.OperationId} message skipped");

                    return;
                }

                var transactionSenderAddress = _settings.LykkePay.LykkePayAddress;
                var balance =
                    await _ercInterfaceService.GetPendingBalanceForExternalTokenAsync(operation.FromAddress,
                                                                                      operation.TokenAddress);

                if (balance == 0)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"DepositAddress: {operation.FromAddress}, TokenAddress: {operation.TokenAddress}");

                    //TODO: Transaction Failed

                    return;
                }

                var trHash = await Erc20SharedService.StartDepositTransferAsync(_web3, _settings.EthereumCore,
                                                                                transactionSenderAddress,
                                                                                operation.FromAddress, operation.TokenAddress, operation.ToAddress);

                await _hotWalletTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                });

                var message = new CoinTransactionMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                };

                //Observe transaction
                await _transactionMonitoringQueue.PutRawMessageAsync(
                    Newtonsoft.Json.JsonConvert.SerializeObject(message));

                var notificationMessage = new LykkePayErc20TransferNotificationMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash,
                    Balance         = balance.ToString() //At the starting moment(may change at the end of the execution)
                };

                await _transactionStartedNotificationQueue.PutRawMessageAsync(
                    Newtonsoft.Json.JsonConvert.SerializeObject(notificationMessage));
            }
            catch (ClientSideException ex)
            {
                if (operation == null)
                {
                    return;
                }

                TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                         "",
                                                         operation.Amount.ToString(),
                                                         operation.TokenAddress,
                                                         operation.FromAddress,
                                                         operation.ToAddress,
                                                         "",
                                                         0,
                                                         SenderType.EthereumCore,
                                                         EventType.Failed,
                                                         WorkflowType.LykkePay,
                                                         DateTime.UtcNow);

                await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob), "Execute",
                                                operation.ToJson(), ex);

                await _rabbitQueuePublisher.PublshEvent(@event);
            }
            catch (Exception ex)
            {
                if (transaction == null)
                {
                    return;
                }

                if (ex.Message != transaction.LastError)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(), "transaction.OperationId");
                }

                transaction.LastError = ex.Message;
                transaction.DequeueCount++;
                context.MoveMessageToEnd(transaction.ToJson());
                context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 200);

                await _logger.WriteErrorAsync(nameof(LykkePayErc20DepositTransferStarterJob), "Execute",
                                              transaction.ToJson(), ex);
            }
        }
コード例 #12
0
 public async Task PutTransactionToQueue(CoinTransactionMessage transaction)
 {
     await _coinTransationMonitoringQueue.PutRawMessageAsync(JsonConvert.SerializeObject(transaction));
 }
コード例 #13
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="hotWalletCashout"></param>
        /// <returns>transaction hash</returns>
        public async Task <string> StartCashoutAsync(string operationId)
        {
            IHotWalletOperation cashout = await _hotWalletCashoutRepository.GetAsync(operationId);

            if (cashout == null || cashout.OperationType != HotWalletOperationType.Cashout)
            {
                await _log.WriteWarningAsync(nameof(HotWalletService),
                                             nameof(StartCashoutAsync),
                                             $"operationId - {operationId}",
                                             "No cashout info for operation",
                                             DateTime.UtcNow);

                return(null);
            }

            var gasPrice = await _gasPriceRepository.GetAsync();

            var currentGasPriceHex = await _web3.Eth.GasPrice.SendRequestAsync();

            var        currentGasPrice  = currentGasPriceHex.Value;
            BigInteger selectedGasPrice = currentGasPrice * _baseSettings.GasPricePercentage / 100;

            if (selectedGasPrice > gasPrice.Max)
            {
                selectedGasPrice = gasPrice.Max;
            }
            else if (selectedGasPrice < gasPrice.Min)
            {
                selectedGasPrice = gasPrice.Min;
            }

            string        transactionForSigning = null;
            string        signedTransaction     = null;
            string        transactionHash       = null;
            bool          isErc20Transfer       = !string.IsNullOrEmpty(cashout.TokenAddress);
            SemaphoreSlim semaphore             = _semaphores.GetOrAdd(cashout.FromAddress, f => new SemaphoreSlim(1, 1));

            try
            {
                await semaphore.WaitAsync();

                //Erc20 transfer
                if (isErc20Transfer)
                {
                    transactionForSigning = await _erc20PrivateWalletService.GetTransferTransactionRaw(new Lykke.Service.EthereumCore.BusinessModels.PrivateWallet.Erc20Transaction()
                    {
                        FromAddress  = cashout.FromAddress,
                        GasAmount    = Constants.GasForHotWalletTransaction,
                        GasPrice     = selectedGasPrice,
                        ToAddress    = cashout.ToAddress,
                        TokenAddress = cashout.TokenAddress,
                        TokenAmount  = cashout.Amount,
                        Value        = 0,
                    }, useTxPool : true);
                }
                //Eth transfer
                else
                {
                    transactionForSigning = await _privateWalletService.GetTransactionForSigning(new Lykke.Service.EthereumCore.BusinessModels.PrivateWallet.EthTransaction()
                    {
                        FromAddress = cashout.FromAddress,
                        GasAmount   = Constants.GasForHotWalletTransaction,
                        GasPrice    = selectedGasPrice,
                        ToAddress   = cashout.ToAddress,
                        Value       = cashout.Amount
                    }, useTxPool : true);
                }

                signedTransaction = await _signatureService.SignRawTransactionAsync(cashout.FromAddress, transactionForSigning);

                if (string.IsNullOrEmpty(signedTransaction))
                {
                    throw new ClientSideException(ExceptionType.WrongSign, "Wrong signature");
                }

                var transactionExecutionCosts =
                    await _privateWalletService.EstimateTransactionExecutionCost(cashout.FromAddress, signedTransaction);

                if (!transactionExecutionCosts.IsAllowed)
                {
                    throw new Exception($"Transaction will not be successfull {JsonSerialisersExt.ToJson(cashout)}");
                }

                transactionHash = isErc20Transfer ? await _erc20PrivateWalletService.SubmitSignedTransaction(cashout.FromAddress, signedTransaction) :
                                  await _privateWalletService.SubmitSignedTransaction(cashout.FromAddress, signedTransaction);
            }
            finally
            {
                semaphore.Release();
            }

            if (string.IsNullOrEmpty(transactionHash))
            {
                throw new Exception("Transaction was not sent");
            }

            CoinTransactionMessage message = new CoinTransactionMessage()
            {
                OperationId     = operationId,
                TransactionHash = transactionHash
            };

            await _hotWalletCashoutTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
            {
                OperationId     = operationId,
                TransactionHash = transactionHash
            });

            await _hotWalletTransactionMonitoringQueue.PutRawMessageAsync(Newtonsoft.Json.JsonConvert.SerializeObject(message));

            return(transactionHash);
        }
コード例 #14
0
        public async Task IndexCashinEventsForErc20Deposits()
        {
            var indexerStatusResponse = await _indexerApi.ApiSystemIsAliveGetWithHttpMessagesAsync();

            if (indexerStatusResponse.Response.IsSuccessStatusCode)
            {
                var responseContent = await indexerStatusResponse.Response.Content.ReadAsStringAsync();

                var indexerStatus    = JObject.Parse(responseContent);
                var lastIndexedBlock = BigInteger.Parse(indexerStatus["blockchainTip"].Value <string>());
                var lastSyncedBlock  = await GetLastSyncedBlockNumber(Erc20HotWalletMarker);

                while (++lastSyncedBlock <= lastIndexedBlock - _baseSettings.Level2TransactionConfirmation)
                {
                    var transfersResponse = await _indexerApi.ApiErc20TransferHistoryGetErc20TransfersPostAsync
                                            (
                        new GetErc20TransferHistoryRequest
                    {
                        AssetHolder = _settingsWrapper.Ethereum.HotwalletAddress?.ToLower(),
                        BlockNumber = (long)lastSyncedBlock,
                    }
                                            );

                    switch (transfersResponse)
                    {
                    case IEnumerable <Erc20TransferHistoryResponse> transfers:

                        foreach (var transfer in transfers)
                        {
                            // Ignore transfers from not deposit contract addresses
                            if (!await _depositContractService.ContainsAsync(transfer.FromProperty))
                            {
                                continue;
                            }

                            var coinTransactionMessage = new CoinTransactionMessage
                            {
                                TransactionHash = transfer.TransactionHash
                            };

                            await _cashinEventRepository.InsertAsync(new CashinEvent
                            {
                                CoinAdapterAddress = Erc20HotWalletMarker,
                                Amount             = transfer.TransferAmount,
                                TransactionHash    = transfer.TransactionHash,
                                UserAddress        = transfer.FromProperty,
                                ContractAddress    = transfer.Contract
                            });

                            await _cointTransactionQueue.PutRawMessageAsync(JsonConvert.SerializeObject(coinTransactionMessage));
                        }

                        break;

                    case ApiException exception:
                        throw new Exception($"Ethereum indexer responded with error: {exception.Error.Message}");

                    default:
                        throw new Exception($"Ethereum indexer returned unexpected response");
                    }

                    await _blockSyncedRepository.InsertAsync(new BlockSynced
                    {
                        BlockNumber        = lastSyncedBlock.ToString(),
                        CoinAdapterAddress = Erc20HotWalletMarker
                    });
                }
            }
            else
            {
                throw new Exception("Can not obtain ethereum indexer status.");
            }
        }
コード例 #15
0
        public async Task Execute(CoinTransactionMessage transaction, QueueTriggeringContext context)
        {
            ICoinTransaction coinTransaction = null;

            try
            {
                bool isTransactionInMemoryPool = await _ethereumTransactionService.IsTransactionInPool(transaction.TransactionHash);

                if (isTransactionInMemoryPool)
                {
                    SendMessageToTheQueueEnd(context, transaction, 100, "Transaction is in memory pool");
                    return;
                }

                coinTransaction = await _coinTransactionService.ProcessTransaction(transaction);
            }
            catch (Exception ex)
            {
                if (ex.Message != transaction.LastError)
                {
                    await _log.WriteWarningAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "Execute", $"TrHash: [{transaction.TransactionHash}]", "");
                }

                SendMessageToTheQueueEnd(context, transaction, 200, ex.Message);

                await _log.WriteErrorAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "Execute", "", ex);

                return;
            }

            if (coinTransaction == null)
            {
                await RepeatOperationTillWin(transaction);

                await _slackNotifier.ErrorAsync($"Airlines: Transaction with hash {transaction.TransactionHash} has ERROR. RETRY. Address is yet blocked");
            }
            else
            {
                if (coinTransaction.ConfirmationLevel >= CoinTransactionService.Level2Confirm)
                {
                    bool sentToRabbit = await SendCompleteEvent(transaction.TransactionHash, transaction.OperationId, !coinTransaction.Error, context, transaction);

                    if (sentToRabbit)
                    {
                        await _log.WriteInfoAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "Execute", "",
                                                  $"Put coin transaction {transaction.TransactionHash} to rabbit queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                    }
                    else
                    {
                        await _log.WriteInfoAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "Execute", "",
                                                  $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                    }

                    if (coinTransaction.Error)
                    {
                        await _slackNotifier.ErrorAsync($"EthereumCoreService: HOTWALLET - Transaction with hash {transaction.TransactionHash} has an Error. Notify Caller about fail!");
                    }
                }
                else
                {
                    SendMessageToTheQueueEnd(context, transaction, 100);
                    await _log.WriteInfoAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "Execute", "",
                                              $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                }
            }
        }
コード例 #16
0
        //return whether we have sent to rabbit or not
        private async Task <bool> SendCompleteEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction)
        {
            try
            {
                var operation = await GetOperationAsync(transactionHash, operationId);

                if (operation == null)
                {
                    return(false);
                }

                string amount;
                Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType type;
                switch (operation.OperationType)
                {
                case HotWalletOperationType.Cashout:
                    amount = operation.Amount.ToString();
                    type   = Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType.CashoutCompleted;
                    break;

                case HotWalletOperationType.Cashin:
                    await _hotWalletService.RemoveCashinLockAsync(operation.TokenAddress, operation.FromAddress);

                    amount = (await _cashinEventRepository.GetAsync(transactionHash)).Amount;
                    type   = Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType.CashinCompleted;
                    break;

                default:
                    return(false);
                }
                HotWalletEvent @event = new HotWalletEvent(operation.OperationId,
                                                           transactionHash,
                                                           operation.FromAddress,
                                                           operation.ToAddress,
                                                           amount,
                                                           operation.TokenAddress,
                                                           type);

                await _rabbitQueuePublisher.PublshEvent(@event);

                return(true);
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync(nameof(HotWalletMonitoringTransactionJob), "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow);

                SendMessageToTheQueueEnd(context, transaction, 100);

                return(false);
            }
        }
コード例 #17
0
        //return whether we have sent to rabbit or not
        private async Task <bool> SendCompleteEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction)
        {
            try
            {
                var operation = await GetOperationAsync(transactionHash, operationId);

                if (operation == null)
                {
                    return(false);
                }

                (BigInteger? amount, string blockHash, ulong blockNumber)transferedInfo = (null, null, 0);
                string amount = operation.Amount.ToString();
                switch (operation.OperationType)
                {
                case HotWalletOperationType.Cashout:
                    break;

                case HotWalletOperationType.Cashin:
                    //There will be nothing to index in failed event
                    if (success)
                    {
                        transferedInfo =
                            await _transactionEventsService.IndexCashinEventsForErc20TransactionHashAsync(
                                transactionHash);

                        if (transferedInfo.amount == null ||
                            transferedInfo.amount == 0)
                        {
                            //Not yet indexed
                            SendMessageToTheQueueEnd(context, transaction, 10000);
                            return(false);
                        }

                        amount = transferedInfo.amount.ToString();
                    }

                    break;

                default:
                    return(false);
                }

                EventType     eventType = success ? EventType.Completed : EventType.Failed;
                TransferEvent @event    = new TransferEvent(operation.OperationId,
                                                            transactionHash,
                                                            amount,
                                                            operation.TokenAddress,
                                                            operation.FromAddress,
                                                            operation.ToAddress,
                                                            transferedInfo.blockHash,
                                                            transferedInfo.blockNumber,
                                                            SenderType.EthereumCore,
                                                            eventType,
                                                            WorkflowType.Airlines,
                                                            DateTime.UtcNow);

                await _rabbitQueuePublisher.PublshEvent(@event);

                return(true);
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow);

                SendMessageToTheQueueEnd(context, transaction, 100);

                return(false);
            }
        }
コード例 #18
0
        public async Task Execute(LykkePayErc20TransferMessage transaction, QueueTriggeringContext context)
        {
            IHotWalletOperation operation = null;

            if (string.IsNullOrEmpty(transaction?.OperationId))
            {
                await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                "Execute", "", "Empty message skipped");

                return;
            }

            try
            {
                operation = await _operationsRepository.GetAsync(transaction.OperationId);

                if (operation == null)
                {
                    await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"No operation for id {transaction?.OperationId} message skipped");

                    return;
                }

                var transactionSenderAddress = _settings.Airlines.AirlinesAddress;
                var balance =
                    await _ercInterfaceService.GetPendingBalanceForExternalTokenAsync(operation.FromAddress,
                                                                                      operation.TokenAddress);

                if (balance < operation.Amount)
                {
                    await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"Sendig Failed Event: DepositAddress: {operation.FromAddress}, " +
                                                    $"TokenAddress: {operation.TokenAddress}, " +
                                                    $"DesiredAmount: {operation.Amount} " +
                                                    $"CurrentBalance {balance}");

                    TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                             "",
                                                             operation.Amount.ToString(),
                                                             operation.TokenAddress,
                                                             operation.FromAddress,
                                                             operation.ToAddress,
                                                             "",
                                                             0,
                                                             SenderType.EthereumCore,
                                                             EventType.NotEnoughFunds,
                                                             WorkflowType.Airlines,
                                                             DateTime.UtcNow);

                    await _rabbitQueuePublisher.PublshEvent(@event);

                    return;
                }

                var trHash = await Erc223SharedService.StartDepositTransferAsync(_web3,
                                                                                 _settings.EthereumCore.Erc223DepositContract.Abi,
                                                                                 transactionSenderAddress,
                                                                                 operation.FromAddress,
                                                                                 operation.TokenAddress,
                                                                                 operation.ToAddress,
                                                                                 operation.Amount);

                await _hotWalletTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                });

                var message = new CoinTransactionMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                };

                //Observe transaction
                await _transactionMonitoringQueue.PutRawMessageAsync(message.ToJson());

                var notificationMessage = new LykkePayErc20TransferNotificationMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash,
                    Balance         = operation.Amount.ToString()
                };

                await _transactionStartedNotificationQueue.PutRawMessageAsync(notificationMessage.ToJson());
            }
            catch (ClientSideException ex)
            {
                if (operation == null)
                {
                    return;
                }

                TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                         "",
                                                         operation.Amount.ToString(),
                                                         operation.TokenAddress,
                                                         operation.FromAddress,
                                                         operation.ToAddress,
                                                         "",
                                                         0,
                                                         SenderType.EthereumCore,
                                                         EventType.Failed,
                                                         WorkflowType.Airlines,
                                                         DateTime.UtcNow);

                _logger.WriteWarning("Execute", operation.ToJson(), "ClientSideException", ex);

                await _rabbitQueuePublisher.PublshEvent(@event);
            }
            catch (Exception ex)
            {
                if (transaction == null)
                {
                    return;
                }

                if (ex.Message != transaction.LastError)
                {
                    _logger.WriteWarning("Execute", transaction.ToJson(), $"{transaction.OperationId}");
                }

                transaction.LastError = ex.Message;
                transaction.DequeueCount++;
                context.MoveMessageToEnd(transaction.ToJson());
                context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 200);

                _logger.WriteError("Execute", transaction.ToJson(), ex);
            }
        }
コード例 #19
0
        public async Task Execute(CoinTransactionMessage transaction, QueueTriggeringContext context)
        {
            ICoinTransaction coinTransaction = null;

            try
            {
                bool isTransactionInMemoryPool = await _ethereumTransactionService.IsTransactionInPool(transaction.TransactionHash);

                if (isTransactionInMemoryPool)
                {
                    SendMessageToTheQueueEnd(context, transaction, 100, "Transaction is in memory pool");
                    return;
                }

                coinTransaction = await _coinTransactionService.ProcessTransaction(transaction);
            }
            catch (Exception ex)
            {
                if (ex.Message != transaction.LastError)
                {
                    await _log.WriteWarningAsync("MonitoringCoinTransactionJob", "Execute", $"TrHash: [{transaction.TransactionHash}]", "");
                }

                SendMessageToTheQueueEnd(context, transaction, 200, ex.Message);

                await _log.WriteErrorAsync("MonitoringCoinTransactionJob", "Execute", "", ex);

                return;
            }

            if ((coinTransaction == null) &&
                (DateTime.UtcNow - transaction.PutDateTime > _broadcastMonitoringPeriodSeconds))
            {
                await RepeatOperationTillWin(transaction);

                await _slackNotifier.ErrorAsync($"EthereumCoreService: Transaction with hash {transaction.TransactionHash} has no confirmations." +
                                                $" Reason - unable to find transaction in txPool and in blockchain within {_broadcastMonitoringPeriodSeconds} seconds");
            }
            else
            {
                if (coinTransaction != null && coinTransaction.ConfirmationLevel >= CoinTransactionService.Level2Confirm)
                {
                    if (!coinTransaction.Error)
                    {
                        bool sentToRabbit = await SendCompletedCoinEvent(
                            transaction.TransactionHash,
                            transaction.OperationId,
                            true,
                            context,
                            transaction);

                        if (sentToRabbit)
                        {
                            await _log.WriteInfoAsync("CoinTransactionService", "Execute", "",
                                                      $"Put coin transaction {transaction.TransactionHash} to rabbit queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                        }
                        else
                        {
                            await _log.WriteInfoAsync("CoinTransactionService", "Execute", "",
                                                      $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                        }
                    }
                    else
                    {
                        ICoinEvent coinEvent = await GetCoinEvent(transaction.TransactionHash, transaction.OperationId, true);

                        await _slackNotifier.ErrorAsync($"EthereumCoreService: Transaction with hash {transaction.TransactionHash} has an Error!({coinEvent.CoinEventType})");

                        if (coinEvent.CoinEventType == CoinEventType.CashoutStarted ||
                            coinEvent.CoinEventType == CoinEventType.CashoutFailed)
                        {
                            if (coinTransaction.ConfirmationLevel >= 2)
                            {
                                //SEND FAILED CASHOUTS EVENTS HERE AND FILL Black LIST
                                await _blackListAddressesRepository.SaveAsync(new BlackListAddress()
                                {
                                    Address = coinEvent.ToAddress
                                });

                                await SendCompletedCoinEvent(transaction.TransactionHash, transaction.OperationId, false, context, transaction);
                            }
                            else
                            {
                                SendMessageToTheQueueEnd(context, transaction, 200, "Did not recieve confirmation level 3 yet");
                            }

                            return;
                        }
                        else
                        {
                            await RepeatOperationTillWin(transaction);

                            await _slackNotifier.ErrorAsync($"EthereumCoreService: Transaction with hash {transaction.TransactionHash} has an Error. RETRY!({coinEvent.CoinEventType})");
                        }
                    }
                }
                else
                {
                    SendMessageToTheQueueEnd(context, transaction, 100);
                    await _log.WriteInfoAsync("CoinTransactionService", "Execute", "",
                                              $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                }
            }
        }
コード例 #20
0
        //return whether we have sent to rabbit or not
        private async Task <bool> SendCompletedCoinEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction)
        {
            try
            {
                ICoinEvent coinEvent = await GetCoinEvent(transactionHash, operationId, success);

                switch (coinEvent.CoinEventType)
                {
                case CoinEventType.CashinStarted:
                    ICashinEvent cashinEvent = await _transactionEventsService.GetCashinEvent(transactionHash);

                    if (cashinEvent == null)
                    {
                        await _transactionEventsService.IndexEventsForTransaction(coinEvent.ContractAddress, transactionHash);

                        SendMessageToTheQueueEnd(context, transaction, 100);

                        return(false);
                    }

                    //transferContract - userAddress
                    await UpdateUserTransferWallet(coinEvent.FromAddress, coinEvent.ToAddress.ToLower());

                    coinEvent.Amount = cashinEvent.Amount;
                    coinEvent.CoinEventType++;
                    break;

                case CoinEventType.CashoutStarted:
                case CoinEventType.TransferStarted:
                    //Say that Event Is completed
                    coinEvent.CoinEventType++;
                    break;

                default: break;
                }

                #region FailedCashout

                if (coinEvent.CoinEventType == CoinEventType.CashoutCompleted && !success)
                {
                    coinEvent.CoinEventType = CoinEventType.CashoutFailed;
                    await _coinEventService.InsertAsync(coinEvent);

                    SendMessageToTheQueueEnd(context, transaction, 200, "Put Failed cashout in the end of the queue");

                    return(false);
                }

                if (coinEvent.CoinEventType == CoinEventType.CashoutFailed && !success)
                {
                    var historycal = await _pendingOperationService.GetHistoricalAsync(operationId);

                    if (historycal != null)
                    {
                        foreach (var match in historycal)
                        {
                            if (!string.IsNullOrEmpty(match.TransactionHash) &&
                                await _ethereumTransactionService.IsTransactionExecuted(match.TransactionHash, Constants.GasForCoinTransaction))
                            {
                                var @event = await _coinEventService.GetCoinEvent(match.TransactionHash);

                                if (@event != null && @event.TransactionHash.ToLower() == match.TransactionHash.ToLower())
                                {
                                    await _slackNotifier.ErrorAsync($"EthereumCoreService: Transaction with hash {coinEvent.TransactionHash} [{coinEvent.OperationId}]" +
                                                                    $" ({coinEvent.CoinEventType}). Previously was successfully transfered");

                                    return(false);
                                }
                            }
                        }
                    }
                }

                #endregion

                await _coinEventService.PublishEvent(coinEvent, putInProcessingQueue : false);

                await _pendingTransactionsRepository.Delete(transactionHash);

                await _pendingOperationService.MatchHashToOpId(transactionHash, coinEvent.OperationId);

                return(true);
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync("MonitoringCoinTransactionJob", "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow);

                SendMessageToTheQueueEnd(context, transaction, 100);

                return(false);
            }
        }
コード例 #21
0
        //return whether we have sent to rabbit or not
        private async Task <bool> SendCompletedCoinEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction)
        {
            try
            {
                ICoinEvent coinEvent = await GetCoinEvent(transactionHash, operationId, success);

                switch (coinEvent.CoinEventType)
                {
                case CoinEventType.CashinStarted:
                    ICashinEvent cashinEvent = await _transactionEventsService.GetCashinEvent(transactionHash);

                    if (cashinEvent == null)
                    {
                        SendMessageToTheQueueEnd(context, transaction, 100);

                        return(false);
                    }

                    //transferContract - userAddress
                    await UpdateUserTransferWallet(coinEvent.FromAddress, coinEvent.ToAddress.ToLower());

                    coinEvent.Amount = cashinEvent.Amount;
                    coinEvent.CoinEventType++;
                    break;

                case CoinEventType.CashoutStarted:
                case CoinEventType.TransferStarted:
                    //Say that Event Is completed
                    coinEvent.CoinEventType++;
                    break;

                default: break;
                }
                await _coinEventService.PublishEvent(coinEvent, putInProcessingQueue : false);

                await _pendingTransactionsRepository.Delete(transactionHash);

                await _pendingOperationService.MatchHashToOpId(transactionHash, coinEvent.OperationId);

                return(true);
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync("MonitoringCoinTransactionJob", "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow);

                SendMessageToTheQueueEnd(context, transaction, 100);

                return(false);
            }
        }
コード例 #22
0
        public async Task Execute(CoinTransactionMessage transaction, QueueTriggeringContext context)
        {
            ICoinTransaction coinTransaction = null;

            try
            {
                bool isTransactionInMemoryPool = await _ethereumTransactionService.IsTransactionInPool(transaction.TransactionHash);

                if (isTransactionInMemoryPool)
                {
                    SendMessageToTheQueueEnd(context, transaction, 100, "Transaction is in memory pool");
                    return;
                }

                coinTransaction = await _coinTransactionService.ProcessTransaction(transaction);
            }
            catch (Exception ex)
            {
                if (ex.Message != transaction.LastError)
                {
                    await _log.WriteWarningAsync(nameof(HotWalletMonitoringTransactionJob), "Execute", $"TrHash: [{transaction.TransactionHash}]", "");
                }

                SendMessageToTheQueueEnd(context, transaction, 200, ex.Message);

                await _log.WriteErrorAsync(nameof(HotWalletMonitoringTransactionJob), "Execute", "", ex);

                return;
            }

            if (coinTransaction == null || coinTransaction.Error)
            {
                await RepeatOperationTillWin(transaction);

                //await _slackNotifier.ErrorAsync($"EthereumCoreService: Transaction with hash {transaction.TransactionHash} has no confirmations." +
                //    $" Reason - unable to find transaction in txPool and in blockchain within {_broadcastMonitoringPeriodSeconds} seconds");
            }
            else
            {
                if (coinTransaction.ConfirmationLevel >= CoinTransactionService.Level2Confirm)
                {
                    if (!coinTransaction.Error)
                    {
                        bool sentToRabbit = await SendCompleteEvent(transaction.TransactionHash, transaction.OperationId, true, context, transaction);

                        if (sentToRabbit)
                        {
                            await _log.WriteInfoAsync(nameof(HotWalletMonitoringTransactionJob), "Execute", "",
                                                      $"Put coin transaction {transaction.TransactionHash} to rabbit queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                        }
                        else
                        {
                            await _log.WriteInfoAsync(nameof(HotWalletMonitoringTransactionJob), "Execute", "",
                                                      $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                        }
                    }
                    else
                    {
                        await _slackNotifier.ErrorAsync($"EthereumCoreService: HOTWALLET - Transaction with hash {transaction.TransactionHash} has an Error!");
                        await RepeatOperationTillWin(transaction);

                        await _slackNotifier.ErrorAsync($"EthereumCoreService: HOTWALLET - Transaction with hash {transaction.TransactionHash} has an Error. RETRY!");
                    }
                }
                else
                {
                    SendMessageToTheQueueEnd(context, transaction, 100);
                    await _log.WriteInfoAsync(nameof(HotWalletMonitoringTransactionJob), "Execute", "",
                                              $"Put coin transaction {transaction.TransactionHash} to monitoring queue with confimation level {coinTransaction?.ConfirmationLevel ?? 0}");
                }
            }
        }