Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merge] implement ExecutionPayload timestamp validation in BlockValidator #4730

Merged
merged 6 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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