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 ColoredTransaction Get(uint256 txId) { ColoredTransaction result = null; if (!_ColoredTransactions.TryGetValue(txId, out result)) { result = _Inner.Get(txId); _ColoredTransactions.Add(txId, result); } return(result); }
public static ColoredTransaction FetchColors(uint256 txId, IColoredTransactionRepository repo) { if (repo == null) { throw new ArgumentNullException("repo"); } repo = EnsureCachedRepository(repo); var colored = repo.Get(txId); if (colored != null) { return(colored); } var tx = repo.Transactions.Get(txId); if (tx == null) { throw new TransactionNotFoundException("Transaction " + txId + " not found in transaction repository", txId); } return(FetchColors(txId, tx, repo)); }
public static ColoredTransaction Get(this IColoredTransactionRepository repo, string txId) { return(repo.Get(uint256.Parse(txId))); }
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); }