Beispiel #1
0
		public ColoredEntry(uint index, AssetMoney asset)
		{
			if(asset == null)
				throw new ArgumentNullException("asset");
			Index = index;
			Asset = asset;
		}
Beispiel #2
0
        private async Task TransferOneDirection(TransactionBuilder builder, TransactionBuildContext context,
                                                BitcoinAddress @from, decimal amount, IAsset asset, BitcoinAddress to, bool addDust = true, bool sendDust = false)
        {
            var fromStr = from.ToString();

            if (OpenAssetsHelper.IsBitcoin(asset.Id))
            {
                var coins   = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(fromStr)).ToList();
                var balance = coins.Cast <Coin>().Select(o => o.Amount).DefaultIfEmpty().Sum(o => o?.ToDecimal(MoneyUnit.BTC) ?? 0);
                if (sendDust && balance > amount &&
                    balance - amount < new TxOut(Money.Zero, from).GetDustThreshold(builder.StandardTransactionPolicy.MinRelayTxFee).ToDecimal(MoneyUnit.BTC))
                {
                    amount = balance;
                }
                await _transactionBuildHelper.SendWithChange(builder, context, coins, to, new Money(amount, MoneyUnit.BTC),
                                                             from, addDust);
            }
            else
            {
                var assetIdObj  = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId;
                var assetAmount = new AssetMoney(assetIdObj, amount, asset.MultiplierPower);

                var coins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(fromStr, assetIdObj)).ToList();
                _transactionBuildHelper.SendAssetWithChange(builder, context, coins, to, assetAmount, @from);
            }
        }
        public async Task Monitoring()
        {
            var currentBlock = await _settingsRepository.Get <int>(Constants.ProcessingBlockSetting);

            if (currentBlock == 0)
            {
                currentBlock = await _rpcBitcoinClient.GetBlockCount() - 1;
            }
            var dbCommitments = (await _commitmentRepository.GetMonitoringCommitments()).GroupBy(o => o.LockedAddress).ToDictionary(o => o.Key, o => o);

            do
            {
                var block = await _qBitNinjaApiCaller.GetBlock(currentBlock);

                if (block == null)
                {
                    break;
                }
                foreach (var transaction in block.Block.Transactions)
                {
                    var marker = transaction.GetColoredMarker();

                    foreach (var transactionOutput in transaction.Outputs.AsIndexedOutputs())
                    {
                        var address = transactionOutput.TxOut.ScriptPubKey.GetDestinationAddress(_connectionParams.Network)?.ToString();
                        if (address != null && dbCommitments.ContainsKey(address))
                        {
                            var   commitments = dbCommitments[address].OrderByDescending(o => o.CreateDt).ToList();
                            ICoin coin        = new Coin(transaction, transactionOutput.TxOut).ToScriptCoin(commitments[0].LockedScript.ToScript());

                            decimal amount;
                            if (marker == null)
                            {
                                amount = transactionOutput.TxOut.Value.ToDecimal(MoneyUnit.BTC);
                            }
                            else
                            {
                                var asset = await _assetRepository.GetAssetById(commitments[0].AssetId);

                                var assetMoney = new AssetMoney(new BitcoinAssetId(asset.BlockChainAssetId).AssetId, marker.Quantities[transactionOutput.N - 1]);
                                coin   = ((Coin)coin).ToColoredCoin(assetMoney);
                                amount = assetMoney.ToDecimal(asset.MultiplierPower);
                            }
                            var commitment = commitments.FirstOrDefault(o => o.Type == CommitmentType.Hub && o.HubAmount == amount ||
                                                                        o.Type == CommitmentType.Client && o.ClientAmount == amount);

                            if (commitment != null)
                            {
                                await ProcessBroadcastedCommitment(commitment, coin);
                            }
                        }
                    }
                }
                currentBlock++;
                await _settingsRepository.Set(Constants.ProcessingBlockSetting, currentBlock);
            } while (true);
        }
Beispiel #4
0
        public void AssetMoneyLessThan()
        {
            AssetId    assetId         = new AssetId("8f316d9a09");
            AssetMoney smallAssetMoney = new AssetMoney(assetId, 2);
            AssetMoney largeAssetMoney = new AssetMoney(assetId, 5);

            Assert.True(smallAssetMoney < largeAssetMoney);
            Assert.False(largeAssetMoney < smallAssetMoney);
        }
Beispiel #5
0
        public void AssetMoneyToStringTest()
        {
            AssetId    assetId    = new AssetId("8f316d9a09");
            AssetMoney assetMoney = new AssetMoney(assetId, 1);

            String actual = assetMoney.ToString();

            Assert.Equal("1-8f316d9a09", actual);
        }
Beispiel #6
0
        public void AssetMoneyMultiply()
        {
            AssetId    assetId    = new AssetId("8f316d9a09");
            AssetMoney assetMoney = new AssetMoney(assetId, 2);

            AssetMoney actual = assetMoney * 2;

            Assert.Equal(4, actual.Quantity);

            actual = 2 * assetMoney;
            Assert.Equal(4, actual.Quantity);
        }
        public void CanSplitMoneyBag()
        {
            var gold = new AssetId(new Key());
            var bag  = new MoneyBag();

            bag += Money.Coins(12);
            bag += new AssetMoney(gold, 10);
            MoneyBag[] splitted = bag.Split(12).ToArray();
            Assert.Equal(Money.Coins(1.0m), splitted[0].GetAmount(null));
            Assert.Equal(new AssetMoney(gold, 1), splitted[0].GetAmount(gold));
            Assert.Equal(new AssetMoney(gold, 0), splitted[11].GetAmount(gold));
        }
Beispiel #8
0
        private void CanSplitAssetMoneyCore(AssetId asset, long amount, int parts)
        {
            AssetMoney money    = new AssetMoney(asset, amount);
            var        splitted = money.Split(parts).ToArray();

            Assert.True(splitted.Length == parts);
            Assert.True(splitted.Sum(asset) == money);
            var groups          = splitted.Select(s => s.Quantity).GroupBy(o => o);
            var differentValues = groups.Count();

            Assert.True(differentValues == 1 || differentValues == 2);
        }
        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));
        }
Beispiel #10
0
 public AssetCoin(AssetMoney assetMoney, ICoin innerCoin)
 {
     if (assetMoney == null)
     {
         throw new ArgumentNullException(nameof(assetMoney));
     }
     if (innerCoin == null)
     {
         throw new ArgumentNullException(nameof(innerCoin));
     }
     Money          = assetMoney;
     this.innerCoin = innerCoin;
 }
Beispiel #11
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));
        }
        private ICoin FindCoin(Transaction tr, string multisig, string walletRedeemScript, decimal amount, IAsset asset)
        {
            if (OpenAssetsHelper.IsBitcoin(asset.Id))
            {
                var money = new Money(amount, MoneyUnit.BTC);
                return(tr.Outputs.AsCoins().FirstOrDefault(o => o.Amount == money &&
                                                           o.ScriptPubKey.GetDestinationAddress(_connectionParams.Network).ToString() == multisig));
            }
            var  assetMoney = new AssetMoney(new BitcoinAssetId(asset.BlockChainAssetId), amount, asset.MultiplierPower);
            uint markerPosition;
            var  marker = ColorMarker.Get(tr, out markerPosition);
            var  found  = tr.Outputs.AsIndexedOutputs()
                          .FirstOrDefault(o => o.TxOut.ScriptPubKey.GetDestinationAddress(_connectionParams.Network)?.ToString() == multisig &&
                                          o.N > markerPosition && marker.Quantities[o.N - markerPosition - 1] == (ulong)assetMoney.Quantity);

            return(found?.ToCoin().ToScriptCoin(new Script(walletRedeemScript)).ToColoredCoin(assetMoney));
        }
        private async Task <decimal> SendToMultisig(BitcoinAddress @from, BitcoinAddress toMultisig, IAsset assetEntity, TransactionBuilder builder, TransactionBuildContext context, decimal amount)
        {
            if (OpenAssetsHelper.IsBitcoin(assetEntity.Id))
            {
                Money sendAmount;
                var   unspentOutputs = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(from.ToString())).ToList();

                if (amount < 0)
                {
                    sendAmount = unspentOutputs.OfType <Coin>().DefaultIfEmpty().Sum(o => o.Amount);
                }
                else
                {
                    sendAmount = Money.FromUnit(amount, MoneyUnit.BTC);
                }

                if (sendAmount > 0)
                {
                    _transactionBuildHelper.SendWithChange(builder, context, unspentOutputs, toMultisig, sendAmount, from);
                }

                return(sendAmount.ToDecimal(MoneyUnit.BTC));
            }
            else
            {
                var  asset = new BitcoinAssetId(assetEntity.BlockChainAssetId, _connectionParams.Network).AssetId;
                long sendAmount;

                var unspentOutputs = (await _bitcoinOutputsService.GetColoredUnspentOutputs(from.ToString(), asset)).ToList();
                if (amount < 0)
                {
                    sendAmount = unspentOutputs.Sum(o => o.Amount.Quantity);
                }
                else
                {
                    sendAmount = new AssetMoney(asset, amount, assetEntity.MultiplierPower).Quantity;
                }
                if (sendAmount > 0)
                {
                    _transactionBuildHelper.SendAssetWithChange(builder, context, unspentOutputs,
                                                                toMultisig, new AssetMoney(asset, sendAmount), @from);
                }

                return(new AssetMoney(asset, sendAmount).ToDecimal(assetEntity.MultiplierPower));
            }
        }
        public void CanSplitAssetMoney()
        {
            var gold = new AssetId(new Key());

            CanSplitAssetMoneyCore(gold, 1234, 3);
            CanSplitAssetMoneyCore(gold, 1234, 2);
            CanSplitAssetMoneyCore(gold, 1234, 10);
            CanSplitAssetMoneyCore(gold, 1, 3);
            Assert.Throws <ArgumentOutOfRangeException>(() => CanSplitAssetMoneyCore(gold, 1000, 0));
            CanSplitAssetMoneyCore(gold, 0, 10);

            AssetMoney[] result = new AssetMoney(gold, 20).Split(3).ToArray();
            Assert.True(result[0].Quantity == 7);
            Assert.True(result[1].Quantity == 7);
            Assert.True(result[2].Quantity == 6);
            Assert.True(result[0].Id == gold);
        }
        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));
        }
Beispiel #16
0
        public void SendAssetWithChange(TransactionBuilder builder, TransactionBuildContext context, List <ColoredCoin> coins, IDestination destination, AssetMoney amount,
                                        IDestination changeDestination)
        {
            if (amount.Quantity <= 0)
            {
                throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter);
            }

            Action throwError = () =>
            {
                throw new BackendException($"The sum of total applicable outputs is less than the required: {amount.Quantity} {amount.Id}.", ErrorCode.NotEnoughAssetAvailable);
            };

            var selectedCoins = OpenAssetsHelper.CoinSelect(coins, amount);

            if (selectedCoins == null)
            {
                throwError();
            }

            var orderedCounts = selectedCoins.Cast <ColoredCoin>().OrderBy(o => o.Amount).ToList();
            var sendAmount    = new AssetMoney(amount.Id);
            var cnt           = 0;

            while (sendAmount < amount && cnt < orderedCounts.Count)
            {
                sendAmount += orderedCounts[cnt].Amount;
                cnt++;
            }

            if (sendAmount < amount)
            {
                throwError();
            }

            builder.AddCoins(orderedCounts.Take(cnt));
            context.AddCoins(orderedCounts.Take(cnt));
            builder.SendAsset(destination, amount);

            if ((sendAmount - amount).Quantity > 0)
            {
                builder.SendAsset(changeDestination, sendAmount - amount);
            }
        }
Beispiel #17
0
		public void ReadWrite(BitcoinStream stream)
		{
			stream.ReadWriteAsVarInt(ref _Index);
			if(stream.Serializing)
			{
				byte[] assetId = Asset.Id.ToBytes();
				stream.ReadWrite(ref assetId);
				long quantity = Asset.Quantity;
				stream.ReadWrite(ref quantity);
			}
			else
			{
				byte[] assetId = new byte[20];
				stream.ReadWrite(ref assetId);
				long quantity = 0;
				stream.ReadWrite(ref quantity);
				Asset = new AssetMoney(new AssetId(assetId), quantity);
			}
		}
        private async Task TransferOneDirection(TransactionBuilder builder, TransactionBuildContext context,
                                                BitcoinAddress @from, decimal amount, IAsset asset, BitcoinAddress to)
        {
            var fromStr = from.ToString();

            if (OpenAssetsHelper.IsBitcoin(asset.Id))
            {
                var coins = (await _bitcoinOutputsService.GetUncoloredUnspentOutputs(fromStr)).ToList();
                _transactionBuildHelper.SendWithChange(builder, context, coins, to, new Money(amount, MoneyUnit.BTC),
                                                       from);
            }
            else
            {
                var assetIdObj  = new BitcoinAssetId(asset.BlockChainAssetId, _connectionParams.Network).AssetId;
                var assetAmount = new AssetMoney(assetIdObj, amount, asset.MultiplierPower);

                var coins = (await _bitcoinOutputsService.GetColoredUnspentOutputs(fromStr, assetIdObj)).ToList();
                _transactionBuildHelper.SendAssetWithChange(builder, context, coins, to, assetAmount, @from);
            }
        }
        public static void DestroyColorCoin(Transaction tr, AssetMoney money, BitcoinAddress destination, Network network)
        {
            if (money == null || money.Quantity <= 0)
            {
                return;
            }
            uint markerPosition;
            var  colorMarker = ColorMarker.Get(tr, out markerPosition);

            for (var i = 0; i < colorMarker.Quantities.Length; i++)
            {
                if ((long)colorMarker.Quantities[i] == money.Quantity &&
                    tr.Outputs[i + 1].ScriptPubKey.GetDestinationAddress(network) == destination)
                {
                    colorMarker.Quantities[i] = 0;
                    break;
                }
            }

            tr.Outputs[markerPosition].ScriptPubKey = colorMarker.GetScript();
        }
Beispiel #20
0
        public Task <CreateTransactionResponse> GetDestroyTransaction(BitcoinAddress bitcoinAddres, decimal modelAmount, IAsset asset, Guid transactionId)
        {
            return(Retry.Try(async() =>
            {
                var context = _transactionBuildContextFactory.Create(_connectionParams.Network);

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

                    OpenAssetsHelper.DestroyColorCoin(tx, assetMoney, changeAddress, _connectionParams.Network);

                    await _spentOutputService.SaveSpentOutputs(transactionId, tx);

                    await SaveNewOutputs(transactionId, tx, context);

                    return new CreateTransactionResponse(tx.ToHex(), transactionId);
                });
            }, exception => (exception as BackendException)?.Code == ErrorCode.TransactionConcurrentInputsProblem, 3, _log));
        }
Beispiel #21
0
 public ColoredCoin(AssetMoney asset, Coin bearer)
 {
     this.Amount = asset;
     this.Bearer = bearer;
 }
Beispiel #22
0
		public TransactionBuilder IssueAsset(Script scriptPubKey, AssetMoney asset)
		{
			AssertOpReturn("Colored Coin");
			if(_IssuedAsset == null)
				_IssuedAsset = asset.Id;
			else if(_IssuedAsset != asset.Id)
				throw new InvalidOperationException("You can issue only one asset type in a transaction");

			CurrentGroup.IssuanceBuilders.Add(ctx =>
			{
				var marker = ctx.GetColorMarker(true);
				if(ctx.IssuanceCoin == null)
				{
					var issuance = ctx.Group.Coins.Values.OfType<IssuanceCoin>().Where(i => i.AssetId == asset.Id).FirstOrDefault();
					if(issuance == null)
						throw new InvalidOperationException("No issuance coin for emitting asset found");
					ctx.IssuanceCoin = issuance;
					ctx.Transaction.Inputs.Insert(0, new TxIn(issuance.Outpoint));
					ctx.AdditionalFees -= issuance.Bearer.Amount;
					if(issuance.DefinitionUrl != null)
					{
						marker.SetMetadataUrl(issuance.DefinitionUrl);
					}
				}

				ctx.Transaction.Outputs.Insert(0, new TxOut(GetDust(scriptPubKey), scriptPubKey));
				marker.Quantities = new[] { checked((ulong)asset.Quantity) }.Concat(marker.Quantities).ToArray();
				ctx.AdditionalFees += ctx.Transaction.Outputs[0].Value;
				return asset;
			});
			return this;
		}
Beispiel #23
0
		public void CanSplitMoneyBag()
		{
			var gold = new AssetId(new Key());
			MoneyBag bag = new MoneyBag();
			bag += Money.Coins(12);
			bag += new AssetMoney(gold, 10);
			var splitted = bag.Split(12).ToArray();
			Assert.Equal(Money.Coins(1.0m), splitted[0].GetAmount(null));
			Assert.Equal(new AssetMoney(gold, 1), splitted[0].GetAmount(gold));
			Assert.Equal(new AssetMoney(gold, 0), splitted[11].GetAmount(gold));
		}
Beispiel #24
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));
        }
Beispiel #25
0
		public void CanSplitAssetMoney()
		{
			var gold = new AssetId(new Key());
			CanSplitAssetMoneyCore(gold, 1234, 3);
			CanSplitAssetMoneyCore(gold, 1234, 2);
			CanSplitAssetMoneyCore(gold, 1234, 10);
			CanSplitAssetMoneyCore(gold, 1, 3);
			Assert.Throws<ArgumentOutOfRangeException>(() => CanSplitAssetMoneyCore(gold, 1000, 0));
			CanSplitAssetMoneyCore(gold, 0, 10);

			var result = new AssetMoney(gold, 20).Split(3).ToArray();
			Assert.True(result[0].Quantity == 7);
			Assert.True(result[1].Quantity == 7);
			Assert.True(result[2].Quantity == 6);
			Assert.True(result[0].Id == gold);
		}
Beispiel #26
0
		public ColoredCoin ToColoredCoin(AssetMoney asset)
		{
			return new ColoredCoin(asset, this);
		}
Beispiel #27
0
		public ColoredCoin(AssetMoney asset, Coin bearer)
		{
			Amount = asset;
			Bearer = bearer;
		}
Beispiel #28
0
		private void CanSplitAssetMoneyCore(AssetId asset, long amount, int parts)
		{
			AssetMoney money = new AssetMoney(asset, amount);
			var splitted = money.Split(parts).ToArray();
			Assert.True(splitted.Length == parts);
			Assert.True(splitted.Sum(asset) == money);
			var groups = splitted.Select(s => s.Quantity).GroupBy(o => o);
			var differentValues = groups.Count();
			Assert.True(differentValues == 1 || differentValues == 2);
		}		
Beispiel #29
0
 public ColoredCoin(AssetMoney asset, Coin bearer)
 {
     Amount = asset;
     Bearer = bearer;
 }
Beispiel #30
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");
                }
            }
        }
Beispiel #31
0
		public TransactionBuilder IssueAsset(IDestination destination, AssetMoney asset)
		{
			return IssueAsset(destination.ScriptPubKey, asset);
		}
Beispiel #32
0
 public ColoredCoin ToColoredCoin(AssetMoney asset)
 {
     return(new ColoredCoin(asset, this));
 }
Beispiel #33
0
		public TransactionBuilder SendAsset(Script scriptPubKey, AssetMoney asset)
		{
			if(asset.Quantity < 0)
				throw new ArgumentOutOfRangeException("asset", "Asset amount can't be negative");
			AssertOpReturn("Colored Coin");
			var builders = CurrentGroup.BuildersByAsset.TryGet(asset.Id);
			if(builders == null)
			{
				builders = new List<Builder>();
				CurrentGroup.BuildersByAsset.Add(asset.Id, builders);
				builders.Add(SetColoredChange);
			}
			builders.Add(ctx =>
			{
				var marker = ctx.GetColorMarker(false);
				var txout = ctx.Transaction.AddOutput(new TxOut(GetDust(scriptPubKey), scriptPubKey));
				marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, asset.Quantity);
				ctx.AdditionalFees += txout.Value;
				return asset;
			});
			return this;
		}