public TargetChainWorker(WorkerConfig workerConfig, IBlockchainRules rules, CoreStorage coreStorage) : base("TargetChainWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime) { this.rules = rules; this.coreStorage = coreStorage; this.coreStorage.ChainedHeaderAdded += HandleChainedHeaderAdded; this.coreStorage.BlockInvalidated += HandleBlockInvalidated; }
public TargetChainWorker(WorkerConfig workerConfig, IChainParams chainParams, CoreStorage coreStorage) : base("TargetChainWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime) { this.chainParams = chainParams; this.coreStorage = coreStorage; this.coreStorage.ChainedHeaderAdded += HandleChanged; this.coreStorage.ChainedHeaderRemoved += HandleChanged; this.coreStorage.BlockInvalidated += HandleChanged; }
public TestDaemon(Block genesisBlock = null, INinjectModule loggingModule = null, INinjectModule[] storageModules = null) { // initialize storage folder this.baseDirectoryCleanup = TempDirectory.CreateTempDirectory(out this.baseDirectory); // initialize kernel this.kernel = new StandardKernel(); // add logging module this.kernel.Load(loggingModule ?? new ConsoleLoggingModule()); // log startup this.logger = LogManager.GetCurrentClassLogger(); this.logger.Info($"Starting up: {DateTime.Now}"); // initialize test blocks this.testBlocks = new TestBlocks(genesisBlock); // add storage module this.kernel.Load(storageModules ?? new[] { new MemoryStorageModule() }); // initialize unit test rules, allow validation methods to run testBlocks.Rules.ValidateTransactionAction = null; testBlocks.Rules.ValidationTransactionScriptAction = null; this.kernel.Bind<ChainType>().ToConstant(ChainType.Regtest); this.kernel.Bind<ICoreRules>().ToConstant(testBlocks.Rules); this.kernel.Bind<IChainParams>().ToConstant(testBlocks.ChainParams); // by default, don't run scripts in unit tests testBlocks.Rules.IgnoreScripts = true; // initialize the blockchain daemon this.kernel.Bind<CoreDaemon>().ToSelf().InSingletonScope(); this.coreDaemon = this.kernel.Get<CoreDaemon>(); try { this.coreStorage = this.coreDaemon.CoreStorage; // start the blockchain daemon this.coreDaemon.Start(); // wait for initial work this.coreDaemon.WaitForUpdate(); // verify initial state Assert.AreEqual(0, this.coreDaemon.TargetChainHeight); Assert.AreEqual(testBlocks.ChainParams.GenesisBlock.Hash, this.coreDaemon.TargetChain.LastBlock.Hash); Assert.AreEqual(testBlocks.ChainParams.GenesisBlock.Hash, this.coreDaemon.CurrentChain.LastBlock.Hash); } catch (Exception) { this.coreDaemon.Dispose(); throw; } }
public UnconfirmedTxesWorker(WorkerConfig workerConfig, ChainStateWorker chainStateWorker, UnconfirmedTxesBuilder unconfirmedTxesBuilder, CoreStorage coreStorage) : base("UnconfirmedTxesWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime) { this.coreStorage = coreStorage; this.chainStateWorker = chainStateWorker; this.unconfirmedTxesBuilder = unconfirmedTxesBuilder; this.currentChain = new Lazy<Chain>(() => this.unconfirmedTxesBuilder.Chain); this.chainStateWorker.OnChainStateChanged += HandleChanged; }
public CoreDaemon(ICoreRules rules, IStorageManager storageManager) { this.rules = rules; this.storageManager = storageManager; coreStorage = new CoreStorage(storageManager); // create chain state builder chainStateBuilder = new ChainStateBuilder(this.rules, coreStorage, this.storageManager); // create unconfirmed txes builder unconfirmedTxesBuilder = new UnconfirmedTxesBuilder(this, coreStorage, this.storageManager); // create workers targetChainWorker = new TargetChainWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(30)), ChainParams, coreStorage); chainStateWorker = new ChainStateWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)), targetChainWorker, chainStateBuilder, this.rules, coreStorage); unconfirmedTxesWorker = new UnconfirmedTxesWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)), chainStateWorker, unconfirmedTxesBuilder, coreStorage); pruningWorker = new PruningWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromMinutes(5)), this, this.storageManager, chainStateWorker); defragWorker = new DefragWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)), this.storageManager); gcWorker = new WorkerMethod("GC Worker", GcWorker, initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)); utxoScanWorker = new WorkerMethod("UTXO Scan Worker", UtxoScanWorker, initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60)); statsWorker = new StatsWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(0), maxIdleTime: TimeSpan.MaxValue), this); // wire events chainStateWorker.BlockMissed += HandleBlockMissed; targetChainWorker.OnTargetChainChanged += HandleTargetChainChanged; chainStateWorker.OnChainStateChanged += HandleChainStateChanged; pruningWorker.OnWorkFinished += defragWorker.NotifyWork; unconfirmedTxesBuilder.UnconfirmedTxAdded += RaiseUnconfirmedTxAdded; unconfirmedTxesBuilder.TxesConfirmed += RaiseTxesConfirmed; unconfirmedTxesBuilder.TxesUnconfirmed += RaiseTxesUnconfirmed; }
public HeadersRequestWorker(WorkerConfig workerConfig, LocalClient localClient, CoreDaemon coreDaemon) : base("HeadersRequestWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime) { this.localClient = localClient; this.coreDaemon = coreDaemon; this.coreStorage = coreDaemon.CoreStorage; this.headersRequestsByPeer = new ConcurrentDictionary<Peer, DateTimeOffset>(); this.localClient.OnBlockHeaders += HandleBlockHeaders; this.coreDaemon.OnTargetChainChanged += HandleTargetChainChanged; this.flushWorker = new WorkerMethod("HeadersRequestWorker.FlushWorker", FlushWorkerMethod, initialNotify: true, minIdleTime: TimeSpan.Zero, maxIdleTime: TimeSpan.MaxValue); this.flushQueue = new ConcurrentQueue<FlushHeaders>(); }
public ChainStateWorker(WorkerConfig workerConfig, TargetChainWorker targetChainWorker, ChainStateBuilder chainStateBuilder, IBlockchainRules rules, CoreStorage coreStorage) : base("ChainStateWorker", workerConfig.initialNotify, workerConfig.minIdleTime, workerConfig.maxIdleTime) { this.rules = rules; this.coreStorage = coreStorage; this.blockProcessingDurationMeasure = new DurationMeasure(sampleCutoff: TimeSpan.FromMinutes(5)); this.blockMissCountMeasure = new CountMeasure(TimeSpan.FromSeconds(30)); this.targetChainWorker = targetChainWorker; this.chainStateBuilder = chainStateBuilder; this.currentChain = new Lazy<Chain>(() => this.chainStateBuilder.Chain); this.coreStorage.BlockInvalidated += HandleChanged; this.coreStorage.BlockTxesAdded += HandleChanged; this.coreStorage.BlockTxesRemoved += HandleChanged; this.coreStorage.ChainedHeaderAdded += HandleChanged; this.targetChainWorker.OnTargetChainChanged += HandleChanged; }
public Simulator(ChainType chainType) { // initialize kernel this.kernel = new StandardKernel(); // add logging module this.kernel.Load(new ConsoleLoggingModule()); // log startup this.logger = LogManager.GetCurrentClassLogger(); this.logger.Info($"Starting up: {DateTime.Now}"); this.random = new Random(); this.blockProvider = TestBlockProvider.CreateForRules(chainType); // add storage module this.kernel.Load(new MemoryStorageModule()); // add rules module this.kernel.Load(new RulesModule(chainType)); // by default, don't run scripts in unit tests var rules = this.kernel.Get<ICoreRules>(); rules.IgnoreScripts = true; // initialize the blockchain daemon this.kernel.Bind<CoreDaemon>().ToSelf().InSingletonScope(); this.coreDaemon = this.kernel.Get<CoreDaemon>(); this.coreStorage = this.coreDaemon.CoreStorage; // start the blockchain daemon this.coreDaemon.Start(); // wait for initial work this.coreDaemon.WaitForUpdate(); // verify initial state Assert.AreEqual(0, this.coreDaemon.TargetChainHeight); Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, this.coreDaemon.TargetChain.LastBlock.Hash); Assert.AreEqual(rules.ChainParams.GenesisBlock.Hash, this.coreDaemon.CurrentChain.LastBlock.Hash); }
public CoreDaemon(IBlockchainRules rules, IStorageManager storageManager) { this.rules = rules; this.storageManager = storageManager; this.coreStorage = new CoreStorage(storageManager); // create chain state builder this.chainStateBuilder = new ChainStateBuilder(this.rules, this.coreStorage, this.storageManager); // create workers this.targetChainWorker = new TargetChainWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(50), maxIdleTime: TimeSpan.FromSeconds(30)), this.rules, this.coreStorage); this.chainStateWorker = new ChainStateWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMilliseconds(0), maxIdleTime: TimeSpan.FromSeconds(5)), this.targetChainWorker, this.chainStateBuilder, this.rules, this.coreStorage); this.pruningWorker = new PruningWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromSeconds(0), maxIdleTime: TimeSpan.FromMinutes(5)), this, this.storageManager, this.chainStateWorker); this.defragWorker = new DefragWorker( new WorkerConfig(initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)), this.storageManager); this.gcWorker = new WorkerMethod("GC Worker", GcWorker, initialNotify: true, minIdleTime: TimeSpan.FromMinutes(5), maxIdleTime: TimeSpan.FromMinutes(5)); this.utxoScanWorker = new WorkerMethod("UTXO Scan Worker", UtxoScanWorker, initialNotify: true, minIdleTime: TimeSpan.FromSeconds(60), maxIdleTime: TimeSpan.FromSeconds(60)); // wire events this.chainStateWorker.BlockMissed += HandleBlockMissed; this.targetChainWorker.OnTargetChainChanged += HandleTargetChainChanged; this.chainStateWorker.OnChainStateChanged += HandleChainStateChanged; this.pruningWorker.OnWorkFinished += this.defragWorker.NotifyWork; }
private void TestRollback(ITestStorageProvider provider) { ConsoleLoggingModule.Configure(); var logger = LogManager.GetCurrentClassLogger(); var blockCount = 10.THOUSAND(); var checkUtxoHashFrequencey = 1000; var blockProvider = new TestNet3BlockProvider(); var blocks = blockProvider.ReadBlocks().Take(blockCount).ToList(); var genesisBlock = blocks[0]; var genesisHeader = new ChainedHeader(genesisBlock.Header, height: 0, totalWork: 0, dateSeen: DateTimeOffset.Now); var genesisChain = Chain.CreateForGenesisBlock(genesisHeader); var chainParams = new Testnet3Params(); var rules = new CoreRules(chainParams) { IgnoreScripts = true, IgnoreSignatures = true, IgnoreScriptErrors = true }; using (var storageManager = provider.OpenStorageManager()) using (var coreStorage = new CoreStorage(storageManager)) using (var chainStateBuilder = new ChainStateBuilder(rules, coreStorage, storageManager)) { // add blocks to storage coreStorage.AddGenesisBlock(ChainedHeader.CreateForGenesisBlock(blocks[0].Header)); foreach (var block in blocks) coreStorage.TryAddBlock(block); // store empty utxo hash var expectedUtxoHashes = new List<UInt256>(); using (var chainState = chainStateBuilder.ToImmutable()) expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState)); // calculate utxo forward and store its state at each step along the way for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++) { logger.Info($"Adding: {blockIndex:N0}"); var block = blocks[blockIndex]; var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now); chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select( (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait(); if (blockIndex % checkUtxoHashFrequencey == 0 || blockIndex == blocks.Count - 1) using (var chainState = chainStateBuilder.ToImmutable()) expectedUtxoHashes.Add(UtxoCommitment.ComputeHash(chainState)); } // verify the utxo state before rolling back var expectedLastUtxoHash = UInt256.ParseHex("5f155c7d8a5c850d5fb2566aec5110caa40e270184126d17022ae9780fd65fd9"); Assert.AreEqual(expectedLastUtxoHash, expectedUtxoHashes.Last()); expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1); // roll utxo backwards and validate its state at each step along the way for (var blockIndex = blocks.Count - 1; blockIndex >= 0; blockIndex--) { logger.Info($"Rolling back: {blockIndex:N0}"); var block = blocks[blockIndex]; var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now); var blockTxes = block.Transactions.Select((tx, txIndex) => BlockTx.Create(txIndex, tx)); chainStateBuilder.RollbackBlock(chainedHeader, blockTxes); if ((blockIndex - 1) % checkUtxoHashFrequencey == 0 || blockIndex == 0) { var expectedUtxoHash = expectedUtxoHashes.Last(); expectedUtxoHashes.RemoveAt(expectedUtxoHashes.Count - 1); using (var chainState = chainStateBuilder.ToImmutable()) Assert.AreEqual(expectedUtxoHash, UtxoCommitment.ComputeHash(chainState)); } } // verify chain state was rolled all the way back Assert.AreEqual(-1, chainStateBuilder.Chain.Height); Assert.AreEqual(0, expectedUtxoHashes.Count); // calculate utxo forward again for (var blockIndex = 0; blockIndex < blocks.Count; blockIndex++) { logger.Info($"Adding: {blockIndex:N0}"); var block = blocks[blockIndex]; var chainedHeader = new ChainedHeader(block.Header, blockIndex, 0, DateTimeOffset.Now); chainStateBuilder.AddBlockAsync(chainedHeader, block.Transactions.Select( (tx, txIndex) => BlockTx.Create(txIndex, tx))).Wait(); } // verify final utxo state again using (var chainState = chainStateBuilder.ToImmutable()) Assert.AreEqual(expectedLastUtxoHash, UtxoCommitment.ComputeHash(chainState)); } }