private async Task Broadcast(Guid transactionId, Transaction tx, IPerformanceMonitor monitor, string hash, bool savePaidFees = true)
        {
            monitor?.Step("Broadcast transaction");
            try
            {
                await _rpcBitcoinClient.BroadcastTransaction(tx, transactionId);
            }
            catch (RPCException ex)
            {
                var builder = new StringBuilder();
                builder.AppendLine($"[{transactionId}], ");
                builder.AppendLine(ex.Message + ":");
                foreach (var input in tx.Inputs)
                {
                    builder.AppendLine(input.PrevOut.ToString());
                }
                await _logger.WriteWarningAsync(nameof(BitcoinBroadcastService), nameof(BroadcastTransaction), builder.ToString(), ex);

                throw;
            }
            monitor?.Step("Set transaction hash and add to monitoring");
            await Task.WhenAll(
                savePaidFees?_paidFeesTaskWriter.AddTask(hash, DateTime.UtcNow, null, null) : Task.CompletedTask,
                _broadcastedOutputRepository.SetTransactionHash(transactionId, hash),
                _monitoringWriter.AddToMonitoring(transactionId, hash)
                );
        }
        private async Task AddBroadcastedOutputs(List <ICoin> coins, string walletAddress, int confirmationsCount, IPerformanceMonitor monitor)
        {
            //get unique saved coins
            if (confirmationsCount == 0)
            {
                var set = new HashSet <OutPoint>(coins.Select(x => x.Outpoint));

                monitor?.Step("Get broadcasted outputs");
                var internalSavedOutputs = (await _broadcastedOutputRepository.GetOutputs(walletAddress))
                                           .Where(o => !set.Contains(new OutPoint(uint256.Parse(o.TransactionHash), o.N)));

                coins.AddRange(internalSavedOutputs.Select(o =>
                {
                    var coin = new Coin(new OutPoint(uint256.Parse(o.TransactionHash), o.N),
                                        new TxOut(new Money(o.Amount, MoneyUnit.Satoshi), o.ScriptPubKey.ToScript()));
                    if (o.AssetId != null)
                    {
                        return
                        ((ICoin)
                         coin.ToColoredCoin(new BitcoinAssetId(o.AssetId, _connectionParams.Network).AssetId,
                                            (ulong)o.Quantity));
                    }
                    return(coin);
                }));
            }
        }
        public async Task BroadcastTransaction(Guid transactionId, List <Guid> notificationIds, Transaction tx, IPerformanceMonitor monitor = null, bool useHandlers = true)
        {
            var hash = tx.GetHash().ToString();

            if (_settings.UseLykkeApi && useHandlers)
            {
                monitor?.Step("Send prebroadcast multi notification");
                await _apiProvider.SendPreBroadcastMultiNotification(new LykkeTransactionMultiNotification(notificationIds, hash));
            }

            await Broadcast(transactionId, tx, monitor, hash);

            if (_settings.UseLykkeApi && useHandlers)
            {
                monitor?.Step("Send postbroadcast multi notification");
                await _apiProvider.SendPostBroadcastMultiNotification(new LykkeTransactionMultiNotification(notificationIds, hash));
            }
        }
        private async Task <List <ICoin> > FilterCoins(List <ICoin> coins, bool useInternalSpentOutputs, IPerformanceMonitor monitor)
        {
            monitor?.Step("Get unspent outputs");
            var unspentOutputs = await _spentOutputRepository.GetUnspentOutputs(coins.Select(o => new Output(o.Outpoint)));

            var unspentSet = new HashSet <OutPoint>(unspentOutputs.Select(x => new OutPoint(uint256.Parse(x.TransactionHash), x.N)));

            if (useInternalSpentOutputs)
            {
                monitor?.Step("Get internal spent outputs");
                var internalSpentOuputs =
                    new HashSet <OutPoint>(
                        (await _internalSpentOutputRepository.GetInternalSpentOutputs()).Select(x => new OutPoint(uint256.Parse(x.TransactionHash), x.N)));
                unspentSet.ExceptWith(internalSpentOuputs);
            }

            monitor?.Step("Filter ouputs");
            return(coins.Where(o => unspentSet.Contains(o.Outpoint)).ToList());
        }
        public async Task BroadcastTransaction(Guid transactionId, Transaction tx, IPerformanceMonitor monitor = null, bool useHandlers = true, Guid?notifyTxId = null, bool savePaidFees = true)
        {
            var hash = tx.GetHash().ToString();

            if (_settings.UseLykkeApi && useHandlers)
            {
                monitor?.Step("Send prebroadcast notification");
                await _apiProvider.SendPreBroadcastNotification(new LykkeTransactionNotification(notifyTxId ?? transactionId, hash));
            }

            await Broadcast(transactionId, tx, monitor, hash, savePaidFees);
        }
        public async Task <IEnumerable <ICoin> > GetUnspentOutputs(string walletAddress, int confirmationsCount = 0, bool useInternalSpentOutputs = true, IPerformanceMonitor monitor = null)
        {
            monitor?.Step("Get address balance");
            var outputResponse = await _qBitNinjaApiCaller.GetAddressBalance(walletAddress);

            var coins = outputResponse.Operations
                        .Where(x => x.Confirmations >= Math.Max(1, confirmationsCount))
                        .SelectMany(o => o.ReceivedCoins).ToList();

            await AddBroadcastedOutputs(coins, walletAddress, confirmationsCount, monitor);

            coins = await FilterCoins(coins, useInternalSpentOutputs, monitor);

            return(await ToScriptCoins(walletAddress, coins));
        }