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);
        }
예제 #2
0
        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
                   ));
        }