Пример #1
0
        private static async Task <bool> BulkLoadIfCached(Transaction transaction, IColoredTransactionRepository repo)
        {
            if (!(repo is CachedColoredTransactionRepository))
            {
                return(false);
            }
            bool hasIssuance = HasIssuance(transaction);

            repo = new NoDuplicateColoredTransactionRepository(repo); //prevent from having concurrent request to the same transaction id
            Task[] all = Enumerable
                         .Range(0, transaction.Inputs.Count)
                         .Select(async i =>
            {
                uint256 txId = transaction.Inputs[i].PrevOut.Hash;
                ColoredTransaction result = await repo.GetAsync(txId).ConfigureAwait(false);
                if (result == null || (i == 0 && hasIssuance))
                {
                    await repo.Transactions.GetAsync(txId).ConfigureAwait(false);
                }
            })
                         .ToArray();
            await Task.WhenAll(all).ConfigureAwait(false);

            return(true);
        }
            public Entity(DynamicTableEntity entity)
            {
                var splitted = entity.RowKey.Split(new string[] { "-" }, StringSplitOptions.None);
                _PartitionKey = entity.PartitionKey;
                Timestamp = entity.Timestamp;
                TxId = uint256.Parse(splitted[0]);
                Type = GetType(splitted[1]);
                if(splitted.Length >= 3 && splitted[2] != string.Empty)
                    BlockId = uint256.Parse(splitted[2]);
                var bytes = Helper.GetEntityProperty(entity, "a");
                if(bytes != null && bytes.Length != 0)
                {
                    Transaction = new Transaction();
                    Transaction.ReadWrite(bytes);
                }
                bytes = Helper.GetEntityProperty(entity, "b");
                if(bytes != null && bytes.Length != 0)
                {
                    ColoredTransaction = new ColoredTransaction();
                    ColoredTransaction.ReadWrite(bytes);
                }
                _PreviousTxOuts = Helper.DeserializeList<TxOut>(Helper.GetEntityProperty(entity, "c"));

                var timestamp = Helper.GetEntityProperty(entity, "d");
                if(timestamp != null && timestamp.Length == 8)
                {
                    Timestamp = new DateTimeOffset((long)ToUInt64(timestamp, 0), TimeSpan.Zero);
                }
            }
Пример #3
0
        public static ColoredTransaction FetchColors(uint256 txId, Transaction tx, IColoredTransactionRepository repo)
        {
            txId = txId ?? tx.GetHash();
            var result = repo.Get(txId);

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

            ColoredTransaction lastColored = null;

            //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
            repo = EnsureCachedRepository(repo);
            HashSet <uint256> invalidColored = new HashSet <uint256>();
            Stack <Tuple <uint256, Transaction> > ancestors = new Stack <Tuple <uint256, Transaction> >();

            ancestors.Push(Tuple.Create(txId, tx));
            while (ancestors.Count != 0)
            {
                var peek = ancestors.Peek();
                txId = peek.Item1;
                tx   = peek.Item2;
                bool isComplete = true;
                if (!tx.HasValidColoredMarker() && ancestors.Count != 1)
                {
                    invalidColored.Add(txId);
                    ancestors.Pop();
                    continue;
                }

                for (int i = 0; i < tx.Inputs.Count; i++)
                {
                    var txin = tx.Inputs[i];
                    if (repo.Get(txin.PrevOut.Hash) == null && !invalidColored.Contains(txin.PrevOut.Hash))
                    {
                        var prevTx = repo.Transactions.Get(txin.PrevOut.Hash);
                        if (prevTx == null)
                        {
                            throw new TransactionNotFoundException("Transaction " + txin.PrevOut.Hash + " not found in transaction repository", txId);
                        }
                        ancestors.Push(Tuple.Create(txin.PrevOut.Hash, prevTx));
                        isComplete = false;
                    }
                }
                if (isComplete)
                {
                    lastColored = FetchColorsWithAncestorsSolved(txId, tx, repo);
                    ancestors.Pop();
                }
            }

            return(lastColored);
        }
 public static async Task <ColoredTransaction> GetColoredTransactionAsync(this Transaction tx, IColoredTransactionRepository repo)
 {
     try
     {
         return(await ColoredTransaction.FetchColorsAsync(tx, repo).ConfigureAwait(false));
     }
     catch (TransactionNotFoundException)
     {
         return(null);
     }
 }
Пример #5
0
        public ColoredTransaction Get(uint256 txId)
        {
            ColoredTransaction result = null;

            if (!_ColoredTransactions.TryGetValue(txId, out result))
            {
                result = _Inner.Get(txId);
                _ColoredTransactions.Add(txId, result);
            }
            return(result);
        }
		public Task PutAsync(uint256 txId, ColoredTransaction tx)
		{
			using(_lock.LockWrite())
			{
				if(!_ColoredTransactions.ContainsKey(txId))
					_ColoredTransactions.AddOrReplace(txId, tx);
				else
					_ColoredTransactions[txId] = tx;
				return _Inner.PutAsync(txId, tx);
			}
		}
		public static void Put(this IColoredTransactionRepository repo, uint256 txId, ColoredTransaction tx)
		{
			try
			{
				repo.PutAsync(txId, tx).Wait();
			}
			catch(AggregateException aex)
			{
				ExceptionDispatchInfo.Capture(aex.InnerException).Throw();
			}
		}
Пример #8
0
 public void Put(uint256 txId, ColoredTransaction tx)
 {
     if (!_ColoredTransactions.ContainsKey(txId))
     {
         _ColoredTransactions.Add(txId, tx);
     }
     else
     {
         _ColoredTransactions[txId] = tx;
     }
     _Inner.Put(txId, tx);
 }
Пример #9
0
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     using (@lock.LockWrite())
     {
         if (!_ColoredTransactions.ContainsKey(txId))
         {
             _ColoredTransactions.AddOrReplace(txId, tx);
         }
         else
         {
             _ColoredTransactions[txId] = tx;
         }
         return(_Inner.PutAsync(txId, tx));
     }
 }
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     if (WriteThrough)
     {
         using (_lock.LockWrite())
         {
             if (!_ColoredTransactions.ContainsKey(txId))
             {
                 _ColoredTransactions.AddOrReplace(txId, tx);
                 EvictIfNecessary(txId);
             }
             else
             {
                 _ColoredTransactions[txId] = tx;
             }
         }
     }
     return(_Inner.PutAsync(txId, tx));
 }
Пример #11
0
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            ColoredTransaction result = null;
            bool found;

            using (@lock.LockRead())
            {
                found = _ColoredTransactions.TryGetValue(txId, out result);
            }
            if (!found)
            {
                result = await _Inner.GetAsync(txId).ConfigureAwait(false);

                using (@lock.LockWrite())
                {
                    _ColoredTransactions.AddOrReplace(txId, result);
                }
            }
            return(result);
        }
Пример #12
0
        public async Task <ColoredTransaction> GetAsync(uint256 txId)
        {
            ColoredTransaction result = null;
            bool found;

            using (this._lock.LockRead())
            {
                found = this._ColoredTransactions.TryGetValue(txId, out result);
            }
            if (!found)
            {
                result = await this._Inner.GetAsync(txId).ConfigureAwait(false);

                if (this.ReadThrough)
                {
                    using (this._lock.LockWrite())
                    {
                        this._ColoredTransactions.AddOrReplace(txId, result);
                        EvictIfNecessary(txId);
                    }
                }
            }
            return(result);
        }
		public Task PutAsync(uint256 txId, ColoredTransaction tx)
		{
			return Task.FromResult<bool>(true);
		}
        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;
            }
        }
Пример #15
0
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     return(Task.FromResult(true));
 }
		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;
			}
		}
Пример #17
0
		//https://github.com/OpenAssets/open-assets-protocol/blob/master/specification.mediawiki
		public void CanColorizeSpecScenario()
		{
			var repo = new NoSqlColoredTransactionRepository();
			var dust = Money.Parse("0.00005");
			var colored = new ColoredTransaction();
			var a1 = new AssetKey();
			var a2 = new AssetKey();
			var h = new AssetKey();
			var sender = new Key().PubKey.GetAddress(Network.Main);
			var receiver = new Key().PubKey.GetAddress(Network.Main);

			colored.Marker = new ColorMarker(new ulong[] { 0, 10, 6, 0, 7, 3 });
			colored.Inputs.Add(new ColoredEntry(0, new AssetMoney(a1.Id, 3UL)));
			colored.Inputs.Add(new ColoredEntry(1, new AssetMoney(a1.Id, 2UL)));
			colored.Inputs.Add(new ColoredEntry(3, new AssetMoney(a1.Id, 5UL)));
			colored.Inputs.Add(new ColoredEntry(4, new AssetMoney(a1.Id, 3UL)));
			colored.Inputs.Add(new ColoredEntry(5, new AssetMoney(a2.Id, 9UL)));

			colored.Issuances.Add(new ColoredEntry(1, new AssetMoney(h.Id, 10UL)));
			colored.Transfers.Add(new ColoredEntry(3, new AssetMoney(a1.Id, 6UL)));
			colored.Transfers.Add(new ColoredEntry(5, new AssetMoney(a1.Id, 7UL)));
			colored.Transfers.Add(new ColoredEntry(6, new AssetMoney(a2.Id, 3UL)));
			var destroyed = colored.GetDestroyedAssets();
			Assert.True(destroyed.Length == 1);
			Assert.True(destroyed[0].Quantity == 6);
			Assert.True(destroyed[0].Id == a2.Id);
			colored = colored.Clone();
			destroyed = colored.GetDestroyedAssets();
			Assert.True(destroyed.Length == 1);
			Assert.True(destroyed[0].Quantity == 6);
			Assert.True(destroyed[0].Id == a2.Id);

			var prior = new Transaction();
			prior.Outputs.Add(new TxOut(dust, a1.ScriptPubKey));
			prior.Outputs.Add(new TxOut(dust, a2.ScriptPubKey));
			prior.Outputs.Add(new TxOut(dust, h.ScriptPubKey));
			repo.Transactions.Put(prior.GetHash(), prior);

			var issuanceA1 = new Transaction();
			issuanceA1.Inputs.Add(new TxIn(new OutPoint(prior.GetHash(), 0)));
			issuanceA1.Outputs.Add(new TxOut(dust, h.ScriptPubKey));
			issuanceA1.Outputs.Add(new TxOut(dust, sender));
			issuanceA1.Outputs.Add(new TxOut(dust, sender));
			issuanceA1.Outputs.Add(new TxOut(dust, sender));
			issuanceA1.Outputs.Add(new TxOut(dust, new ColorMarker(new ulong[] { 3, 2, 5, 3 }).GetScript()));
			repo.Transactions.Put(issuanceA1.GetHash(), issuanceA1);

			var issuanceA2 = new Transaction();
			issuanceA2.Inputs.Add(new TxIn(new OutPoint(prior.GetHash(), 1)));
			issuanceA2.Outputs.Add(new TxOut(dust, sender));
			issuanceA2.Outputs.Add(new TxOut(dust, new ColorMarker(new ulong[] { 9 }).GetScript()));
			repo.Transactions.Put(issuanceA2.GetHash(), issuanceA2);

			var testedTx = CreateSpecTransaction(repo, dust, receiver, prior, issuanceA1, issuanceA2);
			var actualColored = testedTx.GetColoredTransaction(repo);

			Assert.True(colored.ToBytes().SequenceEqual(actualColored.ToBytes()));


			//Finally, for each transfer output, if the asset units forming that output all have the same asset address, the output gets assigned that asset address. If any output contains units from more than one distinct asset address, the whole transaction is considered invalid, and all outputs are uncolored.

			var testedBadTx = CreateSpecTransaction(repo, dust, receiver, prior, issuanceA1, issuanceA2);
			testedBadTx.Outputs[2] = new TxOut(dust, new ColorMarker(new ulong[] { 0, 10, 6, 0, 6, 4 }).GetScript());
			repo.Transactions.Put(testedBadTx.GetHash(), testedBadTx);
			colored = testedBadTx.GetColoredTransaction(repo);

			destroyed = colored.GetDestroyedAssets();
			Assert.True(destroyed.Length == 2);
			Assert.True(destroyed[0].Id == a1.Id);
			Assert.True(destroyed[0].Quantity == 13);
			Assert.True(destroyed[1].Id == a2.Id);
			Assert.True(destroyed[1].Quantity == 9);


			//If there are more items in the  asset quantity list  than the number of colorable outputs, the transaction is deemed invalid, and all outputs are uncolored.
			testedBadTx = CreateSpecTransaction(repo, dust, receiver, prior, issuanceA1, issuanceA2);
			testedBadTx.Outputs[2] = new TxOut(dust, new ColorMarker(new ulong[] { 0, 10, 6, 0, 7, 4, 10, 10 }).GetScript());
			repo.Transactions.Put(testedBadTx.GetHash(), testedBadTx);

			colored = testedBadTx.GetColoredTransaction(repo);

			destroyed = colored.GetDestroyedAssets();
			Assert.True(destroyed.Length == 2);
			Assert.True(destroyed[0].Id == a1.Id);
			Assert.True(destroyed[0].Quantity == 13);
			Assert.True(destroyed[1].Id == a2.Id);
			Assert.True(destroyed[1].Quantity == 9);
		}
 public void Put(uint256 txId, ColoredTransaction tx)
 {
     _Repository.Put(GetId(txId), tx);
 }
 public static void Put(this IColoredTransactionRepository repo, uint256 txId, ColoredTransaction tx)
 {
     try
     {
         repo.PutAsync(txId, tx).Wait();
     }
     catch (AggregateException aex)
     {
         ExceptionDispatchInfo.Capture(aex.InnerException).Throw();
     }
 }
Пример #20
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;
            }
        }
Пример #21
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);
        }
        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;
            }
        }
 public Entity(uint256 txId, ColoredTransaction colored)
 {
     if(txId == null)
         throw new ArgumentNullException("txId");
     TxId = txId;
     ColoredTransaction = colored;
     Type = TransactionEntryType.Colored;
 }
 public static void Put(this IColoredTransactionRepository repo, uint256 txId, ColoredTransaction tx)
 {
     repo.PutAsync(txId, tx).GetAwaiter().GetResult();
 }
Пример #25
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 Task PutAsync(uint256 txId, ColoredTransaction colored)
 {
     _Configuration.CreateIndexer().Index(new TransactionEntry.Entity(txId, colored));
     return Task.FromResult(false);
 }
		public Task PutAsync(uint256 txId, ColoredTransaction tx)
		{
			return _Repository.PutAsync(GetId(txId), tx);
		}
Пример #28
0
 public void Put(uint256 txId, ColoredTransaction tx)
 {
 }
Пример #29
0
		public static IEnumerable<ColoredCoin> Find(uint256 txId, Transaction tx, ColoredTransaction colored)
		{
			if(colored == null)
				throw new ArgumentNullException("colored");
			if(tx == null)
				throw new ArgumentNullException("tx");
			if(txId == null)
				txId = tx.GetHash();
			foreach(var entry in colored.Issuances.Concat(colored.Transfers))
			{
				var txout = tx.Outputs[entry.Index];
				yield return new ColoredCoin(entry.Asset, new Coin(new OutPoint(txId, entry.Index), txout));
			}
		}
Пример #30
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
            var colored = await repo.GetAsync(txId).ConfigureAwait(false);

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

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

            frames.Push(new ColoredFrame()
            {
                TransactionId = txId,
                Transaction   = tx
            });
            while (frames.Count != 0)
            {
                var 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))
                {
                    var txIn     = frame.Transaction.Inputs[0];
                    var 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;
                    }
                }

                List <ColoredCoin> spentCoins = new List <ColoredCoin>();
                for (int i = 0; i < frame.Transaction.Inputs.Count; i++)
                {
                    var txIn  = frame.Transaction.Inputs[i];
                    var 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());
        }
Пример #31
0
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     return(this.Repository.PutAsync(GetId(txId), tx));
 }
Пример #32
0
		public static IEnumerable<ColoredCoin> Find(Transaction tx, ColoredTransaction colored)
		{
			return Find(null, tx, colored);
		}
Пример #33
0
        public static async Task <ColoredTransaction> FetchColorsAsync(uint256 txId, Transaction tx, IColoredTransactionRepository repo)
        {
            if (repo == null)
            {
                throw new ArgumentNullException("repo");
            }
            repo = EnsureCachedRepository(repo);

            ColoredTransaction result = null;

            if (txId != null)
            {
                result = await repo.GetAsync(txId).ConfigureAwait(false);
            }
            else
            {
                if (tx == null)
                {
                    throw new ArgumentException("txId or tx should be different of null");
                }
                txId   = tx.GetHash();
                result = await repo.GetAsync(txId).ConfigureAwait(false);
            }
            if (result != null)
            {
                return(result);
            }

            if (tx == null)
            {
                tx = await repo.Transactions.GetAsync(txId).ConfigureAwait(false);

                if (tx == null)
                {
                    throw new TransactionNotFoundException("Transaction " + txId + " not found in transaction repository", txId);
                }
            }


            ColoredTransaction lastColored = null;
            //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
            HashSet <uint256> invalidColored = new HashSet <uint256>();
            Stack <Tuple <uint256, Transaction> > ancestors = new Stack <Tuple <uint256, Transaction> >();

            ancestors.Push(Tuple.Create(txId, tx));
            while (ancestors.Count != 0)
            {
                var peek = ancestors.Peek();
                txId = peek.Item1;
                tx   = peek.Item2;
                bool isComplete = true;
                if (!tx.HasValidColoredMarker() && ancestors.Count != 1)
                {
                    invalidColored.Add(txId);
                    ancestors.Pop();
                    continue;
                }

                var parentsToResolve =
                    Enumerable
                    .Range(0, tx.Inputs.Count)
                    .Select(async i =>
                {
                    var txin  = tx.Inputs[i];
                    var color = await repo.GetAsync(txin.PrevOut.Hash).ConfigureAwait(false);
                    if (color == null && !invalidColored.Contains(txin.PrevOut.Hash))
                    {
                        var prevTx = await repo.Transactions.GetAsync(txin.PrevOut.Hash).ConfigureAwait(false);
                        if (prevTx == null)
                        {
                            throw new TransactionNotFoundException("Transaction " + txin.PrevOut.Hash + " not found in transaction repository", txId);
                        }
                        return(Tuple.Create(txin.PrevOut.Hash, prevTx));
                    }
                    return(null);
                }).ToArray();

                foreach (var parent in parentsToResolve)
                {
                    var toResolve = await parent.ConfigureAwait(false);

                    if (toResolve != null)
                    {
                        ancestors.Push(toResolve);
                        isComplete = false;
                    }
                }


                if (isComplete)
                {
                    lastColored = await FetchColorsWithAncestorsSolved(txId, tx, (CachedColoredTransactionRepository)repo).ConfigureAwait(false);

                    await repo.PutAsync(txId, lastColored).ConfigureAwait(false);

                    ancestors.Pop();
                }
            }

            return(lastColored);
        }
Пример #34
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
			var colored = await repo.GetAsync(txId).ConfigureAwait(false);
			if(colored != null)
				return colored;

			Stack<ColoredFrame> frames = new Stack<ColoredFrame>();
			Stack<ColoredTransaction> coloreds = new Stack<ColoredTransaction>();
			frames.Push(new ColoredFrame()
			{
				TransactionId = txId,
				Transaction = tx
			});
			while(frames.Count != 0)
			{
				var 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.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))
				{
					var txIn = frame.Transaction.Inputs[0];
					var 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;
				}

				List<ColoredCoin> spentCoins = new List<ColoredCoin>();
				for(int i = 0 ; i < frame.Transaction.Inputs.Count ; i++)
				{
					var txIn = frame.Transaction.Inputs[i];
					var 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();
		}
        internal OrderedBalanceChange(DynamicTableEntity entity)
        {
            var splitted = entity.RowKey.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
            Height = Helper.StringToHeight(splitted[1]);
            BalanceId = BalanceId.Parse(splitted[0]);

            var locator = BalanceLocator.Parse(string.Join("-", splitted.Skip(1).ToArray()), true);
            var confLocator = locator as ConfirmedBalanceLocator;
            if(confLocator != null)
            {
                Height = confLocator.Height;
                TransactionId = confLocator.TransactionId;
                BlockId = confLocator.BlockHash;
            }

            var unconfLocator = locator as UnconfirmedBalanceLocator;
            if(unconfLocator != null)
            {
                TransactionId = unconfLocator.TransactionId;
            }

            SeenUtc = entity.Properties["s"].DateTime.Value;

            _SpentOutpoints = Helper.DeserializeList<OutPoint>(Helper.GetEntityProperty(entity, "a"));

            if(entity.Properties.ContainsKey("b0"))
                _SpentCoins = new CoinCollection(Helper.DeserializeList<Spendable>(Helper.GetEntityProperty(entity, "b")).Select(s => new Coin()
                {
                    Outpoint = s.OutPoint,
                    TxOut = s.TxOut
                }).ToList());
            else if(_SpentOutpoints.Count == 0)
                _SpentCoins = new CoinCollection();

            _SpentIndices = Helper.DeserializeList<IntCompactVarInt>(Helper.GetEntityProperty(entity, "ss")).Select(i => (uint)i.ToLong()).ToList();

            var receivedIndices = Helper.DeserializeList<IntCompactVarInt>(Helper.GetEntityProperty(entity, "c")).Select(i => (uint)i.ToLong()).ToList();
            var receivedTxOuts = Helper.DeserializeList<TxOut>(Helper.GetEntityProperty(entity, "d"));

            _ReceivedCoins = new CoinCollection();
            for(int i = 0; i < receivedIndices.Count; i++)
            {
                _ReceivedCoins.Add(new Coin()
                {
                    Outpoint = new OutPoint(TransactionId, receivedIndices[i]),
                    TxOut = receivedTxOuts[i]
                });
            }

            var flags = entity.Properties["e"].StringValue;
            HasOpReturn = flags[0] == 'o';
            IsCoinbase = flags[1] == 'o';

            _MatchedRules = Helper.DeserializeObject<List<MatchedRule>>(entity.Properties["f"].StringValue).ToList();

            if(entity.Properties.ContainsKey("g"))
            {
                var ctx = new ColoredTransaction();
                ctx.FromBytes(entity["g"].BinaryValue);
                ColoredTransaction = ctx;
            }

            if(entity.Properties.ContainsKey("h"))
            {
                _ScriptPubKey = new Script(entity.Properties["h"].BinaryValue);
            }

            var data = Helper.GetEntityProperty(entity, "cu");
            if(data != null)
                CustomData = Encoding.UTF8.GetString(data);
        }
Пример #36
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);
		}
Пример #37
0
 public static ColoredTransaction GetColoredTransaction(this Transaction tx, IColoredTransactionRepository repo)
 {
     return(ColoredTransaction.FetchColors(tx, repo));
 }
Пример #38
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 Task PutAsync(uint256 txId, ColoredTransaction tx)
		{
			return Task.FromResult(false);
		}
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     return(_Inner.PutAsync(txId, tx));
 }
		public Task PutAsync(uint256 txId, ColoredTransaction tx)
		{

			if(WriteThrough)
			{
				using(_lock.LockWrite())
				{

					if(!_ColoredTransactions.ContainsKey(txId))
					{
						_ColoredTransactions.AddOrReplace(txId, tx);
						EvictIfNecessary(txId);
					}
					else
						_ColoredTransactions[txId] = tx;
				}
			}
			return _Inner.PutAsync(txId, tx);
		}