Support timestamp forks and implement shanghaiTime (#4743)

Implement shanghaiTime including TimestampSchedule and associated infrastructure code.
TimestampSchedule sits alongside the pre and post ProtocolSchedules in TransitionProtocolSchedule.

Introduces getByTimestamp, wrapped inside getByBlockHeader (to also support getByBlockNumber).
General call pattern followed is that if a given timestamp precedes the first timestamp in the schedule, i.e. a pre-shanghai block, then delegate to the appropriate pre or post merge ProtocolSchedule to get by block instead.

cancunTime and a placeholder cancunDefinition has also been implemented in order to effectively test fork order logic.

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Jiri Peinlich <jiri.peinlich@gmail.com>
Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Jason Frame <jason.frame@consensys.net>
pull/4812/head
Jiri Peinlich 2 years ago committed by GitHub
parent 2b9956a328
commit 3fd0681e3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 6
      besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java
  3. 26
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  4. 16
      besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java
  5. 4
      config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java
  6. 12
      config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
  7. 24
      config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java
  8. 12
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java
  9. 2
      config/src/test/resources/all_forks.json
  10. 21
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/MergeProtocolSchedule.java
  11. 4
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionBackwardSyncContext.java
  12. 89
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java
  13. 11
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionUtils.java
  14. 2
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinator.java
  15. 3
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/TransitionCoordinator.java
  16. 82
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/TransitionProtocolScheduleTest.java
  17. 2
      ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockCreator.java
  18. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java
  19. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/ProcessableBlockHeader.java
  20. 192
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractProtocolScheduleBuilder.java
  21. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BaseFeeBlockBodyValidator.java
  22. 105
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultTimestampSchedule.java
  23. 34
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/HeaderBasedProtocolSchedule.java
  24. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockBodyValidator.java
  25. 22
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecFactory.java
  26. 40
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  27. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MutableProtocolSchedule.java
  28. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java
  29. 29
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacySupportingProtocolSchedule.java
  30. 18
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSchedule.java
  31. 305
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java
  32. 9
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecAdapters.java
  33. 113
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java
  34. 32
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TimestampSchedule.java
  35. 81
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TimestampScheduleBuilder.java
  36. 8
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRehydration.java
  37. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BaseFeeBlockBodyValidatorTest.java
  38. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java
  39. 66
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java
  40. 20
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleTest.java
  41. 141
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/TimestampScheduleBuilderTest.java
  42. 5
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestProtocolSchedules.java
  43. 10
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/NoRewardProtocolScheduleWrapper.java
  44. 2
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/methods/TestSetChainParams.java

@ -3,11 +3,10 @@
### Additions and Improvements
- Add access list to Transaction Call Object [#4802](https://github.com/hyperledger/besu/issues/4801)
- Add timestamp fork support, including shanghaiTime and cancunTime forks [#4743](https://github.com/hyperledger/besu/pull/4743)
### Breaking Changes
### Additions and Improvements
### Bug Fixes
### Download Links

@ -43,6 +43,7 @@ import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -233,4 +234,9 @@ public class MergeBesuControllerBuilder extends BesuControllerBuilder {
}
return retval;
}
public TimestampSchedule createTimestampProtocolSchedule() {
return MergeProtocolSchedule.createTimestamp(
configOptionsSupplier.get(), privacyParameters, isRevertReasonEnabled);
}
}

@ -26,10 +26,12 @@ import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ConsensusContext;
import org.hyperledger.besu.ethereum.ConsensusContextFactory;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer;
@ -75,6 +77,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
private final MergeBesuControllerBuilder mergeBesuControllerBuilder;
private static final Logger LOG = LoggerFactory.getLogger(TransitionBesuControllerBuilder.class);
private TransitionProtocolSchedule transitionProtocolSchedule;
public TransitionBesuControllerBuilder(
final BesuControllerBuilder preMergeBesuControllerBuilder,
@ -165,9 +168,26 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
@Override
protected ProtocolSchedule createProtocolSchedule() {
return new TransitionProtocolSchedule(
preMergeBesuControllerBuilder.createProtocolSchedule(),
mergeBesuControllerBuilder.createProtocolSchedule());
transitionProtocolSchedule =
new TransitionProtocolSchedule(
preMergeBesuControllerBuilder.createProtocolSchedule(),
mergeBesuControllerBuilder.createProtocolSchedule(),
PostMergeContext.get(),
mergeBesuControllerBuilder.createTimestampProtocolSchedule());
return transitionProtocolSchedule;
}
@Override
protected ProtocolContext createProtocolContext(
final MutableBlockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule,
final ConsensusContextFactory consensusContextFactory) {
final ProtocolContext protocolContext =
super.createProtocolContext(
blockchain, worldStateArchive, protocolSchedule, consensusContextFactory);
transitionProtocolSchedule.setProtocolContext(protocolContext);
return protocolContext;
}
@Override

@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@ -66,6 +67,7 @@ public class TransitionControllerBuilderTest {
@Mock ProtocolSchedule preMergeProtocolSchedule;
@Mock ProtocolSchedule postMergeProtocolSchedule;
@Mock TimestampSchedule timestampSchedule;
@Mock ProtocolContext protocolContext;
@Mock MutableBlockchain mockBlockchain;
@Mock TransactionPool transactionPool;
@ -86,7 +88,11 @@ public class TransitionControllerBuilderTest {
transitionProtocolSchedule =
spy(
new TransitionProtocolSchedule(
preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext));
preMergeProtocolSchedule,
postMergeProtocolSchedule,
mergeContext,
timestampSchedule));
transitionProtocolSchedule.setProtocolContext(protocolContext);
cliqueBuilder.nodeKey(NodeKeyUtils.generate());
postMergeBuilder.storageProvider(storageProvider);
when(protocolContext.getBlockchain()).thenReturn(mockBlockchain);
@ -126,7 +132,7 @@ public class TransitionControllerBuilderTest {
when(mergeContext.isPostMerge()).thenReturn(Boolean.FALSE);
when(mergeContext.getFinalized()).thenReturn(Optional.empty());
when(preMergeProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(preMergeProtocolSpec);
assertThat(transitionProtocolSchedule.getByBlockHeader(protocolContext, mockBlock))
assertThat(transitionProtocolSchedule.getByBlockHeader(mockBlock))
.isEqualTo(preMergeProtocolSpec);
}
@ -136,7 +142,7 @@ public class TransitionControllerBuilderTest {
var postMergeProtocolSpec = mock(ProtocolSpec.class);
when(mergeContext.getFinalized()).thenReturn(Optional.of(mockBlock));
when(postMergeProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(postMergeProtocolSpec);
assertThat(transitionProtocolSchedule.getByBlockHeader(protocolContext, mockBlock))
assertThat(transitionProtocolSchedule.getByBlockHeader(mockBlock))
.isEqualTo(postMergeProtocolSpec);
}
@ -159,7 +165,7 @@ public class TransitionControllerBuilderTest {
.thenReturn(Optional.of(Difficulty.of(1335L)));
when(preMergeProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(preMergeProtocolSpec);
assertThat(transitionProtocolSchedule.getByBlockHeader(protocolContext, mockBlock))
assertThat(transitionProtocolSchedule.getByBlockHeader(mockBlock))
.isEqualTo(preMergeProtocolSpec);
}
@ -182,7 +188,7 @@ public class TransitionControllerBuilderTest {
.thenReturn(Optional.of(Difficulty.of(1337L)));
when(postMergeProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(postMergeProtocolSpec);
assertThat(transitionProtocolSchedule.getByBlockHeader(protocolContext, mockBlock))
assertThat(transitionProtocolSchedule.getByBlockHeader(mockBlock))
.isEqualTo(postMergeProtocolSpec);
}

@ -90,6 +90,10 @@ public interface GenesisConfigOptions {
OptionalLong getMergeNetSplitBlockNumber();
OptionalLong getShanghaiTime();
OptionalLong getCancunTime();
OptionalLong getShandongBlockNumber();
Optional<Wei> getBaseFeePerGas();

@ -278,6 +278,16 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
return getOptionalLong("mergenetsplitblock");
}
@Override
public OptionalLong getShanghaiTime() {
return getOptionalLong("shanghaitime");
}
@Override
public OptionalLong getCancunTime() {
return getOptionalLong("cancuntime");
}
@Override
public OptionalLong getShandongBlockNumber() {
return getOptionalLong("shandongblock");
@ -433,6 +443,8 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
getArrowGlacierBlockNumber().ifPresent(l -> builder.put("arrowGlacierBlock", l));
getGrayGlacierBlockNumber().ifPresent(l -> builder.put("grayGlacierBlock", l));
getMergeNetSplitBlockNumber().ifPresent(l -> builder.put("mergeNetSplitBlock", l));
getShanghaiTime().ifPresent(l -> builder.put("shanghaiTime", l));
getCancunTime().ifPresent(l -> builder.put("cancunTime", l));
getShandongBlockNumber().ifPresent(l -> builder.put("shandongBlock", l));
getTerminalBlockNumber().ifPresent(l -> builder.put("terminalBlockNumber", l));
getTerminalBlockHash().ifPresent(h -> builder.put("terminalBlockHash", h.toHexString()));

@ -44,6 +44,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
private OptionalLong arrowGlacierBlockNumber = OptionalLong.empty();
private OptionalLong grayGlacierBlockNumber = OptionalLong.empty();
private OptionalLong mergeNetSplitBlockNumber = OptionalLong.empty();
private OptionalLong shanghaiTime = OptionalLong.empty();
private OptionalLong cancunTime = OptionalLong.empty();
private OptionalLong shandongBlockNumber = OptionalLong.empty();
private OptionalLong terminalBlockNumber = OptionalLong.empty();
private Optional<Hash> terminalBlockHash = Optional.empty();
@ -217,6 +219,16 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
return mergeNetSplitBlockNumber;
}
@Override
public OptionalLong getShanghaiTime() {
return shanghaiTime;
}
@Override
public OptionalLong getCancunTime() {
return cancunTime;
}
@Override
public OptionalLong getShandongBlockNumber() {
return shandongBlockNumber;
@ -342,6 +354,8 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
getArrowGlacierBlockNumber().ifPresent(l -> builder.put("arrowGlacierBlock", l));
getGrayGlacierBlockNumber().ifPresent(l -> builder.put("grayGlacierBlock", l));
getMergeNetSplitBlockNumber().ifPresent(l -> builder.put("mergeNetSplitBlock", l));
getShanghaiTime().ifPresent(l -> builder.put("shanghaiTime", l));
getCancunTime().ifPresent(l -> builder.put("cancunTime", l));
getShandongBlockNumber().ifPresent(l -> builder.put("shandongBlock", l));
getTerminalBlockNumber().ifPresent(l -> builder.put("terminalBlockNumber", l));
getTerminalBlockHash().ifPresent(h -> builder.put("terminalBlockHash", h));
@ -486,6 +500,16 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions {
return this;
}
public StubGenesisConfigOptions shanghaiTime(final long timestamp) {
shanghaiTime = OptionalLong.of(timestamp);
return this;
}
public StubGenesisConfigOptions cancunTime(final long timestamp) {
cancunTime = OptionalLong.of(timestamp);
return this;
}
public StubGenesisConfigOptions shandongBlock(final long blockNumber) {
shandongBlockNumber = OptionalLong.of(blockNumber);
return this;

@ -197,6 +197,18 @@ public class GenesisConfigOptionsTest {
assertThat(config.getGrayGlacierBlockNumber()).hasValue(4242);
}
@Test
public void shouldGetShanghaiTime() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("shanghaiTime", 1670470141));
assertThat(config.getShanghaiTime()).hasValue(1670470141);
}
@Test
public void shouldGetCancunTime() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("cancunTime", 1670470142));
assertThat(config.getCancunTime()).hasValue(1670470142);
}
@Test
public void shouldGetShandongBlockNumber() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("shandongBlock", 1337));

@ -14,6 +14,8 @@
"arrowGlacierBlock": 12,
"grayGlacierBlock": 13,
"mergeNetSplitBlock": 14,
"shanghaiTime": 15,
"cancunTime": 16,
"ecip1015Block": 102,
"dieHardBlock": 103,
"gothamBlock": 104,

@ -22,6 +22,8 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;
import org.hyperledger.besu.ethereum.mainnet.TimestampScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
@ -58,6 +60,25 @@ public class MergeProtocolSchedule {
.createProtocolSchedule();
}
public static TimestampSchedule createTimestamp(
final GenesisConfigOptions config,
final PrivacyParameters privacyParameters,
final boolean isRevertReasonEnabled) {
return new TimestampScheduleBuilder(
config,
DEFAULT_CHAIN_ID,
ProtocolSpecAdapters.create(
config.getShanghaiTime().orElse(0),
(specBuilder) ->
MergeProtocolSchedule.applyMergeSpecificModifications(
specBuilder, config.getChainId())),
privacyParameters,
isRevertReasonEnabled,
config.isQuorum(),
EvmConfiguration.DEFAULT)
.createTimestampSchedule();
}
private static ProtocolSpecBuilder applyMergeSpecificModifications(
final ProtocolSpecBuilder specBuilder, final Optional<BigInteger> chainId) {

@ -53,8 +53,6 @@ public class TransitionBackwardSyncContext extends BackwardSyncContext {
*/
@Override
public BlockValidator getBlockValidatorForBlock(final Block block) {
return transitionProtocolSchedule
.getByBlockHeader(protocolContext, block.getHeader())
.getBlockValidator();
return transitionProtocolSchedule.getByBlockHeader(block.getHeader()).getBlockValidator();
}
}

@ -17,12 +17,14 @@ package org.hyperledger.besu.consensus.merge;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.debugLambda;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.mainnet.HeaderBasedProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.math.BigInteger;
@ -32,33 +34,41 @@ import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransitionProtocolSchedule extends TransitionUtils<ProtocolSchedule>
implements ProtocolSchedule {
public class TransitionProtocolSchedule implements ProtocolSchedule {
private final TransitionUtils<ProtocolSchedule> transitionUtils;
private static final Logger LOG = LoggerFactory.getLogger(TransitionProtocolSchedule.class);
public TransitionProtocolSchedule(
final ProtocolSchedule preMergeProtocolSchedule,
final ProtocolSchedule postMergeProtocolSchedule) {
super(preMergeProtocolSchedule, postMergeProtocolSchedule);
}
private final TimestampSchedule timestampSchedule;
private final MergeContext mergeContext;
private ProtocolContext protocolContext;
public TransitionProtocolSchedule(
final ProtocolSchedule preMergeProtocolSchedule,
final ProtocolSchedule postMergeProtocolSchedule,
final MergeContext mergeContext) {
super(preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext);
final MergeContext mergeContext,
final TimestampSchedule timestampSchedule) {
this.timestampSchedule = timestampSchedule;
this.mergeContext = mergeContext;
transitionUtils =
new TransitionUtils<>(preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext);
}
public ProtocolSchedule getPreMergeSchedule() {
return getPreMergeObject();
return transitionUtils.getPreMergeObject();
}
public ProtocolSchedule getPostMergeSchedule() {
return getPostMergeObject();
return transitionUtils.getPostMergeObject();
}
public ProtocolSpec getByBlockHeader(
final ProtocolContext protocolContext, final BlockHeader blockHeader) {
@Override
public ProtocolSpec getByBlockHeader(final ProcessableBlockHeader blockHeader) {
return this.timestampSchedule
.getByTimestamp(blockHeader.getTimestamp())
.orElseGet(() -> getByBlockHeaderFromTransitionUtils(blockHeader));
}
private ProtocolSpec getByBlockHeaderFromTransitionUtils(
final ProcessableBlockHeader blockHeader) {
// if we do not have a finalized block we might return pre or post merge protocol schedule:
if (mergeContext.getFinalized().isEmpty()) {
@ -72,9 +82,11 @@ public class TransitionProtocolSchedule extends TransitionUtils<ProtocolSchedule
}
// otherwise check to see if this block represents a re-org TTD block:
MutableBlockchain blockchain = protocolContext.getBlockchain();
Difficulty parentDifficulty =
blockchain.getTotalDifficultyByHash(blockHeader.getParentHash()).orElseThrow();
protocolContext
.getBlockchain()
.getTotalDifficultyByHash(blockHeader.getParentHash())
.orElseThrow();
Difficulty thisDifficulty = parentDifficulty.add(blockHeader.getDifficulty());
Difficulty terminalDifficulty = mergeContext.getTerminalTotalDifficulty();
debugLambda(
@ -102,32 +114,61 @@ public class TransitionProtocolSchedule extends TransitionUtils<ProtocolSchedule
@Override
public ProtocolSpec getByBlockNumber(final long number) {
return dispatchFunctionAccordingToMergeState(
protocolSchedule -> protocolSchedule.getByBlockNumber(number));
return Optional.ofNullable(protocolContext)
.map(ProtocolContext::getBlockchain)
.flatMap(blockchain -> blockchain.getBlockByNumber(number))
.map(Block::getHeader)
.map(timestampSchedule::getByBlockHeader)
.orElseGet(
() ->
transitionUtils.dispatchFunctionAccordingToMergeState(
protocolSchedule -> protocolSchedule.getByBlockNumber(number)));
}
@Override
public Stream<Long> streamMilestoneBlocks() {
return dispatchFunctionAccordingToMergeState(ProtocolSchedule::streamMilestoneBlocks);
return transitionUtils.dispatchFunctionAccordingToMergeState(
ProtocolSchedule::streamMilestoneBlocks);
}
@Override
public Optional<BigInteger> getChainId() {
return dispatchFunctionAccordingToMergeState(ProtocolSchedule::getChainId);
return transitionUtils.dispatchFunctionAccordingToMergeState(ProtocolSchedule::getChainId);
}
@Override
public void putMilestone(final long blockOrTimestamp, final ProtocolSpec protocolSpec) {
throw new UnsupportedOperationException(
"Should not use TransitionProtocolSchedule wrapper class to create milestones");
}
@Override
public String listMilestones() {
String blockNumberMilestones =
transitionUtils.dispatchFunctionAccordingToMergeState(
HeaderBasedProtocolSchedule::listMilestones);
return blockNumberMilestones + ";" + timestampSchedule.listMilestones();
}
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
dispatchConsumerAccordingToMergeState(
timestampSchedule.setTransactionFilter(transactionFilter);
transitionUtils.dispatchConsumerAccordingToMergeState(
protocolSchedule -> protocolSchedule.setTransactionFilter(transactionFilter));
}
@Override
public void setPublicWorldStateArchiveForPrivacyBlockProcessor(
final WorldStateArchive publicWorldStateArchive) {
dispatchConsumerAccordingToMergeState(
timestampSchedule.setPublicWorldStateArchiveForPrivacyBlockProcessor(publicWorldStateArchive);
transitionUtils.dispatchConsumerAccordingToMergeState(
protocolSchedule ->
protocolSchedule.setPublicWorldStateArchiveForPrivacyBlockProcessor(
publicWorldStateArchive));
}
public void setProtocolContext(final ProtocolContext protocolContext) {
this.protocolContext = protocolContext;
}
}

@ -17,8 +17,8 @@ package org.hyperledger.besu.consensus.merge;
import static org.hyperledger.besu.util.Slf4jLambdaHelper.warnLambda;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import java.util.Optional;
import java.util.function.Consumer;
@ -27,18 +27,13 @@ import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class TransitionUtils<SwitchingObject> {
public class TransitionUtils<SwitchingObject> {
private static final Logger LOG = LoggerFactory.getLogger(TransitionUtils.class);
protected final MergeContext mergeContext;
private final SwitchingObject preMergeObject;
private final SwitchingObject postMergeObject;
public TransitionUtils(
final SwitchingObject preMergeObject, final SwitchingObject postMergeObject) {
this(preMergeObject, postMergeObject, PostMergeContext.get());
}
public TransitionUtils(
final SwitchingObject preMergeObject,
final SwitchingObject postMergeObject,
@ -66,7 +61,7 @@ public abstract class TransitionUtils<SwitchingObject> {
}
public static boolean isTerminalProofOfWorkBlock(
final BlockHeader header, final ProtocolContext context) {
final ProcessableBlockHeader header, final ProtocolContext context) {
Difficulty headerDifficulty =
Optional.ofNullable(header.getDifficulty()).orElse(Difficulty.ZERO);

@ -435,7 +435,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
public BlockProcessingResult validateBlock(final Block block) {
final var validationResult =
protocolSchedule
.getByBlockNumber(block.getHeader().getNumber())
.getByBlockHeader(block.getHeader())
.getBlockValidator()
.validateAndProcessBlock(
protocolContext,

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.consensus.merge.blockcreation;
import org.hyperledger.besu.consensus.merge.PostMergeContext;
import org.hyperledger.besu.consensus.merge.TransitionUtils;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
@ -41,7 +42,7 @@ public class TransitionCoordinator extends TransitionUtils<MiningCoordinator>
public TransitionCoordinator(
final MiningCoordinator miningCoordinator, final MiningCoordinator mergeCoordinator) {
super(miningCoordinator, mergeCoordinator);
super(miningCoordinator, mergeCoordinator, PostMergeContext.get());
this.miningCoordinator = miningCoordinator;
this.mergeCoordinator = (MergeMiningCoordinator) mergeCoordinator;
}

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.consensus.merge;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@ -22,9 +23,13 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.mainnet.TimestampSchedule;
import java.util.Optional;
@ -42,10 +47,13 @@ public class TransitionProtocolScheduleTest {
@Mock MergeContext mergeContext;
@Mock ProtocolSchedule preMergeProtocolSchedule;
@Mock ProtocolSchedule postMergeProtocolSchedule;
@Mock TimestampSchedule timestampSchedule;
@Mock BlockHeader blockHeader;
private static final Difficulty TTD = Difficulty.of(100L);
private static final long BLOCK_NUMBER = 29L;
private static final long TIMESTAMP = 1L;
private TransitionProtocolSchedule transitionProtocolSchedule;
@Before
@ -53,10 +61,13 @@ public class TransitionProtocolScheduleTest {
when(protocolContext.getBlockchain()).thenReturn(blockchain);
when(protocolContext.getConsensusContext(MergeContext.class)).thenReturn(mergeContext);
when(mergeContext.getTerminalTotalDifficulty()).thenReturn(TTD);
when(blockHeader.getTimestamp()).thenReturn(TIMESTAMP);
when(timestampSchedule.getByTimestamp(TIMESTAMP)).thenReturn(Optional.empty());
transitionProtocolSchedule =
new TransitionProtocolSchedule(
preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext);
preMergeProtocolSchedule, postMergeProtocolSchedule, mergeContext, timestampSchedule);
transitionProtocolSchedule.setProtocolContext(protocolContext);
}
@Test
@ -64,7 +75,7 @@ public class TransitionProtocolScheduleTest {
when(mergeContext.getFinalized()).thenReturn(Optional.of(mock(BlockHeader.class)));
when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER);
transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader);
transitionProtocolSchedule.getByBlockHeader(blockHeader);
verifyPostMergeProtocolScheduleReturned();
}
@ -76,7 +87,7 @@ public class TransitionProtocolScheduleTest {
when(blockHeader.getNumber()).thenReturn(BLOCK_NUMBER);
transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader);
transitionProtocolSchedule.getByBlockHeader(blockHeader);
verifyPreMergeProtocolScheduleReturned();
}
@ -95,7 +106,7 @@ public class TransitionProtocolScheduleTest {
when(blockchain.getTotalDifficultyByHash(parentHash))
.thenReturn(Optional.of(Difficulty.of(95L)));
transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader);
transitionProtocolSchedule.getByBlockHeader(blockHeader);
verifyPreMergeProtocolScheduleReturned();
}
@ -114,7 +125,7 @@ public class TransitionProtocolScheduleTest {
when(blockchain.getTotalDifficultyByHash(parentHash))
.thenReturn(Optional.of(Difficulty.of(95L)));
transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader);
transitionProtocolSchedule.getByBlockHeader(blockHeader);
verifyPreMergeProtocolScheduleReturned();
}
@ -133,7 +144,61 @@ public class TransitionProtocolScheduleTest {
when(blockchain.getTotalDifficultyByHash(parentHash))
.thenReturn(Optional.of(Difficulty.of(105L)));
transitionProtocolSchedule.getByBlockHeader(protocolContext, blockHeader);
transitionProtocolSchedule.getByBlockHeader(blockHeader);
verifyPostMergeProtocolScheduleReturned();
}
@Test
public void getByBlockHeader_returnsTimestampScheduleIfPresent() {
when(timestampSchedule.getByTimestamp(TIMESTAMP))
.thenReturn(Optional.of(mock(ProtocolSpec.class)));
assertThat(transitionProtocolSchedule.getByBlockHeader(blockHeader)).isNotNull();
verify(timestampSchedule).getByTimestamp(TIMESTAMP);
verifyNoMergeScheduleInteractions();
}
@Test
public void getByBlockNumber_returnsTimestampScheduleIfPresent() {
final Block block = new Block(blockHeader, BlockBody.empty());
when(blockchain.getBlockByNumber(BLOCK_NUMBER)).thenReturn(Optional.of(block));
when(timestampSchedule.getByBlockHeader(blockHeader)).thenReturn(mock(ProtocolSpec.class));
assertThat(transitionProtocolSchedule.getByBlockNumber(BLOCK_NUMBER)).isNotNull();
verify(timestampSchedule).getByBlockHeader(blockHeader);
verifyNoMergeScheduleInteractions();
}
@Test
public void getByBlockNumber_delegatesToPreMergeScheduleWhenBlockNotFound() {
when(blockchain.getBlockByNumber(BLOCK_NUMBER)).thenReturn(Optional.empty());
when(mergeContext.isPostMerge()).thenReturn(false);
transitionProtocolSchedule.getByBlockNumber(BLOCK_NUMBER);
verifyPreMergeProtocolScheduleReturned();
}
@Test
public void getByBlockNumber_delegatesToPostMergeScheduleWhenBlockNotFound() {
when(blockchain.getBlockByNumber(BLOCK_NUMBER)).thenReturn(Optional.empty());
when(mergeContext.isPostMerge()).thenReturn(true);
transitionProtocolSchedule.getByBlockNumber(BLOCK_NUMBER);
verifyPostMergeProtocolScheduleReturned();
}
@Test
public void getByBlockNumber_delegatesToPostMergeScheduleWhenTimestampScheduleDoesNotExist() {
final Block block = new Block(blockHeader, BlockBody.empty());
when(blockchain.getBlockByNumber(BLOCK_NUMBER)).thenReturn(Optional.of(block));
when(mergeContext.isPostMerge()).thenReturn(true);
transitionProtocolSchedule.getByBlockNumber(BLOCK_NUMBER);
verifyPostMergeProtocolScheduleReturned();
}
@ -147,4 +212,9 @@ public class TransitionProtocolScheduleTest {
verify(postMergeProtocolSchedule).getByBlockNumber(BLOCK_NUMBER);
verifyNoInteractions(preMergeProtocolSchedule);
}
private void verifyNoMergeScheduleInteractions() {
verifyNoInteractions(preMergeProtocolSchedule);
verifyNoInteractions(postMergeProtocolSchedule);
}
}

@ -168,7 +168,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
throwIfStopped();
final ProtocolSpec newProtocolSpec =
protocolSchedule.getByBlockNumber(processableBlockHeader.getNumber());
protocolSchedule.getByBlockHeader(processableBlockHeader);
if (rewardCoinbase
&& !rewardBeneficiary(

@ -332,6 +332,7 @@ public class BlockHeader extends SealableBlockHeader
blockHeaderFunctions);
}
@Override
public String toLogString() {
return getNumber() + " (" + getHash() + ")";
}

@ -158,4 +158,8 @@ public class ProcessableBlockHeader implements BlockValues {
public Optional<Bytes32> getPrevRandao() {
return Optional.ofNullable(mixHashOrPrevRandao);
}
public String toLogString() {
return getNumber() + " (time: " + getTimestamp() + ")";
}
}

@ -0,0 +1,192 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractProtocolScheduleBuilder {
private static final Logger LOG = LoggerFactory.getLogger(AbstractProtocolScheduleBuilder.class);
protected final GenesisConfigOptions config;
protected final ProtocolSpecAdapters protocolSpecAdapters;
protected final PrivacyParameters privacyParameters;
protected final boolean isRevertReasonEnabled;
protected final BadBlockManager badBlockManager = new BadBlockManager();
protected final boolean quorumCompatibilityMode;
protected final EvmConfiguration evmConfiguration;
protected AbstractProtocolScheduleBuilder(
final GenesisConfigOptions config,
final ProtocolSpecAdapters protocolSpecAdapters,
final PrivacyParameters privacyParameters,
final boolean isRevertReasonEnabled,
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
this.config = config;
this.protocolSpecAdapters = protocolSpecAdapters;
this.privacyParameters = privacyParameters;
this.isRevertReasonEnabled = isRevertReasonEnabled;
this.quorumCompatibilityMode = quorumCompatibilityMode;
this.evmConfiguration = evmConfiguration;
}
protected void initSchedule(
final HeaderBasedProtocolSchedule protocolSchedule, final Optional<BigInteger> chainId) {
final MainnetProtocolSpecFactory specFactory =
new MainnetProtocolSpecFactory(
chainId,
config.getContractSizeLimit(),
config.getEvmStackSize(),
isRevertReasonEnabled,
quorumCompatibilityMode,
config.getEcip1017EraRounds(),
evmConfiguration);
validateForkOrdering();
final TreeMap<Long, BuilderMapEntry> builders = buildMilestoneMap(specFactory);
// At this stage, all milestones are flagged with correct modifier, but ProtocolSpecs must be
// inserted _AT_ the modifier block entry.
if (!builders.isEmpty()) {
protocolSpecAdapters.stream()
.forEach(
entry -> {
final long modifierBlock = entry.getKey();
final BuilderMapEntry parent =
Optional.ofNullable(builders.floorEntry(modifierBlock))
.orElse(builders.firstEntry())
.getValue();
builders.put(
modifierBlock,
new BuilderMapEntry(modifierBlock, parent.getBuilder(), entry.getValue()));
});
}
// Create the ProtocolSchedule, such that the Dao/fork milestones can be inserted
builders
.values()
.forEach(
e ->
addProtocolSpec(
protocolSchedule, e.getBlockIdentifier(), e.getBuilder(), e.modifier));
postBuildStep(specFactory);
LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones());
}
abstract void validateForkOrdering();
protected long validateForkOrder(
final String forkName, final OptionalLong thisForkBlock, final long lastForkBlock) {
final long referenceForkBlock = thisForkBlock.orElse(lastForkBlock);
if (lastForkBlock > referenceForkBlock) {
throw new RuntimeException(
String.format(
"Genesis Config Error: '%s' is scheduled for %s %d but it must be on or after %s %d.",
forkName,
getBlockIdentifierName(),
thisForkBlock.getAsLong(),
getBlockIdentifierName(),
lastForkBlock));
}
return referenceForkBlock;
}
abstract String getBlockIdentifierName();
private TreeMap<Long, BuilderMapEntry> buildMilestoneMap(
final MainnetProtocolSpecFactory specFactory) {
return createMilestones(specFactory)
.flatMap(Optional::stream)
.collect(
Collectors.toMap(
BuilderMapEntry::getBlockIdentifier,
b -> b,
(existing, replacement) -> replacement,
TreeMap::new));
}
abstract Stream<Optional<BuilderMapEntry>> createMilestones(
final MainnetProtocolSpecFactory specFactory);
protected Optional<BuilderMapEntry> create(
final OptionalLong blockIdentifier, final ProtocolSpecBuilder builder) {
if (blockIdentifier.isEmpty()) {
return Optional.empty();
}
final long blockVal = blockIdentifier.getAsLong();
return Optional.of(
new BuilderMapEntry(blockVal, builder, protocolSpecAdapters.getModifierForBlock(blockVal)));
}
protected void addProtocolSpec(
final HeaderBasedProtocolSchedule protocolSchedule,
final long blockNumberOrTimestamp,
final ProtocolSpecBuilder definition,
final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
definition
.badBlocksManager(badBlockManager)
.privacyParameters(privacyParameters)
.privateTransactionValidatorBuilder(
() -> new PrivateTransactionValidator(protocolSchedule.getChainId()));
protocolSchedule.putMilestone(
blockNumberOrTimestamp, modifier.apply(definition).build(protocolSchedule));
}
abstract void postBuildStep(final MainnetProtocolSpecFactory specFactory);
protected static class BuilderMapEntry {
private final long blockIdentifier;
private final ProtocolSpecBuilder builder;
private final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier;
public BuilderMapEntry(
final long blockIdentifier,
final ProtocolSpecBuilder builder,
final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
this.blockIdentifier = blockIdentifier;
this.builder = builder;
this.modifier = modifier;
}
public long getBlockIdentifier() {
return blockIdentifier;
}
public ProtocolSpecBuilder getBuilder() {
return builder;
}
}
}

@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
public class BaseFeeBlockBodyValidator extends MainnetBlockBodyValidator {
private static final Logger LOG = LoggerFactory.getLogger(BaseFeeBlockBodyValidator.class);
public BaseFeeBlockBodyValidator(final ProtocolSchedule protocolSchedule) {
public BaseFeeBlockBodyValidator(final HeaderBasedProtocolSchedule protocolSchedule) {
super(protocolSchedule);
}
@ -54,7 +54,7 @@ public class BaseFeeBlockBodyValidator extends MainnetBlockBodyValidator {
final List<Transaction> transactions = body.getTransactions();
final TransactionPriceCalculator transactionPriceCalculator =
protocolSchedule
.getByBlockNumber(block.getHeader().getNumber())
.getByBlockHeader(block.getHeader())
.getFeeMarket()
.getTransactionPriceCalculator();

@ -0,0 +1,105 @@
/*
*
* * Copyright Hyperledger Besu Contributors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* * the License. You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* * specific language governing permissions and limitations under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.math.BigInteger;
import java.util.Comparator;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;
public class DefaultTimestampSchedule implements TimestampSchedule {
private final NavigableSet<TimedProtocolSpec> protocolSpecs =
new TreeSet<>(Comparator.comparing(TimedProtocolSpec::getTimestamp).reversed());
private final Optional<BigInteger> chainId;
DefaultTimestampSchedule(final Optional<BigInteger> chainId) {
this.chainId = chainId;
}
@Override
public Optional<ProtocolSpec> getByTimestamp(final long timestamp) {
for (final TimedProtocolSpec protocolSpec : protocolSpecs) {
if (protocolSpec.getTimestamp() <= timestamp) {
return Optional.of(protocolSpec.getSpec());
}
}
return Optional.empty();
}
@Override
public Optional<BigInteger> getChainId() {
return chainId;
}
@Override
public void putMilestone(final long timestamp, final ProtocolSpec protocolSpec) {
final TimedProtocolSpec scheduledProtocolSpec = new TimedProtocolSpec(timestamp, protocolSpec);
// Ensure this replaces any existing spec at the same block number.
protocolSpecs.remove(scheduledProtocolSpec);
protocolSpecs.add(scheduledProtocolSpec);
}
@Override
public String listMilestones() {
return protocolSpecs.stream()
.sorted(Comparator.comparing(TimedProtocolSpec::getTimestamp))
.map(spec -> spec.getSpec().getName() + ": " + spec.getTimestamp())
.collect(Collectors.joining(", ", "[", "]"));
}
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
protocolSpecs.forEach(
spec -> spec.getSpec().getTransactionValidator().setTransactionFilter(transactionFilter));
}
@Override
public void setPublicWorldStateArchiveForPrivacyBlockProcessor(
final WorldStateArchive publicWorldStateArchive) {
protocolSpecs.forEach(
spec -> {
final BlockProcessor blockProcessor = spec.getSpec().getBlockProcessor();
if (PrivacyBlockProcessor.class.isAssignableFrom(blockProcessor.getClass()))
((PrivacyBlockProcessor) blockProcessor)
.setPublicWorldStateArchive(publicWorldStateArchive);
});
}
private static class TimedProtocolSpec {
private final long timestamp;
private final ProtocolSpec spec;
public TimedProtocolSpec(final long timestamp, final ProtocolSpec spec) {
this.timestamp = timestamp;
this.spec = spec;
}
public long getTimestamp() {
return timestamp;
}
public ProtocolSpec getSpec() {
return spec;
}
}
}

@ -0,0 +1,34 @@
/*
*
* * Copyright Hyperledger Besu Contributors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* * the License. You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* * specific language governing permissions and limitations under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import java.math.BigInteger;
import java.util.Optional;
public interface HeaderBasedProtocolSchedule {
ProtocolSpec getByBlockHeader(final ProcessableBlockHeader blockHeader);
Optional<BigInteger> getChainId();
void putMilestone(final long blockOrTimestamp, final ProtocolSpec protocolSpec);
String listMilestones();
}

@ -37,9 +37,9 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
private static final int MAX_OMMERS = 2;
private static final int MAX_GENERATION = 6;
protected final ProtocolSchedule protocolSchedule;
protected final HeaderBasedProtocolSchedule protocolSchedule;
public MainnetBlockBodyValidator(final ProtocolSchedule protocolSchedule) {
public MainnetBlockBodyValidator(final HeaderBasedProtocolSchedule protocolSchedule) {
this.protocolSchedule = protocolSchedule;
}
@ -223,7 +223,7 @@ public class MainnetBlockBodyValidator implements BlockBodyValidator {
final BlockHeader current,
final BlockHeader ommer,
final HeaderValidationMode ommerValidationMode) {
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(ommer.getNumber());
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(ommer);
if (!protocolSpec
.getOmmerHeaderValidator()
.validateHeader(ommer, context, ommerValidationMode)) {

@ -185,6 +185,28 @@ public class MainnetProtocolSpecFactory {
evmConfiguration);
}
public ProtocolSpecBuilder shanghaiDefinition(final GenesisConfigOptions genesisConfigOptions) {
return MainnetProtocolSpecs.shanghaiDefinition(
chainId,
contractSizeLimit,
evmStackSize,
isRevertReasonEnabled,
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration);
}
public ProtocolSpecBuilder cancunDefinition(final GenesisConfigOptions genesisConfigOptions) {
return MainnetProtocolSpecs.cancunDefinition(
chainId,
contractSizeLimit,
evmStackSize,
isRevertReasonEnabled,
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration);
}
public ProtocolSpecBuilder shandongDefinition(final GenesisConfigOptions genesisConfigOptions) {
return MainnetProtocolSpecs.shandongDefinition(
chainId,

@ -637,6 +637,46 @@ public abstract class MainnetProtocolSpecs {
.name("ParisFork");
}
static ProtocolSpecBuilder shanghaiDefinition(
final Optional<BigInteger> chainId,
final OptionalInt configContractSizeLimit,
final OptionalInt configStackSizeLimit,
final boolean enableRevertReason,
final GenesisConfigOptions genesisConfigOptions,
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
return parisDefinition(
chainId,
configContractSizeLimit,
configStackSizeLimit,
enableRevertReason,
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration)
.name("Shanghai");
}
static ProtocolSpecBuilder cancunDefinition(
final Optional<BigInteger> chainId,
final OptionalInt configContractSizeLimit,
final OptionalInt configStackSizeLimit,
final boolean enableRevertReason,
final GenesisConfigOptions genesisConfigOptions,
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
return shanghaiDefinition(
chainId,
configContractSizeLimit,
configStackSizeLimit,
enableRevertReason,
genesisConfigOptions,
quorumCompatibilityMode,
evmConfiguration)
.name("Cancun");
}
static ProtocolSpecBuilder shandongDefinition(
final Optional<BigInteger> chainId,
final OptionalInt configContractSizeLimit,

@ -45,6 +45,7 @@ public class MutableProtocolSchedule implements ProtocolSchedule {
return chainId;
}
@Override
public void putMilestone(final long blockNumber, final ProtocolSpec protocolSpec) {
final ScheduledProtocolSpec scheduledProtocolSpec =
new ScheduledProtocolSpec(blockNumber, protocolSpec);
@ -70,6 +71,7 @@ public class MutableProtocolSchedule implements ProtocolSchedule {
return null;
}
@Override
public String listMilestones() {
return protocolSpecs.stream()
.sorted(Comparator.comparing(ScheduledProtocolSpec::getBlock))

@ -51,7 +51,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
private static final Logger LOG = LoggerFactory.getLogger(PrivacyBlockProcessor.class);
private final BlockProcessor blockProcessor;
private final ProtocolSchedule protocolSchedule;
private final HeaderBasedProtocolSchedule protocolSchedule;
private final Enclave enclave;
private final PrivateStateStorage privateStateStorage;
private final WorldStateArchive privateWorldStateArchive;
@ -61,7 +61,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
public PrivacyBlockProcessor(
final BlockProcessor blockProcessor,
final ProtocolSchedule protocolSchedule,
final HeaderBasedProtocolSchedule protocolSchedule,
final Enclave enclave,
final PrivateStateStorage privateStateStorage,
final WorldStateArchive privateWorldStateArchive,

@ -0,0 +1,29 @@
/*
*
* * Copyright Hyperledger Besu Contributors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* * the License. You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* * specific language governing permissions and limitations under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
public interface PrivacySupportingProtocolSchedule {
void setTransactionFilter(final TransactionFilter transactionFilter);
void setPublicWorldStateArchiveForPrivacyBlockProcessor(
final WorldStateArchive publicWorldStateArchive);
}

@ -14,23 +14,19 @@
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import java.math.BigInteger;
import java.util.Optional;
import java.util.stream.Stream;
public interface ProtocolSchedule {
public interface ProtocolSchedule
extends HeaderBasedProtocolSchedule, PrivacySupportingProtocolSchedule {
ProtocolSpec getByBlockNumber(long number);
Stream<Long> streamMilestoneBlocks();
Optional<BigInteger> getChainId();
void setTransactionFilter(TransactionFilter transactionFilter);
void setPublicWorldStateArchiveForPrivacyBlockProcessor(
WorldStateArchive publicWorldStateArchive);
@Override
default ProtocolSpec getByBlockHeader(final ProcessableBlockHeader blockHeader) {
return getByBlockNumber(blockHeader.getNumber());
}
}

@ -15,57 +15,19 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ProtocolScheduleBuilder extends AbstractProtocolScheduleBuilder {
public class ProtocolScheduleBuilder {
private static class BuilderMapEntry {
private final long block;
private final ProtocolSpecBuilder builder;
private final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier;
public BuilderMapEntry(
final long block,
final ProtocolSpecBuilder builder,
final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
this.block = block;
this.builder = builder;
this.modifier = modifier;
}
public long getBlock() {
return block;
}
public ProtocolSpecBuilder getBuilder() {
return builder;
}
}
private static final Logger LOG = LoggerFactory.getLogger(ProtocolScheduleBuilder.class);
private final GenesisConfigOptions config;
private final ProtocolSpecAdapters protocolSpecAdapters;
private final Optional<BigInteger> defaultChainId;
private final PrivacyParameters privacyParameters;
private final boolean isRevertReasonEnabled;
private final BadBlockManager badBlockManager = new BadBlockManager();
private final boolean quorumCompatibilityMode;
private final EvmConfiguration evmConfiguration;
private MutableProtocolSchedule protocolSchedule;
public ProtocolScheduleBuilder(
final GenesisConfigOptions config,
@ -85,16 +47,6 @@ public class ProtocolScheduleBuilder {
evmConfiguration);
}
private Optional<BuilderMapEntry> create(
final OptionalLong block, final ProtocolSpecBuilder builder) {
if (block.isEmpty()) {
return Optional.empty();
}
final long blockVal = block.getAsLong();
return Optional.of(
new BuilderMapEntry(blockVal, builder, protocolSpecAdapters.getModifierForBlock(blockVal)));
}
public ProtocolScheduleBuilder(
final GenesisConfigOptions config,
final ProtocolSpecAdapters protocolSpecAdapters,
@ -120,172 +72,25 @@ public class ProtocolScheduleBuilder {
final boolean isRevertReasonEnabled,
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
this.config = config;
super(
config,
protocolSpecAdapters,
privacyParameters,
isRevertReasonEnabled,
quorumCompatibilityMode,
evmConfiguration);
this.defaultChainId = defaultChainId;
this.protocolSpecAdapters = protocolSpecAdapters;
this.privacyParameters = privacyParameters;
this.isRevertReasonEnabled = isRevertReasonEnabled;
this.quorumCompatibilityMode = quorumCompatibilityMode;
this.evmConfiguration = evmConfiguration;
}
public ProtocolSchedule createProtocolSchedule() {
final Optional<BigInteger> chainId = config.getChainId().or(() -> defaultChainId);
final MutableProtocolSchedule protocolSchedule = new MutableProtocolSchedule(chainId);
final MainnetProtocolSpecFactory specFactory =
new MainnetProtocolSpecFactory(
chainId,
config.getContractSizeLimit(),
config.getEvmStackSize(),
isRevertReasonEnabled,
quorumCompatibilityMode,
config.getEcip1017EraRounds(),
evmConfiguration);
validateForkOrdering();
final TreeMap<Long, BuilderMapEntry> builders = buildMilestoneMap(specFactory);
// At this stage, all milestones are flagged with correct modifier, but ProtocolSpecs must be
// inserted _AT_ the modifier block entry.
protocolSpecAdapters.stream()
.forEach(
entry -> {
final long modifierBlock = entry.getKey();
final BuilderMapEntry parent = builders.floorEntry(modifierBlock).getValue();
builders.put(
modifierBlock,
new BuilderMapEntry(modifierBlock, parent.getBuilder(), entry.getValue()));
});
// Create the ProtocolSchedule, such that the Dao/fork milestones can be inserted
builders
.values()
.forEach(e -> addProtocolSpec(protocolSchedule, e.getBlock(), e.getBuilder(), e.modifier));
// NOTE: It is assumed that Daofork blocks will not be used for private networks
// as too many risks exist around inserting a protocol-spec between daoBlock and daoBlock+10.
config
.getDaoForkBlock()
.ifPresent(
daoBlockNumber -> {
final ProtocolSpec originalProtocolSpec =
protocolSchedule.getByBlockNumber(daoBlockNumber);
addProtocolSpec(
protocolSchedule,
daoBlockNumber,
specFactory.daoRecoveryInitDefinition(),
protocolSpecAdapters.getModifierForBlock(daoBlockNumber));
addProtocolSpec(
protocolSchedule,
daoBlockNumber + 1L,
specFactory.daoRecoveryTransitionDefinition(),
protocolSpecAdapters.getModifierForBlock(daoBlockNumber + 1L));
// Return to the previous protocol spec after the dao fork has completed.
protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec);
});
// specs for classic network
config
.getClassicForkBlock()
.ifPresent(
classicBlockNumber -> {
final ProtocolSpec originalProtocolSpec =
protocolSchedule.getByBlockNumber(classicBlockNumber);
addProtocolSpec(
protocolSchedule,
OptionalLong.of(classicBlockNumber),
ClassicProtocolSpecs.classicRecoveryInitDefinition(
config.getContractSizeLimit(),
config.getEvmStackSize(),
quorumCompatibilityMode,
evmConfiguration));
protocolSchedule.putMilestone(classicBlockNumber + 1, originalProtocolSpec);
});
LOG.info("Protocol schedule created with milestones: {}", protocolSchedule.listMilestones());
protocolSchedule = new MutableProtocolSchedule(chainId);
initSchedule(protocolSchedule, chainId);
return protocolSchedule;
}
private TreeMap<Long, BuilderMapEntry> buildMilestoneMap(
final MainnetProtocolSpecFactory specFactory) {
return Stream.of(
create(OptionalLong.of(0), specFactory.frontierDefinition()),
create(config.getHomesteadBlockNumber(), specFactory.homesteadDefinition()),
create(
config.getTangerineWhistleBlockNumber(), specFactory.tangerineWhistleDefinition()),
create(config.getSpuriousDragonBlockNumber(), specFactory.spuriousDragonDefinition()),
create(config.getByzantiumBlockNumber(), specFactory.byzantiumDefinition()),
create(config.getConstantinopleBlockNumber(), specFactory.constantinopleDefinition()),
create(config.getPetersburgBlockNumber(), specFactory.petersburgDefinition()),
create(config.getIstanbulBlockNumber(), specFactory.istanbulDefinition()),
create(config.getMuirGlacierBlockNumber(), specFactory.muirGlacierDefinition()),
create(config.getBerlinBlockNumber(), specFactory.berlinDefinition()),
create(config.getLondonBlockNumber(), specFactory.londonDefinition(config)),
create(config.getArrowGlacierBlockNumber(), specFactory.arrowGlacierDefinition(config)),
create(config.getGrayGlacierBlockNumber(), specFactory.grayGlacierDefinition(config)),
create(config.getMergeNetSplitBlockNumber(), specFactory.parisDefinition(config)),
create(config.getShandongBlockNumber(), specFactory.shandongDefinition(config)),
// Classic Milestones
create(config.getEcip1015BlockNumber(), specFactory.tangerineWhistleDefinition()),
create(config.getDieHardBlockNumber(), specFactory.dieHardDefinition()),
create(config.getGothamBlockNumber(), specFactory.gothamDefinition()),
create(
config.getDefuseDifficultyBombBlockNumber(),
specFactory.defuseDifficultyBombDefinition()),
create(config.getAtlantisBlockNumber(), specFactory.atlantisDefinition()),
create(config.getAghartaBlockNumber(), specFactory.aghartaDefinition()),
create(config.getPhoenixBlockNumber(), specFactory.phoenixDefinition()),
create(config.getThanosBlockNumber(), specFactory.thanosDefinition()),
create(config.getMagnetoBlockNumber(), specFactory.magnetoDefinition()),
create(config.getMystiqueBlockNumber(), specFactory.mystiqueDefinition()),
create(config.getEcip1049BlockNumber(), specFactory.ecip1049Definition()))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(
Collectors.toMap(
BuilderMapEntry::getBlock,
b -> b,
(existing, replacement) -> replacement,
TreeMap::new));
}
private void addProtocolSpec(
final MutableProtocolSchedule protocolSchedule,
final OptionalLong blockNumber,
final ProtocolSpecBuilder definition) {
blockNumber.ifPresent(
bn -> addProtocolSpec(protocolSchedule, bn, definition, Function.identity()));
}
private void addProtocolSpec(
final MutableProtocolSchedule protocolSchedule,
final long blockNumber,
final ProtocolSpecBuilder definition,
final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
definition
.badBlocksManager(badBlockManager)
.privacyParameters(privacyParameters)
.privateTransactionValidatorBuilder(
() -> new PrivateTransactionValidator(protocolSchedule.getChainId()));
protocolSchedule.putMilestone(blockNumber, modifier.apply(definition).build(protocolSchedule));
}
private long validateForkOrder(
final String forkName, final OptionalLong thisForkBlock, final long lastForkBlock) {
final long referenceForkBlock = thisForkBlock.orElse(lastForkBlock);
if (lastForkBlock > referenceForkBlock) {
throw new RuntimeException(
String.format(
"Genesis Config Error: '%s' is scheduled for block %d but it must be on or after block %d.",
forkName, thisForkBlock.getAsLong(), lastForkBlock));
}
return referenceForkBlock;
}
private void validateForkOrdering() {
@Override
protected void validateForkOrdering() {
if (config.getDaoForkBlock().isEmpty()) {
validateClassicForkOrdering();
} else {
@ -339,4 +144,88 @@ public class ProtocolScheduleBuilder {
lastForkBlock = validateForkOrder("Mystique", config.getMystiqueBlockNumber(), lastForkBlock);
assert (lastForkBlock >= 0);
}
@Override
protected String getBlockIdentifierName() {
return "block";
}
@Override
protected Stream<Optional<BuilderMapEntry>> createMilestones(
final MainnetProtocolSpecFactory specFactory) {
return Stream.of(
create(OptionalLong.of(0), specFactory.frontierDefinition()),
create(config.getHomesteadBlockNumber(), specFactory.homesteadDefinition()),
create(config.getTangerineWhistleBlockNumber(), specFactory.tangerineWhistleDefinition()),
create(config.getSpuriousDragonBlockNumber(), specFactory.spuriousDragonDefinition()),
create(config.getByzantiumBlockNumber(), specFactory.byzantiumDefinition()),
create(config.getConstantinopleBlockNumber(), specFactory.constantinopleDefinition()),
create(config.getPetersburgBlockNumber(), specFactory.petersburgDefinition()),
create(config.getIstanbulBlockNumber(), specFactory.istanbulDefinition()),
create(config.getMuirGlacierBlockNumber(), specFactory.muirGlacierDefinition()),
create(config.getBerlinBlockNumber(), specFactory.berlinDefinition()),
create(config.getLondonBlockNumber(), specFactory.londonDefinition(config)),
create(config.getArrowGlacierBlockNumber(), specFactory.arrowGlacierDefinition(config)),
create(config.getGrayGlacierBlockNumber(), specFactory.grayGlacierDefinition(config)),
create(config.getMergeNetSplitBlockNumber(), specFactory.parisDefinition(config)),
create(config.getShandongBlockNumber(), specFactory.shandongDefinition(config)),
// Classic Milestones
create(config.getEcip1015BlockNumber(), specFactory.tangerineWhistleDefinition()),
create(config.getDieHardBlockNumber(), specFactory.dieHardDefinition()),
create(config.getGothamBlockNumber(), specFactory.gothamDefinition()),
create(
config.getDefuseDifficultyBombBlockNumber(),
specFactory.defuseDifficultyBombDefinition()),
create(config.getAtlantisBlockNumber(), specFactory.atlantisDefinition()),
create(config.getAghartaBlockNumber(), specFactory.aghartaDefinition()),
create(config.getPhoenixBlockNumber(), specFactory.phoenixDefinition()),
create(config.getThanosBlockNumber(), specFactory.thanosDefinition()),
create(config.getMagnetoBlockNumber(), specFactory.magnetoDefinition()),
create(config.getMystiqueBlockNumber(), specFactory.mystiqueDefinition()),
create(config.getEcip1049BlockNumber(), specFactory.ecip1049Definition()));
}
@Override
protected void postBuildStep(final MainnetProtocolSpecFactory specFactory) {
// NOTE: It is assumed that Daofork blocks will not be used for private networks
// as too many risks exist around inserting a protocol-spec between daoBlock and daoBlock+10.
config
.getDaoForkBlock()
.ifPresent(
daoBlockNumber -> {
final ProtocolSpec originalProtocolSpec =
protocolSchedule.getByBlockNumber(daoBlockNumber);
addProtocolSpec(
protocolSchedule,
daoBlockNumber,
specFactory.daoRecoveryInitDefinition(),
protocolSpecAdapters.getModifierForBlock(daoBlockNumber));
addProtocolSpec(
protocolSchedule,
daoBlockNumber + 1L,
specFactory.daoRecoveryTransitionDefinition(),
protocolSpecAdapters.getModifierForBlock(daoBlockNumber + 1L));
// Return to the previous protocol spec after the dao fork has completed.
protocolSchedule.putMilestone(daoBlockNumber + 10, originalProtocolSpec);
});
// specs for classic network
config
.getClassicForkBlock()
.ifPresent(
classicBlockNumber -> {
final ProtocolSpec originalProtocolSpec =
protocolSchedule.getByBlockNumber(classicBlockNumber);
addProtocolSpec(
protocolSchedule,
classicBlockNumber,
ClassicProtocolSpecs.classicRecoveryInitDefinition(
config.getContractSizeLimit(),
config.getEvmStackSize(),
quorumCompatibilityMode,
evmConfiguration),
Function.identity());
protocolSchedule.putMilestone(classicBlockNumber + 1, originalProtocolSpec);
});
}
}

@ -31,16 +31,17 @@ public class ProtocolSpecAdapters {
}
public static ProtocolSpecAdapters create(
final long block, final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
final long blockNumberOrTimestamp,
final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier) {
final Map<Long, Function<ProtocolSpecBuilder, ProtocolSpecBuilder>> entries = new HashMap<>();
entries.put(block, modifier);
entries.put(blockNumberOrTimestamp, modifier);
return new ProtocolSpecAdapters(entries);
}
public Function<ProtocolSpecBuilder, ProtocolSpecBuilder> getModifierForBlock(
final long blockNumber) {
final long blockNumberOrTimestamp) {
final NavigableSet<Long> epochs = new TreeSet<>(modifiers.keySet());
final Long modifier = epochs.floor(blockNumber);
final Long modifier = epochs.floor(blockNumberOrTimestamp);
if (modifier == null) {
return Function.identity();

@ -57,7 +57,7 @@ public class ProtocolSpecBuilder {
private Function<GasCalculator, MainnetTransactionValidator> transactionValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> blockHeaderValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> ommerHeaderValidatorBuilder;
private Function<ProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder;
private Function<HeaderBasedProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder;
private BiFunction<GasCalculator, EVM, AbstractMessageProcessor> contractCreationProcessorBuilder;
private Function<PrecompiledContractConfiguration, PrecompileContractRegistry>
precompileContractRegistryBuilder;
@ -137,7 +137,7 @@ public class ProtocolSpecBuilder {
}
public ProtocolSpecBuilder blockBodyValidatorBuilder(
final Function<ProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder) {
final Function<HeaderBasedProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder) {
this.blockBodyValidatorBuilder = blockBodyValidatorBuilder;
return this;
}
@ -243,7 +243,7 @@ public class ProtocolSpecBuilder {
return this;
}
public ProtocolSpec build(final ProtocolSchedule protocolSchedule) {
public ProtocolSpec build(final HeaderBasedProtocolSchedule protocolSchedule) {
checkNotNull(gasCalculatorBuilder, "Missing gasCalculator");
checkNotNull(gasLimitCalculator, "Missing gasLimitCalculator");
checkNotNull(evmBuilder, "Missing operation registry");
@ -288,54 +288,23 @@ public class ProtocolSpecBuilder {
gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor);
final BlockHeaderValidator blockHeaderValidator =
blockHeaderValidatorBuilder
.apply(feeMarket)
.difficultyCalculator(difficultyCalculator)
.build();
createBlockHeaderValidator(blockHeaderValidatorBuilder);
final BlockHeaderValidator ommerHeaderValidator =
ommerHeaderValidatorBuilder
.apply(feeMarket)
.difficultyCalculator(difficultyCalculator)
.build();
createBlockHeaderValidator(ommerHeaderValidatorBuilder);
final BlockBodyValidator blockBodyValidator = blockBodyValidatorBuilder.apply(protocolSchedule);
BlockProcessor blockProcessor =
blockProcessorBuilder.apply(
transactionProcessor,
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
privacyParameters.getGoQuorumPrivacyParameters());
BlockProcessor blockProcessor = createBlockProcessor(transactionProcessor);
// Set private Tx Processor
PrivateTransactionProcessor privateTransactionProcessor = null;
if (privacyParameters.isEnabled()) {
final PrivateTransactionValidator privateTransactionValidator =
privateTransactionValidatorBuilder.apply();
privateTransactionProcessor =
privateTransactionProcessorBuilder.apply(
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
privateTransactionValidator);
if (privacyParameters.isPrivacyPluginEnabled()) {
final PrivacyPluginPrecompiledContract privacyPluginPrecompiledContract =
(PrivacyPluginPrecompiledContract) precompileContractRegistry.get(PLUGIN_PRIVACY);
privacyPluginPrecompiledContract.setPrivateTransactionProcessor(
privateTransactionProcessor);
} else if (privacyParameters.isFlexiblePrivacyGroupsEnabled()) {
final FlexiblePrivacyPrecompiledContract flexiblePrivacyPrecompiledContract =
(FlexiblePrivacyPrecompiledContract) precompileContractRegistry.get(FLEXIBLE_PRIVACY);
flexiblePrivacyPrecompiledContract.setPrivateTransactionProcessor(
privateTransactionProcessor);
} else {
final PrivacyPrecompiledContract privacyPrecompiledContract =
(PrivacyPrecompiledContract) precompileContractRegistry.get(DEFAULT_PRIVACY);
privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor);
}
PrivateTransactionProcessor privateTransactionProcessor =
createPrivateTransactionProcessor(
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
precompileContractRegistry);
if (privacyParameters.isEnabled()) {
blockProcessor =
new PrivacyBlockProcessor(
blockProcessor,
@ -381,6 +350,60 @@ public class ProtocolSpecBuilder {
Optional.ofNullable(powHasher));
}
private PrivateTransactionProcessor createPrivateTransactionProcessor(
final MainnetTransactionValidator transactionValidator,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final PrecompileContractRegistry precompileContractRegistry) {
PrivateTransactionProcessor privateTransactionProcessor = null;
if (privacyParameters.isEnabled()) {
final PrivateTransactionValidator privateTransactionValidator =
privateTransactionValidatorBuilder.apply();
privateTransactionProcessor =
privateTransactionProcessorBuilder.apply(
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
privateTransactionValidator);
if (privacyParameters.isPrivacyPluginEnabled()) {
final PrivacyPluginPrecompiledContract privacyPluginPrecompiledContract =
(PrivacyPluginPrecompiledContract) precompileContractRegistry.get(PLUGIN_PRIVACY);
privacyPluginPrecompiledContract.setPrivateTransactionProcessor(
privateTransactionProcessor);
} else if (privacyParameters.isFlexiblePrivacyGroupsEnabled()) {
final FlexiblePrivacyPrecompiledContract flexiblePrivacyPrecompiledContract =
(FlexiblePrivacyPrecompiledContract) precompileContractRegistry.get(FLEXIBLE_PRIVACY);
flexiblePrivacyPrecompiledContract.setPrivateTransactionProcessor(
privateTransactionProcessor);
} else {
final PrivacyPrecompiledContract privacyPrecompiledContract =
(PrivacyPrecompiledContract) precompileContractRegistry.get(DEFAULT_PRIVACY);
privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor);
}
}
return privateTransactionProcessor;
}
private BlockProcessor createBlockProcessor(
final MainnetTransactionProcessor transactionProcessor) {
return blockProcessorBuilder.apply(
transactionProcessor,
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator,
skipZeroBlockRewards,
privacyParameters.getGoQuorumPrivacyParameters());
}
private BlockHeaderValidator createBlockHeaderValidator(
final Function<FeeMarket, BlockHeaderValidator.Builder> blockHeaderValidatorBuilder) {
return blockHeaderValidatorBuilder
.apply(feeMarket)
.difficultyCalculator(difficultyCalculator)
.build();
}
public interface TransactionProcessorBuilder {
MainnetTransactionProcessor apply(
GasCalculator gasCalculator,

@ -0,0 +1,32 @@
/*
*
* * Copyright Hyperledger Besu Contributors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* * the License. You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* * specific language governing permissions and limitations under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
*
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import java.util.Optional;
public interface TimestampSchedule
extends HeaderBasedProtocolSchedule, PrivacySupportingProtocolSchedule {
Optional<ProtocolSpec> getByTimestamp(final long timestamp);
@Override
default ProtocolSpec getByBlockHeader(final ProcessableBlockHeader blockHeader) {
return getByTimestamp(blockHeader.getTimestamp()).orElse(null);
}
}

@ -0,0 +1,81 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
import java.util.Optional;
import java.util.stream.Stream;
public class TimestampScheduleBuilder extends AbstractProtocolScheduleBuilder {
private final Optional<BigInteger> defaultChainId;
public TimestampScheduleBuilder(
final GenesisConfigOptions config,
final BigInteger defaultChainId,
final ProtocolSpecAdapters protocolSpecAdapters,
final PrivacyParameters privacyParameters,
final boolean isRevertReasonEnabled,
final boolean quorumCompatibilityMode,
final EvmConfiguration evmConfiguration) {
super(
config,
protocolSpecAdapters,
privacyParameters,
isRevertReasonEnabled,
quorumCompatibilityMode,
evmConfiguration);
this.defaultChainId = Optional.of(defaultChainId);
}
public TimestampSchedule createTimestampSchedule() {
final Optional<BigInteger> chainId = config.getChainId().or(() -> defaultChainId);
TimestampSchedule timestampSchedule = new DefaultTimestampSchedule(chainId);
initSchedule(timestampSchedule, chainId);
return timestampSchedule;
}
@Override
protected void validateForkOrdering() {
long lastForkTimestamp = 0;
lastForkTimestamp = validateForkOrder("Shanghai", config.getShanghaiTime(), lastForkTimestamp);
lastForkTimestamp = validateForkOrder("Cancun", config.getCancunTime(), lastForkTimestamp);
assert (lastForkTimestamp >= 0);
}
@Override
protected String getBlockIdentifierName() {
return "timestamp";
}
@Override
protected Stream<Optional<BuilderMapEntry>> createMilestones(
final MainnetProtocolSpecFactory specFactory) {
return Stream.of(
// generally this TimestampSchedule will not have an entry for 0 instead it is relying
// on defaulting to a MergeProtocolSchedule in
// TransitionProtocolSchedule.getByBlockHeader if the given timestamp is before the
// first entry in TimestampSchedule
create(config.getShanghaiTime(), specFactory.shanghaiDefinition(config)),
create(config.getCancunTime(), specFactory.cancunDefinition(config)));
}
@Override
protected void postBuildStep(final MainnetProtocolSpecFactory specFactory) {}
}

@ -21,7 +21,7 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.HeaderBasedProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
@ -43,7 +43,7 @@ public class PrivateStateRehydration {
private final PrivateStateStorage privateStateStorage;
private final Blockchain blockchain;
private final ProtocolSchedule protocolSchedule;
private final HeaderBasedProtocolSchedule protocolSchedule;
private final WorldStateArchive publicWorldStateArchive;
private final WorldStateArchive privateWorldStateArchive;
private final PrivateStateRootResolver privateStateRootResolver;
@ -52,7 +52,7 @@ public class PrivateStateRehydration {
public PrivateStateRehydration(
final PrivateStateStorage privateStateStorage,
final Blockchain blockchain,
final ProtocolSchedule protocolSchedule,
final HeaderBasedProtocolSchedule protocolSchedule,
final WorldStateArchive publicWorldStateArchive,
final WorldStateArchive privateWorldStateArchive,
final PrivateStateRootResolver privateStateRootResolver,
@ -140,7 +140,7 @@ public class PrivateStateRehydration {
.map(Transaction::getHash)
.collect(Collectors.toList()));
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(blockHeader.getNumber());
final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(blockHeader);
final PrivateGroupRehydrationBlockProcessor privateGroupRehydrationBlockProcessor =
new PrivateGroupRehydrationBlockProcessor(
protocolSpec.getTransactionProcessor(),

@ -15,7 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
@ -54,7 +54,7 @@ public class BaseFeeBlockBodyValidatorTest {
public void setup() {
when(protocolSpec.getFeeMarket()).thenReturn(FeeMarket.london(0L));
when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(block.getHeader()).thenReturn(blockHeader);

@ -154,7 +154,7 @@ public class PrivacyBlockProcessorTest {
when(blockchain.getBlockByHash(any())).thenReturn(Optional.of(firstBlock));
when(blockchain.getBlockHeader(any())).thenReturn(Optional.of(firstBlock.getHeader()));
final ProtocolSpec protocolSpec = mockProtocolSpec();
when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
when(publicWorldStateArchive.getMutable(any(), any()))
.thenReturn(Optional.of(mutableWorldState));
final MutableWorldState mockPrivateStateArchive = mockPrivateStateArchive();

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -29,6 +30,7 @@ import java.util.OptionalLong;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@ -39,8 +41,64 @@ import org.mockito.stubbing.Answer;
public class ProtocolScheduleBuilderTest {
@Mock GenesisConfigOptions configOptions;
@Mock private Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier;
private static final BigInteger CHAIN_ID = BigInteger.ONE;
private ProtocolScheduleBuilder builder;
@Before
public void setup() {
builder =
new ProtocolScheduleBuilder(
configOptions,
CHAIN_ID,
ProtocolSpecAdapters.create(0, Function.identity()),
new PrivacyParameters(),
false,
false,
EvmConfiguration.DEFAULT);
}
@Test
public void createProtocolScheduleInOrder() {
when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(1L));
when(configOptions.getDaoForkBlock()).thenReturn(OptionalLong.of(2L));
when(configOptions.getByzantiumBlockNumber()).thenReturn(OptionalLong.of(13L));
when(configOptions.getMergeNetSplitBlockNumber()).thenReturn(OptionalLong.of(15L));
final ProtocolSchedule protocolSchedule = builder.createProtocolSchedule();
assertThat(protocolSchedule.getChainId()).contains(CHAIN_ID);
assertThat(protocolSchedule.getByBlockNumber(0).getName()).isEqualTo("Frontier");
assertThat(protocolSchedule.getByBlockNumber(1).getName()).isEqualTo("Homestead");
assertThat(protocolSchedule.getByBlockNumber(2).getName()).isEqualTo("DaoRecoveryInit");
assertThat(protocolSchedule.getByBlockNumber(3).getName()).isEqualTo("DaoRecoveryTransition");
assertThat(protocolSchedule.getByBlockNumber(12).getName()).isEqualTo("Homestead");
assertThat(protocolSchedule.getByBlockNumber(13).getName()).isEqualTo("Byzantium");
assertThat(protocolSchedule.getByBlockNumber(14).getName()).isEqualTo("Byzantium");
assertThat(protocolSchedule.getByBlockNumber(15).getName()).isEqualTo("ParisFork");
assertThat(protocolSchedule.getByBlockNumber(50).getName()).isEqualTo("ParisFork");
}
@Test
public void createProtocolScheduleOverlappingUsesLatestFork() {
when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(0L));
when(configOptions.getByzantiumBlockNumber()).thenReturn(OptionalLong.of(0L));
final ProtocolSchedule protocolSchedule = builder.createProtocolSchedule();
assertThat(protocolSchedule.getChainId()).contains(CHAIN_ID);
assertThat(protocolSchedule.getByBlockNumber(0).getName()).isEqualTo("Byzantium");
assertThat(protocolSchedule.getByBlockNumber(1).getName()).isEqualTo("Byzantium");
}
@Test
public void createProtocolScheduleOutOfOrderThrows() {
when(configOptions.getDaoForkBlock()).thenReturn(OptionalLong.of(0L));
when(configOptions.getArrowGlacierBlockNumber()).thenReturn(OptionalLong.of(12L));
when(configOptions.getGrayGlacierBlockNumber()).thenReturn(OptionalLong.of(11L));
assertThatThrownBy(() -> builder.createProtocolSchedule())
.isInstanceOf(RuntimeException.class)
.hasMessage(
"Genesis Config Error: 'GrayGlacier' is scheduled for block 11 but it must be on or after block 12.");
}
@Test
public void modifierInsertedBetweenBlocksIsAppliedToLaterAndCreatesInterimMilestone() {
@ -52,7 +110,7 @@ public class ProtocolScheduleBuilderTest {
final ProtocolScheduleBuilder builder =
new ProtocolScheduleBuilder(
configOptions,
BigInteger.ONE,
CHAIN_ID,
ProtocolSpecAdapters.create(2, modifier),
new PrivacyParameters(),
false,
@ -81,7 +139,7 @@ public class ProtocolScheduleBuilderTest {
final ProtocolScheduleBuilder builder =
new ProtocolScheduleBuilder(
configOptions,
BigInteger.ONE,
CHAIN_ID,
ProtocolSpecAdapters.create(2, modifier),
new PrivacyParameters(),
false,
@ -110,7 +168,7 @@ public class ProtocolScheduleBuilderTest {
final ProtocolScheduleBuilder builder =
new ProtocolScheduleBuilder(
configOptions,
BigInteger.ONE,
CHAIN_ID,
ProtocolSpecAdapters.create(5, modifier),
new PrivacyParameters(),
false,

@ -16,6 +16,9 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import java.math.BigInteger;
import java.util.Optional;
@ -67,4 +70,21 @@ public class ProtocolScheduleTest {
protocolSchedule.putMilestone(0, spec2);
assertThat(protocolSchedule.getByBlockNumber(0)).isSameAs(spec2);
}
@Test
public void getByBlockHeader_defaultMethodShouldUseGetByBlockNumber() {
final ProtocolSpec spec1 = mock(ProtocolSpec.class);
final ProtocolSpec spec2 = mock(ProtocolSpec.class);
final MutableProtocolSchedule protocolSchedule = new MutableProtocolSchedule(CHAIN_ID);
protocolSchedule.putMilestone(0, spec1);
protocolSchedule.putMilestone(1, spec2);
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getNumber()).thenReturn(1L);
final ProtocolSpec spec = protocolSchedule.getByBlockHeader(blockHeader);
assertThat(spec).isEqualTo(spec2);
}
}

@ -0,0 +1,141 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
import java.util.function.Function;
import org.junit.Before;
import org.junit.Test;
public class TimestampScheduleBuilderTest {
private static final BigInteger chainId = BigInteger.ONE;
private static final BigInteger defaultChainId = BigInteger.ONE;
private static final PrivacyParameters privacyParameters = new PrivacyParameters();
private static final EvmConfiguration evmConfiguration = EvmConfiguration.DEFAULT;
private static final BlockHeader BLOCK_HEADER =
new BlockHeaderTestFixture().timestamp(1L).buildHeader();
private TimestampScheduleBuilder builder;
private StubGenesisConfigOptions config;
private final Function<ProtocolSpecBuilder, ProtocolSpecBuilder> modifier = Function.identity();
private final long FIRST_TIMESTAMP_FORK = 1L;
@Before
public void setup() {
config = new StubGenesisConfigOptions();
config.chainId(chainId);
boolean isRevertReasonEnabled = false;
boolean quorumCompatibilityMode = false;
builder =
new TimestampScheduleBuilder(
config,
defaultChainId,
ProtocolSpecAdapters.create(FIRST_TIMESTAMP_FORK, modifier),
privacyParameters,
isRevertReasonEnabled,
quorumCompatibilityMode,
evmConfiguration);
}
@Test
public void createTimestampScheduleInOrder() {
config.shanghaiTime(FIRST_TIMESTAMP_FORK);
config.cancunTime(3);
final TimestampSchedule timestampSchedule = builder.createTimestampSchedule();
assertThat(timestampSchedule.getChainId()).contains(chainId);
assertThat(timestampSchedule.getByTimestamp(0)).isEmpty();
assertThat(timestampSchedule.getByTimestamp(1))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Shanghai");
assertThat(timestampSchedule.getByTimestamp(2))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Shanghai");
assertThat(timestampSchedule.getByTimestamp(3))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Cancun");
assertThat(timestampSchedule.getByTimestamp(4))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Cancun");
}
@Test
public void createTimestampScheduleOverlappingUsesLatestFork() {
config.shanghaiTime(0);
config.cancunTime(0);
final TimestampSchedule timestampSchedule = builder.createTimestampSchedule();
assertThat(timestampSchedule.getChainId()).contains(chainId);
assertThat(timestampSchedule.getByTimestamp(0))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Cancun");
assertThat(timestampSchedule.getByTimestamp(1))
.isPresent()
.map(ProtocolSpec::getName)
.hasValue("Cancun");
}
@Test
public void createTimestampScheduleOutOfOrderThrows() {
config.shanghaiTime(3);
config.cancunTime(2);
assertThatThrownBy(() -> builder.createTimestampSchedule())
.isInstanceOf(RuntimeException.class)
.hasMessage(
"Genesis Config Error: 'Cancun' is scheduled for timestamp 2 but it must be on or after timestamp 3.");
}
@Test
public void getByBlockHeader_whenSpecFound() {
config.shanghaiTime(FIRST_TIMESTAMP_FORK);
final TimestampSchedule schedule = builder.createTimestampSchedule();
assertThat(schedule.getByBlockHeader(BLOCK_HEADER)).isNotNull();
}
@Test
public void getByBlockHeader_whenSpecNotFoundReturnsNull() {
config.shanghaiTime(2L);
builder =
new TimestampScheduleBuilder(
config,
defaultChainId,
ProtocolSpecAdapters.create(2L, modifier),
privacyParameters,
false,
false,
evmConfiguration);
final TimestampSchedule schedule = builder.createTimestampSchedule();
assertThat(schedule.getByBlockHeader(BLOCK_HEADER)).isNull();
}
}

@ -73,9 +73,8 @@ public class ReferenceTestProtocolSchedules {
builder.put(
"Merge",
createSchedule(new StubGenesisConfigOptions().mergeNetSplitBlock(0).baseFeePerGas(0x0a)));
builder.put(
"Shanghai",
createSchedule(new StubGenesisConfigOptions().shandongBlock(0).baseFeePerGas(0x0a)));
builder.put("Shanghai", createSchedule(new StubGenesisConfigOptions().shanghaiTime(0)));
builder.put("Cancun", createSchedule(new StubGenesisConfigOptions().cancunTime(0)));
builder.put("Shandong", createSchedule(new StubGenesisConfigOptions().shandongBlock(0)));
return new ReferenceTestProtocolSchedules(builder.build());
}

@ -92,6 +92,16 @@ public class NoRewardProtocolScheduleWrapper implements ProtocolSchedule {
return delegate.getChainId();
}
@Override
public void putMilestone(final long blockOrTimestamp, final ProtocolSpec protocolSpec) {
delegate.putMilestone(blockOrTimestamp, protocolSpec);
}
@Override
public String listMilestones() {
return delegate.listMilestones();
}
@Override
public void setTransactionFilter(final TransactionFilter transactionFilter) {
delegate.setTransactionFilter(transactionFilter);

@ -126,6 +126,8 @@ public class TestSetChainParams implements JsonRpcMethod {
maybeMoveToNumber(params, "arrowGlacierForkBlock", config, "arrowGlacierBlock");
maybeMoveToNumber(params, "grayGlacierForkBlock", config, "grayGlacierBlock");
maybeMoveToNumber(params, "mergeNetSplitForkBlock", config, "mergeNetSplitBlock");
maybeMoveToNumber(params, "shanghaiForkTime", config, "shanghaiTime");
maybeMoveToNumber(params, "cancunForkTime", config, "cancunTime");
maybeMoveToNumber(params, "shandongForkBlock", config, "shandongBlock");
maybeMoveToNumber(params, "chainID", config, "chainId", 1);

Loading…
Cancel
Save