Skip to content

Commit

Permalink
Move current and previous epoch attestations to phase0 specific schem…
Browse files Browse the repository at this point in the history
…a. (#3849)
  • Loading branch information
ajsutton authored Apr 12, 2021
1 parent 6a3de68 commit 71000af
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 23 deletions.
4 changes: 4 additions & 0 deletions ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,10 @@ public List<Integer> getActiveValidatorIndices(final BeaconState state, final UI
return atEpoch(epoch).beaconStateAccessors().getActiveValidatorIndices(state, epoch);
}

public int getPreviousEpochAttestationCapacity(final BeaconState state) {
return atState(state).beaconStateAccessors().getPreviousEpochAttestationCapacity(state);
}

// Validator Utils
public int countActiveValidators(final BeaconState state, final UInt64 epoch) {
return getActiveValidatorIndices(state, epoch).size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data;
import tech.pegasys.teku.spec.datastructures.state.PendingAttestation;
import tech.pegasys.teku.spec.datastructures.state.Validator;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields;
import tech.pegasys.teku.ssz.primitive.SszBytes32;
Expand Down Expand Up @@ -79,18 +78,6 @@ default SszBytes32VectorSchema<?> getRandaoMixesSchema() {
getChildSchema(getFieldIndex(BeaconStateFields.SLASHINGS.name()));
}

@SuppressWarnings("unchecked")
default SszListSchema<PendingAttestation, ?> getPreviousEpochAttestationsSchema() {
return (SszListSchema<PendingAttestation, ?>)
getChildSchema(getFieldIndex(BeaconStateFields.PREVIOUS_EPOCH_ATTESTATIONS.name()));
}

@SuppressWarnings("unchecked")
default SszListSchema<PendingAttestation, ?> getCurrentEpochAttestationsSchema() {
return (SszListSchema<PendingAttestation, ?>)
getChildSchema(getFieldIndex(BeaconStateFields.CURRENT_EPOCH_ATTESTATIONS.name()));
}

default SszBitvectorSchema<?> getJustificationBitsSchema() {
return (SszBitvectorSchema<?>)
getChildSchema(getFieldIndex(BeaconStateFields.JUSTIFICATION_BITS.name()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ private static List<SszField> getUniqueFields(final SpecConfig specConfig) {
return List.of(previousEpochAttestationsField, currentEpochAttestationsField);
}

@SuppressWarnings("unchecked")
public SszListSchema<PendingAttestation, ?> getPreviousEpochAttestationsSchema() {
return (SszListSchema<PendingAttestation, ?>)
getChildSchema(getFieldIndex(BeaconStateFields.PREVIOUS_EPOCH_ATTESTATIONS.name()));
}

@SuppressWarnings("unchecked")
public SszListSchema<PendingAttestation, ?> getCurrentEpochAttestationsSchema() {
return (SszListSchema<PendingAttestation, ?>)
getChildSchema(getFieldIndex(BeaconStateFields.CURRENT_EPOCH_ATTESTATIONS.name()));
}

@Override
public BeaconStatePhase0 createFromBackingNode(TreeNode node) {
return new BeaconStatePhase0Impl(this, node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
import tech.pegasys.teku.ssz.SszList;
import tech.pegasys.teku.ssz.type.Bytes4;

public class BeaconStateAccessors {
public abstract class BeaconStateAccessors {
protected final SpecConfig config;
protected final Predicates predicates;
protected final MiscHelpers miscHelpers;

public BeaconStateAccessors(
protected BeaconStateAccessors(
final SpecConfig config, final Predicates predicates, final MiscHelpers miscHelpers) {
this.config = config;
this.predicates = predicates;
Expand Down Expand Up @@ -200,4 +200,19 @@ private void validateStateCanCalculateProposerIndexAtSlot(
state.getSlot(),
stateEpoch);
}

// Custom accessors

/**
* Calculates how many additional attestations from the previous epoch can be accommodated by this
* state during block processing
*
* @param state The state to be processed, should already be at the slot of the block being
* processed
* @return The remaining capacity for attestations from the previous epoch
*/
public int getPreviousEpochAttestationCapacity(final BeaconState state) {
// No strict limit in general
return Integer.MAX_VALUE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import tech.pegasys.teku.spec.logic.common.util.SyncCommitteeUtil;
import tech.pegasys.teku.spec.logic.common.util.ValidatorsUtil;
import tech.pegasys.teku.spec.logic.versions.phase0.block.BlockProcessorPhase0;
import tech.pegasys.teku.spec.logic.versions.phase0.helpers.BeaconStateAccessorsPhase0;
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.schemas.SchemaDefinitions;
Expand Down Expand Up @@ -74,7 +75,7 @@ public static SpecLogicPhase0 create(
final Predicates predicates = new Predicates();
final MiscHelpers miscHelpers = new MiscHelpers(config);
final BeaconStateAccessors beaconStateAccessors =
new BeaconStateAccessors(config, predicates, miscHelpers);
new BeaconStateAccessorsPhase0(config, predicates, miscHelpers);
final BeaconStateMutators beaconStateMutators =
new BeaconStateMutators(config, miscHelpers, beaconStateAccessors);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2021 ConsenSys AG.
*
* 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.phase0.helpers;

import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.phase0.BeaconStatePhase0;
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.helpers.Predicates;

public class BeaconStateAccessorsPhase0 extends BeaconStateAccessors {
public BeaconStateAccessorsPhase0(
final SpecConfig config, final Predicates predicates, final MiscHelpers miscHelpers) {
super(config, predicates, miscHelpers);
}

// Custom accessors
@Override
public int getPreviousEpochAttestationCapacity(final BeaconState genericState) {
final BeaconStatePhase0 state = BeaconStatePhase0.required(genericState);
final int absoluteMax =
Math.toIntExact(
state.getBeaconStateSchema().getPreviousEpochAttestationsSchema().getMaxLength());
return absoluteMax - state.getPrevious_epoch_attestations().size();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2021 ConsenSys AG.
*
* 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.phase0;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.config.SpecConfig;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors;
import tech.pegasys.teku.spec.util.DataStructureUtil;

public class BeaconStateAccessorsPhase0Test {
private final Spec spec = TestSpecFactory.createMinimalPhase0();
private final SpecConfig config = spec.getGenesisSpecConfig();
private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);
private final BeaconStateAccessors stateAccessors = spec.getGenesisSpec().beaconStateAccessors();

@Test
public void getPreviousEpochAttestationCapacity_intermediateValue() {
final int maxAttestations = config.getMaxAttestations() * config.getSlotsPerEpoch();
final int existingAttCount = maxAttestations / 2;
final BeaconState state = dataStructureUtil.stateBuilderPhase0(5, existingAttCount).build();

assertThat(stateAccessors.getPreviousEpochAttestationCapacity(state))
.isEqualTo(maxAttestations - existingAttCount);
}

@Test
public void getPreviousEpochAttestationCapacity_zero() {
final int maxAttestations = config.getMaxAttestations() * config.getSlotsPerEpoch();
final BeaconState state = dataStructureUtil.stateBuilderPhase0(5, maxAttestations).build();

assertThat(stateAccessors.getPreviousEpochAttestationCapacity(state)).isEqualTo(0);
}

@Test
public void getPreviousEpochAttestationCapacity_max() {
final int maxAttestations = config.getMaxAttestations() * config.getSlotsPerEpoch();
final BeaconState state = dataStructureUtil.stateBuilderPhase0(5, 0).build();

assertThat(stateAccessors.getPreviousEpochAttestationCapacity(state))
.isEqualTo(maxAttestations);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,27 +65,30 @@ public static BeaconStateBuilderAltair create(
spec, dataStructureUtil, defaultValidatorCount, defaultItemsInSSZLists);
}

private BeaconStateSchemaAltair getBeaconStateSchemaAltair() {
return (BeaconStateSchemaAltair) getEmptyState().getSchema();
private BeaconStateSchemaAltair getBeaconStateSchema() {
return (BeaconStateSchemaAltair)
spec.getGenesisSpec().getSchemaDefinitions().getBeaconStateSchema();
}

@Override
protected void initDefaults() {
super.initDefaults();

final BeaconStateSchemaAltair schema = getBeaconStateSchema();

previousEpochParticipation =
dataStructureUtil.randomSszList(
getBeaconStateSchemaAltair().getPreviousEpochParticipationSchema(),
schema.getPreviousEpochParticipationSchema(),
defaultItemsInSSZLists,
dataStructureUtil::randomSszByte);
currentEpochParticipation =
dataStructureUtil.randomSszList(
getBeaconStateSchemaAltair().getCurrentEpochParticipationSchema(),
schema.getCurrentEpochParticipationSchema(),
defaultItemsInSSZLists,
dataStructureUtil::randomSszByte);
inactivityScores =
dataStructureUtil.randomSszUInt64List(
getBeaconStateSchemaAltair().getInactivityScoresSchema(), defaultItemsInSSZLists);
schema.getInactivityScoresSchema(), defaultItemsInSSZLists);
currentSyncCommittee = dataStructureUtil.randomSyncCommittee();
nextSyncCommittee = dataStructureUtil.randomSyncCommittee();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,25 @@ public static BeaconStateBuilderPhase0 create(
spec, dataStructureUtil, defaultValidatorCount, defaultItemsInSSZLists);
}

private BeaconStateSchemaPhase0 getBeaconStateSchema() {
return (BeaconStateSchemaPhase0)
spec.getGenesisSpec().getSchemaDefinitions().getBeaconStateSchema();
}

@Override
protected void initDefaults() {
super.initDefaults();

final BeaconStateSchemaPhase0 schema = getBeaconStateSchema();

previousEpochAttestations =
dataStructureUtil.randomSszList(
dataStructureUtil.getBeaconStateSchema().getPreviousEpochAttestationsSchema(),
schema.getPreviousEpochAttestationsSchema(),
defaultItemsInSSZLists,
dataStructureUtil::randomPendingAttestation);
currentEpochAttestations =
dataStructureUtil.randomSszList(
dataStructureUtil.getBeaconStateSchema().getCurrentEpochAttestationsSchema(),
schema.getCurrentEpochAttestationsSchema(),
defaultItemsInSSZLists,
dataStructureUtil::randomPendingAttestation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,10 @@ public int getSize() {

public synchronized SszList<Attestation> getAttestationsForBlock(
final BeaconState stateAtBlockSlot, final AttestationForkChecker forkChecker) {
final UInt64 currentEpoch = spec.getCurrentEpoch(stateAtBlockSlot);
final int previousEpochLimit = spec.getPreviousEpochAttestationCapacity(stateAtBlockSlot);

final AtomicInteger prevEpochCount = new AtomicInteger(0);
return dataHashBySlot.descendingMap().values().stream()
.flatMap(Collection::stream)
.map(attestationGroupByDataHash::get)
Expand All @@ -168,6 +171,14 @@ public synchronized SszList<Attestation> getAttestationsForBlock(
.flatMap(MatchingDataAttestationGroup::stream)
.limit(ATTESTATIONS_SCHEMA.getMaxLength())
.map(ValidateableAttestation::getAttestation)
.filter(
att -> {
if (spec.computeEpochAtSlot(att.getData().getSlot()).isLessThan(currentEpoch)) {
final int currentCount = prevEpochCount.getAndIncrement();
return currentCount < previousEpochLimit;
}
return true;
})
.collect(ATTESTATIONS_SCHEMA.collector());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import static tech.pegasys.teku.util.config.Constants.ATTESTATION_RETENTION_EPOCHS;
import static tech.pegasys.teku.util.config.Constants.SLOTS_PER_EPOCH;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -56,6 +58,11 @@ class AggregatingAttestationPoolTest {
@BeforeEach
public void setUp() {
when(forkChecker.areAttestationsFromCorrectFork(any())).thenReturn(true);
when(mockSpec.getPreviousEpochAttestationCapacity(any())).thenReturn(Integer.MAX_VALUE);
// Fwd some calls to the real spec
when(mockSpec.computeEpochAtSlot(any()))
.thenAnswer(i -> spec.computeEpochAtSlot(i.getArgument(0)));
when(mockSpec.getCurrentEpoch(any())).thenAnswer(i -> spec.getCurrentEpoch(i.getArgument(0)));
}

@AfterEach
Expand Down Expand Up @@ -197,6 +204,48 @@ public void getAttestationsForBlock_shouldNotAddMoreAttestationsThanAllowedInBlo
.containsExactly(attestation1, attestation2);
}

@Test
void getAttestationsForBlock_shouldLimitPreviousEpochAttestations_capacityOf2() {
testPrevEpochLimits(2);
}

@Test
void getAttestationsForBlock_shouldLimitPreviousEpochAttestations_capacityOf1() {
testPrevEpochLimits(1);
}

@Test
void getAttestationsForBlock_shouldLimitPreviousEpochAttestations_capacityOf0() {
testPrevEpochLimits(0);
}

void testPrevEpochLimits(final int prevEpochCapacity) {
final UInt64 currentEpoch = UInt64.valueOf(5);
final UInt64 startSlotAtCurrentEpoch = spec.computeStartSlotAtEpoch(currentEpoch);
final BeaconState stateAtBlockSlot =
dataStructureUtil.stateBuilderPhase0(10, 20).slot(startSlotAtCurrentEpoch.plus(5)).build();
when(mockSpec.getPreviousEpochAttestationCapacity(stateAtBlockSlot))
.thenReturn(prevEpochCapacity);

final List<Attestation> expectedAttestations = new ArrayList<>();
// Current epoch Attestations
expectedAttestations.add(addAttestationFromValidators(startSlotAtCurrentEpoch.plus(2), 1));
expectedAttestations.add(addAttestationFromValidators(startSlotAtCurrentEpoch.plus(1), 2));
expectedAttestations.add(addAttestationFromValidators(startSlotAtCurrentEpoch, 3));

// Prev epoch attestations within capacity limit
for (int i = 0; i < prevEpochCapacity; i++) {
expectedAttestations.add(
addAttestationFromValidators(startSlotAtCurrentEpoch.minus(i + 1), 3 + i));
}
// Add a few extras
addAttestationFromValidators(startSlotAtCurrentEpoch.minus(3), 1);
addAttestationFromValidators(startSlotAtCurrentEpoch.minus(4), 2);

assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker))
.containsExactlyElementsOf(expectedAttestations);
}

@Test
public void onSlot_shouldPruneAttestationsMoreThanTwoEpochsBehindCurrentSlot() {
final AttestationData pruneAttestationData = dataStructureUtil.randomAttestationData(SLOT);
Expand Down Expand Up @@ -349,6 +398,10 @@ public void getAttestations_shouldReturnAttestationsForGivenSlotOnly() {
.containsExactly(attestation1);
}

private Attestation addAttestationFromValidators(final UInt64 slot, final int... validators) {
return addAttestationFromValidators(dataStructureUtil.randomAttestationData(slot), validators);
}

private Attestation addAttestationFromValidators(
final AttestationData data, final int... validators) {
final SszBitlist bitlist =
Expand Down

0 comments on commit 71000af

Please sign in to comment.