예제 #1
0
        private static void WriteEntry(Network network, JArray inputs, ColoredEntry entry)
        {
            JProperty index    = new JProperty("index", entry.Index);
            JProperty asset    = new JProperty("asset", entry.Asset.Id.GetWif(network).ToString());
            JProperty quantity = new JProperty("quantity", entry.Asset.Quantity);

            inputs.Add(new JObject(index, asset, quantity));
        }
예제 #2
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);
		}
예제 #3
0
		private static void WriteEntry(Network network, JArray inputs, ColoredEntry entry)
		{
			JProperty index = new JProperty("index", entry.Index);
			JProperty asset = new JProperty("asset", entry.Asset.Id.GetWif(network).ToString());
			JProperty quantity = new JProperty("quantity", entry.Asset.Quantity);
			inputs.Add(new JObject(index, 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
파일: Coin.cs 프로젝트: woutersmit/NBitcoin
		public ColoredCoin(Transaction tx, ColoredEntry entry)
			: this(entry.Asset, new Coin(tx, entry.Index))
		{
			if (tx == null) throw new ArgumentNullException("tx");
			if (entry == null) throw new ArgumentNullException("entry");
		}
예제 #6
0
        public ColoredTransaction Get(uint256 txId)
        {
            try
            {
                ColoredTransaction result = new ColoredTransaction();
                WebClient          client = new WebClient();
                var str    = client.DownloadString("https://api.coinprism.com/v1/transactions/" + txId);
                var json   = JObject.Parse(str);
                var inputs = json["inputs"] as JArray;
                if (inputs != null)
                {
                    for (int i = 0; i < inputs.Count; i++)
                    {
                        if (inputs[i]["asset_address"].Value <string>() == null)
                        {
                            continue;
                        }
                        var entry = new ColoredEntry();
                        entry.Index = i;
                        entry.Asset = new Asset(
                            (ScriptId) new BitcoinScriptAddress(inputs[i]["asset_address"].ToString(), null).ID,
                            inputs[i]["asset_quantity"].Value <ulong>());

                        result.Inputs.Add(entry);
                    }
                }

                var outputs = json["outputs"] as JArray;
                if (outputs != null)
                {
                    bool issuance = true;
                    for (int i = 0; i < outputs.Count; i++)
                    {
                        var marker = ColorMarker.TryParse(new Script(Encoders.Hex.DecodeData(outputs[i]["script"].ToString())));
                        if (marker != null)
                        {
                            issuance      = false;
                            result.Marker = marker;
                            continue;
                        }
                        if (outputs[i]["asset_address"].Value <string>() == null)
                        {
                            continue;
                        }
                        ColoredEntry entry = new ColoredEntry();
                        entry.Index = i;
                        entry.Asset = new Asset(
                            (ScriptId) new BitcoinScriptAddress(outputs[i]["asset_address"].ToString(), null).ID,
                            outputs[i]["asset_quantity"].Value <ulong>()
                            );

                        if (issuance)
                        {
                            result.Issuances.Add(entry);
                        }
                        else
                        {
                            result.Transfers.Add(entry);
                        }
                    }
                }
                return(result);
            }
            catch (WebException ex)
            {
                try
                {
                    var error = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
                    if (error["ErrorCode"].ToString() == "InvalidTransactionHash")
                    {
                        return(null);
                    }
                    throw new CoinprismException(error["ErrorCode"].ToString());
                }
                catch (CoinprismException)
                {
                    throw;
                }
                catch
                {
                }
                throw;
            }
        }
예제 #7
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);
            }
        }
예제 #8
0
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            try
            {
                ColoredTransaction result = new ColoredTransaction();
                using (HttpClient client = new HttpClient())
                {
                    var response = await client.GetAsync("https://api.coinprism.com/v1/transactions/" + txId).ConfigureAwait(false);

                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        return(null);
                    }
                    var str = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    var json   = JObject.Parse(str);
                    var inputs = json["inputs"] as JArray;
                    if (inputs != null)
                    {
                        for (int i = 0; i < inputs.Count; i++)
                        {
                            if (inputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            var entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new Asset(
                                new BitcoinAssetId(inputs[i]["asset_id"].ToString(), null).AssetId,
                                inputs[i]["asset_quantity"].Value <ulong>());

                            result.Inputs.Add(entry);
                        }
                    }

                    var outputs = json["outputs"] as JArray;
                    if (outputs != null)
                    {
                        bool issuance = true;
                        for (int i = 0; i < outputs.Count; i++)
                        {
                            var marker = ColorMarker.TryParse(new Script(Encoders.Hex.DecodeData(outputs[i]["script"].ToString())));
                            if (marker != null)
                            {
                                issuance      = false;
                                result.Marker = marker;
                                continue;
                            }
                            if (outputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            ColoredEntry entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new Asset(
                                new BitcoinAssetId(outputs[i]["asset_id"].ToString(), null).AssetId,
                                outputs[i]["asset_quantity"].Value <ulong>()
                                );

                            if (issuance)
                            {
                                result.Issuances.Add(entry);
                            }
                            else
                            {
                                result.Transfers.Add(entry);
                            }
                        }
                    }
                }
                return(result);
            }
            catch (WebException ex)
            {
                try
                {
                    var error = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
                    if (error["ErrorCode"].ToString() == "InvalidTransactionHash")
                    {
                        return(null);
                    }
                    throw new CoinprismException(error["ErrorCode"].ToString());
                }
                catch (CoinprismException)
                {
                    throw;
                }
                catch
                {
                }
                throw;
            }
        }
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            try
            {
                var result = new ColoredTransaction();

                String url = this._network == Network.Main ? String.Format("https://api.coinprism.com/v1/transactions/{0}", txId) : String.Format("https://testnet.api.coinprism.com/v1/transactions/{0}", txId);

                HttpWebRequest req = WebRequest.CreateHttp(url);
                req.Method = "GET";

//#if !NOCUSTOMSSLVALIDATION
//                if(_network == Network.TestNet)
//                    req.ServerCertificateValidationCallback += (a, b, c, d) => true;
//#endif
                using (WebResponse response = await req.GetResponseAsync().ConfigureAwait(false))
                {
                    var    writer = new StreamReader(response.GetResponseStream());
                    string str    = await writer.ReadToEndAsync().ConfigureAwait(false);

                    JObject json   = JObject.Parse(str);
                    var     inputs = json["inputs"] as JArray;
                    if (inputs != null)
                    {
                        for (int i = 0; i < inputs.Count; i++)
                        {
                            if (inputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            var entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new AssetMoney(
                                new BitcoinAssetId(inputs[i]["asset_id"].ToString(), null).AssetId,
                                inputs[i]["asset_quantity"].Value <ulong>());

                            result.Inputs.Add(entry);
                        }
                    }

                    var outputs = json["outputs"] as JArray;
                    if (outputs != null)
                    {
                        bool issuance = true;
                        for (int i = 0; i < outputs.Count; i++)
                        {
                            ColorMarker marker = ColorMarker.TryParse(new Script(Encoders.Hex.DecodeData(outputs[i]["script"].ToString())));
                            if (marker != null)
                            {
                                issuance      = false;
                                result.Marker = marker;
                                continue;
                            }
                            if (outputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            var entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new AssetMoney(
                                new BitcoinAssetId(outputs[i]["asset_id"].ToString(), null).AssetId,
                                outputs[i]["asset_quantity"].Value <ulong>()
                                );

                            if (issuance)
                            {
                                result.Issuances.Add(entry);
                            }
                            else
                            {
                                result.Transfers.Add(entry);
                            }
                        }
                    }
                    return(result);
                }
            }
            catch (WebException ex)
            {
                try
                {
                    JObject error = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
                    if (error["ErrorCode"].ToString() == "InvalidTransactionHash")
                    {
                        return(null);
                    }
                    throw new CoinprismException(error["ErrorCode"].ToString());
                }
                catch (CoinprismException)
                {
                    throw;
                }
                catch
                {
                }
                throw;
            }
        }
예제 #10
0
        public static async Task <ColoredTransaction> FetchColorsAsync(uint256 txId, Transaction tx, IColoredTransactionRepository repo)
        {
            if (repo == null)
            {
                throw new ArgumentNullException("repo");
            }
            if (txId == null)
            {
                if (tx == null)
                {
                    throw new ArgumentException("txId or tx should be different of null");
                }
                txId = tx.GetHash();
            }
            //The following code is to prevent recursion of FetchColors that would fire a StackOverflow if the origin of traded asset were deep in the transaction dependency tree
            ColoredTransaction colored = await repo.GetAsync(txId).ConfigureAwait(false);

            if (colored != null)
            {
                return(colored);
            }

            var frames   = new Stack <ColoredFrame>();
            var coloreds = new Stack <ColoredTransaction>();

            frames.Push(new ColoredFrame()
            {
                TransactionId = txId,
                Transaction   = tx
            });
            while (frames.Count != 0)
            {
                ColoredFrame frame = frames.Pop();
                colored = frame.PreviousTransactions != null ? null : await repo.GetAsync(frame.TransactionId).ConfigureAwait(false); //Already known

                if (colored != null)
                {
                    coloreds.Push(colored);
                    continue;
                }
                frame.Transaction = frame.Transaction ?? await repo.Transactions.GetAsync(frame.TransactionId).ConfigureAwait(false);

                if (frame.Transaction == null)
                {
                    throw new TransactionNotFoundException("Transaction " + frame.TransactionId + " not found in transaction repository", frame.TransactionId);
                }
                if (frame.PreviousTransactions == null)
                {
                    if (frame.Transaction.IsCoinBase ||
                        (!frame.Transaction.HasValidColoredMarker() &&
                         frame.TransactionId != txId)) //We care about destroyed asset, if this is the requested transaction
                    {
                        coloreds.Push(new ColoredTransaction());
                        continue;
                    }
                    frame.PreviousTransactions = new ColoredTransaction[frame.Transaction.Inputs.Count];
                    await BulkLoadIfCached(frame.Transaction, repo).ConfigureAwait(false);

                    frames.Push(frame);
                    for (int i = 0; i < frame.Transaction.Inputs.Count; i++)
                    {
                        frames.Push(new ColoredFrame()
                        {
                            TransactionId = frame.Transaction.Inputs[i].PrevOut.Hash
                        });
                    }
                    frame.Transaction = frame.TransactionId == txId ? frame.Transaction : null; //Do not waste memory, will refetch later
                    continue;
                }
                else
                {
                    for (int i = 0; i < frame.Transaction.Inputs.Count; i++)
                    {
                        frame.PreviousTransactions[i] = coloreds.Pop();
                    }
                }

                Script issuanceScriptPubkey = null;
                if (HasIssuance(frame.Transaction))
                {
                    TxIn        txIn     = frame.Transaction.Inputs[0];
                    Transaction previous = await repo.Transactions.GetAsync(txIn.PrevOut.Hash).ConfigureAwait(false);

                    if (previous == null)
                    {
                        throw new TransactionNotFoundException("An 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);
                    }
                    if (txIn.PrevOut.N < previous.Outputs.Count)
                    {
                        issuanceScriptPubkey = previous.Outputs[txIn.PrevOut.N].ScriptPubKey;
                    }
                }

                var spentCoins = new List <ColoredCoin>();
                for (int i = 0; i < frame.Transaction.Inputs.Count; i++)
                {
                    TxIn         txIn  = frame.Transaction.Inputs[i];
                    ColoredEntry entry = frame.PreviousTransactions[i].GetColoredEntry(txIn.PrevOut.N);
                    if (entry != null)
                    {
                        spentCoins.Add(new ColoredCoin(entry.Asset, new Coin(txIn.PrevOut, new TxOut())));
                    }
                }
                colored = new ColoredTransaction(frame.TransactionId, frame.Transaction, spentCoins.ToArray(), issuanceScriptPubkey);
                coloreds.Push(colored);
                await repo.PutAsync(frame.TransactionId, colored).ConfigureAwait(false);
            }
            if (coloreds.Count != 1)
            {
                throw new InvalidOperationException("Colored stack length != 1, this is a NBitcoin bug, please contact us.");
            }
            return(coloreds.Pop());
        }
예제 #11
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);
        }
예제 #12
0
		private static async Task<ColoredTransaction> FetchColorsWithAncestorsSolved(uint256 txId, Transaction tx, CachedColoredTransactionRepository 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.GetFromCache(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)
			{
				return colored;
			}
			colored.Marker = marker;
			if(!marker.HasValidQuantitiesCount(tx))
			{
				return colored;
			}

			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;
					var prev = await repo.Transactions.GetAsync(txIn.PrevOut.Hash).ConfigureAwait(false);
					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 = new AssetMoney(issuedAsset, entry.Asset.Quantity);
				colored.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)
				{
					colored.Transfers.Clear();
					colored.Issuances.Clear();
					return colored;
				}
				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)
					{
						colored.Transfers.Clear();
						colored.Issuances.Clear();
						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);
			}
			return colored;
		}
		public async Task<ColoredTransaction> GetAsync(uint256 txId)
		{
			try
			{
				ColoredTransaction result = new ColoredTransaction();
				using(HttpClient client = new HttpClient())
				{
					var response = await client.GetAsync("https://api.coinprism.com/v1/transactions/" + txId).ConfigureAwait(false);
					if(response.StatusCode != HttpStatusCode.OK)
						return null;
					var str = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
					var json = JObject.Parse(str);
					var inputs = json["inputs"] as JArray;
					if(inputs != null)
					{
						for(int i = 0 ; i < inputs.Count ; i++)
						{
							if(inputs[i]["asset_id"].Value<string>() == null)
								continue;
							var entry = new ColoredEntry();
							entry.Index = (uint)i;
							entry.Asset = new AssetMoney(
								new BitcoinAssetId(inputs[i]["asset_id"].ToString(), null).AssetId,
								inputs[i]["asset_quantity"].Value<ulong>());

							result.Inputs.Add(entry);
						}
					}

					var outputs = json["outputs"] as JArray;
					if(outputs != null)
					{
						bool issuance = true;
						for(int i = 0 ; i < outputs.Count ; i++)
						{
							var marker = ColorMarker.TryParse(new Script(Encoders.Hex.DecodeData(outputs[i]["script"].ToString())));
							if(marker != null)
							{
								issuance = false;
								result.Marker = marker;
								continue;
							}
							if(outputs[i]["asset_id"].Value<string>() == null)
								continue;
							ColoredEntry entry = new ColoredEntry();
							entry.Index = (uint)i;
							entry.Asset = new AssetMoney(
								new BitcoinAssetId(outputs[i]["asset_id"].ToString(), null).AssetId,
								outputs[i]["asset_quantity"].Value<ulong>()
								);

							if(issuance)
								result.Issuances.Add(entry);
							else
								result.Transfers.Add(entry);
						}
					}
				}
				return result;
			}
			catch(WebException ex)
			{
				try
				{
					var error = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
					if(error["ErrorCode"].ToString() == "InvalidTransactionHash")
						return null;
					throw new CoinprismException(error["ErrorCode"].ToString());
				}
				catch(CoinprismException)
				{
					throw;
				}
				catch
				{
				}
				throw;
			}
		}
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            try
            {
                var result = new ColoredTransaction();

                var url = string.Empty;
                if (this.network.NetworkType == NetworkType.Testnet || this.network.NetworkType == NetworkType.Regtest)
                {
                    url = string.Format("https://testnet.api.coinprism.com/v1/transactions/{0}", txId);
                }
                else
                {
                    url = string.Format("https://api.coinprism.com/v1/transactions/{0}", txId);
                }

                var req = WebRequest.CreateHttp(url);
                req.Method = "GET";

                using (var response = await req.GetResponseAsync().ConfigureAwait(false))
                {
                    var writer = new StreamReader(response.GetResponseStream());
                    var str    = await writer.ReadToEndAsync().ConfigureAwait(false);

                    var json   = JObject.Parse(str);
                    var inputs = json["inputs"] as JArray;
                    if (inputs != null)
                    {
                        for (var i = 0; i < inputs.Count; i++)
                        {
                            if (inputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            var entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new AssetMoney(
                                new BitcoinAssetId(inputs[i]["asset_id"].ToString()).AssetId,
                                inputs[i]["asset_quantity"].Value <ulong>());

                            result.Inputs.Add(entry);
                        }
                    }

                    var outputs = json["outputs"] as JArray;
                    if (outputs != null)
                    {
                        var issuance = true;
                        for (var i = 0; i < outputs.Count; i++)
                        {
                            var marker =
                                ColorMarker.TryParse(
                                    new Script(Encoders.Hex.DecodeData(outputs[i]["script"].ToString())));
                            if (marker != null)
                            {
                                issuance      = false;
                                result.Marker = marker;
                                continue;
                            }

                            if (outputs[i]["asset_id"].Value <string>() == null)
                            {
                                continue;
                            }
                            var entry = new ColoredEntry();
                            entry.Index = (uint)i;
                            entry.Asset = new AssetMoney(
                                new BitcoinAssetId(outputs[i]["asset_id"].ToString()).AssetId,
                                outputs[i]["asset_quantity"].Value <ulong>()
                                );

                            if (issuance)
                            {
                                result.Issuances.Add(entry);
                            }
                            else
                            {
                                result.Transfers.Add(entry);
                            }
                        }
                    }

                    return(result);
                }
            }
            catch (WebException ex)
            {
                try
                {
                    var error = JObject.Parse(new StreamReader(ex.Response.GetResponseStream()).ReadToEnd());
                    if (error["ErrorCode"].ToString() == "InvalidTransactionHash")
                    {
                        return(null);
                    }
                    throw new CoinprismException(error["ErrorCode"].ToString());
                }
                catch (CoinprismException)
                {
                    throw;
                }
                catch
                {
                }

                throw;
            }
        }