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 @@ -96,6 +96,9 @@ private void ValidateProcessedBlock(Block suggestedBlock, ProcessingOptions opti
protected bool ShouldComputeStateRoot(BlockHeader header) =>
!header.IsGenesis || !specProvider.GenesisStateUnavailable;

protected virtual BlockExecutionContext CreateBlockExecutionContext(BlockHeader header, IReleaseSpec spec) =>
new(header, spec);

protected virtual TxReceipt[] ProcessBlock(
Block block,
IBlockTracer blockTracer,
Expand All @@ -108,7 +111,7 @@ protected virtual TxReceipt[] ProcessBlock(
ReceiptsTracer.SetOtherTracer(blockTracer);
ReceiptsTracer.StartNewBlockTrace(block);

blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec));
blockTransactionsExecutor.SetBlockExecutionContext(CreateBlockExecutionContext(block.Header, spec));

StoreBeaconRoot(block, spec);
blockHashStore.ApplyBlockhashStateChanges(header, spec);
Expand Down
51 changes: 37 additions & 14 deletions src/Nethermind/Nethermind.Evm/BlockExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,49 @@

namespace Nethermind.Evm;

public readonly struct BlockExecutionContext(BlockHeader blockHeader, IReleaseSpec spec, in UInt256 blobBaseFee)
public readonly struct BlockExecutionContext
{
public readonly BlockHeader Header = blockHeader;
public readonly Address Coinbase = blockHeader.GasBeneficiary ?? Address.Zero;
public readonly ulong Number = (ulong)blockHeader.Number;
public readonly ulong GasLimit = (ulong)blockHeader.GasLimit;
public readonly ValueHash256 BlobBaseFee = blobBaseFee.ToValueHash();
public readonly IReleaseSpec Spec = spec;

// Use the random value if post-merge; otherwise, use block difficulty.
public readonly ValueHash256 PrevRandao = blockHeader.IsPostMerge
? (blockHeader.Random ?? Hash256.Zero).ValueHash256
: blockHeader.Difficulty.ToValueHash();
public readonly BlockHeader Header;
public readonly Address Coinbase;
public readonly ulong Number;
public readonly ulong GasLimit;
public readonly ValueHash256 BlobBaseFee;
public readonly IReleaseSpec Spec;
public readonly ValueHash256 PrevRandao;
public readonly bool IsGenesis;

public BlockExecutionContext(BlockHeader blockHeader, IReleaseSpec spec)
: this(blockHeader, spec, GetBlobBaseFee(blockHeader, spec), GetDefaultPrevRandao(blockHeader)) { }

public BlockExecutionContext(BlockHeader blockHeader, IReleaseSpec spec, in UInt256 blobBaseFee)
: this(blockHeader, spec, blobBaseFee, GetDefaultPrevRandao(blockHeader)) { }

public readonly bool IsGenesis = blockHeader.IsGenesis;
public static BlockExecutionContext WithPrevRandao(
BlockHeader blockHeader,
IReleaseSpec spec,
in ValueHash256 prevRandao)
=> new(blockHeader, spec, GetBlobBaseFee(blockHeader, spec), prevRandao);

public BlockExecutionContext(BlockHeader blockHeader, IReleaseSpec spec) : this(blockHeader, spec, GetBlobBaseFee(blockHeader, spec))
private BlockExecutionContext(
BlockHeader blockHeader,
IReleaseSpec spec,
in UInt256 blobBaseFee,
in ValueHash256 prevRandao)
{
Header = blockHeader;
Coinbase = blockHeader.GasBeneficiary ?? Address.Zero;
Number = (ulong)blockHeader.Number;
GasLimit = (ulong)blockHeader.GasLimit;
BlobBaseFee = blobBaseFee.ToValueHash();
Spec = spec;
PrevRandao = prevRandao;
IsGenesis = blockHeader.IsGenesis;
}

private static ValueHash256 GetDefaultPrevRandao(BlockHeader blockHeader) => blockHeader.IsPostMerge
? (blockHeader.Random ?? Hash256.Zero).ValueHash256
: blockHeader.Difficulty.ToValueHash();

private static UInt256 GetBlobBaseFee(BlockHeader? blockHeader, IReleaseSpec spec) =>
blockHeader?.ExcessBlobGas is not null
? !BlobGasCalculator.TryCalculateFeePerBlobGas(blockHeader.ExcessBlobGas.Value, spec.BlobBaseFeeUpdateFraction, out UInt256 feePerBlobGas)
Expand Down
75 changes: 75 additions & 0 deletions src/Nethermind/Nethermind.Xdc.Test/XdcBlockProcessorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using FluentAssertions;
using Nethermind.Blockchain.BeaconBlockRoot;
using Nethermind.Blockchain.Blocks;
using Nethermind.Blockchain.Receipts;
using Nethermind.Consensus.ExecutionRequests;
using Nethermind.Consensus.Processing;
using Nethermind.Consensus.Rewards;
using Nethermind.Consensus.Validators;
using Nethermind.Consensus.Withdrawals;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Evm;
using Nethermind.Evm.State;
using Nethermind.Logging;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.Xdc.Test;

[TestFixture]
[Parallelizable(ParallelScope.All)]
internal class XdcBlockProcessorTests
{
private TestableXdcBlockProcessor _processor = null!;

[SetUp]
public void SetUp()
{
_processor = new TestableXdcBlockProcessor();
}

[
TestCase(0L, "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"),
TestCase(1L, "0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"),
TestCase(2L, "0xf2ee15ea639b73fa3db9b34a245bdfa015c260c598b211bf05a1ecc4b3e3b4f2"),
TestCase(100L, "0xf1918e8562236eb17adc8502332f4c9c82bc14e19bfc0aa10ab674ff75b3d2f3"),
TestCase(10_000_000L, "0xcb2bea23225e166d181633a5500bb13f45947baf4699fe7d7be7709662ed2cd4"),
TestCase(50_000_000L, "0xcfa4f717b73afc03b1397871f456c85553ea93c536860d57572d2f2b6947e962"),
TestCase(999_999_999L, "0xecfe0cf238d62038a8de7039c5c1700a26581c9c3c61e6352085728207948d1f"),
]
public void CreateBlockExecutionContext_PrevRandao_MatchesGoXdcRandomValue(long blockNumber, string expectedPrevRandaoHex)
{
BlockHeader header = Build.A.BlockHeader.WithNumber(blockNumber).TestObject;
IReleaseSpec spec = Substitute.For<IReleaseSpec>();

BlockExecutionContext ctx = _processor.CreateBlockExecutionContext(header, spec);

ctx.PrevRandao.Should().Be(new ValueHash256(expectedPrevRandaoHex));
}

private class TestableXdcBlockProcessor : XdcBlockProcessor
{
public TestableXdcBlockProcessor() : base(
Substitute.For<ISpecProvider>(),
Substitute.For<IBlockValidator>(),
Substitute.For<IRewardCalculator>(),
Substitute.For<IBlockProcessor.IBlockTransactionsExecutor>(),
Substitute.For<IWorldState>(),
Substitute.For<IReceiptStorage>(),
Substitute.For<IBeaconBlockRootHandler>(),
Substitute.For<IBlockhashStore>(),
NullLogManager.Instance,
Substitute.For<IWithdrawalProcessor>(),
Substitute.For<IExecutionRequestsProcessor>())
{ }

public new BlockExecutionContext CreateBlockExecutionContext(BlockHeader header, IReleaseSpec spec)
=> base.CreateBlockExecutionContext(header, spec);
}
}
8 changes: 8 additions & 0 deletions src/Nethermind/Nethermind.Xdc/XdcBlockProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
using Nethermind.Consensus.Validators;
using Nethermind.Consensus.Withdrawals;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.State;
using Nethermind.Logging;

Expand All @@ -22,6 +25,11 @@ public XdcBlockProcessor(ISpecProvider specProvider, IBlockValidator blockValida
{
}

// Match Go's big.Int.Bytes() behavior: zero produces empty bytes, not [0x00].
protected override BlockExecutionContext CreateBlockExecutionContext(BlockHeader header, IReleaseSpec spec) =>
BlockExecutionContext.WithPrevRandao(header, spec,
ValueKeccak.Compute(header.Number != 0 ? header.Number.ToBigEndianSpanWithoutLeadingZeros(out _) : default));

protected override Block PrepareBlockForProcessing(Block suggestedBlock)
{
//TODO find a better way to do this copy
Expand Down
Loading