Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<ITxSource>(),
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<TestRpcBlockchain> CreateTestRpc()
{
Address address = TestItem.Addresses[0];
Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Config/BlocksConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ public string ExtraData
_extraDataBytes = bytes;
}
}

public bool BuildBlocksOnMainState { get; set; }

public byte[] GetExtraDataBytes()
{
return _extraDataBytes;
Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Config/IBlocksConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace Nethermind.Consensus.Producers
/// </summary>
public abstract class BlockProducerBase : IBlockProducer
{
private IBlockchainProcessor Processor { get; }
protected IBlockchainProcessor Processor { get; }
protected IBlockTree BlockTree { get; }
private ITimestamper Timestamper { get; }

Expand Down Expand Up @@ -189,8 +189,11 @@ protected BlockProducerBase(
protected virtual Task<Block> 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)
{
Expand Down Expand Up @@ -244,5 +247,12 @@ protected virtual BlockToProduce PrepareBlock(BlockHeader parent, PayloadAttribu

return new BlockToProduce(header, transactions, Array.Empty<BlockHeader>(), payloadAttributes?.Withdrawals);
}

private ProcessingOptions GetProcessingOptions()
{
if (_blocksConfig.BuildBlocksOnMainState)
return ProcessingOptions.NoValidation | ProcessingOptions.StoreReceipts | ProcessingOptions.DoNotUpdateHead;
return ProcessingOptions.ProducingBlock;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) =>
.AddScoped<IBlockProcessor.IBlockTransactionsExecutor, BlockProcessor.BlockProductionTransactionsExecutor>()
.AddDecorator<IWithdrawalProcessor, BlockProductionWithdrawalProcessor>()
.AddDecorator<IBlockchainProcessor, OneTimeChainProcessor>()

.AddScoped<IBlockProducerEnv, BlockProducerEnv>();

public IBlockProducerEnv Create()
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Block producer environment factory that uses the global world state and stores receipts by default.
/// Combined with NonProcessingProducedBlockSuggester will add blocks to block tree
/// </summary>
/// <param name="rootLifetime"></param>
/// <param name="worldStateManager"></param>
/// <param name="txSourceFactory"></param>
public class GlobalWorldStateBlockProducerEnvFactory(
ILifetimeScope rootLifetime,
IWorldStateManager worldStateManager,
IBlockProducerTxSourceFactory txSourceFactory)
: IBlockProducerEnvFactory
{
protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => builder
.AddScoped<ITxSource>(txSourceFactory.Create())
.AddScoped(BlockchainProcessor.Options.Default)
.AddScoped<ITransactionProcessorAdapter, BuildUpTransactionProcessorAdapter>()
.AddScoped<IBlockProcessor.IBlockTransactionsExecutor, BlockProcessor.BlockProductionTransactionsExecutor>()
.AddDecorator<IWithdrawalProcessor, BlockProductionWithdrawalProcessor>()
.AddDecorator<IBlockchainProcessor, OneTimeChainProcessor>()

.AddScoped<IBlockProducerEnv, BlockProducerEnv>();

public virtual IBlockProducerEnv Create()
{
ILifetimeScope lifetimeScope = rootLifetime.BeginLifetimeScope(builder =>
ConfigureBuilder(builder)
.AddScoped(worldStateManager.GlobalWorldState));

rootLifetime.Disposer.AddInstanceForAsyncDisposal(lifetimeScope);
return lifetimeScope.Resolve<IBlockProducerEnv>();
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
15 changes: 12 additions & 3 deletions src/Nethermind/Nethermind.Init/Modules/BlockProcessingModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -84,8 +84,6 @@ protected override void Load(ContainerBuilder builder)
.AddSingleton<ISealValidator>(NullSealEngine.Instance)
.AddSingleton<ISealer>(NullSealEngine.Instance)
.AddSingleton<ISealEngine, SealEngine>()

.AddSingleton<IBlockProducerEnvFactory, BlockProducerEnvFactory>()
.AddSingleton<IBlockProducerTxSourceFactory, TxPoolTxSourceFactory>()

.AddSingleton<IGasPriceOracle, IBlockFinder, ISpecProvider, ILogManager, IBlocksConfig>((blockTree, specProvider, logManager, blocksConfig) =>
Expand All @@ -104,6 +102,17 @@ protected override void Load(ContainerBuilder builder)
.AddScoped<IGenesisLoader, GenesisLoader>()
;

if (blocksConfig.BuildBlocksOnMainState)
{
builder.AddSingleton<IBlockProducerEnvFactory, GlobalWorldStateBlockProducerEnvFactory>()
.AddScoped<IProducedBlockSuggester, NonProcessingProducedBlockSuggester>();
}
else
{
builder.AddSingleton<IBlockProducerEnvFactory, BlockProducerEnvFactory>()
.AddScoped<IProducedBlockSuggester, ProducedBlockSuggester>();
}

if (initConfig.ExitOnInvalidBlock) builder.AddStep(typeof(ExitOnInvalidBlock));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected override void Load(ContainerBuilder builder)
.AddModule(new RpcModules(configProvider.GetConfig<IJsonRpcConfig>()))
.AddModule(new EraModule())
.AddSource(new ConfigRegistrationSource())
.AddModule(new BlockProcessingModule(configProvider.GetConfig<IInitConfig>()))
.AddModule(new BlockProcessingModule(configProvider.GetConfig<IInitConfig>(), configProvider.GetConfig<IBlocksConfig>()))
.AddModule(new BlockTreeModule(configProvider.GetConfig<IReceiptConfig>()))
.AddSingleton<ISpecProvider, ChainSpecBasedSpecProvider>()

Expand Down
8 changes: 4 additions & 4 deletions src/Nethermind/Nethermind.Init/Steps/StartBlockProducer.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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<IProducedBlockSuggester>();
_api.BlockProducerRunner.Start();
}

Expand Down