public Task <CreateTransactionResponse> GetTransferTransaction(BitcoinAddress sourceAddress, BitcoinAddress destAddress, decimal amount, IAsset asset, Guid transactionId, bool shouldReserveFee = false, bool sentDust = false) { return(Retry.Try(async() => { var context = _transactionBuildContextFactory.Create(_connectionParams.Network); return await context.Build(async() => { var builder = new TransactionBuilder(); await TransferOneDirection(builder, context, sourceAddress, amount, asset, destAddress, !shouldReserveFee, sentDust); await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await _spentOutputService.SaveSpentOutputs(transactionId, buildedTransaction); await SaveNewOutputs(transactionId, buildedTransaction, context); if (shouldReserveFee) { await _feeReserveMonitoringWriter.AddTransactionFeeReserve(transactionId, context.FeeCoins); } return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
private async Task FinishOutputs(Guid transactionId, Transaction tr, BitcoinPubKeyAddress hotWallet) { await _broadcastedOutputRepository.InsertOutputs(tr.Outputs.AsCoins() .Where(o => o.ScriptPubKey.GetDestinationAddress(_connectionParams.Network).ToString() == hotWallet.ToString()) .Select(o => new BroadcastedOutput(o, transactionId, _connectionParams.Network))); await _broadcastedOutputRepository.SetTransactionHash(transactionId, tr.GetHash().ToString()); await _spentOutputService.SaveSpentOutputs(transactionId, tr); }
public async Task Send() { var feePerByte = (await _feeProvider.GetFeeRate()).FeePerK.Satoshi * _baseSettings.SpendChangeFeeRateMultiplier / 1000; var coins = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(_baseSettings.ChangeAddress, ConfirmationsCount)).OfType <Coin>() .Where(o => o.Amount.Satoshi > feePerByte * Constants.InputSize).ToList(); Utils.Shuffle(coins, new Random()); while (coins.Count > _baseSettings.NumberOfChangeInputsForTransaction) { var part = coins.Take(_baseSettings.NumberOfChangeInputsForTransaction).ToList(); var balance = part.Sum(o => o.Amount); var builder = new TransactionBuilder(); builder.AddCoins(part); builder.Send(new BitcoinPubKeyAddress(_baseSettings.HotWalletForPregeneratedOutputs), balance); var tr = builder.BuildTransaction(false); var fee = new Money((await _feeProvider.CalcFeeForTransaction(builder)).Satoshi * _baseSettings.SpendChangeFeeRateMultiplier, MoneyUnit.Satoshi); if (fee > balance) { await _log.WriteWarningAsync("SendFromChangeToHotWalletFunction", "Send", null, $"Calculated fee is more than balance ({fee.Satoshi}>{balance})"); continue; } tr.Outputs[0].Value = tr.Outputs[0].Value - fee; var signed = await _signatureApiProvider.SignTransaction(tr.ToHex()); var signedTr = new Transaction(signed); var transactionId = Guid.NewGuid(); await _bitcoinBroadcastService.BroadcastTransaction(transactionId, signedTr); await _spentOutputService.SaveSpentOutputs(transactionId, signedTr); coins = coins.Skip(_baseSettings.NumberOfChangeInputsForTransaction).ToList(); } }
private async Task SignAndBroadcastTransaction(Transaction buildedTransaction, TransactionBuildContext context) { try { var id = Guid.NewGuid(); var signed = await _signatureApi.SignTransaction(buildedTransaction.ToHex()); var tr = new Transaction(signed); await _spentOutputService.SaveSpentOutputs(id, tr); await SaveNewOutputs(id, buildedTransaction, context); await _bitcoinBroadcastService.BroadcastTransaction(id, tr); } catch (Exception) { await _spentOutputService.RemoveSpentOutputs(buildedTransaction); throw; } }