Skip to content

Commit

Permalink
attestation processing logic (Consensys#8260)
Browse files Browse the repository at this point in the history
Co-authored-by: Lucas Saldanha <[email protected]>
  • Loading branch information
tbenr and lucassaldanha authored Apr 30, 2024
1 parent bdb39f6 commit e26a8ea
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public interface AttestationDataValidator

enum AttestationInvalidReason implements OperationInvalidReason {
COMMITTEE_INDEX_TOO_HIGH("CommitteeIndex too high"),
COMMITTEE_INDEX_MUST_BE_ZERO("CommitteeIndex must be set to zero"),
PARTICIPANTS_COUNT_MISMATCH("Attesting participants count do not match aggregation bits"),
NOT_FROM_CURRENT_OR_PREVIOUS_EPOCH("Attestation not from current or previous epoch"),
SLOT_NOT_IN_EPOCH("Attestation slot not in specified epoch"),
SUBMITTED_TOO_QUICKLY("Attestation submitted too quickly"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@
import tech.pegasys.teku.spec.logic.versions.bellatrix.util.BlindBlockUtilBellatrix;
import tech.pegasys.teku.spec.logic.versions.capella.operations.validation.OperationValidatorCapella;
import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb;
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.electra.block.BlockProcessorElectra;
import tech.pegasys.teku.spec.logic.versions.electra.forktransition.ElectraStateUpgrade;
import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateAccessorsElectra;
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.AttestationDataValidatorElectra;
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.logic.versions.electra.util.AttestationUtilElectra;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;

public class SpecLogicElectra extends AbstractSpecLogic {
Expand Down Expand Up @@ -114,9 +114,9 @@ public static SpecLogicElectra create(
new BeaconStateUtil(
config, schemaDefinitions, predicates, miscHelpers, beaconStateAccessors);
final AttestationUtil attestationUtil =
new AttestationUtilDeneb(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
new AttestationUtilElectra(config, schemaDefinitions, beaconStateAccessors, miscHelpers);
final AttestationDataValidator attestationDataValidator =
new AttestationDataValidatorDeneb(config, miscHelpers, beaconStateAccessors);
new AttestationDataValidatorElectra(config, miscHelpers, beaconStateAccessors);
final VoluntaryExitValidatorElectra voluntaryExitValidatorElectra =
new VoluntaryExitValidatorElectra(config, predicates, beaconStateAccessors);
final OperationValidator operationValidator =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH;
import static tech.pegasys.teku.spec.config.SpecConfigElectra.FULL_EXIT_REQUEST_AMOUNT;

import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
Expand All @@ -26,6 +28,7 @@
import tech.pegasys.teku.infrastructure.bytes.Bytes20;
import tech.pegasys.teku.infrastructure.ssz.SszList;
import tech.pegasys.teku.infrastructure.ssz.SszMutableList;
import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist;
import tech.pegasys.teku.infrastructure.ssz.primitive.SszByte;
import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
Expand All @@ -37,6 +40,7 @@
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerWithdrawalRequest;
import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.datastructures.state.Validator;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.MutableBeaconState;
Expand All @@ -47,6 +51,8 @@
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators.ValidatorExitContext;
import tech.pegasys.teku.spec.logic.common.helpers.Predicates;
import tech.pegasys.teku.spec.logic.common.operations.OperationSignatureVerifier;
import tech.pegasys.teku.spec.logic.common.operations.validation.AttestationDataValidator.AttestationInvalidReason;
import tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason;
import tech.pegasys.teku.spec.logic.common.operations.validation.OperationValidator;
import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException;
import tech.pegasys.teku.spec.logic.common.util.AttestationUtil;
Expand Down Expand Up @@ -371,6 +377,50 @@ protected void addValidatorToRegistry(
stateElectra.setPendingBalanceDeposits(pendingBalanceDeposits);
}

@Override
protected void assertAttestationValid(
final MutableBeaconState state, final Attestation attestation) {
super.assertAttestationValid(state, attestation);
final List<UInt64> committeeIndices = attestation.getCommitteeIndicesRequired();
final UInt64 committeeCountPerSlot =
beaconStateAccessors.getCommitteeCountPerSlot(
state, attestation.getData().getTarget().getEpoch());
beaconStateAccessors.getCommitteeCountPerSlot(
state, attestation.getData().getTarget().getEpoch());
final SszBitlist aggregationBits = attestation.getAggregationBits();
final Optional<OperationInvalidReason> committeeCheckResult =
checkCommittees(
committeeIndices,
committeeCountPerSlot,
state,
attestation.getData().getSlot(),
aggregationBits);
if (committeeCheckResult.isPresent()) {
throw new IllegalArgumentException(committeeCheckResult.get().describe());
}
}

private Optional<OperationInvalidReason> checkCommittees(
final List<UInt64> committeeIndices,
final UInt64 committeeCountPerSlot,
final BeaconState state,
final UInt64 slot,
final SszBitlist aggregationBits) {
int participantsCount = 0;
for (final UInt64 committeeIndex : committeeIndices) {
if (committeeIndex.isGreaterThan(committeeCountPerSlot)) {
return Optional.of(AttestationInvalidReason.COMMITTEE_INDEX_TOO_HIGH);
}
final IntList committee =
beaconStateAccessors.getBeaconCommittee(state, slot, committeeIndex);
participantsCount += committee.size();
}
if (participantsCount != aggregationBits.size()) {
return Optional.of(AttestationInvalidReason.PARTICIPANTS_COUNT_MISMATCH);
}
return Optional.empty();
}

protected Validator getValidatorFromDeposit(
final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials) {
final UInt64 effectiveBalance = UInt64.ZERO;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 java.util.Optional;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.operations.AttestationData;
import tech.pegasys.teku.spec.datastructures.state.Fork;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors;
import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers;
import tech.pegasys.teku.spec.logic.common.operations.validation.AttestationDataValidator;
import tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason;

public class AttestationDataValidatorElectra implements AttestationDataValidator {

private final SpecConfig specConfig;
private final MiscHelpers miscHelpers;
private final BeaconStateAccessors beaconStateAccessors;

public AttestationDataValidatorElectra(
final SpecConfig specConfig,
final MiscHelpers miscHelpers,
final BeaconStateAccessors beaconStateAccessors) {
this.specConfig = specConfig;
this.miscHelpers = miscHelpers;
this.beaconStateAccessors = beaconStateAccessors;
}

@Override
public Optional<OperationInvalidReason> validate(
final Fork fork, final BeaconState state, final AttestationData data) {
return firstOf(
() ->
check(
data.getTarget().getEpoch().equals(beaconStateAccessors.getPreviousEpoch(state))
|| data.getTarget()
.getEpoch()
.equals(beaconStateAccessors.getCurrentEpoch(state)),
AttestationInvalidReason.NOT_FROM_CURRENT_OR_PREVIOUS_EPOCH),
() ->
check(
data.getTarget().getEpoch().equals(miscHelpers.computeEpochAtSlot(data.getSlot())),
AttestationInvalidReason.SLOT_NOT_IN_EPOCH),
() ->
check(
data.getSlot()
.plus(specConfig.getMinAttestationInclusionDelay())
.compareTo(state.getSlot())
<= 0,
AttestationInvalidReason.SUBMITTED_TOO_QUICKLY),
() ->
check(
data.getIndex().equals(UInt64.ZERO),
AttestationInvalidReason.COMMITTEE_INDEX_MUST_BE_ZERO),
() -> {
if (data.getTarget().getEpoch().equals(beaconStateAccessors.getCurrentEpoch(state))) {
return check(
data.getSource().equals(state.getCurrentJustifiedCheckpoint()),
AttestationInvalidReason.INCORRECT_CURRENT_JUSTIFIED_CHECKPOINT);
} else {
return check(
data.getSource().equals(state.getPreviousJustifiedCheckpoint()),
AttestationInvalidReason.INCORRECT_PREVIOUS_JUSTIFIED_CHECKPOINT);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.util;

import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
import java.util.stream.IntStream;
import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors;
import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers;
import tech.pegasys.teku.spec.logic.versions.deneb.util.AttestationUtilDeneb;
import tech.pegasys.teku.spec.schemas.SchemaDefinitions;

public class AttestationUtilElectra extends AttestationUtilDeneb {
public AttestationUtilElectra(
final SpecConfig specConfig,
final SchemaDefinitions schemaDefinitions,
final BeaconStateAccessors beaconStateAccessors,
final MiscHelpers miscHelpers) {
super(specConfig, schemaDefinitions, beaconStateAccessors, miscHelpers);
}

/**
* Return the attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``.
*
* @param state
* @param attestation
* @return
* @throws IllegalArgumentException
* @see
* <a>https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#modified-get_attesting_indices</a>
*/
@Override
public IntList getAttestingIndices(final BeaconState state, final Attestation attestation) {
final List<UInt64> committeeIndices = attestation.getCommitteeIndicesRequired();
final SszBitlist aggregationBits = attestation.getAggregationBits();
final IntList attestingIndices = new IntArrayList();
int committeeOffset = 0;
for (final UInt64 committeeIndex : committeeIndices) {
final IntList committee =
beaconStateAccessors.getBeaconCommittee(
state, attestation.getData().getSlot(), committeeIndex);
final IntList committeeAttesters =
getCommitteeAttesters(committee, aggregationBits, committeeOffset);
attestingIndices.addAll(committeeAttesters);
committeeOffset += committee.size();
}
return attestingIndices;
}

public IntList getCommitteeAttesters(
final IntList committee, final SszBitlist aggregationBits, final int committeeOffset) {
return IntList.of(
streamCommitteeAttesters(committee, aggregationBits, committeeOffset).toArray());
}

public IntStream streamCommitteeAttesters(
final IntList committee, final SszBitlist aggregationBits, final int committeeOffset) {
return IntStream.range(committeeOffset, committeeOffset + committee.size())
.filter(aggregationBits::isSet)
.map(attesterIndex -> committee.getInt(attesterIndex - committeeOffset));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators.ValidatorExitContext;
import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException;
import tech.pegasys.teku.spec.logic.versions.deneb.block.BlockProcessorDenebTest;
import tech.pegasys.teku.spec.logic.versions.electra.util.AttestationUtilElectra;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra;

class BlockProcessorElectraTest extends BlockProcessorDenebTest {
Expand Down Expand Up @@ -584,6 +585,12 @@ public void processExecutionLayerWithdrawalRequests_PartialWithdrawalRequestForE
.isEqualTo(UInt64.valueOf(1_261));
}

@Test
void shouldUseElectraAttestationUtil() {
assertThat(spec.getGenesisSpec().getAttestationUtil())
.isInstanceOf(AttestationUtilElectra.class);
}

private Supplier<ValidatorExitContext> validatorExitContextSupplier(final BeaconState state) {
return spec.getGenesisSpec().beaconStateMutators().createValidatorExitContextSupplier(state);
}
Expand Down

0 comments on commit e26a8ea

Please sign in to comment.