示例#1
0
        public async Task <SplitTransactionResponse> GetSplitTransaction([FromQuery] string multisig, [FromQuery] string clientDestination, [FromQuery] string hubDestination)
        {
            var result = await _bccTransactionService.CreateSplitTransaction(multisig, OpenAssetsHelper.ParseAddress(clientDestination),
                                                                             OpenAssetsHelper.ParseAddress(hubDestination));

            return(new SplitTransactionResponse(result));
        }
示例#2
0
        public async Task SpendMultisigWitOverP2ShOutput()
        {
            var pk1     = Key.Parse("cMahea7zqjxrtgAbB7LSGbcZDo359LNtib5kYpwbiSqBqvs6cqPV");
            var pk2     = Key.Parse("cQDux9gANFC1mPiwPpx7feHpiZu9xKn8RyV8yLErazuzWt146oY1");
            var pubKey1 = new PubKey("03ebcc2d675d17c5b5e250307cb0189bfc5adf6809bfd3c2823a2884dbbcaec58b").Compress();
            var pubKey2 = new PubKey("02235060021d06f6c4e766574b0374dde8d050a0a036ee52cde04608a87eebc3e1").Compress();

            var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKey1, pubKey2);

            var coin = new Coin(new OutPoint(uint256.Parse("2b2e47cd0ba3be2a013fd8658f24e66aae5d492837423ccee76e4e670e980b6f"), 0),
                                new TxOut(Money.FromUnit(1, MoneyUnit.BTC), "a914d388706006374eb728135014c9cad72e5dcd72fe87".ToScript()));

            var scriptCoin = coin.ToScriptCoin(redeem);

            var builder = new TransactionBuilder();

            builder.AddCoins(scriptCoin);
            builder.Send(OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r"), Money.FromUnit(0.5M, MoneyUnit.BTC));
            builder.SetChange(coin.ScriptPubKey);
            builder.SendFees(Money.FromUnit(0.0001M, MoneyUnit.BTC));

            builder.AddKeys(pk1, pk2);
            var tr = builder.BuildTransaction(true);

            //await Broadcast(tr);
        }
示例#3
0
        public async Task SpendSegwitOutput()
        {
            var pk1     = Key.Parse("cMahea7zqjxrtgAbB7LSGbcZDo359LNtib5kYpwbiSqBqvs6cqPV");
            var pk2     = Key.Parse("cQDux9gANFC1mPiwPpx7feHpiZu9xKn8RyV8yLErazuzWt146oY1");
            var pubKey1 = new PubKey("03ebcc2d675d17c5b5e250307cb0189bfc5adf6809bfd3c2823a2884dbbcaec58b").Compress();
            var pubKey2 = new PubKey("02235060021d06f6c4e766574b0374dde8d050a0a036ee52cde04608a87eebc3e1").Compress();

            var redeem = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKey1, pubKey2);

            var coin = new Coin(new OutPoint(uint256.Parse("9aff0ea55be499f0c664e447a93f7a38a87cc34f24f464642c7553b40fd18958"), 1),
                                new TxOut(Money.FromUnit(0.2M, MoneyUnit.BTC), redeem.WitHash.ScriptPubKey));

            var scriptCoin = coin.ToScriptCoin(redeem);

            var builder = new TransactionBuilder();

            builder.AddCoins(scriptCoin);
            builder.Send(OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r"), Money.FromUnit(0.1M, MoneyUnit.BTC));
            builder.SetChange(coin.ScriptPubKey);
            builder.SendFees(Money.FromUnit(0.0001M, MoneyUnit.BTC));

            builder.AddKeys(pk1, pk2);
            var tr = builder.BuildTransaction(true);

            //await Broadcast(tr);
        }
示例#4
0
        public async Task <PrivateBccTransferResponse> GetPrivateTransfer([FromQuery] string sourceAddress, [FromQuery] string destinationAddress, [FromQuery] decimal fee)
        {
            var result = await _bccTransactionService.CreatePrivateTransfer(OpenAssetsHelper.ParseAddress(sourceAddress),
                                                                            OpenAssetsHelper.ParseAddress(destinationAddress), fee);

            return(new PrivateBccTransferResponse
            {
                Transaction = result.TransactionHex,
                Outputs = result.Outputs
            });
        }
示例#5
0
        public async Task Process(SpendCommitmentMonitorindMessage message, QueueTriggeringContext context)
        {
            if (DateTime.UtcNow - message.LastTryTime.GetValueOrDefault() < MessageProcessDelay)
            {
                MoveToEnd(context, message);
                return;
            }
            message.LastTryTime = DateTime.UtcNow;

            var tr = await _qBitNinjaApiCaller.GetTransaction(message.TransactionHash);

            if (tr?.Block == null || tr.Block.Confirmations < OffchainService.OneDayDelay)
            {
                MoveToEnd(context, message);
                return;
            }
            var commitment = await _commitmentRepository.GetCommitment(message.CommitmentId);

            var lockedAddr = OpenAssetsHelper.ParseAddress(commitment.LockedAddress);
            var coin       = tr.ReceivedCoins
                             .FirstOrDefault(o => o.TxOut.ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == lockedAddr);

            if (coin == null)
            {
                throw new Exception("Not found coin for spending for " + message.ToJson());
            }

            if (coin is Coin)
            {
                coin = ((Coin)coin).ToScriptCoin(commitment.LockedScript.ToScript());
            }
            else
            {
                var colored = coin as ColoredCoin;
                coin = colored.Bearer.ToScriptCoin(commitment.LockedScript.ToScript()).ToColoredCoin(colored.Amount);
            }

            var assetSettings = await _offchainService.GetAssetSetting(commitment.AssetId);

            try
            {
                var hash = await _offchainService.SpendCommitmemtByPubkey(commitment, coin,
                                                                          !string.IsNullOrEmpty(assetSettings.ChangeWallet)?assetSettings.ChangeWallet : assetSettings.HotWallet);

                await _log.WriteInfoAsync(nameof(SpendBroadcastedCommitmentFunction), nameof(Process), message.ToJson(),
                                          "Spent commitment by transaction" + hash);
            }
            catch (Exception ex)
            {
                await _log.WriteWarningAsync(nameof(SpendBroadcastedCommitmentFunction), nameof(Process), message.ToJson(), ex);

                MoveToEnd(context, message);
            }
        }
示例#6
0
        private async Task ValidateAddress(string address, bool checkOffchain = true)
        {
            var bitcoinAddres = OpenAssetsHelper.ParseAddress(address);

            if (bitcoinAddres == null)
            {
                throw new BackendException($"Invalid Address provided: {address}", ErrorCode.InvalidAddress);
            }
            if (checkOffchain && await _offchainService.HasChannel(address))
            {
                throw new BackendException("Address was used in offchain", ErrorCode.AddressUsedInOffchain);
            }
        }
        public async Task Save(string address, IEnumerable <ICoin> coins)
        {
            try
            {
                var str = Serializer.ToString(coins, OpenAssetsHelper.ParseAddress(address).Network);

                await _storage.SaveBlobAsync(BlobContainer, $"{address}_{DateTime.UtcNow:yyyy-MM-dd_HH-mm-ss.fff}.txt", Encoding.UTF8.GetBytes(str));
            }
            // ReSharper disable once EmptyGeneralCatchClause
            catch (Exception)
            {
            }
        }
示例#8
0
        private Task GenerateColorOutputs(string assetId)
        {
            return(Retry.Try(async() =>
            {
                var asset = await _assetRepostory.GetItemAsync(assetId);
                var setting = await GetAssetSetting(assetId);
                var hotWallet = OpenAssetsHelper.ParseAddress(setting.HotWallet);

                var assetIdObj = new BitcoinAssetId(asset.BlockChainAssetId).AssetId;

                var outputs = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.HotWallet, assetIdObj, 0, false);

                var balance = outputs.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);
                var outputSize = new AssetMoney(assetIdObj, setting.OutputSize, asset.MultiplierPower);


                var changeBalance = new AssetMoney(assetIdObj);
                if (setting.ChangeWallet != setting.HotWallet && !string.IsNullOrEmpty(setting.ChangeWallet))
                {
                    var changeOutputs = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.ChangeWallet, assetIdObj, 0, false);
                    changeBalance = changeOutputs.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);
                }

                if ((balance + changeBalance).ToDecimal(asset.MultiplierPower) < setting.MinBalance)
                {
                    await SendBalanceNotifications(assetId, setting.HotWallet, setting.MinBalance);
                }

                var existingCoinsCount = outputs.Count(o => o.Amount <= outputSize && o.Amount.Quantity > outputSize.Quantity / 2);

                if (existingCoinsCount > setting.MinOutputsCount)
                {
                    return;
                }

                var generateCnt = setting.MaxOutputsCount - existingCoinsCount;

                var coins = outputs.Where(o => o.Amount > outputSize * 2).ToList();

                balance = coins.Aggregate(new AssetMoney(assetIdObj, 0), (accum, coin) => accum + coin.Amount);

                generateCnt = Math.Min(generateCnt, (int)(balance.Quantity / outputSize.Quantity));
                if (generateCnt == 0)
                {
                    return;
                }

                await GenerateOutputs(generateCnt, coins, hotWallet, outputSize, asset, setting);
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _logger));
        }
示例#9
0
        public async Task <CreateTransactionResponse> GetTransferFromSegwitWallet(BitcoinAddress source, Guid transactionId)
        {
            var assetSetting = await _assetSettingCache.GetItemAsync("BTC");

            var asset = await _assetRepository.GetItemAsync("BTC");

            var hotWallet = OpenAssetsHelper.ParseAddress(!string.IsNullOrEmpty(assetSetting.ChangeWallet) ? assetSetting.ChangeWallet : assetSetting.HotWallet);
            var context   = _transactionBuildContextFactory.Create(_connectionParams.Network);
            var lowVolume = new Money((decimal)asset.LowVolumeAmount, MoneyUnit.BTC);

            return(await context.Build(async() =>
            {
                var outputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(source.ToString())).OfType <Coin>()
                              .Where(o => o.Amount >= lowVolume).ToList();
                if (!outputs.Any())
                {
                    throw new BackendException($"Address {source} has not unspent outputs", ErrorCode.NoCoinsFound);
                }

                var totalAmount = new Money(outputs.Sum(o => o.Amount));

                var builder = new TransactionBuilder();
                builder.DustPrevention = false;
                builder.AddCoins(outputs);
                builder.Send(hotWallet, totalAmount);
                builder.SubtractFees();

                builder.SendEstimatedFees(await _feeProvider.GetFeeRate());

                builder.SetChange(hotWallet);

                var tx = builder.BuildTransaction(true);

                _transactionBuildHelper.AggregateOutputs(tx);

                if (tx.Outputs[0].Value <= tx.Outputs[0].GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee))
                {
                    throw new BackendException($"Address {source} balance is too low", ErrorCode.NoCoinsFound);
                }

                await _spentOutputService.SaveSpentOutputs(transactionId, tx);

                await SaveNewOutputs(transactionId, tx, context);

                return new CreateTransactionResponse(tx.ToHex(), transactionId);
            }));
        }
示例#10
0
        private Task GenerateBtcOutputs()
        {
            return(Retry.Try(async() =>
            {
                var setting = await GetAssetSetting("BTC");
                var hotWallet = OpenAssetsHelper.ParseAddress(setting.HotWallet);

                var outputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(setting.HotWallet, 0, false)).Cast <Coin>().ToList();

                var balance = new Money(outputs.DefaultIfEmpty().Sum(o => o?.Amount ?? Money.Zero));
                var outputSize = new Money(setting.OutputSize, MoneyUnit.BTC);

                Money changeBalance = Money.Zero;
                if (setting.ChangeWallet != setting.HotWallet && !string.IsNullOrEmpty(setting.ChangeWallet))
                {
                    var changeOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(setting.ChangeWallet, 0, false)).Cast <Coin>().ToList();
                    changeBalance = new Money(changeOutputs.DefaultIfEmpty().Sum(o => o?.Amount ?? Money.Zero));
                }

                if ((balance + changeBalance).ToDecimal(MoneyUnit.BTC) < setting.MinBalance)
                {
                    await SendBalanceNotifications("BTC", setting.HotWallet, setting.MinBalance);
                }


                var existingCoinsCount = outputs.Count(o => o.Amount <= outputSize && o.Amount > outputSize / 2);

                if (existingCoinsCount > setting.MinOutputsCount)
                {
                    return;
                }

                var generateCnt = setting.MaxOutputsCount - existingCoinsCount;

                var coins = outputs.Where(o => o.Amount > outputSize * 2).ToList();

                balance = coins.DefaultIfEmpty().Sum(o => o?.Amount ?? Money.Zero);

                generateCnt = Math.Min(generateCnt, (int)(balance / outputSize));
                if (generateCnt == 0)
                {
                    return;
                }
                await GenerateOutputs(generateCnt, coins, hotWallet, outputSize, await _assetRepostory.GetItemAsync("BTC"), setting);
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _logger));
        }
示例#11
0
        public async Task SendToWitnessAddress()
        {
            var sourceAddr = OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r");
            var addr       = OpenAssetsHelper.ParseAddress("tb1q2xvc2c503h95sm8nu5wyj68xee23su5wt46au5ztdsa9neqs424qh8kxal");
            var coin       = new Coin(new OutPoint(uint256.Parse("1fcedb863194c3c31f133e09c509e54b2cb40ef0a651a41a23d534301eb57af0"), 1),
                                      new TxOut(Money.FromUnit(0.5M, MoneyUnit.BTC), sourceAddr));
            var key     = Key.Parse("93586ks3uwSAgJ6q3He4CkuXeVg1N4syvszP514TitfcA9mXjVo");
            var builder = new TransactionBuilder();

            builder.AddCoins(coin);
            builder.SetChange(sourceAddr);
            builder.SendFees(Money.FromUnit(0.0001M, MoneyUnit.BTC));
            builder.Send(addr, Money.FromUnit(0.2M, MoneyUnit.BTC));
            builder.AddKeys(key);
            var tr = builder.BuildTransaction(true);
            //await Broadcast(tr);
        }
示例#12
0
        public async Task <IActionResult> CreateMultipleTransfer([FromBody] MultipleTransferRequest model)
        {
            foreach (var source in model.Sources)
            {
                await ValidateAddress(source.Address);
            }

            if (model.FixedFee.GetValueOrDefault() < 0)
            {
                throw new BackendException("Fixed fee must be greater than or equal to zero", ErrorCode.BadInputParameter);
            }

            var destAddress = OpenAssetsHelper.ParseAddress(model.Destination);

            if (destAddress == null)
            {
                throw new BackendException("Invalid destination address provided", ErrorCode.InvalidAddress);
            }

            var asset = await _assetRepository.GetAssetById(model.Asset);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }

            var transactionId = await _builder.AddTransactionId(model.TransactionId, $"MultipleTransfer: {model.ToJson()}");

            var response = await _builder.GetMultipleTransferTransaction(destAddress, asset,
                                                                         model.Sources.ToDictionary(x => x.Address, x => x.Amount), model.FeeRate, model.FixedFee.GetValueOrDefault(), transactionId);

            var fullSignedHex = await _signatureApiProvider.SignTransaction(response.Transaction);

            await _transactionBlobStorage.AddOrReplaceTransaction(transactionId, TransactionBlobType.Signed, fullSignedHex);

            var fullSigned = new Transaction(fullSignedHex);

            await _broadcastService.BroadcastTransaction(transactionId, fullSigned, useHandlers : false);

            return(Ok(new TransactionIdAndHashResponse
            {
                TransactionId = transactionId,
                Hash = fullSigned.GetHash().ToString()
            }));
        }
示例#13
0
        private async Task GenerateIssueAllowedCoins()
        {
            foreach (var asset in await _assetRepostory.Values())
            {
                if (OpenAssetsHelper.IsBitcoin(asset.Id) || OpenAssetsHelper.IsLkk(asset.Id) || !asset.IssueAllowed)
                {
                    continue;
                }

                try
                {
                    var setting = await GetAssetSetting(asset.Id);

                    if (setting.HotWallet != setting.ChangeWallet)
                    {
                        continue;
                    }

                    var hotWallet = OpenAssetsHelper.ParseAddress(setting.HotWallet);
                    var assetId   = new BitcoinAssetId(asset.BlockChainAssetId).AssetId;

                    var coins = await _bitcoinOutputsService.GetColoredUnspentOutputs(setting.HotWallet, assetId);

                    var outputSize = new AssetMoney(assetId, setting.OutputSize, asset.MultiplierPower);

                    await _logger.WriteInfoAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, "Start process");

                    var existingCoinsCount = coins.Count(o => o.Amount <= outputSize && o.Amount * 2 > outputSize);

                    if (existingCoinsCount > setting.MinOutputsCount)
                    {
                        continue;
                    }

                    var generateCnt = setting.MaxOutputsCount - existingCoinsCount;
                    var generated   = 0;
                    while (generated < generateCnt)
                    {
                        var outputsCount = Math.Min(setting.MaxOutputsCountInTx, generateCnt - generated);

                        var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

                        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)
                                };

                                builder.AddCoins(issueCoin);

                                for (var i = 0; i < outputsCount; i++)
                                {
                                    builder.IssueAsset(hotWallet, outputSize);
                                }
                                context.IssueAsset(assetId);
                                await _transactionBuildHelper.AddFee(builder, context);

                                var tr = builder.BuildTransaction(true);

                                await SignAndBroadcastTransaction(tr, context);

                                return("");
                            }
                            catch (Exception)
                            {
                                await queue.EnqueueOutputs(coin);

                                throw;
                            }
                        });

                        generated += outputsCount;
                    }
                }
                catch (Exception ex)
                {
                    await _logger.WriteWarningAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, ex);
                }
                finally
                {
                    await _logger.WriteInfoAsync("GenerateOffchainOutputsFunction", "GenerateIssueAllowedCoins", "AssetId " + asset.Id, "End process");
                }
            }
        }
示例#14
0
        public Task <CreateMultiCashoutTransactionResult> GetMultipleCashoutTransaction(List <ICashoutRequest> cashoutRequests, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);
                var assetSetting = await _assetSettingCache.GetItemAsync("BTC");

                var hotWallet = OpenAssetsHelper.ParseAddress(assetSetting.HotWallet);
                var changeWallet = OpenAssetsHelper.ParseAddress(string.IsNullOrWhiteSpace(assetSetting.ChangeWallet)
                    ? assetSetting.HotWallet
                    : assetSetting.ChangeWallet);

                return await context.Build(async() =>
                {
                    var builder = new TransactionBuilder();

                    var hotWalletOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(hotWallet.ToString())).OfType <Coin>().ToList();

                    var hotWalletBalance = new Money(hotWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));

                    var maxFeeForTransaction = Money.FromUnit(0.099M, MoneyUnit.BTC);

                    var selectedCoins = new List <Coin>();

                    var maxInputsCount = maxFeeForTransaction.Satoshi / (await _feeProvider.GetFeeRate()).GetFee(Constants.InputSize).Satoshi;
                    var tryCount = 100;
                    do
                    {
                        if (selectedCoins.Count > maxInputsCount && cashoutRequests.Count > 1)
                        {
                            cashoutRequests = cashoutRequests.Take(cashoutRequests.Count - 1).ToList();
                            selectedCoins.Clear();
                        }
                        else
                        if (selectedCoins.Count > 0)
                        {
                            break;
                        }

                        var totalAmount = Money.FromUnit(cashoutRequests.Select(o => o.Amount).Sum(), MoneyUnit.BTC);

                        if (hotWalletBalance < totalAmount + maxFeeForTransaction)
                        {
                            var changeBalance = Money.Zero;
                            List <Coin> changeWalletOutputs = new List <Coin>();
                            if (hotWallet != changeWallet)
                            {
                                changeWalletOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(changeWallet.ToString()))
                                                      .OfType <Coin>().ToList();
                                changeBalance = new Money(changeWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));
                            }
                            if (changeBalance + hotWalletBalance >= totalAmount + maxFeeForTransaction)
                            {
                                selectedCoins.AddRange(hotWalletOutputs);
                                selectedCoins.AddRange(OpenAssetsHelper
                                                       .CoinSelect(changeWalletOutputs, totalAmount + maxFeeForTransaction - hotWalletBalance).OfType <Coin>());
                            }
                            else
                            {
                                selectedCoins.AddRange(hotWalletOutputs);
                                selectedCoins.AddRange(changeWalletOutputs);

                                int cashoutsUsedCount = 0;
                                var cashoutsAmount = await _transactionBuildHelper.CalcFee(selectedCoins.Count, cashoutRequests.Count + 1);
                                foreach (var cashoutRequest in cashoutRequests)
                                {
                                    cashoutsAmount += Money.FromUnit(cashoutRequest.Amount, MoneyUnit.BTC);
                                    if (cashoutsAmount > hotWalletBalance + changeBalance)
                                    {
                                        break;
                                    }
                                    cashoutsUsedCount++;
                                }
                                if (cashoutsUsedCount == 0)
                                {
                                    throw new BackendException("Not enough bitcoin available", ErrorCode.NotEnoughBitcoinAvailable);
                                }
                                cashoutRequests = cashoutRequests.Take(cashoutsUsedCount).ToList();
                            }

                            if (changeWallet != hotWallet)
                            {
                                if (assetSetting.Asset == Constants.DefaultAssetSetting)
                                {
                                    assetSetting = await CreateAssetSetting("BTC", assetSetting);
                                }

                                if (assetSetting.Asset != Constants.DefaultAssetSetting)
                                {
                                    await _assetSettingRepository.UpdateHotWallet(assetSetting.Asset, assetSetting.ChangeWallet);
                                }
                            }
                        }
                        else
                        {
                            selectedCoins.AddRange(OpenAssetsHelper.CoinSelect(hotWalletOutputs, totalAmount + maxFeeForTransaction).OfType <Coin>());
                        }
                    } while (tryCount-- > 0);

                    var selectedCoinsAmount = new Money(selectedCoins.Sum(o => o.Amount));
                    var sendAmount = new Money(cashoutRequests.Sum(o => o.Amount), MoneyUnit.BTC);

                    builder.AddCoins(selectedCoins);
                    foreach (var cashout in cashoutRequests)
                    {
                        var amount = Money.FromUnit(cashout.Amount, MoneyUnit.BTC);
                        builder.Send(OpenAssetsHelper.ParseAddress(cashout.DestinationAddress), amount);
                    }

                    builder.Send(changeWallet, selectedCoinsAmount - sendAmount);
                    builder.SubtractFees();
                    builder.SendEstimatedFees(await _feeProvider.GetFeeRate());
                    builder.SetChange(changeWallet);

                    var tx = builder.BuildTransaction(true);
                    _transactionBuildHelper.AggregateOutputs(tx);

                    await _broadcastedOutputRepository.InsertOutputs(
                        tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == changeWallet.ScriptPubKey).Select(o =>
                                                                                                            new BroadcastedOutput(o, transactionId, _connectionParams.Network)).ToList());

                    await _spentOutputService.SaveSpentOutputs(transactionId, tx);

                    return new CreateMultiCashoutTransactionResult
                    {
                        Transaction = tx,
                        UsedRequests = cashoutRequests
                    };
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
示例#15
0
        public Task <CreateTransactionResponse> GetMultipleTransferTransaction(BitcoinAddress destination, IAsset asset, Dictionary <string, decimal> transferAddresses, int feeRate, decimal fixedFee, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

                return await context.Build(async() =>
                {
                    var builder = new TransactionBuilder();

                    builder.SetChange(destination, ChangeType.Uncolored);

                    foreach (var transferAddress in transferAddresses)
                    {
                        var source = OpenAssetsHelper.ParseAddress(transferAddress.Key);
                        await TransferOneDirection(builder, context, source, transferAddress.Value, asset, destination);
                    }

                    Transaction buildedTransaction;
                    try
                    {
                        buildedTransaction = builder.BuildTransaction(true);
                    }
                    catch (NotEnoughFundsException ex)
                    {
                        if (ex.Missing is Money)
                        {
                            var missingAmount = ((Money)ex.Missing).Satoshi;
                            _transactionBuildHelper.AddFakeInput(builder, new Money(missingAmount, MoneyUnit.Satoshi));
                            buildedTransaction = builder.BuildTransaction(true);
                        }
                        else
                        {
                            throw;
                        }
                    }

                    _transactionBuildHelper.RemoveFakeInput(buildedTransaction);

                    _transactionBuildHelper.AggregateOutputs(buildedTransaction);

                    var fee = fixedFee > 0 ? Money.FromUnit(fixedFee, MoneyUnit.BTC) : await _transactionBuildHelper.CalcFee(buildedTransaction, feeRate);

                    foreach (var output in buildedTransaction.Outputs)
                    {
                        if (output.ScriptPubKey.GetDestinationAddress(_connectionParams.Network) == destination)
                        {
                            if (output.Value <= fee)
                            {
                                throw new BackendException("Amount is lower than fee", ErrorCode.NotEnoughBitcoinAvailable);
                            }
                            output.Value -= fee;
                            break;
                        }
                    }

                    await _spentOutputService.SaveSpentOutputs(transactionId, buildedTransaction);

                    await SaveNewOutputs(transactionId, buildedTransaction, context);

                    return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId);
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
示例#16
0
        public Task <CreateTransactionResponse> GetTransferAllTransaction(BitcoinAddress @from, BitcoinAddress to, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

                var channels = await _offchainService.GetCurrentChannels(from.ToString());

                var assets = await _assetRepository.Values();

                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);
                    }

                    async Task <IDestination> GetChangeWallet(string asset)
                    {
                        var assetSetting = await _offchainService.GetAssetSetting(asset);
                        return OpenAssetsHelper.ParseAddress(!string.IsNullOrEmpty(assetSetting.ChangeWallet) ? assetSetting.ChangeWallet
                            : assetSetting.HotWallet);
                    };

                    if (uncoloredCoins.Count > 0)
                    {
                        var hubAmount = Money.Zero;
                        IDestination hubAmountAddress = null;
                        var channel = channels.FirstOrDefault(o => o.Asset == "BTC");
                        if (channel != null)
                        {
                            hubAmount = Money.FromUnit(channel.HubAmount, MoneyUnit.BTC);
                            hubAmountAddress = await GetChangeWallet("BTC");
                        }

                        builder.AddCoins(uncoloredCoins);
                        context.AddCoins(uncoloredCoins);
                        builder.Send(to, uncoloredCoins.Sum(o => o.TxOut.Value) - hubAmount);
                        if (hubAmount > 0)
                        {
                            builder.Send(hubAmountAddress, hubAmount);
                        }
                    }
                    foreach (var assetGroup in coloredCoins.GroupBy(o => o.AssetId))
                    {
                        var asset = assets.First(o => o.BlockChainAssetId == assetGroup.Key.GetWif(_connectionParams.Network).ToString());

                        var channel = channels.FirstOrDefault(o => o.Asset == asset.Id);

                        var sum = new AssetMoney(assetGroup.Key);
                        foreach (var coloredCoin in assetGroup)
                        {
                            sum += coloredCoin.Amount;
                        }

                        var hubAmount = new AssetMoney(assetGroup.Key);
                        IDestination hubAmountAddress = null;
                        if (channel != null)
                        {
                            hubAmount = new AssetMoney(assetGroup.Key, channel.HubAmount, asset.MultiplierPower);
                            hubAmountAddress = await GetChangeWallet(asset.Id);
                        }
                        builder.AddCoins(assetGroup.ToList());
                        context.AddCoins(assetGroup.ToList());
                        builder.SendAsset(to, sum - hubAmount);
                        if (hubAmount.Quantity > 0)
                        {
                            builder.SendAsset(hubAmountAddress, hubAmount);
                        }
                    }
                    await _transactionBuildHelper.AddFee(builder, context);

                    var buildedTransaction = builder.BuildTransaction(true);

                    await _spentOutputService.SaveSpentOutputs(transactionId, buildedTransaction);

                    await SaveNewOutputs(transactionId, buildedTransaction, context);

                    foreach (var offchainChannel in channels)
                    {
                        await _offchainService.RemoveChannel(offchainChannel);
                    }

                    return new CreateTransactionResponse(buildedTransaction.ToHex(), transactionId);
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
 public Task OnMessage(BccTransferCommand command)
 {
     return(_bccTransactionService.Transfer(OpenAssetsHelper.ParseAddress(command.SourceAddress),
                                            OpenAssetsHelper.ParseAddress(command.DestinationAddress), command.Amount));
 }
示例#18
0
        public async Task Test()
        {
            var hotWallet    = OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r");
            var changeWallet = OpenAssetsHelper.ParseAddress("minog49vnNVuWK5kSs5Ut1iPyNZcR1i7he");

            var hotWalletOutputs = GenerateOutputs(5);

            var hotWalletBalance = new Money(hotWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));

            var maxFeeForTransaction = Money.FromUnit(0.099M, MoneyUnit.BTC);

            var selectedCoins = new List <Coin>();

            var _feeProvider            = Config.Services.GetService <IFeeProvider>();
            var _transactionBuildHelper = Config.Services.GetService <ITransactionBuildHelper>();
            var cashoutRequests         = (GenerateCashoutRequest(200)).ToList();

            var maxInputsCount = maxFeeForTransaction.Satoshi / (await _feeProvider.GetFeeRate()).GetFee(Constants.InputSize).Satoshi;

            do
            {
                if (selectedCoins.Count > maxInputsCount && cashoutRequests.Count > 1)
                {
                    cashoutRequests = cashoutRequests.Take(cashoutRequests.Count - 1).ToList();
                    selectedCoins.Clear();
                }
                else
                if (selectedCoins.Count > 0)
                {
                    break;
                }

                var totalAmount = Money.FromUnit(cashoutRequests.Select(o => o.Amount).Sum(), MoneyUnit.BTC);

                if (hotWalletBalance < totalAmount + maxFeeForTransaction)
                {
                    var         changeBalance       = Money.Zero;
                    List <Coin> changeWalletOutputs = new List <Coin>();
                    if (hotWallet != changeWallet)
                    {
                        changeWalletOutputs = GenerateOutputs(1).ToList();
                        changeBalance       = new Money(changeWalletOutputs.Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.Satoshi ?? 0));
                    }
                    if (changeBalance + hotWalletBalance >= totalAmount + maxFeeForTransaction)
                    {
                        selectedCoins.AddRange(hotWalletOutputs);
                        selectedCoins.AddRange(OpenAssetsHelper
                                               .CoinSelect(changeWalletOutputs, totalAmount + maxFeeForTransaction - hotWalletBalance).OfType <Coin>());
                    }
                    else
                    {
                        selectedCoins.AddRange(hotWalletOutputs);
                        selectedCoins.AddRange(changeWalletOutputs);

                        int cashoutsUsedCount = 0;
                        var cashoutsAmount    = await _transactionBuildHelper.CalcFee(selectedCoins.Count, cashoutRequests.Count + 1);

                        foreach (var cashoutRequest in cashoutRequests)
                        {
                            cashoutsAmount += Money.FromUnit(cashoutRequest.Amount, MoneyUnit.BTC);
                            if (cashoutsAmount > hotWalletBalance + changeBalance)
                            {
                                break;
                            }
                            cashoutsUsedCount++;
                        }
                        if (cashoutsUsedCount == 0)
                        {
                            throw new BackendException("Not enough bitcoin available", ErrorCode.NotEnoughBitcoinAvailable);
                        }
                        cashoutRequests = cashoutRequests.Take(cashoutsUsedCount).ToList();
                    }
                }
                else
                {
                    selectedCoins.AddRange(OpenAssetsHelper.CoinSelect(hotWalletOutputs, totalAmount + maxFeeForTransaction).OfType <Coin>());
                }
            } while (true);

            var selectedCoinsAmount = new Money(selectedCoins.Sum(o => o.Amount));
            var sendAmount          = new Money(cashoutRequests.Sum(o => o.Amount), MoneyUnit.BTC);
            var builder             = new TransactionBuilder();

            builder.AddCoins(selectedCoins);
            foreach (var cashout in cashoutRequests)
            {
                var amount = Money.FromUnit(cashout.Amount, MoneyUnit.BTC);
                builder.Send(OpenAssetsHelper.ParseAddress(cashout.DestinationAddress), amount);
            }

            builder.Send(changeWallet, selectedCoinsAmount - sendAmount);

            builder.SubtractFees();
            builder.SendEstimatedFees(await _feeProvider.GetFeeRate());
            builder.SetChange(changeWallet);

            var tx = builder.BuildTransaction(true);

            _transactionBuildHelper.AggregateOutputs(tx);
        }
示例#19
0
        private IEnumerable <Coin> GenerateOutputs(int cnt)
        {
            var rand = new Random(rndINit);

            for (int i = 0; i < cnt; i++)
            {
                yield return(new Coin(new OutPoint(uint256.One, _N++), new TxOut(rand.Next(2700, 500000000), OpenAssetsHelper.ParseAddress("mj5FEqrC2P4FjFNfX8q3eZ4UABWUcRNy9r"))));
            }
        }
        public async Task ProcessMessage(TransactionQueueMessage message, QueueTriggeringContext context)
        {
            CreateTransactionResponse transactionResponse;

            try
            {
                var request = await _signRequestRepository.GetSignRequest(message.TransactionId);

                if (request?.Invalidated == true)
                {
                    context.MoveMessageToPoison(message.ToJson());
                    return;
                }

                switch (message.Type)
                {
                case TransactionCommandType.Issue:
                    var issue = message.Command.DeserializeJson <IssueCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetIssueTransaction(
                        OpenAssetsHelper.ParseAddress(issue.Address),
                        issue.Amount, await _assetRepository.GetAssetById(issue.Asset), message.TransactionId);

                    break;

                case TransactionCommandType.Transfer:
                    var transfer = message.Command.DeserializeJson <TransferCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferTransaction(
                        OpenAssetsHelper.ParseAddress(transfer.SourceAddress),
                        OpenAssetsHelper.ParseAddress(transfer.DestinationAddress), transfer.Amount,
                        await _assetRepository.GetAssetById(transfer.Asset), message.TransactionId);

                    break;

                case TransactionCommandType.TransferAll:
                    var transferAll = message.Command.DeserializeJson <TransferAllCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferAllTransaction(
                        OpenAssetsHelper.ParseAddress(transferAll.SourceAddress),
                        OpenAssetsHelper.ParseAddress(transferAll.DestinationAddress),
                        message.TransactionId);

                    break;

                case TransactionCommandType.Swap:
                    var swap = message.Command.DeserializeJson <SwapCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetSwapTransaction(
                        OpenAssetsHelper.ParseAddress(swap.MultisigCustomer1),
                        swap.Amount1,
                        await _assetRepository.GetAssetById(swap.Asset1),
                        OpenAssetsHelper.ParseAddress(swap.MultisigCustomer2),
                        swap.Amount2,
                        await _assetRepository.GetAssetById(swap.Asset2),
                        message.TransactionId);

                    break;

                case TransactionCommandType.Destroy:
                    var destroy = message.Command.DeserializeJson <DestroyCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetDestroyTransaction(
                        OpenAssetsHelper.ParseAddress(destroy.Address),
                        destroy.Amount,
                        await _assetRepository.GetAssetById(destroy.Asset),
                        message.TransactionId);

                    break;

                case TransactionCommandType.SegwitTransferToHotwallet:
                    var segwitTransfer = message.Command.DeserializeJson <SegwitTransferCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferFromSegwitWallet(
                        OpenAssetsHelper.ParseAddress(segwitTransfer.SourceAddress), message.TransactionId);

                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
            catch (BackendException e) when(e.Code == ErrorCode.NoCoinsFound)
            {
                if (message.Type == TransactionCommandType.SegwitTransferToHotwallet)
                {
                    _cqrsEngine.PublishEvent(new CashinCompletedEvent {
                        OperationId = message.TransactionId
                    }, BitcoinBoundedContext.Name);
                }
                return;
            }
            catch (BackendException e)
            {
                if (e.Text != message.LastError)
                {
                    await _logger.WriteWarningAsync("TransactionBuildFunction", "ProcessMessage", $"Id: [{message.TransactionId}], cmd: [{message.Command}]", e.Text);
                }

                message.LastError = e.Text;
                if (message.DequeueCount >= _settings.MaxDequeueCount)
                {
                    context.MoveMessageToPoison(message.ToJson());
                }
                else
                {
                    message.DequeueCount++;
                    context.MoveMessageToEnd(message.ToJson());
                    context.SetCountQueueBasedDelay(_settings.MaxQueueDelay, 200);
                }
                return;
            }

            await _transactionBlobStorage.AddOrReplaceTransaction(message.TransactionId, TransactionBlobType.Initial, transactionResponse.Transaction);


            await _queueFactory(Constants.BroadcastingQueue).PutRawMessageAsync(new BroadcastingTransaction
            {
                TransactionCommandType = message.Type,
                TransactionId          = message.TransactionId
            }.ToJson());
        }
示例#21
0
        public async Task <IActionResult> CreateCashout([FromBody] TransferRequest model)
        {
            if (model.Amount <= 0)
            {
                throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter);
            }

            var sourceAddress = OpenAssetsHelper.ParseAddress(model.SourceAddress);

            if (sourceAddress == null)
            {
                throw new BackendException("Invalid source address provided", ErrorCode.InvalidAddress);
            }

            var destAddress = OpenAssetsHelper.ParseAddress(model.DestinationAddress);

            if (destAddress == null)
            {
                throw new BackendException("Invalid destination address provided", ErrorCode.InvalidAddress);
            }

            var asset = await _assetRepository.GetAssetById(model.Asset);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }

            if (model.Fee.GetValueOrDefault() < 0)
            {
                throw new BackendException("Fee must be greater than or equal to zero", ErrorCode.BadInputParameter);
            }

            if (model.Amount <= model.Fee.GetValueOrDefault())
            {
                throw new BackendException("Amount is less than fee", ErrorCode.BadInputParameter);
            }

            var transactionId = await _builder.AddTransactionId(model.TransactionId, model.ToJson());

            CreateTransactionResponse createTransactionResponse;

            if (OpenAssetsHelper.IsBitcoin(asset.Id) && model.Fee.HasValue)
            {
                createTransactionResponse = await _builder.GetPrivateTransferTransaction(sourceAddress, destAddress, model.Amount,
                                                                                         model.Fee.Value, transactionId);

                await _transactionSignRequestRepository.DoNotSign(transactionId);
            }
            else
            {
                createTransactionResponse = await _builder.GetTransferTransaction(sourceAddress, destAddress, model.Amount, asset, transactionId, true, true);
            }

            await _transactionBlobStorage.AddOrReplaceTransaction(transactionId, TransactionBlobType.Initial, createTransactionResponse.Transaction);

            return(Ok(new TransactionResponse
            {
                Transaction = createTransactionResponse.Transaction,
                TransactionId = createTransactionResponse.TransactionId,
                Fee = (createTransactionResponse as PrivateTransferResponse)?.Fee ?? 0
            }));
        }