コード例 #1
0
ファイル: ColoredTransaction.cs プロジェクト: knocte/NBitcoin
		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();
		}
コード例 #2
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;
		}
コード例 #3
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());
        }
コード例 #4
0
 public Task PutAsync(uint256 txId, ColoredTransaction tx)
 {
     return(_Inner.PutAsync(txId, tx));
 }
コード例 #5
0
 public static void Put(this IColoredTransactionRepository repo, uint256 txId, ColoredTransaction tx)
 {
     repo.PutAsync(txId, tx).GetAwaiter().GetResult();
 }
コード例 #6
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);
        }