diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs b/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs index 3b9582889cbb..1470ec994713 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Producers/BlockProducerBaseTests.IsProducingBlocks.cs @@ -145,6 +145,33 @@ public async Task CliqueBlockProducer_IsProducingBlocks_returns_expected_results await AssertIsProducingBlocks(runner); } + [Test, MaxTime(Timeout.MaxTestTime)] + public async Task DevBlockProducer_OnGlobalWorldState_IsProducingAndSuggestingBlocks() + { + TestRpcBlockchain testRpc = await CreateTestRpc(); + DevBlockProducer blockProducer = new( + Substitute.For(), + testRpc.BlockchainProcessor, + testRpc.WorldStateManager.GlobalWorldState, + testRpc.BlockTree, + testRpc.Timestamper, + testRpc.SpecProvider, + new BlocksConfig(), + LimboLogs.Instance); + + BuildBlocksWhenRequested trigger = new(); + StandardBlockProducerRunner runner = new(trigger, testRpc.BlockTree, blockProducer); + long currentHead = testRpc.BlockTree.Head?.Number ?? 0; + + _ = new NonProcessingProducedBlockSuggester(testRpc.BlockTree, runner); + + runner.Start(); + + await trigger.BuildBlock(testRpc.BlockTree.Head?.Header); + + Assert.That(testRpc.BlockTree.BestSuggestedHeader?.Number, Is.EqualTo(currentHead + 1)); + } + private async Task CreateTestRpc() { Address address = TestItem.Addresses[0]; diff --git a/src/Nethermind/Nethermind.Config/BlocksConfig.cs b/src/Nethermind/Nethermind.Config/BlocksConfig.cs index 36a1c5c5834d..4d8df415df72 100644 --- a/src/Nethermind/Nethermind.Config/BlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/BlocksConfig.cs @@ -85,6 +85,9 @@ public string ExtraData _extraDataBytes = bytes; } } + + public bool BuildBlocksOnMainState { get; set; } + public byte[] GetExtraDataBytes() { return _extraDataBytes; diff --git a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs index 91d4345d1eb5..320bee4879c1 100644 --- a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs @@ -55,5 +55,8 @@ public interface IBlocksConfig : IConfig [ConfigItem(Description = "The ticker that gas rewards are denominated in for processing logs", DefaultValue = "ETH", HiddenFromDocs = true)] string GasToken { get; set; } + [ConfigItem(Description = "Builds blocks on main (non-readonly) state", DefaultValue = "false", HiddenFromDocs = true)] + bool BuildBlocksOnMainState { get; set; } + byte[] GetExtraDataBytes(); } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs index 00d5328a5bc2..ce7b47a24d0e 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs @@ -34,7 +34,7 @@ namespace Nethermind.Consensus.Producers /// public abstract class BlockProducerBase : IBlockProducer { - private IBlockchainProcessor Processor { get; } + protected IBlockchainProcessor Processor { get; } protected IBlockTree BlockTree { get; } private ITimestamper Timestamper { get; } @@ -189,8 +189,11 @@ protected BlockProducerBase( protected virtual Task SealBlock(Block block, BlockHeader parent, CancellationToken token) => Sealer.SealBlock(block, token); - protected virtual Block? ProcessPreparedBlock(Block block, IBlockTracer? blockTracer, CancellationToken token = default) => - Processor.Process(block, ProcessingOptions.ProducingBlock, blockTracer ?? NullBlockTracer.Instance, token); + protected virtual Block? ProcessPreparedBlock(Block block, IBlockTracer? blockTracer, + CancellationToken token = default) + { + return Processor.Process(block, GetProcessingOptions(), blockTracer ?? NullBlockTracer.Instance, token); + } private bool PreparedBlockCanBeMined(Block? block) { @@ -244,5 +247,12 @@ protected virtual BlockToProduce PrepareBlock(BlockHeader parent, PayloadAttribu return new BlockToProduce(header, transactions, Array.Empty(), payloadAttributes?.Withdrawals); } + + private ProcessingOptions GetProcessingOptions() + { + if (_blocksConfig.BuildBlocksOnMainState) + return ProcessingOptions.NoValidation | ProcessingOptions.StoreReceipts | ProcessingOptions.DoNotUpdateHead; + return ProcessingOptions.ProducingBlock; + } } } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs index 6f5923352525..83b40ae45eb1 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs @@ -27,7 +27,6 @@ protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => .AddScoped() .AddDecorator() .AddDecorator() - .AddScoped(); public IBlockProducerEnv Create() diff --git a/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs new file mode 100644 index 000000000000..3427d9f494b7 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/GlobalWorldStateBlockProducerEnvFactory.cs @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Transactions; +using Nethermind.Consensus.Withdrawals; +using Nethermind.Core; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.State; + +namespace Nethermind.Consensus.Producers +{ + /// + /// Block producer environment factory that uses the global world state and stores receipts by default. + /// Combined with NonProcessingProducedBlockSuggester will add blocks to block tree + /// + /// + /// + /// + public class GlobalWorldStateBlockProducerEnvFactory( + ILifetimeScope rootLifetime, + IWorldStateManager worldStateManager, + IBlockProducerTxSourceFactory txSourceFactory) + : IBlockProducerEnvFactory + { + protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => builder + .AddScoped(txSourceFactory.Create()) + .AddScoped(BlockchainProcessor.Options.Default) + .AddScoped() + .AddScoped() + .AddDecorator() + .AddDecorator() + + .AddScoped(); + + public virtual IBlockProducerEnv Create() + { + ILifetimeScope lifetimeScope = rootLifetime.BeginLifetimeScope(builder => + ConfigureBuilder(builder) + .AddScoped(worldStateManager.GlobalWorldState)); + + rootLifetime.Disposer.AddInstanceForAsyncDisposal(lifetimeScope); + return lifetimeScope.Resolve(); + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs new file mode 100644 index 000000000000..82b2bf7ccf28 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/IProducedBlockSuggester.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Consensus.Producers; + +public interface IProducedBlockSuggester : IDisposable +{ + // Just a marker interface to support DI +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs new file mode 100644 index 000000000000..d1d46263fb17 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Producers/NonProcessingProducedBlockSuggester.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain; +using Nethermind.Core; + +namespace Nethermind.Consensus.Producers; +public class NonProcessingProducedBlockSuggester : IProducedBlockSuggester +{ + private readonly IBlockTree _blockTree; + private readonly IBlockProducerRunner _blockProducerRunner; + + public NonProcessingProducedBlockSuggester(IBlockTree blockTree, IBlockProducerRunner blockProducer) + { + _blockTree = blockTree; + _blockProducerRunner = blockProducer; + _blockProducerRunner.BlockProduced += OnBlockProduced; + } + + private void OnBlockProduced(object? sender, BlockEventArgs e) + { + if (_blockTree.SuggestBlock(e.Block, BlockTreeSuggestOptions.None) == AddBlockResult.Added) + _blockTree.UpdateMainChain([e.Block], true); + } + + public void Dispose() => _blockProducerRunner.BlockProduced -= OnBlockProduced; +} diff --git a/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs b/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs index a061e7f6e8ce..b74aba7c5714 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/ProducedBlockSuggester.cs @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using Nethermind.Blockchain; using Nethermind.Core; namespace Nethermind.Consensus.Producers { - public class ProducedBlockSuggester : IDisposable + public class ProducedBlockSuggester : IProducedBlockSuggester { private readonly IBlockTree _blockTree; private readonly IBlockProducerRunner _blockProducerRunner; diff --git a/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs b/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs index 0238c1ae1fe9..83e2614a4e0b 100644 --- a/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs @@ -32,7 +32,7 @@ namespace Nethermind.Init.Modules; -public class BlockProcessingModule(IInitConfig initConfig) : Module +public class BlockProcessingModule(IInitConfig initConfig, IBlocksConfig blocksConfig) : Module { protected override void Load(ContainerBuilder builder) { @@ -84,8 +84,6 @@ protected override void Load(ContainerBuilder builder) .AddSingleton(NullSealEngine.Instance) .AddSingleton(NullSealEngine.Instance) .AddSingleton() - - .AddSingleton() .AddSingleton() .AddSingleton((blockTree, specProvider, logManager, blocksConfig) => @@ -104,6 +102,17 @@ protected override void Load(ContainerBuilder builder) .AddScoped() ; + if (blocksConfig.BuildBlocksOnMainState) + { + builder.AddSingleton() + .AddScoped(); + } + else + { + builder.AddSingleton() + .AddScoped(); + } + if (initConfig.ExitOnInvalidBlock) builder.AddStep(typeof(ExitOnInvalidBlock)); } diff --git a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs index 9bdfb7ee49f7..19dbd2105bba 100644 --- a/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs +++ b/src/Nethermind/Nethermind.Init/Modules/NethermindModule.cs @@ -53,7 +53,7 @@ protected override void Load(ContainerBuilder builder) .AddModule(new RpcModules(configProvider.GetConfig())) .AddModule(new EraModule()) .AddSource(new ConfigRegistrationSource()) - .AddModule(new BlockProcessingModule(configProvider.GetConfig())) + .AddModule(new BlockProcessingModule(configProvider.GetConfig(), configProvider.GetConfig())) .AddModule(new BlockTreeModule(configProvider.GetConfig())) .AddSingleton() diff --git a/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs b/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs index 4dfbe0dd5aa6..b07ed09a9244 100644 --- a/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs +++ b/src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs @@ -1,12 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Threading; -using System.Threading.Tasks; +using Autofac; using Nethermind.Api; using Nethermind.Api.Steps; using Nethermind.Consensus.Producers; using Nethermind.Logging; +using System.Threading; +using System.Threading.Tasks; namespace Nethermind.Init.Steps { @@ -30,8 +31,7 @@ public Task Execute(CancellationToken _) ILogger logger = _api.LogManager.GetClassLogger(); if (logger.IsInfo) logger.Info($"Starting {_api.SealEngineType} block producer & sealer"); - ProducedBlockSuggester suggester = new(_api.BlockTree, _api.BlockProducerRunner); - _api.DisposeStack.Push(suggester); + _api.Context.ResolveOptional(); _api.BlockProducerRunner.Start(); }