From bee2c3883b3c28aadd3a990aa4a810a7888e9d90 Mon Sep 17 00:00:00 2001 From: DavePearce Date: Fri, 17 Oct 2025 10:23:15 +1300 Subject: [PATCH 1/2] Encode/decode parentBeaconBlockRoot as hex string This changes the type of the field parentBeaconBlockRoot in BlockHeaderSnapshot to be an (optional) String instead of a Bytes32 instance. This is to avoid decoding issues because the GSONDecoder is not aware of the Bytes32 type. --- .../blockcapture/snapshots/BlockHeaderSnapshot.java | 9 ++++----- .../net/consensys/linea/zktracer/ChainConfig.java | 12 ++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java b/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java index ae722bc214..49387cf196 100644 --- a/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java +++ b/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java @@ -46,7 +46,7 @@ public record BlockHeaderSnapshot( String mixHashOrPrevRandao, long nonce, Optional baseFee, - Bytes32 parentBeaconBlockRoot) { + Optional parentBeaconBlockRoot) { public static BlockHeaderSnapshot from(BlockHeader header) { return new BlockHeaderSnapshot( header.getParentHash().toHexString(), @@ -65,7 +65,7 @@ public static BlockHeaderSnapshot from(BlockHeader header) { header.getMixHashOrPrevRandao().toHexString(), header.getNonce(), header.getBaseFee().map(Quantity::toHexString), - header.getParentBeaconBlockRoot().orElse(null)); + header.getParentBeaconBlockRoot().map(Bytes::toHexString)); } public BlockHeader toBlockHeader() { @@ -87,11 +87,10 @@ public BlockHeader toBlockHeader() { .mixHash(Hash.fromHexString(this.mixHashOrPrevRandao)) .prevRandao(Bytes32.fromHexString(this.mixHashOrPrevRandao)) .nonce(this.nonce) - .blockHeaderFunctions(new CliqueBlockHeaderFunctions()) - .parentBeaconBlockRoot(parentBeaconBlockRoot); + .blockHeaderFunctions(new CliqueBlockHeaderFunctions()); this.baseFee.ifPresent(baseFee -> builder.baseFee(Wei.fromHexString(baseFee))); - + this.parentBeaconBlockRoot.ifPresent(root -> builder.parentBeaconBlockRoot(Bytes32.fromHexString(root))); return builder.buildBlockHeader(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java index fac6a3253e..1b917fc731 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java @@ -15,6 +15,7 @@ package net.consensys.linea.zktracer; import static net.consensys.linea.zktracer.Fork.LONDON; +import static net.consensys.linea.zktracer.Fork.PRAGUE; import static net.consensys.linea.zktracer.Trace.ETHEREUM_GAS_LIMIT_MAXIMUM; import static net.consensys.linea.zktracer.Trace.ETHEREUM_GAS_LIMIT_MINIMUM; import static net.consensys.linea.zktracer.Trace.LINEA_CHAIN_ID; @@ -36,6 +37,7 @@ public class ChainConfig { * billion). As the name suggest, this is only intended for testing purposes. */ public static final ChainConfig MAINNET_LONDON_TESTCONFIG = MAINNET_TESTCONFIG(LONDON); + public static final ChainConfig SEPOLIA_PRAGUE_TESTCONFIG = SEPOLIA_TESTCONFIG(PRAGUE); public static final ChainConfig MAINNET_TESTCONFIG(final Fork fork) { return new ChainConfig( @@ -47,6 +49,16 @@ public static final ChainConfig MAINNET_TESTCONFIG(final Fork fork) { LineaL1L2BridgeSharedConfiguration.TEST_DEFAULT); } + public static final ChainConfig SEPOLIA_TESTCONFIG(final Fork fork) { + return new ChainConfig( + fork, + LINEA_SEPOLIA_CHAIN_ID, + true, + BigInteger.valueOf(LINEA_GAS_LIMIT_MINIMUM), + BigInteger.valueOf(LINEA_GAS_LIMIT_MAXIMUM), + LineaL1L2BridgeSharedConfiguration.TEST_DEFAULT); + } + /** * Represents Linea mainnet prior to the block gas limit being enforced for the purposes of * running existing replay tests. As the name suggest, this is only intended for testing purposes. From add5f6a59c28b42686bb0a6eef1f15adbc03d63f Mon Sep 17 00:00:00 2001 From: DavePearce Date: Fri, 17 Oct 2025 11:23:48 +1300 Subject: [PATCH 2/2] don't load clique headers from Shanghai onwards The clique consensus protocol was only used prior to shanghai, hence we cannot extract key information from block headers generated from Shanghai nodes and onwards. --- .../snapshots/BlockHeaderSnapshot.java | 3 ++- .../net/consensys/linea/zktracer/ChainConfig.java | 1 + .../linea/testing/ReplayExecutionEnvironment.java | 14 +++++++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java b/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java index 49387cf196..45c472bd8e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java +++ b/arithmetization/src/main/java/net/consensys/linea/blockcapture/snapshots/BlockHeaderSnapshot.java @@ -90,7 +90,8 @@ public BlockHeader toBlockHeader() { .blockHeaderFunctions(new CliqueBlockHeaderFunctions()); this.baseFee.ifPresent(baseFee -> builder.baseFee(Wei.fromHexString(baseFee))); - this.parentBeaconBlockRoot.ifPresent(root -> builder.parentBeaconBlockRoot(Bytes32.fromHexString(root))); + this.parentBeaconBlockRoot.ifPresent( + root -> builder.parentBeaconBlockRoot(Bytes32.fromHexString(root))); return builder.buildBlockHeader(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java index 1b917fc731..0480f3e7e2 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/ChainConfig.java @@ -37,6 +37,7 @@ public class ChainConfig { * billion). As the name suggest, this is only intended for testing purposes. */ public static final ChainConfig MAINNET_LONDON_TESTCONFIG = MAINNET_TESTCONFIG(LONDON); + public static final ChainConfig SEPOLIA_PRAGUE_TESTCONFIG = SEPOLIA_TESTCONFIG(PRAGUE); public static final ChainConfig MAINNET_TESTCONFIG(final Fork fork) { diff --git a/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java b/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java index 5ef2a139f7..b12f27fab3 100644 --- a/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java +++ b/testing/src/main/java/net/consensys/linea/testing/ReplayExecutionEnvironment.java @@ -41,6 +41,7 @@ import net.consensys.linea.corset.CorsetValidator; import net.consensys.linea.zktracer.ChainConfig; import net.consensys.linea.zktracer.ConflationAwareOperationTracer; +import net.consensys.linea.zktracer.Fork; import net.consensys.linea.zktracer.ZkTracer; import net.consensys.linea.zktracer.module.hub.Hub; import org.apache.commons.io.FileUtils; @@ -288,10 +289,12 @@ private static void executeFrom( new BlockBody( blockSnapshot.txs().stream().map(TransactionSnapshot::toTransaction).toList(), new ArrayList<>()); + // Determine mining beneficiay final Address miningBeneficiary = useCoinbaseAddressFromBlockHeader ? header.getCoinbase() - : CliqueHelpers.getProposerOfBlock(header); + : determineMiningBeneficiary(header, chain.fork); + tracer.traceStartBlock(world.updater(), header, body, miningBeneficiary); runSystemInitialTransactions(protocolSpec, chain.fork, world, header, tracer); @@ -321,6 +324,15 @@ public Hub getHub() { return zkTracer.getHub(); } + private static Address determineMiningBeneficiary(BlockHeader header, Fork fork) { + // Clique was only used on forks prior to Shanghai + if (Fork.isPostShanghai(fork)) { + return header.getCoinbase(); + } else { + return CliqueHelpers.getProposerOfBlock(header); + } + } + /** * Initialise a fresh world state from a conflation. *