public async Task BroadcastTransaction(NeoModules.NEP6.Transactions.Transaction signedTransaction,
                                               OperationAggregate aggregate)
        {
            var txHash = signedTransaction.Hash.ToString().Substring(2);

            var lastBlockHeight = await _blockchainProvider.GetHeightAsync();

            try
            {
                var isSuccess = await _neoRawTransactionSender.SendRequestAsync(signedTransaction.ToHexString());

                if (!isSuccess)
                {
                    throw new Exception("Unknown error while broadcasting the tx");
                }
            }
            catch (RpcResponseException e) when(e.RpcError.Code == -501)
            {
                throw new TransactionAlreadyBroadcastedException(e);
            }

            await _observableOperationRepository.InsertOrReplace(ObervableOperation.Create(aggregate,
                                                                                           BroadcastStatus.InProgress,
                                                                                           txHash,
                                                                                           (int)lastBlockHeight));

            await _unconfirmedTransactionRepository.InsertOrReplace(
                UnconfirmedTransaction.Create(aggregate.OperationId, txHash));

            await _transactionOutputsService.CompleteTxOutputs(aggregate.OperationId, signedTransaction);
        }
        public async Task <decimal?> UpdateBalance(string address)
        {
            if (await _observableWalletRepository.Get(address) != null)
            {
                var lastBlock = await _blockchainProvider.GetHeightAsync();

                var unspentOutputs = (await _transactionOutputsService.GetUnspentOutputsAsync(address))
                                     .ToList();

                var blockHeightFromTxHash = new ConcurrentDictionary <UInt256,
                                                                      (string txHash, int blockHeight, string blockHash)>();

                await unspentOutputs.Select(p => p.Reference.PrevHash).Distinct()
                .ForEachAsyncSemaphore(8, async txHash =>
                {
                    var tx = await _blockchainProvider.GetTransactionOrDefaultAsync(txHash.ToString().Substring(2));

                    if (tx == null)
                    {
                        throw new InvalidOperationException($"Unable to find transaction with hash {txHash}");
                    }

                    blockHeightFromTxHash.TryAdd(txHash, tx.Value);
                });

                var validatedUnspentOutputs = unspentOutputs
                                              .Where(p =>
                {
                    var tx = blockHeightFromTxHash[p.Reference.PrevHash];

                    if (tx.blockHash == null)     // unconfirmed tx
                    {
                        return(false);
                    }

                    return(tx.blockHeight <= lastBlock);
                }).ToList();


                var neoBalance = (decimal)validatedUnspentOutputs
                                 .Where(p => p.Output.AssetId == Utils.NeoToken)
                                 .Sum(p => p.Output.Value);

                var gasBalance = (decimal)validatedUnspentOutputs
                                 .Where(p => p.Output.AssetId == Utils.GasToken)
                                 .Sum(p => p.Output.Value);

                await Task.WhenAll(UpdateBalanceInRepo(lastBlock, address, neoBalance, Constants.Assets.Neo.AssetId),
                                   UpdateBalanceInRepo(lastBlock, address, gasBalance, Constants.Assets.Gas.AssetId));;
            }

            return(null);
        }
Ejemplo n.º 3
0
        private async Task CheckTransaction(IUnconfirmedTransaction unconfirmedTx)
        {
            var operation = await _operationRepository.GetOrDefault(unconfirmedTx.OperationId);

            if (operation == null)
            {
                _log.Error(nameof(DetectTransactionsPeriodicalHandler),
                           message: $"Aggregate for operation {unconfirmedTx.OperationId} not found");

                return;
            }

            var blockchainTx = await _blockchainProvider.GetTransactionOrDefaultAsync(unconfirmedTx.TxHash);

            var isCompleted = blockchainTx?.blockHash != null; //once a tx included in a block means the tx is confirmed by the 7 consensus nodes and cannt be reversed

            var lastBlockHeight = await _blockchainProvider.GetHeightAsync();

            var status = isCompleted
                ? BroadcastStatus.Completed
                : BroadcastStatus.InProgress;

            await _observableOperationRepository.InsertOrReplace(ObervableOperation.Create(operation, status,
                                                                                           unconfirmedTx.TxHash,
                                                                                           lastBlockHeight));

            if (isCompleted)
            {
                var fromAddressBalance = await _walletBalanceService.UpdateBalance(operation.FromAddress);

                var toAddressBalance = await _walletBalanceService.UpdateBalance(operation.ToAddress);

                var operationCompletedLoggingContext = new
                {
                    unconfirmedTx.OperationId,
                    unconfirmedTx.TxHash,
                    fromAddressBalance,
                    toAddressBalance
                };

                _log.Info("Transaction detected on blockchain", context: operationCompletedLoggingContext);


                await _unconfirmedTransactionRepository.DeleteIfExist(unconfirmedTx.OperationId);

                operation.OnDetectedOnBlockcain(DateTime.UtcNow);
                await _operationRepository.Save(operation);
            }
        }