diff --git a/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java b/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java index 88d8ebf5a58..cb734ae5a3d 100644 --- a/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java +++ b/data/provider/src/test/java/tech/pegasys/teku/api/ValidatorDataProviderTest.java @@ -51,17 +51,17 @@ import tech.pegasys.teku.api.schema.BeaconBlock; import tech.pegasys.teku.api.schema.ValidatorBlockResult; import tech.pegasys.teku.api.schema.altair.SignedBeaconBlockAltair; +import tech.pegasys.teku.api.schema.merge.SignedBeaconBlockMerge; import tech.pegasys.teku.api.schema.phase0.BeaconBlockPhase0; +import tech.pegasys.teku.api.schema.phase0.SignedBeaconBlockPhase0; import tech.pegasys.teku.bls.BLSTestUtil; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.async.SafeFutureAssert; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.provider.JsonProvider; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.TestSpecContext; import tech.pegasys.teku.spec.TestSpecInvocationContextProvider.SpecContext; -import tech.pegasys.teku.spec.TestSpecInvocationContextProvider.SpecContextExecutionHelper; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.storage.client.ChainDataUnavailableException; @@ -198,9 +198,8 @@ void parseBlock_shouldParseBlocks() throws JsonProcessingException { } @TestTemplate - void parseBlock_shouldParseAltairBlocks(SpecContext specContext) throws JsonProcessingException { - SpecContextExecutionHelper.only(specContext, SpecMilestone.ALTAIR); - + void parseBlock_shouldParseMilestoneSpecificBlocks(SpecContext specContext) + throws JsonProcessingException { final SignedBeaconBlock internalSignedBlock = dataStructureUtil.randomSignedBeaconBlock(ONE); final tech.pegasys.teku.api.schema.SignedBeaconBlock signedBlock = schemaProvider.getSignedBeaconBlock(internalSignedBlock); @@ -210,7 +209,17 @@ void parseBlock_shouldParseAltairBlocks(SpecContext specContext) throws JsonProc provider.parseBlock(jsonProvider, signedBlockJson); assertThat(parsedBlock).isEqualTo(signedBlock); - assertThat(parsedBlock).isInstanceOf(SignedBeaconBlockAltair.class); + switch (specContext.getSpecMilestone()) { + case PHASE0: + assertThat(parsedBlock).isInstanceOf(SignedBeaconBlockPhase0.class); + break; + case ALTAIR: + assertThat(parsedBlock).isInstanceOf(SignedBeaconBlockAltair.class); + break; + case MERGE: + assertThat(parsedBlock).isInstanceOf(SignedBeaconBlockMerge.class); + break; + } } @TestTemplate diff --git a/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/BlockProposalTestUtil.java b/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/BlockProposalTestUtil.java index 9f18059f00b..1c706a122f8 100644 --- a/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/BlockProposalTestUtil.java +++ b/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/BlockProposalTestUtil.java @@ -34,6 +34,7 @@ import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema; import tech.pegasys.teku.spec.datastructures.operations.Attestation; @@ -46,7 +47,6 @@ import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.EpochProcessingException; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.SlotProcessingException; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.StateTransitionException; -import tech.pegasys.teku.spec.logic.versions.merge.helpers.MiscHelpersMerge; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsMerge; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -72,7 +72,8 @@ public SignedBlockAndState createNewBlock( final SszList deposits, final SszList exits, final Optional> transactions, - final Optional terminalBlock) + final Optional terminalBlock, + final Optional executionPayload) throws StateTransitionException, EpochProcessingException, SlotProcessingException { final UInt64 newEpoch = spec.computeEpochAtSlot(newSlot); @@ -100,8 +101,10 @@ public SignedBlockAndState createNewBlock( () -> dataStructureUtil.emptySyncAggregateIfRequiredByState(blockSlotState)) .executionPayload( () -> - createExecutionPayload( - newSlot, state, newEpoch, transactions, terminalBlock))); + executionPayload.orElseGet( + () -> + createExecutionPayload( + newSlot, state, newEpoch, transactions, terminalBlock)))); // Sign block and set block signature final BeaconBlock block = newBlockAndState.getBlock(); @@ -111,6 +114,77 @@ public SignedBlockAndState createNewBlock( return new SignedBlockAndState(signedBlock, newBlockAndState.getState()); } + public SignedBlockAndState createNewBlockSkippingStateTransition( + final Signer signer, + final UInt64 newSlot, + final BeaconState state, + final Bytes32 parentBlockSigningRoot, + final Eth1Data eth1Data, + final SszList attestations, + final SszList slashings, + final SszList deposits, + final SszList exits, + final Optional> transactions, + final Optional terminalBlock, + final Optional executionPayload) + throws EpochProcessingException, SlotProcessingException { + + final UInt64 newEpoch = spec.computeEpochAtSlot(newSlot); + final BLSSignature randaoReveal = + signer.createRandaoReveal(newEpoch, state.getForkInfo()).join(); + + final BeaconState blockSlotState = spec.processSlots(state, newSlot); + + // Sign block and set block signature + + final BeaconBlockBody blockBody = + spec.atSlot(newSlot) + .getSchemaDefinitions() + .getBeaconBlockBodySchema() + .createBlockBody( + builder -> + builder + .randaoReveal(randaoReveal) + .eth1Data(eth1Data) + .graffiti(Bytes32.ZERO) + .attestations(attestations) + .proposerSlashings(slashings) + .attesterSlashings(blockBodyLists.createAttesterSlashings()) + .deposits(deposits) + .voluntaryExits(exits) + .syncAggregate( + () -> + dataStructureUtil.emptySyncAggregateIfRequiredByState( + blockSlotState)) + .executionPayload( + () -> + executionPayload.orElseGet( + () -> + createExecutionPayload( + newSlot, + state, + newEpoch, + transactions, + terminalBlock)))); + + final BeaconBlock block = + spec.atSlot(newSlot) + .getSchemaDefinitions() + .getBeaconBlockSchema() + .create( + newSlot, + UInt64.valueOf(spec.getBeaconProposerIndex(blockSlotState, newSlot)), + parentBlockSigningRoot, + blockSlotState.hashTreeRoot(), + blockBody); + + // Sign block and set block signature + BLSSignature blockSignature = signer.signBlock(block, state.getForkInfo()).join(); + + final SignedBeaconBlock signedBlock = SignedBeaconBlock.create(spec, block, blockSignature); + return new SignedBlockAndState(signedBlock, blockSlotState); + } + private ExecutionPayload createExecutionPayload( final UInt64 newSlot, final BeaconState state, @@ -142,7 +216,7 @@ private ExecutionPayload createExecutionPayload( newSlot, UInt64.valueOf(30_000_000L), UInt64.valueOf(30_000_000L), - ((MiscHelpersMerge) specVersion.miscHelpers()).computeTimestampAtSlot(state, newSlot), + specVersion.miscHelpers().computeTimeAtSlot(state, newSlot), dataStructureUtil.randomBytes32(), UInt256.ONE, dataStructureUtil.randomBytes32(), @@ -163,9 +237,26 @@ public SignedBlockAndState createBlock( final Optional> exits, final Optional eth1Data, final Optional> transactions, - final Optional terminalBlock) + final Optional terminalBlock, + final Optional executionPayload, + final boolean skipStateTransition) throws StateTransitionException, EpochProcessingException, SlotProcessingException { final UInt64 newEpoch = spec.computeEpochAtSlot(newSlot); + if (skipStateTransition) { + return createNewBlockSkippingStateTransition( + signer, + newSlot, + previousState, + parentBlockSigningRoot, + eth1Data.orElse(get_eth1_data_stub(previousState, newEpoch)), + attestations.orElse(blockBodyLists.createAttestations()), + blockBodyLists.createProposerSlashings(), + deposits.orElse(blockBodyLists.createDeposits()), + exits.orElse(blockBodyLists.createVoluntaryExits()), + transactions, + terminalBlock, + executionPayload); + } return createNewBlock( signer, newSlot, @@ -177,7 +268,8 @@ public SignedBlockAndState createBlock( deposits.orElse(blockBodyLists.createDeposits()), exits.orElse(blockBodyLists.createVoluntaryExits()), transactions, - terminalBlock); + terminalBlock, + executionPayload); } private Eth1Data get_eth1_data_stub(BeaconState state, UInt64 current_epoch) { diff --git a/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/ChainBuilder.java b/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/ChainBuilder.java index 73769965bc1..d35af57bc28 100644 --- a/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/ChainBuilder.java +++ b/ethereum/core/src/testFixtures/java/tech/pegasys/teku/core/ChainBuilder.java @@ -46,6 +46,7 @@ import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader; import tech.pegasys.teku.spec.datastructures.interop.MockStartBeaconStateGenerator; import tech.pegasys.teku.spec.datastructures.interop.MockStartDepositGenerator; @@ -410,7 +411,9 @@ private SignedBlockAndState appendNewBlockToChain(final UInt64 slot, final Block Optional.empty(), options.getEth1Data(), options.getTransactions(), - options.getTerminalBlockHash()); + options.getTerminalBlockHash(), + options.getExecutionPayload(), + options.getSkipStateTransition()); trackBlock(nextBlockAndState); return nextBlockAndState; } catch (StateTransitionException | EpochProcessingException | SlotProcessingException e) { @@ -511,7 +514,7 @@ public BLSSignature sign( return result.join(); } - private Signer getSigner(final int validatorId) { + public Signer getSigner(final int validatorId) { return new LocalSigner(spec, validatorKeys.get(validatorId), SYNC_RUNNER); } @@ -521,6 +524,8 @@ public static final class BlockOptions { private Optional eth1Data = Optional.empty(); private Optional> transactions = Optional.empty(); private Optional terminalBlockHash = Optional.empty(); + private Optional executionPayload = Optional.empty(); + private boolean skipStateTransition = false; private BlockOptions() {} @@ -548,6 +553,16 @@ public BlockOptions setTerminalBlockHash(Bytes32 blockHash) { return this; } + public BlockOptions setExecutionPayload(ExecutionPayload executionPayload) { + this.executionPayload = Optional.of(executionPayload); + return this; + } + + public BlockOptions setSkipStateTransition(boolean skipStateTransition) { + this.skipStateTransition = skipStateTransition; + return this; + } + private List getAttestations() { return attestations; } @@ -563,5 +578,13 @@ public Optional> getTransactions() { public Optional getTerminalBlockHash() { return terminalBlockHash; } + + public Optional getExecutionPayload() { + return executionPayload; + } + + public boolean getSkipStateTransition() { + return skipStateTransition; + } } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/block/BlockProcessorMerge.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/block/BlockProcessorMerge.java index 6f741a9ca3e..8d7291ddbb9 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/block/BlockProcessorMerge.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/block/BlockProcessorMerge.java @@ -108,7 +108,7 @@ public void processExecutionPayload( } if (!miscHelpersMerge - .computeTimestampAtSlot(state, state.getSlot()) + .computeTimeAtSlot(state, state.getSlot()) .equals(payload.getTimestamp())) { throw new BlockProcessingException( "Execution payload timestamp does not match time for state slot"); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/helpers/MiscHelpersMerge.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/helpers/MiscHelpersMerge.java index 07df798c8ca..8f7824ad386 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/helpers/MiscHelpersMerge.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/merge/helpers/MiscHelpersMerge.java @@ -13,7 +13,6 @@ package tech.pegasys.teku.spec.logic.versions.merge.helpers; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.merge.BeaconBlockBodyMerge; @@ -42,8 +41,4 @@ public boolean isMergeBlock(final BeaconState genericState, final BeaconBlock bl public boolean isExecutionEnabled(final BeaconState genericState, final BeaconBlock block) { return isMergeBlock(genericState, block) || isMergeComplete(genericState); } - - public UInt64 computeTimestampAtSlot(final BeaconState state, final UInt64 slot) { - return state.getGenesis_time().plus(slot.times(specConfig.getSecondsPerSlot())); - } } diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecInvocationContextProvider.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecInvocationContextProvider.java index 3492721f2f6..def1ce636c6 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecInvocationContextProvider.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecInvocationContextProvider.java @@ -128,6 +128,34 @@ public SpecMilestone getSpecMilestone() { public Eth2Network getNetwork() { return network; } + + public void assumeIsOneOf(SpecMilestone... milestones) { + Assumptions.assumeTrue(List.of(milestones).contains(specMilestone), "Milestone skipped"); + } + + public void assumeIsOneOf(Eth2Network... networks) { + Assumptions.assumeTrue(List.of(networks).contains(network), "Network skipped"); + } + + public void assumeIsNotOneOf(SpecMilestone... milestones) { + Assumptions.assumeFalse(List.of(milestones).contains(specMilestone), "Milestone skipped"); + } + + public void assumeIsNotOneOf(Eth2Network... networks) { + Assumptions.assumeFalse(List.of(networks).contains(network), "Network skipped"); + } + + public void assumeMilestoneActive(SpecMilestone milestone) { + Assumptions.assumeTrue(specMilestone.isGreaterThanOrEqualTo(milestone), "Milestone skipped"); + } + + public void assumeMergeActive() { + assumeMilestoneActive(SpecMilestone.MERGE); + } + + public void assumeAltairActive() { + assumeMilestoneActive(SpecMilestone.ALTAIR); + } } public static class SpecContextParameterResolver implements ParameterResolver { @@ -151,41 +179,4 @@ public Object resolveParameter( return data; } } - - public static class SpecContextExecutionHelper { - public static void skip(SpecContext specContext, SpecMilestone... milestones) { - Assumptions.assumeFalse( - List.of(milestones).contains(specContext.getSpecMilestone()), "Milestone skipped"); - } - - public static void only(SpecContext specContext, SpecMilestone... milestones) { - Assumptions.assumeTrue( - List.of(milestones).contains(specContext.getSpecMilestone()), "Milestone skipped"); - } - - public static void skip(SpecContext specContext, Eth2Network... networks) { - Assumptions.assumeFalse( - List.of(networks).contains(specContext.getNetwork()), "Network skipped"); - } - - public static void only(SpecContext specContext, Eth2Network... networks) { - Assumptions.assumeTrue( - List.of(networks).contains(specContext.getNetwork()), "Network skipped"); - } - - public static void onlyPhase0(SpecContext specContext) { - Assumptions.assumeTrue( - specContext.getSpecMilestone().equals(SpecMilestone.PHASE0), "Milestone skipped"); - } - - public static void onlyAltair(SpecContext specContext) { - Assumptions.assumeTrue( - specContext.getSpecMilestone().equals(SpecMilestone.ALTAIR), "Milestone skipped"); - } - - public static void onlyMerge(SpecContext specContext) { - Assumptions.assumeTrue( - specContext.getSpecMilestone().equals(SpecMilestone.MERGE), "Milestone skipped"); - } - } } diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlockValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlockValidator.java index c4822c3cb42..db0338628aa 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlockValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/BlockValidator.java @@ -19,6 +19,7 @@ import static tech.pegasys.teku.util.config.Constants.VALID_BLOCK_SET_SIZE; import com.google.common.base.Objects; +import java.util.Optional; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,6 +34,7 @@ import tech.pegasys.teku.spec.constants.Domain; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; import tech.pegasys.teku.spec.datastructures.forkchoice.ReadOnlyStore; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.storage.client.RecentChainData; @@ -101,19 +103,37 @@ public SafeFuture validate(SignedBeaconBlock block) { parentBlock.get().getSlot().max(firstSlotInBlockEpoch), block.getParentRoot())) .thenApply( - postState -> { - if (postState.isEmpty()) { + maybePostState -> { + if (maybePostState.isEmpty()) { LOG.trace( "Block was available but state wasn't. Must have been pruned by finalized."); return InternalValidationResult.IGNORE; } - if (!blockIsProposedByTheExpectedProposer(block, postState.get())) { + final BeaconState postState = maybePostState.get(); + + if (!blockIsProposedByTheExpectedProposer(block, postState)) { return reject( "Block proposed by incorrect proposer (%s)", block.getProposerIndex()); } - if (!blockSignatureIsValidWithRespectToProposerIndex( - block, postState.get())) { + if (spec.atSlot(block.getSlot()).miscHelpers().isMergeComplete(postState)) { + Optional executionPayload = + block.getMessage().getBody().getOptionalExecutionPayload(); + + if (executionPayload.isEmpty()) { + return reject("Missing execution payload"); + } + + if (executionPayload + .get() + .getTimestamp() + .compareTo(spec.computeTimeAtSlot(postState, block.getSlot())) + != 0) { + return reject( + "Execution Payload timestamp is not consistence with and block slot time"); + } + } + if (!blockSignatureIsValidWithRespectToProposerIndex(block, postState)) { return reject("Block signature is invalid"); } return InternalValidationResult.ACCEPT; diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/validation/BlockValidatorTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/validation/BlockValidatorTest.java index 4b94695e2ac..f8d510f35fd 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/validation/BlockValidatorTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/validation/BlockValidatorTest.java @@ -19,65 +19,85 @@ import java.util.List; import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; import tech.pegasys.teku.bls.BLSKeyGenerator; import tech.pegasys.teku.bls.BLSKeyPair; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.bls.BLSTestUtil; import tech.pegasys.teku.core.ChainBuilder; +import tech.pegasys.teku.core.ChainBuilder.BlockOptions; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.TestSpecContext; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider.SpecContext; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState; -import tech.pegasys.teku.statetransition.BeaconChainUtil; +import tech.pegasys.teku.spec.logic.common.block.AbstractBlockProcessor; import tech.pegasys.teku.storage.client.ChainUpdater; -import tech.pegasys.teku.storage.client.MemoryOnlyRecentChainData; import tech.pegasys.teku.storage.client.RecentChainData; import tech.pegasys.teku.storage.storageSystem.InMemoryStorageSystemBuilder; import tech.pegasys.teku.storage.storageSystem.StorageSystem; +@TestSpecContext(milestone = {SpecMilestone.ALTAIR, SpecMilestone.MERGE}) public class BlockValidatorTest { - private final Spec spec = TestSpecFactory.createMinimalPhase0(); - private final RecentChainData recentChainData = - MemoryOnlyRecentChainData.builder().specProvider(spec).build(); - private final BeaconChainUtil beaconChainUtil = BeaconChainUtil.create(spec, 10, recentChainData); + private Spec spec; + private RecentChainData recentChainData; + private StorageSystem storageSystem; private BlockValidator blockValidator; + @BeforeAll + public static void initSession() { + AbstractBlockProcessor.BLS_VERIFY_DEPOSIT = false; + } + + @AfterAll + public static void resetSession() { + AbstractBlockProcessor.BLS_VERIFY_DEPOSIT = true; + } + @BeforeEach - void setUp() { - beaconChainUtil.initializeStorage(); + void setUp(SpecContext specContext) { + spec = specContext.getSpec(); + storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); + storageSystem.chainUpdater().initializeGenesis(false); + recentChainData = storageSystem.recentChainData(); blockValidator = new BlockValidator(spec, recentChainData); } - @Test - void shouldReturnValidForValidBlock() throws Exception { + @TestTemplate + void shouldReturnValidForValidBlock() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - beaconChainUtil.setSlot(nextSlot); - final SignedBeaconBlock block = beaconChainUtil.createBlockAtSlot(nextSlot); + final SignedBlockAndState signedBlockAndState = + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot); + final SignedBeaconBlock block = signedBlockAndState.getBlock(); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); InternalValidationResult result = blockValidator.validate(block).join(); assertTrue(result.isAccept()); } - @Test - void shouldIgnoreAlreadyImportedBlock() throws Exception { - final SignedBeaconBlock block = - beaconChainUtil.createAndImportBlockAtSlot(recentChainData.getHeadSlot().plus(ONE)); + @TestTemplate + void shouldIgnoreAlreadyImportedBlock() { + final SignedBeaconBlock block = storageSystem.chainUpdater().advanceChain().getBlock(); InternalValidationResult result = blockValidator.validate(block).join(); assertTrue(result.isIgnore()); } - @Test - void shouldReturnInvalidForSecondValidBlockForSlotAndProposer() throws Exception { + @TestTemplate + void shouldReturnInvalidForSecondValidBlockForSlotAndProposer() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - beaconChainUtil.setSlot(nextSlot); - final SignedBeaconBlock block = beaconChainUtil.createBlockAtSlot(nextSlot); + final SignedBlockAndState signedBlockAndState = + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot); + final SignedBeaconBlock block = signedBlockAndState.getBlock(); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); InternalValidationResult result1 = blockValidator.validate(block).join(); assertTrue(result1.isAccept()); @@ -86,21 +106,23 @@ void shouldReturnInvalidForSecondValidBlockForSlotAndProposer() throws Exception assertTrue(result2.isIgnore()); } - @Test - void shouldReturnSavedForFutureForBlockFromFuture() throws Exception { + @TestTemplate + void shouldReturnSavedForFutureForBlockFromFuture() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - final SignedBeaconBlock block = beaconChainUtil.createBlockAtSlot(nextSlot); + final SignedBeaconBlock block = + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot).getBlock(); InternalValidationResult result = blockValidator.validate(block).join(); assertTrue(result.isSaveForFuture()); } - @Test - void shouldReturnSavedForFutureForBlockWithParentUnavailable() throws Exception { + @TestTemplate + void shouldReturnSavedForFutureForBlockWithParentUnavailable() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - beaconChainUtil.setSlot(nextSlot); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); - final SignedBeaconBlock signedBlock = beaconChainUtil.createBlockAtSlot(nextSlot); + final SignedBeaconBlock signedBlock = + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot).getBlock(); final UInt64 proposerIndex = signedBlock.getMessage().getProposerIndex(); final BeaconBlock block = new BeaconBlock( @@ -112,7 +134,8 @@ void shouldReturnSavedForFutureForBlockWithParentUnavailable() throws Exception signedBlock.getMessage().getBody()); BLSSignature blockSignature = - beaconChainUtil + storageSystem + .chainBuilder() .getSigner(proposerIndex.intValue()) .signBlock(block, recentChainData.getBestState().orElseThrow().getForkInfo()) .join(); @@ -123,26 +146,31 @@ void shouldReturnSavedForFutureForBlockWithParentUnavailable() throws Exception assertTrue(result.isSaveForFuture()); } - @Test - void shouldReturnInvalidForBlockOlderThanFinalizedSlot() throws Exception { + @TestTemplate + void shouldReturnInvalidForBlockOlderThanFinalizedSlot() { UInt64 finalizedEpoch = UInt64.valueOf(10); UInt64 finalizedSlot = spec.computeStartSlotAtEpoch(finalizedEpoch); - final SignedBeaconBlock block = beaconChainUtil.createBlockAtSlot(finalizedSlot.minus(ONE)); - beaconChainUtil.finalizeChainAtEpoch(finalizedEpoch); - beaconChainUtil.setSlot(recentChainData.getHeadSlot()); + storageSystem.chainUpdater().advanceChain(finalizedSlot); + storageSystem.chainUpdater().finalizeEpoch(finalizedEpoch); + + StorageSystem storageSystem2 = InMemoryStorageSystemBuilder.buildDefault(spec); + storageSystem2.chainUpdater().initializeGenesis(false); + final SignedBeaconBlock block = + storageSystem2.chainBuilder().generateBlockAtSlot(finalizedSlot.minus(ONE)).getBlock(); InternalValidationResult result = blockValidator.validate(block).join(); assertTrue(result.isIgnore()); } - @Test - void shouldReturnInvalidForBlockWithWrongProposerIndex() throws Exception { + @TestTemplate + void shouldReturnInvalidForBlockWithWrongProposerIndex() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - beaconChainUtil.setSlot(nextSlot); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); - final SignedBeaconBlock signedBlock = beaconChainUtil.createBlockAtSlot(nextSlot); + final SignedBeaconBlock signedBlock = + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot).getBlock(); - UInt64 invalidProposerIndex = signedBlock.getMessage().getProposerIndex().minus(ONE); + UInt64 invalidProposerIndex = signedBlock.getMessage().getProposerIndex().plus(ONE); final BeaconBlock block = new BeaconBlock( @@ -154,7 +182,8 @@ void shouldReturnInvalidForBlockWithWrongProposerIndex() throws Exception { signedBlock.getMessage().getBody()); BLSSignature blockSignature = - beaconChainUtil + storageSystem + .chainBuilder() .getSigner(invalidProposerIndex.intValue()) .signBlock(block, recentChainData.getBestState().orElseThrow().getForkInfo()) .join(); @@ -165,22 +194,22 @@ void shouldReturnInvalidForBlockWithWrongProposerIndex() throws Exception { assertTrue(result.isReject()); } - @Test - void shouldReturnInvalidForBlockWithWrongSignature() throws Exception { + @TestTemplate + void shouldReturnInvalidForBlockWithWrongSignature() { final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); - beaconChainUtil.setSlot(nextSlot); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); final SignedBeaconBlock block = SignedBeaconBlock.create( spec, - beaconChainUtil.createBlockAtSlot(nextSlot).getMessage(), + storageSystem.chainBuilder().generateBlockAtSlot(nextSlot).getBlock().getMessage(), BLSTestUtil.randomSignature(0)); InternalValidationResult result = blockValidator.validate(block).join(); assertTrue(result.isReject()); } - @Test + @TestTemplate void shouldReturnInvalidForBlockThatDoesNotDescendFromFinalizedCheckpoint() { List VALIDATOR_KEYS = BLSKeyGenerator.generateKeyPairs(4); @@ -210,4 +239,49 @@ void shouldReturnInvalidForBlockThatDoesNotDescendFromFinalizedCheckpoint() { blockValidator.validate(blockAndState.getBlock()); assertThat(result).isCompletedWithValueMatching(InternalValidationResult::isReject); } + + @TestTemplate + void shouldReturnAcceptOnCorrectExecutionPayloadTimestamp(SpecContext specContext) { + specContext.assumeMergeActive(); + + storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); + storageSystem.chainUpdater().initializeGenesisWithPayload(false); + recentChainData = storageSystem.recentChainData(); + blockValidator = new BlockValidator(spec, recentChainData); + + final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); + + SignedBeaconBlock block = storageSystem.chainBuilder().generateBlockAtSlot(nextSlot).getBlock(); + + InternalValidationResult result = blockValidator.validate(block).join(); + assertTrue(result.isAccept()); + } + + @TestTemplate + void shouldReturnInvalidOnWrongExecutionPayloadTimestamp(SpecContext specContext) { + specContext.assumeMergeActive(); + + storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); + storageSystem.chainUpdater().initializeGenesisWithPayload(false); + recentChainData = storageSystem.recentChainData(); + blockValidator = new BlockValidator(spec, recentChainData); + + final UInt64 nextSlot = recentChainData.getHeadSlot().plus(ONE); + storageSystem.chainUpdater().setCurrentSlot(nextSlot); + + final SignedBlockAndState signedBlockAndState = + storageSystem + .chainBuilder() + .generateBlockAtSlot( + nextSlot, + BlockOptions.create() + .setSkipStateTransition(true) + .setExecutionPayload( + specContext.getDataStructureUtil().randomExecutionPayload())); + + InternalValidationResult result = + blockValidator.validate(signedBlockAndState.getBlock()).join(); + assertTrue(result.isReject()); + } } diff --git a/ethereum/statetransition/src/testFixtures/java/tech/pegasys/teku/statetransition/BeaconChainUtil.java b/ethereum/statetransition/src/testFixtures/java/tech/pegasys/teku/statetransition/BeaconChainUtil.java index 3cd6c90ffe9..a004723676a 100644 --- a/ethereum/statetransition/src/testFixtures/java/tech/pegasys/teku/statetransition/BeaconChainUtil.java +++ b/ethereum/statetransition/src/testFixtures/java/tech/pegasys/teku/statetransition/BeaconChainUtil.java @@ -312,7 +312,9 @@ private SignedBlockAndState createBlockAndStateAtSlot( exits, eth1Data, Optional.empty(), - Optional.empty()); + Optional.empty(), + Optional.empty(), + false); } public void finalizeChainAtEpoch(final UInt64 epoch) throws Exception {