Skip to content

Commit

Permalink
EIP7251 - voluntary exits operation validation (Consensys#8237)
Browse files Browse the repository at this point in the history
Extended the voluntary exits validation for electra, where voluntary exits can't be processed if there are pending withdrawals in the queue.

Structured this validation to be individually tested but didn't refactor the rest. also looks like a lot of the specLogic classes have heavily duplicated code but this is not really new, so I left it.

partially addresses Consensys#8149

Signed-off-by: Paul Harris <paul.harris@consensys.net>
  • Loading branch information
rolfyone authored Apr 22, 2024
1 parent 283f5d6 commit 533fcc8
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import tech.pegasys.teku.spec.logic.versions.bellatrix.helpers.BellatrixTransitionHelpers;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;
import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsAltair;

Expand Down Expand Up @@ -110,9 +111,15 @@ public static SpecLogicAltair create(
// execution change
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidator voluntaryExitValidator =
new VoluntaryExitValidator(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorPhase0(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
final ValidatorStatusFactoryAltair validatorStatusFactory =
new ValidatorStatusFactoryAltair(
config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import tech.pegasys.teku.spec.logic.versions.bellatrix.util.BlindBlockUtilBellatrix;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;
import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix;

Expand Down Expand Up @@ -114,9 +115,15 @@ public static SpecLogicBellatrix create(
new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidator voluntaryExitValidator =
new VoluntaryExitValidator(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorPhase0(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
final ValidatorStatusFactoryAltair validatorStatusFactory =
new ValidatorStatusFactoryAltair(
config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import tech.pegasys.teku.spec.logic.versions.capella.operations.validation.OperationValidatorCapella;
import tech.pegasys.teku.spec.logic.versions.capella.statetransition.epoch.EpochProcessorCapella;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;
import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsCapella;

Expand Down Expand Up @@ -114,9 +115,15 @@ public static SpecLogicCapella create(
new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidator voluntaryExitValidator =
new VoluntaryExitValidator(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorCapella(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
final ValidatorStatusFactoryAltair validatorStatusFactory =
new ValidatorStatusFactoryAltair(
config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package tech.pegasys.teku.spec.logic.versions.capella.operations.validation;

import java.util.Optional;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.operations.BlsToExecutionChange;
import tech.pegasys.teku.spec.datastructures.state.Fork;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
Expand All @@ -24,19 +23,25 @@
import tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason;
import tech.pegasys.teku.spec.logic.common.util.AttestationUtil;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;

public class OperationValidatorCapella extends OperationValidatorPhase0 {

private final BlsToExecutionChangesValidator blsToExecutionChangesValidator =
new BlsToExecutionChangesValidator();

public OperationValidatorCapella(
final SpecConfig specConfig,
final Predicates predicates,
final BeaconStateAccessors beaconStateAccessors,
final AttestationDataValidator attestationDataValidator,
final AttestationUtil attestationUtil) {
super(specConfig, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
final AttestationUtil attestationUtil,
final VoluntaryExitValidator voluntaryExitValidator) {
super(
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import tech.pegasys.teku.spec.logic.versions.deneb.operations.validation.AttestationDataValidatorDeneb;
import tech.pegasys.teku.spec.logic.versions.deneb.util.AttestationUtilDeneb;
import tech.pegasys.teku.spec.logic.versions.deneb.util.ForkChoiceUtilDeneb;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb;

public class SpecLogicDeneb extends AbstractSpecLogic {
Expand Down Expand Up @@ -114,9 +115,15 @@ public static SpecLogicDeneb create(
new AttestationUtilDeneb(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorDeneb(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidator voluntaryExitValidator =
new VoluntaryExitValidator(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorCapella(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
final ValidatorStatusFactoryAltair validatorStatusFactory =
new ValidatorStatusFactoryAltair(
config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateMutatorsElectra;
import tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra;
import tech.pegasys.teku.spec.logic.versions.electra.helpers.PredicatesElectra;
import tech.pegasys.teku.spec.logic.versions.electra.operations.validation.VoluntaryExitValidatorElectra;
import tech.pegasys.teku.spec.logic.versions.electra.statetransition.epoch.EpochProcessorElectra;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;

Expand Down Expand Up @@ -116,9 +117,15 @@ public static SpecLogicElectra create(
new AttestationUtilDeneb(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorDeneb(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidatorElectra voluntaryExitValidatorElectra =
new VoluntaryExitValidatorElectra(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorCapella(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidatorElectra);
final ValidatorStatusFactoryAltair validatorStatusFactory =
new ValidatorStatusFactoryAltair(
config,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.
*/

package tech.pegasys.teku.spec.logic.versions.electra.operations.validation;

import static tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason.check;
import static tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason.firstOf;

import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit;
import tech.pegasys.teku.spec.datastructures.state.Fork;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra;
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors;
import tech.pegasys.teku.spec.logic.common.helpers.Predicates;
import tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason;
import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateAccessorsElectra;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;

public class VoluntaryExitValidatorElectra extends VoluntaryExitValidator {
private final BeaconStateAccessorsElectra stateAccessorsElectra;

public VoluntaryExitValidatorElectra(
final SpecConfig specConfig,
final Predicates predicates,
final BeaconStateAccessors beaconStateAccessors) {
super(specConfig, predicates, beaconStateAccessors);
this.stateAccessorsElectra = BeaconStateAccessorsElectra.required(beaconStateAccessors);
}

@Override
public Optional<OperationInvalidReason> validate(
final Fork fork, final BeaconState state, final SignedVoluntaryExit signedExit) {
final BeaconStateElectra stateElectra = BeaconStateElectra.required(state);
return firstOf(
() -> super.validate(fork, state, signedExit),
() -> validateElectraConditions(stateElectra, signedExit));
}

@VisibleForTesting
Optional<OperationInvalidReason> validateElectraConditions(
final BeaconStateElectra stateElectra, final SignedVoluntaryExit exit) {
final int validatorId = exit.getValidatorId();
return check(
stateAccessorsElectra
.getPendingBalanceToWithdraw(stateElectra, validatorId)
.equals(UInt64.ZERO),
VoluntaryExitValidator.ExitInvalidReason.pendingWithdrawalsInQueue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import tech.pegasys.teku.spec.logic.versions.phase0.helpers.BeaconStateAccessorsPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.VoluntaryExitValidator;
import tech.pegasys.teku.spec.logic.versions.phase0.statetransition.epoch.EpochProcessorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.statetransition.epoch.ValidatorStatusFactoryPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0;
Expand Down Expand Up @@ -100,9 +101,15 @@ public static SpecLogicPhase0 create(
new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidator voluntaryExitValidator =
new VoluntaryExitValidator(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
new OperationValidatorPhase0(
config, predicates, beaconStateAccessors, attestationDataValidator, attestationUtil);
predicates,
beaconStateAccessors,
attestationDataValidator,
attestationUtil,
voluntaryExitValidator);
final ValidatorStatusFactoryPhase0 validatorStatusFactory =
new ValidatorStatusFactoryPhase0(
config, beaconStateUtil, attestationUtil, beaconStateAccessors, predicates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
package tech.pegasys.teku.spec.logic.versions.phase0.operations.validation;

import java.util.Optional;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.operations.AttestationData;
import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing;
import tech.pegasys.teku.spec.datastructures.operations.BlsToExecutionChange;
Expand All @@ -38,18 +37,17 @@ public class OperationValidatorPhase0 implements OperationValidator {
private final VoluntaryExitValidator voluntaryExitValidator;

public OperationValidatorPhase0(
final SpecConfig specConfig,
final Predicates predicates,
final BeaconStateAccessors beaconStateAccessors,
final AttestationDataValidator attestationDataValidator,
final AttestationUtil attestationUtil) {
final AttestationUtil attestationUtil,
final VoluntaryExitValidator voluntaryExitValidator) {
this.attestationDataValidator = attestationDataValidator;
this.attesterSlashingValidator =
new AttesterSlashingValidator(predicates, beaconStateAccessors, attestationUtil);
this.proposerSlashingValidator =
new ProposerSlashingValidator(predicates, beaconStateAccessors);
this.voluntaryExitValidator =
new VoluntaryExitValidator(specConfig, predicates, beaconStateAccessors);
this.voluntaryExitValidator = voluntaryExitValidator;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class VoluntaryExitValidator
private final Predicates predicates;
private final BeaconStateAccessors beaconStateAccessors;

VoluntaryExitValidator(
public VoluntaryExitValidator(
final SpecConfig specConfig,
final Predicates predicates,
final BeaconStateAccessors beaconStateAccessors) {
Expand All @@ -49,7 +49,7 @@ public class VoluntaryExitValidator
@Override
public Optional<OperationInvalidReason> validate(
final Fork fork, final BeaconState state, final SignedVoluntaryExit signedExit) {
VoluntaryExit exit = signedExit.getMessage();
final VoluntaryExit exit = signedExit.getMessage();
return firstOf(
() ->
check(
Expand Down Expand Up @@ -109,5 +109,10 @@ public static OperationInvalidReason validatorTooYoung(final UInt64 exitEpoch) {
public static OperationInvalidReason invalidSignature() {
return () -> "Signature is invalid";
}

public static OperationInvalidReason pendingWithdrawalsInQueue() {
return () ->
"Validator cannot be exited while there are pending withdrawals in the withdrawal queue";
}
}
}
Loading

0 comments on commit 533fcc8

Please sign in to comment.