예제 #1
0
        public AssetMoney(AssetId assetId, ulong quantity)
        {
            if (assetId == null)
            {
                throw new ArgumentNullException("assetId");
            }
            _Id = assetId;

            // overflow check.
            // ulong.MaxValue is greater than long.MaxValue
            checked
            {
                Quantity = (long)quantity;
            }
        }
예제 #2
0
 public AssetMoney(AssetId assetId, decimal amount, int divisibility)
 {
     if (assetId == null)
     {
         throw new ArgumentNullException("assetId");
     }
     _Id = assetId;
     // sanity check. Only valid units are allowed
     checked
     {
         int dec     = Pow10(divisibility);
         var satoshi = amount * dec;
         Quantity = (long)satoshi;
     }
 }
예제 #3
0
파일: Coin.cs 프로젝트: woutersmit/NBitcoin
		public ColoredCoin ToColoredCoin(AssetId asset, ulong quantity)
		{
			return ToColoredCoin(new AssetMoney(asset, quantity));
		}
예제 #4
0
        public ColoredTransaction(uint256 txId, Transaction tx, ColoredCoin[] spentCoins, Script issuanceScriptPubkey)
            : this()
        {
            if (tx == null)
            {
                throw new ArgumentNullException("tx");
            }
            if (spentCoins == null)
            {
                throw new ArgumentNullException("spentCoins");
            }
            if (tx.IsCoinBase || tx.Inputs.Count == 0)
            {
                return;
            }
            txId = txId ?? tx.GetHash();

            Queue <ColoredEntry> previousAssetQueue = new Queue <ColoredEntry>();

            for (uint i = 0; i < tx.Inputs.Count; i++)
            {
                var txin      = tx.Inputs[i];
                var prevAsset = spentCoins.FirstOrDefault(s => s.Outpoint == txin.PrevOut);
                if (prevAsset != null)
                {
                    var input = new ColoredEntry()
                    {
                        Index = i,
                        Asset = prevAsset.Amount
                    };
                    previousAssetQueue.Enqueue(input);
                    Inputs.Add(input);
                }
            }

            uint markerPos = 0;
            var  marker    = ColorMarker.Get(tx, out markerPos);

            if (marker == null)
            {
                return;
            }
            Marker = marker;
            if (!marker.HasValidQuantitiesCount(tx))
            {
                return;
            }

            AssetId issuedAsset = null;

            for (uint i = 0; i < markerPos; i++)
            {
                var entry = new ColoredEntry();
                entry.Index = i;
                entry.Asset = new AssetMoney(entry.Asset.Id, i >= marker.Quantities.Length ? 0 : marker.Quantities[i]);
                if (entry.Asset.Quantity == 0)
                {
                    continue;
                }

                if (issuedAsset == null)
                {
                    var txIn = tx.Inputs.FirstOrDefault();
                    if (txIn == null)
                    {
                        continue;
                    }
                    if (issuanceScriptPubkey == null)
                    {
                        throw new ArgumentException("The transaction has an issuance detected, but issuanceScriptPubkey is null.", "issuanceScriptPubkey");
                    }
                    issuedAsset = issuanceScriptPubkey.Hash.ToAssetId();
                }
                entry.Asset = new AssetMoney(issuedAsset, entry.Asset.Quantity);
                Issuances.Add(entry);
            }

            long used = 0;

            for (uint i = markerPos + 1; i < tx.Outputs.Count; i++)
            {
                var entry = new ColoredEntry();
                entry.Index = i;
                //If there are less items in the  asset quantity list  than the number of colorable outputs (all the outputs except the marker output), the outputs in excess receive an asset quantity of zero.
                entry.Asset = new AssetMoney(entry.Asset.Id, (i - 1) >= marker.Quantities.Length ? 0 : marker.Quantities[i - 1]);
                if (entry.Asset.Quantity == 0)
                {
                    continue;
                }

                //If there are less asset units in the input sequence than in the output sequence, the transaction is considered invalid and all outputs are uncolored.
                if (previousAssetQueue.Count == 0)
                {
                    Transfers.Clear();
                    Issuances.Clear();
                    return;
                }
                entry.Asset = new AssetMoney(previousAssetQueue.Peek().Asset.Id, entry.Asset.Quantity);
                var remaining = entry.Asset.Quantity;
                while (remaining != 0)
                {
                    if (previousAssetQueue.Count == 0 || previousAssetQueue.Peek().Asset.Id != entry.Asset.Id)
                    {
                        Transfers.Clear();
                        Issuances.Clear();
                        return;
                    }
                    var assertPart = Math.Min(previousAssetQueue.Peek().Asset.Quantity - used, remaining);
                    remaining = remaining - assertPart;
                    used     += assertPart;
                    if (used == previousAssetQueue.Peek().Asset.Quantity)
                    {
                        previousAssetQueue.Dequeue();
                        used = 0;
                    }
                }
                Transfers.Add(entry);
            }
        }
예제 #5
0
		public void MoneyBagOperations()
		{
			var msft = new AssetId("8f316d9a09");
			var goog = new AssetId("097f175bc8");
			var usd = new AssetId("6d2e8c766a");

			// 10 MSFT + 3 GOOG
			var mb = new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, 3));

			// (10 MSFT + 3 GOOG) + 1000 satoshis
			Assert.Equal(
				new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, 3), new Money(1000)),
				mb + Money.Satoshis(1000));

			// (10 MSFT + 3 GOOG) + 30 GOOG == (10 MSFT + 33 GOOG)
			Assert.Equal(
				new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, 33)),
				mb + new AssetMoney(goog, 30));

			// (10 MSFT + 3 GOOG) + (10 MSFT + 3 GOOG) == (20 MSFT + 6 GOOG)
			Assert.Equal(
				new MoneyBag(new AssetMoney(msft, 20), new AssetMoney(goog, 6)),
				mb + mb);

			//-----
			// (10 MSFT + 3 GOOG) - 1000 satoshis
			Assert.Equal(
				new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, 3), new Money(-1000)),
				mb - (Money.Satoshis(1000)));

			// (10 MSFT + 3 GOOG) - 30 GOOG == (10 MSFT - 27 GOOG)
			Assert.Equal(
				new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, -27)),
				mb - (new AssetMoney(goog, 30)));

			// (10 MSFT + 3 GOOG) - (10 MSFT + 3 GOOG) == ()
			Assert.Equal(
				new MoneyBag(),
				mb - (mb));

			// (10 MSFT + 3 GOOG) - (1 MSFT - 5 GOOG) +  10000 Satoshi == (9 MSFT + 8 GOOG + 10000 Satoshi)
			var b1 = new MoneyBag(new AssetMoney(msft, 10), new AssetMoney(goog, 3));
			var b2 = new MoneyBag(new AssetMoney(msft, 1), new AssetMoney(goog, -5));

			var b1_2 = b1 - (b2) + (new Money(10000));
			Assert.True(
				b1_2.SequenceEqual(new IMoney[] { new AssetMoney(msft, 9), new AssetMoney(goog, 8), new Money(10000) }));
		}
예제 #6
0
 public BitcoinAssetId(AssetId assetId, Network network)
     : this(assetId._Bytes, network)
 {
 }
 /// <summary>
 /// Get the quantity of asset in this balance change
 /// </summary>
 /// <param name="assetId">The asset id, if null, returns uncolored satoshi</param>
 /// <returns></returns>
 public IMoney GetAssetAmount(AssetId assetId)
 {
     if(assetId == null)
         return Amount;
     var amount = _ReceivedCoins.WhereColored(assetId)
         .Select(c => c.Amount).Sum(assetId) - _SpentCoins.WhereColored(assetId).Select(c => c.Amount).Sum(assetId);
     return amount;
 }
예제 #8
0
		public AssetMoney(AssetId assetId)
		{
			if(assetId == null)
				throw new ArgumentNullException(nameof(assetId));
			_Id = assetId;
		}
예제 #9
0
		public TransactionBuilder SendAsset(Script scriptPubKey, AssetId assetId, ulong assetQuantity)
		{
			return SendAsset(scriptPubKey, new AssetMoney(assetId, assetQuantity));
		}
예제 #10
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);
		}
예제 #11
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);
		}		
예제 #12
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));
		}
예제 #13
0
		public BitcoinAssetId(AssetId assetId, Network network)
			: this(assetId._Bytes, network)
		{
			if (assetId == null) throw new ArgumentNullException("assetId");
			if (network == null) throw new ArgumentNullException("network");
		}
예제 #14
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;
		}
예제 #15
0
		private void AssertHasAsset(Transaction tx, ColoredTransaction colored, ColoredEntry entry, AssetId assetId, int quantity, PubKey destination)
		{
			var txout = tx.Outputs[entry.Index];
			Assert.True(entry.Asset.Id == assetId);
			Assert.True(entry.Asset.Quantity == quantity);
			if(destination != null)
				Assert.True(txout.ScriptPubKey == destination.ScriptPubKey);
		}
예제 #16
0
        private static ColoredTransaction FetchColorsWithAncestorsSolved(uint256 txId, Transaction tx, IColoredTransactionRepository repo)
        {
            ColoredTransaction colored = new ColoredTransaction();

            Queue <ColoredEntry> previousAssetQueue = new Queue <ColoredEntry>();

            for (uint i = 0; i < tx.Inputs.Count; i++)
            {
                var txin        = tx.Inputs[i];
                var prevColored = repo.Get(txin.PrevOut.Hash);
                if (prevColored == null)
                {
                    continue;
                }
                var prevAsset = prevColored.GetColoredEntry(txin.PrevOut.N);
                if (prevAsset != null)
                {
                    var input = new ColoredEntry()
                    {
                        Index = i,
                        Asset = prevAsset.Asset
                    };
                    previousAssetQueue.Enqueue(input);
                    colored.Inputs.Add(input);
                }
            }

            uint markerPos = 0;
            var  marker    = ColorMarker.Get(tx, out markerPos);

            if (marker == null)
            {
                repo.Put(txId, colored);
                return(colored);
            }
            colored.Marker = marker;
            if (!marker.HasValidQuantitiesCount(tx))
            {
                repo.Put(txId, colored);
                return(colored);
            }

            AssetId issuedAsset = null;

            for (uint i = 0; i < markerPos; i++)
            {
                var entry = new ColoredEntry();
                entry.Index          = i;
                entry.Asset.Quantity = i >= marker.Quantities.Length ? 0 : marker.Quantities[i];
                if (entry.Asset.Quantity == 0)
                {
                    continue;
                }

                if (issuedAsset == null)
                {
                    var txIn = tx.Inputs.FirstOrDefault();
                    if (txIn == null)
                    {
                        continue;
                    }
                    var prev = repo.Transactions.Get(txIn.PrevOut.Hash);
                    if (prev == null)
                    {
                        throw new TransactionNotFoundException("This open asset transaction is issuing assets, but it needs a parent transaction in the TransactionRepository to know the address of the issued asset (missing : " + txIn.PrevOut.Hash + ")", txIn.PrevOut.Hash);
                    }
                    issuedAsset = prev.Outputs[(int)txIn.PrevOut.N].ScriptPubKey.Hash.ToAssetId();
                }
                entry.Asset.Id = issuedAsset;
                colored.Issuances.Add(entry);
            }

            ulong used = 0;

            for (uint i = markerPos + 1; i < tx.Outputs.Count; i++)
            {
                var entry = new ColoredEntry();
                entry.Index = i;
                //If there are less items in the  asset quantity list  than the number of colorable outputs (all the outputs except the marker output), the outputs in excess receive an asset quantity of zero.
                entry.Asset.Quantity = (i - 1) >= marker.Quantities.Length ? 0 : marker.Quantities[i - 1];
                if (entry.Asset.Quantity == 0)
                {
                    continue;
                }

                //If there are less asset units in the input sequence than in the output sequence, the transaction is considered invalid and all outputs are uncolored.
                if (previousAssetQueue.Count == 0)
                {
                    colored.Transfers.Clear();
                    colored.Issuances.Clear();
                    repo.Put(txId, colored);
                    return(colored);
                }
                entry.Asset.Id = previousAssetQueue.Peek().Asset.Id;
                var remaining = entry.Asset.Quantity;
                while (remaining != 0)
                {
                    if (previousAssetQueue.Count == 0 || previousAssetQueue.Peek().Asset.Id != entry.Asset.Id)
                    {
                        colored.Transfers.Clear();
                        colored.Issuances.Clear();
                        repo.Put(txId, colored);
                        return(colored);
                    }
                    var assertPart = Math.Min(previousAssetQueue.Peek().Asset.Quantity - used, remaining);
                    remaining = remaining - assertPart;
                    used     += assertPart;
                    if (used == previousAssetQueue.Peek().Asset.Quantity)
                    {
                        previousAssetQueue.Dequeue();
                        used = 0;
                    }
                }
                colored.Transfers.Add(entry);
            }
            repo.Put(txId, colored);
            return(colored);
        }
예제 #17
0
 public AssetMoney(AssetId assetId, BigInteger quantity)
예제 #18
0
		/// <summary>
		/// Send assets (Open Asset) to a destination
		/// </summary>
		/// <param name="destination">The destination</param>
		/// <param name="asset">The asset and amount</param>
		/// <returns></returns>
		public TransactionBuilder SendAsset(IDestination destination, AssetId assetId, ulong quantity)
		{
			return SendAsset(destination, new AssetMoney(assetId, quantity));
		}