mirror of https://github.com/hyperledger/besu
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
parent
2b9956a328
commit
3fd0681e3d
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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(); |
||||
} |
@ -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); |
||||
} |
@ -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) {} |
||||
} |
@ -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(); |
||||
} |
||||
} |
Loading…
Reference in new issue