private Task InitBlockchain() { InitBlockTraceDumper(); (IApiWithStores getApi, IApiWithBlockchain setApi) = _api.ForBlockchain; if (getApi.ChainSpec == null) { throw new StepDependencyException(nameof(getApi.ChainSpec)); } if (getApi.DbProvider == null) { throw new StepDependencyException(nameof(getApi.DbProvider)); } if (getApi.SpecProvider == null) { throw new StepDependencyException(nameof(getApi.SpecProvider)); } if (getApi.BlockTree == null) { throw new StepDependencyException(nameof(getApi.BlockTree)); } _logger = getApi.LogManager.GetClassLogger(); IInitConfig initConfig = getApi.Config <IInitConfig>(); ISyncConfig syncConfig = getApi.Config <ISyncConfig>(); IPruningConfig pruningConfig = getApi.Config <IPruningConfig>(); IMiningConfig miningConfig = getApi.Config <IMiningConfig>(); if (syncConfig.DownloadReceiptsInFastSync && !syncConfig.DownloadBodiesInFastSync) { _logger.Warn($"{nameof(syncConfig.DownloadReceiptsInFastSync)} is selected but {nameof(syncConfig.DownloadBodiesInFastSync)} - enabling bodies to support receipts download."); syncConfig.DownloadBodiesInFastSync = true; } Account.AccountStartNonce = getApi.ChainSpec.Parameters.AccountStartNonce; IWitnessCollector witnessCollector; if (syncConfig.WitnessProtocolEnabled) { WitnessCollector witnessCollectorImpl = new(getApi.DbProvider.WitnessDb, _api.LogManager); witnessCollector = setApi.WitnessCollector = witnessCollectorImpl; setApi.WitnessRepository = witnessCollectorImpl.WithPruning(getApi.BlockTree !, getApi.LogManager); } else { witnessCollector = setApi.WitnessCollector = NullWitnessCollector.Instance; setApi.WitnessRepository = NullWitnessCollector.Instance; } CachingStore cachedStateDb = getApi.DbProvider.StateDb .Cached(Trie.MemoryAllowance.TrieNodeCacheCount); setApi.MainStateDbWithCache = cachedStateDb; IKeyValueStore codeDb = getApi.DbProvider.CodeDb .WitnessedBy(witnessCollector); TrieStore trieStore; IKeyValueStoreWithBatching stateWitnessedBy = setApi.MainStateDbWithCache.WitnessedBy(witnessCollector); if (pruningConfig.Mode.IsMemory()) { IPersistenceStrategy persistenceStrategy = Persist.IfBlockOlderThan(pruningConfig.PersistenceInterval); // TODO: this should be based on time if (pruningConfig.Mode.IsFull()) { PruningTriggerPersistenceStrategy triggerPersistenceStrategy = new((IFullPruningDb)getApi.DbProvider !.StateDb, getApi.BlockTree !, getApi.LogManager); getApi.DisposeStack.Push(triggerPersistenceStrategy); persistenceStrategy = persistenceStrategy.Or(triggerPersistenceStrategy); } setApi.TrieStore = trieStore = new TrieStore( stateWitnessedBy, Prune.WhenCacheReaches(pruningConfig.CacheMb.MB()), // TODO: memory hint should define this persistenceStrategy, getApi.LogManager); if (pruningConfig.Mode.IsFull()) { IFullPruningDb fullPruningDb = (IFullPruningDb)getApi.DbProvider !.StateDb; fullPruningDb.PruningStarted += (_, args) => { cachedStateDb.PersistCache(args.Context); trieStore.PersistCache(args.Context, args.Context.CancellationTokenSource.Token); }; } } else { setApi.TrieStore = trieStore = new TrieStore( stateWitnessedBy, No.Pruning, Persist.EveryBlock, getApi.LogManager); } TrieStoreBoundaryWatcher trieStoreBoundaryWatcher = new(trieStore, _api.BlockTree !, _api.LogManager); getApi.DisposeStack.Push(trieStoreBoundaryWatcher); getApi.DisposeStack.Push(trieStore); ITrieStore readOnlyTrieStore = setApi.ReadOnlyTrieStore = trieStore.AsReadOnly(cachedStateDb); IStateProvider stateProvider = setApi.StateProvider = new StateProvider( trieStore, codeDb, getApi.LogManager); ReadOnlyDbProvider readOnly = new(getApi.DbProvider, false); IStateReader stateReader = setApi.StateReader = new StateReader(readOnlyTrieStore, readOnly.GetDb <IDb>(DbNames.Code), getApi.LogManager); setApi.TransactionComparerProvider = new TransactionComparerProvider(getApi.SpecProvider !, getApi.BlockTree.AsReadOnly()); setApi.ChainHeadStateProvider = new ChainHeadReadOnlyStateProvider(getApi.BlockTree, stateReader); Account.AccountStartNonce = getApi.ChainSpec.Parameters.AccountStartNonce; stateProvider.StateRoot = getApi.BlockTree !.Head?.StateRoot ?? Keccak.EmptyTreeHash; if (_api.Config <IInitConfig>().DiagnosticMode == DiagnosticMode.VerifyTrie) { Task.Run(() => { try { _logger !.Info("Collecting trie stats and verifying that no nodes are missing..."); TrieStats stats = stateProvider.CollectStats(getApi.DbProvider.CodeDb, _api.LogManager); _logger.Info($"Starting from {getApi.BlockTree.Head?.Number} {getApi.BlockTree.Head?.StateRoot}{Environment.NewLine}" + stats); } catch (Exception ex) { _logger !.Error(ex.ToString()); } }); } // Init state if we need system calls before actual processing starts if (getApi.BlockTree !.Head?.StateRoot != null) { stateProvider.StateRoot = getApi.BlockTree.Head.StateRoot; } TxValidator txValidator = setApi.TxValidator = new TxValidator(getApi.SpecProvider.ChainId); ITxPool txPool = _api.TxPool = CreateTxPool(); ReceiptCanonicalityMonitor receiptCanonicalityMonitor = new(getApi.BlockTree, getApi.ReceiptStorage, _api.LogManager); getApi.DisposeStack.Push(receiptCanonicalityMonitor); _api.ReceiptMonitor = receiptCanonicalityMonitor; _api.BlockPreprocessor.AddFirst( new RecoverSignatures(getApi.EthereumEcdsa, txPool, getApi.SpecProvider, getApi.LogManager)); IStorageProvider storageProvider = setApi.StorageProvider = new StorageProvider( trieStore, stateProvider, getApi.LogManager); // blockchain processing BlockhashProvider blockhashProvider = new ( getApi.BlockTree, getApi.LogManager); VirtualMachine virtualMachine = new ( blockhashProvider, getApi.SpecProvider, getApi.LogManager); WorldState worldState = new (stateProvider, storageProvider); _api.TransactionProcessor = new TransactionProcessor( getApi.SpecProvider, worldState, virtualMachine, getApi.LogManager); InitSealEngine(); if (_api.SealValidator == null) { throw new StepDependencyException(nameof(_api.SealValidator)); } setApi.HeaderValidator = CreateHeaderValidator(); IHeaderValidator?headerValidator = setApi.HeaderValidator; IUnclesValidator unclesValidator = setApi.UnclesValidator = new UnclesValidator( getApi.BlockTree, headerValidator, getApi.LogManager); setApi.BlockValidator = new BlockValidator( txValidator, headerValidator, unclesValidator, getApi.SpecProvider, getApi.LogManager); IChainHeadInfoProvider chainHeadInfoProvider = new ChainHeadInfoProvider(getApi.SpecProvider, getApi.BlockTree, stateReader); setApi.TxPoolInfoProvider = new TxPoolInfoProvider(chainHeadInfoProvider.AccountStateProvider, txPool); setApi.GasPriceOracle = new GasPriceOracle(getApi.BlockTree, getApi.SpecProvider, _api.LogManager, miningConfig.MinGasPrice); IBlockProcessor mainBlockProcessor = setApi.MainBlockProcessor = CreateBlockProcessor(); BlockchainProcessor blockchainProcessor = new( getApi.BlockTree, mainBlockProcessor, _api.BlockPreprocessor, stateReader, getApi.LogManager, new BlockchainProcessor.Options { StoreReceiptsByDefault = initConfig.StoreReceipts, DumpOptions = initConfig.AutoDump }); setApi.BlockProcessingQueue = blockchainProcessor; setApi.BlockchainProcessor = blockchainProcessor; setApi.EthSyncingInfo = new EthSyncingInfo(getApi.BlockTree); // TODO: can take the tx sender from plugin here maybe ITxSigner txSigner = new WalletTxSigner(getApi.Wallet, getApi.SpecProvider.ChainId); TxSealer standardSealer = new(txSigner, getApi.Timestamper); NonceReservingTxSealer nonceReservingTxSealer = new(txSigner, getApi.Timestamper, txPool); setApi.TxSender = new TxPoolSender(txPool, nonceReservingTxSealer, standardSealer); // TODO: possibly hide it (but need to confirm that NDM does not really need it) IFilterStore filterStore = setApi.FilterStore = new FilterStore(); setApi.FilterManager = new FilterManager(filterStore, mainBlockProcessor, txPool, getApi.LogManager); setApi.HealthHintService = CreateHealthHintService(); setApi.BlockProductionPolicy = new BlockProductionPolicy(miningConfig); InitializeFullPruning(pruningConfig, initConfig, _api, stateReader); return(Task.CompletedTask); }
protected async Task <EthereumTestResult> RunTest(BlockchainTest test, Stopwatch?stopwatch = null) { TestContext.Write($"Running {test.Name} at {DateTime.UtcNow:HH:mm:ss.ffffff}"); Assert.IsNull(test.LoadFailure, "test data loading failure"); IDb stateDb = new MemDb(); IDb codeDb = new MemDb(); ISpecProvider specProvider; if (test.NetworkAfterTransition != null) { specProvider = new CustomSpecProvider(1, (0, Frontier.Instance), (1, test.Network), (test.TransitionBlockNumber, test.NetworkAfterTransition)); } else { specProvider = new CustomSpecProvider(1, (0, Frontier.Instance), // TODO: this thing took a lot of time to find after it was removed!, genesis block is always initialized with Frontier (1, test.Network)); } if (specProvider.GenesisSpec != Frontier.Instance) { Assert.Fail("Expected genesis spec to be Frontier for blockchain tests"); } bool isNetworkAfterTransitionLondon = test.NetworkAfterTransition == London.Instance; HeaderDecoder.Eip1559TransitionBlock = isNetworkAfterTransitionLondon ? test.TransitionBlockNumber : long.MaxValue; DifficultyCalculator.Wrapped = new EthashDifficultyCalculator(specProvider); IRewardCalculator rewardCalculator = new RewardCalculator(specProvider); IEthereumEcdsa ecdsa = new EthereumEcdsa(specProvider.ChainId, _logManager); TrieStore trieStore = new(stateDb, _logManager); IStateProvider stateProvider = new StateProvider(trieStore, codeDb, _logManager); MemDb blockInfoDb = new MemDb(); IBlockTree blockTree = new BlockTree(new MemDb(), new MemDb(), blockInfoDb, new ChainLevelInfoRepository(blockInfoDb), specProvider, NullBloomStorage.Instance, _logManager); ITransactionComparerProvider transactionComparerProvider = new TransactionComparerProvider(specProvider, blockTree); IStateReader stateReader = new StateReader(trieStore, codeDb, _logManager); IChainHeadInfoProvider chainHeadInfoProvider = new ChainHeadInfoProvider(specProvider, blockTree, stateReader); ITxPool transactionPool = new TxPool(ecdsa, chainHeadInfoProvider, new TxPoolConfig(), new TxValidator(specProvider.ChainId), _logManager, transactionComparerProvider.GetDefaultComparer()); IReceiptStorage receiptStorage = NullReceiptStorage.Instance; IBlockhashProvider blockhashProvider = new BlockhashProvider(blockTree, _logManager); ITxValidator txValidator = new TxValidator(ChainId.Mainnet); IHeaderValidator headerValidator = new HeaderValidator(blockTree, Sealer, specProvider, _logManager); IUnclesValidator unclesValidator = new UnclesValidator(blockTree, headerValidator, _logManager); IBlockValidator blockValidator = new BlockValidator(txValidator, headerValidator, unclesValidator, specProvider, _logManager); IStorageProvider storageProvider = new StorageProvider(trieStore, stateProvider, _logManager); IVirtualMachine virtualMachine = new VirtualMachine( blockhashProvider, specProvider, _logManager); IBlockProcessor blockProcessor = new BlockProcessor( specProvider, blockValidator, rewardCalculator, new BlockProcessor.BlockValidationTransactionsExecutor( new TransactionProcessor( specProvider, stateProvider, storageProvider, virtualMachine, _logManager), stateProvider), stateProvider, storageProvider, receiptStorage, NullWitnessCollector.Instance, _logManager); IBlockchainProcessor blockchainProcessor = new BlockchainProcessor( blockTree, blockProcessor, new RecoverSignatures(ecdsa, NullTxPool.Instance, specProvider, _logManager), _logManager, BlockchainProcessor.Options.NoReceipts); InitializeTestState(test, stateProvider, storageProvider, specProvider); List <(Block Block, string ExpectedException)> correctRlp = new(); for (int i = 0; i < test.Blocks.Length; i++) { try { TestBlockJson testBlockJson = test.Blocks[i]; var rlpContext = Bytes.FromHexString(testBlockJson.Rlp).AsRlpStream(); Block suggestedBlock = Rlp.Decode <Block>(rlpContext); suggestedBlock.Header.SealEngineType = test.SealEngineUsed ? SealEngineType.Ethash : SealEngineType.None; Assert.AreEqual(new Keccak(testBlockJson.BlockHeader.Hash), suggestedBlock.Header.Hash, "hash of the block"); for (int uncleIndex = 0; uncleIndex < suggestedBlock.Uncles.Length; uncleIndex++) { Assert.AreEqual(new Keccak(testBlockJson.UncleHeaders[uncleIndex].Hash), suggestedBlock.Uncles[uncleIndex].Hash, "hash of the uncle"); } correctRlp.Add((suggestedBlock, testBlockJson.ExpectedException)); } catch (Exception) { _logger?.Info($"Invalid RLP ({i})"); } } if (correctRlp.Count == 0) { EthereumTestResult result; if (test.GenesisBlockHeader is null) { result = new EthereumTestResult(test.Name, "Genesis block header missing in the test spec."); } else if (!new Keccak(test.GenesisBlockHeader.Hash).Equals(test.LastBlockHash)) { result = new EthereumTestResult(test.Name, "Genesis hash mismatch"); } else { result = new EthereumTestResult(test.Name, null, true); } return(result); } if (test.GenesisRlp == null) { test.GenesisRlp = Rlp.Encode(new Block(JsonToEthereumTest.Convert(test.GenesisBlockHeader))); } Block genesisBlock = Rlp.Decode <Block>(test.GenesisRlp.Bytes); Assert.AreEqual(new Keccak(test.GenesisBlockHeader.Hash), genesisBlock.Header.Hash, "genesis header hash"); ManualResetEvent genesisProcessed = new(false); blockTree.NewHeadBlock += (_, args) => { if (args.Block.Number == 0) { Assert.AreEqual(genesisBlock.Header.StateRoot, stateProvider.StateRoot, "genesis state root"); genesisProcessed.Set(); } }; blockchainProcessor.Start(); blockTree.SuggestBlock(genesisBlock); genesisProcessed.WaitOne(); for (int i = 0; i < correctRlp.Count; i++) { stopwatch?.Start(); try { if (correctRlp[i].ExpectedException != null) { _logger.Info($"Expecting block exception: {correctRlp[i].ExpectedException}"); } if (correctRlp[i].Block.Hash == null) { throw new Exception($"null hash in {test.Name} block {i}"); } // TODO: mimic the actual behaviour where block goes through validating sync manager? if (!test.SealEngineUsed || blockValidator.ValidateSuggestedBlock(correctRlp[i].Block)) { blockTree.SuggestBlock(correctRlp[i].Block); } else { Console.WriteLine("Invalid block"); } } catch (InvalidBlockException) { } catch (Exception ex) { _logger?.Info(ex.ToString()); } } await blockchainProcessor.StopAsync(true); stopwatch?.Stop(); List <string> differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), storageProvider, stateProvider); // if (differences.Any()) // { // BlockTrace blockTrace = blockchainProcessor.TraceBlock(blockTree.BestSuggested.Hash); // _logger.Info(new UnforgivingJsonSerializer().Serialize(blockTrace, true)); // } Assert.Zero(differences.Count, "differences"); return(new EthereumTestResult ( test.Name, null, differences.Count == 0 )); }