Skip to content

Commit

Permalink
[Merge] implement ExecutionPayload timestamp validation in BlockValid…
Browse files Browse the repository at this point in the history
…ator (#4730)

* implements ExecutionPayload timestamp validation in BlockValidator

* implements test block production without state transition, to be able to generate invalid blocks.

* Improves SpecContext assumptions helpers

* improves a ValidatorDataProvider test
  • Loading branch information
tbenr authored Dec 6, 2021
1 parent 56cb885 commit 8c51458
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -72,7 +72,8 @@ public SignedBlockAndState createNewBlock(
final SszList<Deposit> deposits,
final SszList<SignedVoluntaryExit> exits,
final Optional<List<Bytes>> transactions,
final Optional<Bytes32> terminalBlock)
final Optional<Bytes32> terminalBlock,
final Optional<ExecutionPayload> executionPayload)
throws StateTransitionException, EpochProcessingException, SlotProcessingException {

final UInt64 newEpoch = spec.computeEpochAtSlot(newSlot);
Expand Down Expand Up @@ -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();
Expand All @@ -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<Attestation> attestations,
final SszList<ProposerSlashing> slashings,
final SszList<Deposit> deposits,
final SszList<SignedVoluntaryExit> exits,
final Optional<List<Bytes>> transactions,
final Optional<Bytes32> terminalBlock,
final Optional<ExecutionPayload> 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,
Expand Down Expand Up @@ -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(),
Expand All @@ -163,9 +237,26 @@ public SignedBlockAndState createBlock(
final Optional<SszList<SignedVoluntaryExit>> exits,
final Optional<Eth1Data> eth1Data,
final Optional<List<Bytes>> transactions,
final Optional<Bytes32> terminalBlock)
final Optional<Bytes32> terminalBlock,
final Optional<ExecutionPayload> 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,
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}

Expand All @@ -521,6 +524,8 @@ public static final class BlockOptions {
private Optional<Eth1Data> eth1Data = Optional.empty();
private Optional<List<Bytes>> transactions = Optional.empty();
private Optional<Bytes32> terminalBlockHash = Optional.empty();
private Optional<ExecutionPayload> executionPayload = Optional.empty();
private boolean skipStateTransition = false;

private BlockOptions() {}

Expand Down Expand Up @@ -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<Attestation> getAttestations() {
return attestations;
}
Expand All @@ -563,5 +578,13 @@ public Optional<List<Bytes>> getTransactions() {
public Optional<Bytes32> getTerminalBlockHash() {
return terminalBlockHash;
}

public Optional<ExecutionPayload> getExecutionPayload() {
return executionPayload;
}

public boolean getSkipStateTransition() {
return skipStateTransition;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> implements ParameterResolver {
Expand All @@ -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");
}
}
}
Loading

0 comments on commit 8c51458

Please sign in to comment.