private void AdjustCoinviewForBlock(Block block, ReconstructedCoinviewContext coinView) { foreach (Transaction tx in block.Transactions) { // Add outputs for (int i = 0; i < tx.Outputs.Count; i++) { TxOut output = tx.Outputs[i]; if (output.Value <= 0 || output.ScriptPubKey.IsUnspendable) { continue; } coinView.UnspentOutputs.Add(new OutPoint(tx, i)); coinView.Transactions[tx.GetHash()] = tx; } // Spend inputs. Coinbases are special in that they don't reference previous transactions for their inputs, so ignore them. if (tx.IsCoinBase) { continue; } foreach (TxIn txIn in tx.Inputs) { // TODO: This is inefficient because the Transactions dictionary entry does not get deleted, so extra memory gets used if (!coinView.UnspentOutputs.Remove(txIn.PrevOut)) { throw new Exception("Attempted to spend a prevOut that isn't in the coinview at this time."); } } } }
public ReconstructedCoinviewContext GetCoinviewAtHeight(int blockHeight) { var coinView = new ReconstructedCoinviewContext(); // TODO: Make this a command line option const int batchSize = 1000; IEnumerable <ChainedHeader> allBlockHeaders = this.chainIndexer.EnumerateToTip(this.chainIndexer.Genesis); int totalBlocksCounted = 0; int i = 0; while (true) { List <ChainedHeader> headers = allBlockHeaders.Skip(i * batchSize).Take(batchSize).ToList(); List <Block> blocks = this.blockStore.GetBlocks(headers.Select(x => x.HashBlock).ToList()); foreach (Block block in blocks) { this.AdjustCoinviewForBlock(block, coinView); totalBlocksCounted += 1; // We have reached the block height asked for. if (totalBlocksCounted >= blockHeight) { break; } } // We have seen every block up to the tip or the chosen block height. if (headers.Count < batchSize || totalBlocksCounted >= blockHeight) { break; } // Give the user some feedback every 10k blocks if (i % 10 == 9) { this.logger.LogInformation($"Processed {totalBlocksCounted} blocks for UTXO indexing."); } i++; } return(coinView); }