private void LogBlockchainProgress(Data.Blockchain blockchain, Stopwatch totalStopwatch, long totalTxCount, long totalInputCount, Stopwatch currentRateStopwatch, long currentBlockCount, long currentTxCount, long currentInputCount) { var currentBlockRate = (float)currentBlockCount / currentRateStopwatch.ElapsedSecondsFloat(); var currentTxRate = (float)currentTxCount / currentRateStopwatch.ElapsedSecondsFloat(); var currentInputRate = (float)currentInputCount / currentRateStopwatch.ElapsedSecondsFloat(); Debug.WriteLine( string.Join("\n", new string('-', 80), "Height: {0,10} | Date: {1} | Duration: {7} hh:mm:ss | Validation: {8} hh:mm:ss | Blocks/s: {2,7} | Tx/s: {3,7} | Inputs/s: {4,7} | Total Tx: {5,7} | Total Inputs: {6,7} | Utxo Size: {9,7}", "GC Memory: {10,10:#,##0.00} MB", "Process Memory: {11,10:#,##0.00} MB", new string('-', 80) ) .Format2 ( blockchain.Height.ToString("#,##0"), "", currentBlockRate.ToString("#,##0"), currentTxRate.ToString("#,##0"), currentInputRate.ToString("#,##0"), totalTxCount.ToString("#,##0"), totalInputCount.ToString("#,##0"), totalStopwatch.Elapsed.ToString(@"hh\:mm\:ss"), validateStopwatch.Elapsed.ToString(@"hh\:mm\:ss"), blockchain.Utxo.Count.ToString("#,##0"), (float)GC.GetTotalMemory(false) / 1.MILLION(), (float)Process.GetCurrentProcess().PrivateMemorySize64 / 1.MILLION() )); }
public void RevalidateBlockchain(Data.Blockchain blockchain, Block genesisBlock) { var stopwatch = new Stopwatch(); stopwatch.Start(); try { //TODO delete corrupted data? could get stuck in a fail-loop on the winning chain otherwise // verify blockchain has blocks if (blockchain.BlockList.Count == 0) throw new ValidationException(); // verify genesis block hash if (blockchain.BlockList[0].BlockHash != genesisBlock.Hash) throw new ValidationException(); // get genesis block header var chainGenesisBlockHeader = this.CacheContext.GetBlockHeader(blockchain.BlockList[0].BlockHash); // verify genesis block header if ( genesisBlock.Header.Version != chainGenesisBlockHeader.Version || genesisBlock.Header.PreviousBlock != chainGenesisBlockHeader.PreviousBlock || genesisBlock.Header.MerkleRoot != chainGenesisBlockHeader.MerkleRoot || genesisBlock.Header.Time != chainGenesisBlockHeader.Time || genesisBlock.Header.Bits != chainGenesisBlockHeader.Bits || genesisBlock.Header.Nonce != chainGenesisBlockHeader.Nonce || genesisBlock.Hash != chainGenesisBlockHeader.Hash || genesisBlock.Hash != CalculateHash(chainGenesisBlockHeader)) { throw new ValidationException(); } // setup expected previous block hash value to verify each chain actually does link var expectedPreviousBlockHash = genesisBlock.Header.PreviousBlock; for (var height = 0; height < blockchain.BlockList.Count; height++) { // cooperative loop this.shutdownToken.ThrowIfCancellationRequested(); // get the current link in the chain var chainedBlock = blockchain.BlockList[height]; // verify height if (chainedBlock.Height != height) throw new ValidationException(); // verify blockchain linking if (chainedBlock.PreviousBlockHash != expectedPreviousBlockHash) throw new ValidationException(); // verify block exists var blockHeader = this.CacheContext.GetBlockHeader(chainedBlock.BlockHash); // verify block metadata matches header values if (blockHeader.PreviousBlock != chainedBlock.PreviousBlockHash) throw new ValidationException(); // verify block header hash if (CalculateHash(blockHeader) != chainedBlock.BlockHash) throw new ValidationException(); // next block metadata should have the current metadata's hash as its previous hash value expectedPreviousBlockHash = chainedBlock.BlockHash; } // all validation passed } finally { stopwatch.Stop(); Debug.WriteLine("Blockchain revalidation: {0:#,##0.000000}s".Format2(stopwatch.ElapsedSecondsFloat())); } }
private void WriteBlockchainWorker() { var stopwatch = new Stopwatch(); stopwatch.Start(); // grab a snapshot var currentBlockchainLocal = this._currentBlockchain; // don't write out genesis blockchain if (!currentBlockchainLocal.IsDefault && currentBlockchainLocal.Height > 0) { //TODO this.StorageContext.BlockchainStorage.WriteBlockchain(currentBlockchainLocal); this.StorageContext.BlockchainStorage.RemoveBlockchains(currentBlockchainLocal.TotalWork); } stopwatch.Stop(); Debug.WriteLine("WriteBlockchainWorker: {0:#,##0.000}s".Format2(stopwatch.ElapsedSecondsFloat())); }
private void ValidationWorker() { var stopwatch = new Stopwatch(); stopwatch.Start(); stopwatch.Stop(); Debug.WriteLine("ValidationWorker: {0:#,##0.000}s".Format2(stopwatch.ElapsedSecondsFloat())); }
private void ValidateCurrentChainWorker() { var currentBlockchainLocal = this._currentBlockchain; if (!currentBlockchainLocal.IsDefault && !this.Rules.GenesisBlock.IsDefault) { var stopwatch = new Stopwatch(); stopwatch.Start(); // revalidate current blockchain try { Calculator.RevalidateBlockchain(currentBlockchainLocal, this.Rules.GenesisBlock); } catch (ValidationException e) { //TODO this does not cancel a blockchain that is currently being processed Debug.WriteLine("******************************"); Debug.WriteLine("******************************"); Debug.WriteLine("BLOCKCHAIN ERROR DETECTED, ROLLING BACK TO GENESIS"); Debug.WriteLine("******************************"); Debug.WriteLine("******************************"); UpdateCurrentBlockchain(this._rules.GenesisBlockchain); } catch (MissingDataException e) { HandleMissingData(e); } stopwatch.Stop(); Debug.WriteLine("ValidateCurrentChainWorker: {0:#,##0.000}s".Format2(stopwatch.ElapsedSecondsFloat())); } }
private void LoadExistingState() { var stopwatch = new Stopwatch(); stopwatch.Start(); //TODO Tuple<BlockchainKey, BlockchainMetadata> winner = null; foreach (var tuple in this.StorageContext.BlockchainStorage.ListBlockchains()) { if (winner == null) winner = tuple; if (tuple.Item2.TotalWork > winner.Item2.TotalWork) { winner = tuple; } } // check if an existing blockchain has been found if (winner != null) { // read the winning blockchain var blockchain = this.StorageContext.BlockchainStorage.ReadBlockchain(winner.Item1); UpdateCurrentBlockchain(blockchain); UpdateWinningBlock(blockchain.RootBlock); // collect after loading GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true); // clean up any old blockchains this.StorageContext.BlockchainStorage.RemoveBlockchains(winner.Item2.TotalWork); // log statistics stopwatch.Stop(); Debug.WriteLine( string.Join("\n", new string('-', 80), "Loaded blockchain on startup in {0:#,##0.000} seconds, height: {1:#,##0}, utxo size: {2:#,##0}", "GC Memory: {3,10:#,##0.00} MB", "Process Memory: {4,10:#,##0.00} MB", new string('-', 80) ) .Format2 ( stopwatch.ElapsedSecondsFloat(), blockchain.Height, blockchain.Utxo.Count, (float)GC.GetTotalMemory(false) / 1.MILLION(), (float)Process.GetCurrentProcess().PrivateMemorySize64 / 1.MILLION() )); } }
public bool TryWriteValues(IEnumerable<KeyValuePair<UInt256, WriteValue<ImmutableArray<Transaction>>>> values) { var stopwatch = new Stopwatch(); var count = 0; try { using (var conn = this.OpenWriteConnection()) using (var cmd = conn.CreateCommand()) { cmd.CommandText = @" INSERT OR IGNORE INTO BlockTransactions ( BlockHash, TxIndex, TxHash, TxBytes ) VALUES ( @blockHash, @txIndex, @txHash, @txBytes )"; cmd.Parameters.Add(new SQLiteParameter { ParameterName = "@blockHash", DbType = DbType.Binary, Size = 32 }); cmd.Parameters.Add(new SQLiteParameter { ParameterName = "@txIndex", DbType = DbType.Int32 }); cmd.Parameters.Add(new SQLiteParameter { ParameterName = "@txHash", DbType = DbType.Binary, Size = 32 }); cmd.Parameters.Add(new SQLiteParameter { ParameterName = "@txBytes", DbType = DbType.Binary }); foreach (var keyPair in values) { var blockHash = keyPair.Key; cmd.Parameters["@blockHash"].Value = blockHash.ToDbByteArray(); for (var txIndex = 0; txIndex < keyPair.Value.Value.Length; txIndex++) { var tx = keyPair.Value.Value[txIndex]; var txBytes = StorageEncoder.EncodeTransaction(tx); cmd.Parameters["@txIndex"].Value = txIndex; cmd.Parameters["@txHash"].Value = tx.Hash.ToDbByteArray(); cmd.Parameters["@txBytes"].Size = txBytes.Length; cmd.Parameters["@txBytes"].Value = txBytes; count++; cmd.ExecuteNonQuery(); } } stopwatch.Start(); conn.Commit(); stopwatch.Stop(); return true; } } finally { Debug.WriteLine("flushed {0,5}: {1:#,##0.000000}s @ {2:#,##0.000}/s".Format2(count, stopwatch.ElapsedSecondsFloat(), count / stopwatch.ElapsedSecondsFloat())); } }