public Task <CreateTransactionResponse> GetTransferTransaction(BitcoinAddress sourceAddress, BitcoinAddress destAddress, decimal amount, IAsset asset, Guid transactionId, bool shouldReserveFee = false) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); await TransferOneDirection(builder, context, sourceAddress, amount, asset, destAddress); await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await _signRequestRepository.InsertTransactionId(transactionId); 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)); }
public Task <CreateTransactionResponse> GetSwapTransaction(BitcoinAddress address1, decimal amount1, IAsset asset1, BitcoinAddress address2, decimal amount2, IAsset asset2, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); await TransferOneDirection(builder, context, address1, amount1, asset1, address2); await TransferOneDirection(builder, context, address2, amount2, asset2, address1); await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await _signRequestRepository.InsertTransactionId(transactionId); await SaveNewOutputs(transactionId, buildedTransaction, context); return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public async Task <string> BroadcastCommitment(string clientPubKey, IAsset asset, string transactionHex) { var address = await _multisigService.GetMultisig(clientPubKey); if (address == null) { throw new BackendException($"Client {clientPubKey} is not registered", ErrorCode.BadInputParameter); } var channel = await _offchainChannelRepository.GetChannel(address.MultisigAddress, asset.Id); if (channel == null) { throw new BackendException("Channel is not found", ErrorCode.ShouldOpenNewChannel); } if (!channel.IsBroadcasted) { throw new BackendException("Channel is not finalized", ErrorCode.ChannelNotFinalized); } var commitment = await _commitmentRepository.GetCommitment(address.MultisigAddress, asset.Id, transactionHex); if (commitment == null) { throw new BackendException("Commitment is not found", ErrorCode.CommitmentNotFound); } if (commitment.ChannelId != channel.ChannelId) { throw new BackendException("Commitment is expired", ErrorCode.CommitmentExpired); } var lastCommitment = await _commitmentRepository.GetLastCommitment(address.MultisigAddress, asset.Id, commitment.Type); if (commitment.CommitmentId != lastCommitment.CommitmentId) { throw new BackendException("Commitment is expired", ErrorCode.CommitmentExpired); } TransactionBuildContext context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return(await context.Build(async() => { var transaction = new Transaction(transactionHex); await _transactionBuildHelper.AddFee(transaction, context); var signed = await _signatureApiProvider.SignTransaction(transaction.ToHex()); var signedTr = new Transaction(signed); await _rpcBitcoinClient.BroadcastTransaction(signedTr, commitment.CommitmentId); await CloseChannel(commitment); return signedTr.GetHash().ToString(); })); }
public Task <CreateTransactionResponse> GetDestroyTransaction(BitcoinAddress bitcoinAddres, decimal modelAmount, IAsset asset, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var assetId = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId; var coins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(bitcoinAddres.ToString(), assetId)).ToList(); builder.SetChange(bitcoinAddres, ChangeType.Colored); builder.AddCoins(coins); var assetMoney = new AssetMoney(assetId, modelAmount, asset.MultiplierPower); var changeAddress = BitcoinAddress.Create(_baseSettings.ChangeAddress, _connectionParams.Network); _transactionBuildHelper.SendAssetWithChange(builder, context, coins, changeAddress, assetMoney, bitcoinAddres); await _transactionBuildHelper.AddFee(builder, context); var tx = builder.BuildTransaction(true); uint markerPosition; var colorMarker = ColorMarker.Get(tx, out markerPosition); for (var i = 0; i < colorMarker.Quantities.Length; i++) { if ((long)colorMarker.Quantities[i] == assetMoney.Quantity && tx.Outputs[i + 1].ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == changeAddress) { colorMarker.Quantities[i] = 0; break; } } tx.Outputs[markerPosition].ScriptPubKey = colorMarker.GetScript(); await SaveSpentOutputs(transactionId, tx); await _signRequestRepository.InsertTransactionId(transactionId); await SaveNewOutputs(transactionId, tx, context); return new CreateTransactionResponse(tx.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public Task <CreateTransactionResponse> GetTransferAllTransaction(BitcoinAddress @from, BitcoinAddress to, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var uncoloredCoins = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(from.ToString())).ToList(); var coloredCoins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(from.ToString())).ToList(); if (uncoloredCoins.Count == 0 && coloredCoins.Count == 0) { throw new BackendException("Address has no unspent outputs", ErrorCode.NoCoinsFound); } if (uncoloredCoins.Count > 0) { _transactionBuildHelper.SendWithChange(builder, context, uncoloredCoins, to, uncoloredCoins.Sum(o => o.TxOut.Value), from); } foreach (var assetGroup in coloredCoins.GroupBy(o => o.AssetId)) { var sum = new AssetMoney(assetGroup.Key); foreach (var coloredCoin in assetGroup) { sum += coloredCoin.Amount; } _transactionBuildHelper.SendAssetWithChange(builder, context, assetGroup.ToList(), to, sum, from); } await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await _signRequestRepository.InsertTransactionId(transactionId); await SaveNewOutputs(transactionId, buildedTransaction, context); return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public Task <CreateTransactionResponse> GetIssueTransaction(BitcoinAddress bitcoinAddres, decimal amount, IAsset asset, Guid transactionId) { return(Retry.Try(async() => { var context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); return await context.Build(async() => { var builder = new TransactionBuilder(); var queue = _pregeneratedOutputsQueueFactory.Create(asset.BlockChainAssetId); var coin = await queue.DequeueCoin(); try { var issueCoin = new IssuanceCoin(coin) { DefinitionUrl = new Uri(asset.DefinitionUrl) }; var assetId = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId; builder.AddCoins(issueCoin) .IssueAsset(bitcoinAddres, new AssetMoney(assetId, amount, asset.MultiplierPower)); context.IssueAsset(assetId); await _transactionBuildHelper.AddFee(builder, context); var buildedTransaction = builder.BuildTransaction(true); await SaveSpentOutputs(transactionId, buildedTransaction); await SaveNewOutputs(transactionId, buildedTransaction, context); return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId); } catch (Exception) { await queue.EnqueueOutputs(coin); throw; } }); }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log)); }
public async Task SpendCommitmemtByMultisig(ICommitment commitment, ICoin spendingCoin, string destination) { TransactionBuildContext context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); var destinationAddress = BitcoinAddress.Create(destination); await context.Build(async() => { TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(spendingCoin); if (OpenAssetsHelper.IsBitcoin(commitment.AssetId)) { builder.Send(destinationAddress, spendingCoin.Amount); } else { builder.SendAsset(destinationAddress, ((ColoredCoin)spendingCoin).Amount); } await _transactionBuildHelper.AddFee(builder, context); var tr = builder.BuildTransaction(false); var scriptParams = new OffchainScriptParams { IsMultisig = true, RedeemScript = commitment.LockedScript.ToScript().ToBytes(), Pushes = new[] { new byte[0], new byte[0], new byte[0] } }; tr.Inputs[0].ScriptSig = OffchainScriptCommitmentTemplate.GenerateScriptSig(scriptParams); var signed = await _signatureApiProvider.SignTransaction(tr.ToHex()); var signedTr = new Transaction(signed); var id = Guid.NewGuid(); await _rpcBitcoinClient.BroadcastTransaction(signedTr, id); await _lykkeTransactionBuilderService.SaveSpentOutputs(id, signedTr); return(Task.CompletedTask); }); }
public async Task <string> CreateUnsignedChannel(string clientPubKey, string hotWalletPubKey, decimal clientAmount, decimal hubAmount, IAsset asset) { var address = await _multisigService.GetMultisig(clientPubKey); if (address == null) { throw new BackendException($"Client {clientPubKey} is not registered", ErrorCode.BadInputParameter); } var multisig = new BitcoinScriptAddress(address.MultisigAddress, _connectionParams.Network); var clientAddress = new PubKey(clientPubKey).GetAddress(_connectionParams.Network); var hotWalletAddress = new PubKey(hotWalletPubKey).GetAddress(_connectionParams.Network); TransactionBuildContext context = new TransactionBuildContext(_connectionParams.Network, _pregeneratedOutputsQueueFactory); var currentChannel = await _offchainChannelRepository.GetChannel(address.MultisigAddress, asset.Id); if (currentChannel != null && !currentChannel.IsBroadcasted) { throw new BackendException("There is another pending channel setup", ErrorCode.AnotherChannelSetupExists); } return(await context.Build(async() => { var builder = new TransactionBuilder(); var multisigAmount = await SendToMultisig(multisig, multisig, asset, builder, context, -1); decimal clientChannelAmount, hubChannelAmount; if (currentChannel == null) { clientChannelAmount = Math.Max(0, clientAmount - multisigAmount); hubChannelAmount = hubAmount; await SendToMultisig(clientAddress, multisig, asset, builder, context, clientChannelAmount); await SendToMultisig(hotWalletAddress, multisig, asset, builder, context, hubAmount); clientChannelAmount += multisigAmount; } else { clientChannelAmount = Math.Max(0, clientAmount - currentChannel.ClientAmount); hubChannelAmount = Math.Max(0, hubAmount - currentChannel.HubAmount); await SendToMultisig(clientAddress, multisig, asset, builder, context, clientChannelAmount); await SendToMultisig(hotWalletAddress, multisig, asset, builder, context, hubChannelAmount); clientChannelAmount += currentChannel.ClientAmount; hubChannelAmount += currentChannel.HubAmount; } await _transactionBuildHelper.AddFee(builder, context); var tr = builder.BuildTransaction(true); _transactionBuildHelper.AggregateOutputs(tr); var hex = tr.ToHex(); var channel = await _offchainChannelRepository.CreateChannel(multisig.ToString(), asset.Id, hex, clientChannelAmount, hubChannelAmount); await _broadcastedOutputRepository.InsertOutputs(OpenAssetsHelper.OrderBasedColoringOutputs(tr, context) .Select(o => new BroadcastedOutput(o, channel.ChannelId, _connectionParams.Network))); return hex; })); }