From bb3856f3b3d42139e436ae5e99e1381898f45ccb Mon Sep 17 00:00:00 2001 From: Enrico Del Fante Date: Wed, 25 Sep 2024 17:45:03 +0200 Subject: [PATCH 1/3] SingleAttestation --- .../attestation/ValidatableAttestation.java | 40 ++++++++++ .../operations/Attestation.java | 17 +++- .../operations/AttestationSchema.java | 5 ++ .../operations/SingleAttestation.java | 79 +++++++++++++++++++ .../operations/SingleAttestationSchema.java | 70 ++++++++++++++++ .../versions/electra/AttestationElectra.java | 14 ---- .../versions/phase0/AttestationPhase0.java | 14 ---- .../electra/util/AttestationUtilElectra.java | 66 ++++++++++++++++ .../schemas/SchemaDefinitionsElectra.java | 10 +++ .../MatchingDataAttestationGroup.java | 10 +-- .../validation/AttestationValidator.java | 25 +++--- .../AttestationSubnetSubscriptions.java | 10 ++- 12 files changed, 315 insertions(+), 45 deletions(-) create mode 100644 ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java create mode 100644 ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestationSchema.java diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java index 7e50d6acefb..909b06c2e7f 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.constants.Domain; @@ -31,6 +32,8 @@ import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation; import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectra; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; public class ValidatableAttestation { @@ -49,6 +52,33 @@ public class ValidatableAttestation { private volatile Optional committeeShufflingSeed = Optional.empty(); private volatile Optional committeesSize = Optional.empty(); + private volatile Optional singleAttestationAggregationBits = Optional.empty(); + + public ValidatableAttestation convertFromSingleAttestationIfRequired() { + if (!attestation.isSingleAttestation()) { + return this; + } + + final AttestationElectraSchema attestationElectraSchema = + spec.atSlot(attestation.getData().getSlot()) + .getSchemaDefinitions() + .getAttestationSchema() + .toVersionElectra() + .orElseThrow(); + + final AttestationElectra convertedAttestation = + attestationElectraSchema.create( + singleAttestationAggregationBits.orElseThrow(), + attestation.getData(), + attestation.getAggregateSignature(), + attestationElectraSchema + .getCommitteeBitsSchema() + .orElseThrow() + .ofBits(attestation.getFirstCommitteeIndex().intValue())); + + return from(spec, convertedAttestation); + } + public static ValidatableAttestation from(final Spec spec, final Attestation attestation) { return new ValidatableAttestation( spec, attestation, Optional.empty(), OptionalInt.empty(), false); @@ -204,6 +234,15 @@ private void saveCommitteesSize(final BeaconState state) { this.committeesSize = Optional.of(committeesSize); } + public Optional getSingleAttestationAggregationBits() { + return singleAttestationAggregationBits; + } + + public void setSingleAttestationAggregationBits( + final SszBitlist singleAttestationAggregationBits) { + this.singleAttestationAggregationBits = Optional.of(singleAttestationAggregationBits); + } + public boolean isGossiped() { return gossiped.get(); } @@ -273,6 +312,7 @@ public String toString() { .add("committeeShufflingSeed", committeeShufflingSeed) .add("committeesSize", committeesSize) .add("receivedSubnetId", receivedSubnetId) + .add("singleAttestationAggregationBits", singleAttestationAggregationBits) .toString(); } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java index 57f0ff3f234..03be364170b 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.spec.datastructures.operations; +import com.google.common.collect.Sets; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -34,9 +35,13 @@ public interface Attestation extends SszContainer { @Override AttestationSchema getSchema(); - UInt64 getEarliestSlotForForkChoiceProcessing(final Spec spec); + default UInt64 getEarliestSlotForForkChoiceProcessing(final Spec spec) { + return getData().getEarliestSlotForForkChoice(spec); + } - Collection getDependentBlockRoots(); + default Collection getDependentBlockRoots() { + return Sets.newHashSet(getData().getTarget().getRoot(), getData().getBeaconBlockRoot()); + } AttestationData getData(); @@ -65,4 +70,12 @@ default List getCommitteeIndicesRequired() { } boolean requiresCommitteeBits(); + + default boolean isSingleAttestation() { + return false; + } + + default Optional getValidatorIndex() { + return Optional.empty(); + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java index 3b0b0f880ec..7c7569d5598 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java @@ -41,6 +41,11 @@ default SszBitlist createEmptyAggregationBits() { return bitsSchema.ofBits(Math.toIntExact(bitsSchema.getMaxLength())); } + default SszBitlist createAggregationBitsOf(final int... indices) { + final SszBitlistSchema bitsSchema = getAggregationBitsSchema(); + return bitsSchema.ofBits(Math.toIntExact(bitsSchema.getMaxLength()), indices); + } + default Optional createEmptyCommitteeBits() { return getCommitteeBitsSchema().map(SszBitvectorSchema::ofBits); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java new file mode 100644 index 00000000000..4510bd31d99 --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java @@ -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.datastructures.operations; + +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; +import tech.pegasys.teku.infrastructure.ssz.containers.Container4; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; +import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.datastructures.type.SszSignature; + +public class SingleAttestation + extends Container4 + implements Attestation { + public SingleAttestation(final SingleAttestationSchema type, final TreeNode backingNode) { + super(type, backingNode); + } + + public SingleAttestation( + final SingleAttestationSchema schema, + final UInt64 committeeIndex, + final UInt64 validatorIndex, + final AttestationData data, + final BLSSignature signature) { + super( + schema, + SszUInt64.of(committeeIndex), + SszUInt64.of(validatorIndex), + data, + new SszSignature(signature)); + } + + @Override + public SingleAttestationSchema getSchema() { + return (SingleAttestationSchema) super.getSchema(); + } + + @Override + public AttestationData getData() { + return getField2(); + } + + @Override + public SszBitlist getAggregationBits() { + throw new UnsupportedOperationException("Not supported in SingleAttestation"); + } + + @Override + public UInt64 getFirstCommitteeIndex() { + return getField0().get(); + } + + @Override + public BLSSignature getAggregateSignature() { + return getField3().getSignature(); + } + + @Override + public boolean requiresCommitteeBits() { + return false; + } + + @Override + public boolean isSingleAttestation() { + return true; + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestationSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestationSchema.java new file mode 100644 index 00000000000..c458757df55 --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestationSchema.java @@ -0,0 +1,70 @@ +/* + * 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.datastructures.operations; + +import java.util.Optional; +import java.util.function.Supplier; +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; +import tech.pegasys.teku.infrastructure.ssz.collections.SszBitvector; +import tech.pegasys.teku.infrastructure.ssz.containers.ContainerSchema4; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; +import tech.pegasys.teku.infrastructure.ssz.schema.SszPrimitiveSchemas; +import tech.pegasys.teku.infrastructure.ssz.schema.collections.SszBitlistSchema; +import tech.pegasys.teku.infrastructure.ssz.schema.collections.SszBitvectorSchema; +import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; +import tech.pegasys.teku.spec.datastructures.type.SszSignature; +import tech.pegasys.teku.spec.datastructures.type.SszSignatureSchema; + +public class SingleAttestationSchema + extends ContainerSchema4 + implements AttestationSchema { + public SingleAttestationSchema() { + super( + "SingleAttestation", + namedSchema("committee_index", SszPrimitiveSchemas.UINT64_SCHEMA), + namedSchema("attester_index", SszPrimitiveSchemas.UINT64_SCHEMA), + namedSchema("data", AttestationData.SSZ_SCHEMA), + namedSchema("signature", SszSignatureSchema.INSTANCE)); + } + + @Override + public SingleAttestation createFromBackingNode(final TreeNode node) { + return new SingleAttestation(this, node); + } + + @Override + public Attestation create( + final SszBitlist aggregationBits, + final AttestationData data, + final BLSSignature signature, + final Supplier committeeBits) { + throw new UnsupportedOperationException("Not supported in SingleAttestation"); + } + + @Override + public SszBitlistSchema getAggregationBitsSchema() { + throw new UnsupportedOperationException("Not supported in SingleAttestation"); + } + + @Override + public Optional> getCommitteeBitsSchema() { + return Optional.empty(); + } + + @Override + public boolean requiresCommitteeBits() { + return false; + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java index 1842c46e897..cebb76f5cb3 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java @@ -13,18 +13,14 @@ package tech.pegasys.teku.spec.datastructures.operations.versions.electra; -import com.google.common.collect.Sets; -import java.util.Collection; import java.util.List; import java.util.Optional; -import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.ssz.collections.SszBitvector; import tech.pegasys.teku.infrastructure.ssz.containers.Container4; import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.type.SszSignature; @@ -51,16 +47,6 @@ public AttestationElectraSchema getSchema() { return (AttestationElectraSchema) super.getSchema(); } - @Override - public UInt64 getEarliestSlotForForkChoiceProcessing(final Spec spec) { - return getData().getEarliestSlotForForkChoice(spec); - } - - @Override - public Collection getDependentBlockRoots() { - return Sets.newHashSet(getData().getTarget().getRoot(), getData().getBeaconBlockRoot()); - } - @Override public SszBitlist getAggregationBits() { return getField0(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/phase0/AttestationPhase0.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/phase0/AttestationPhase0.java index dd343f37361..e7d464a43aa 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/phase0/AttestationPhase0.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/phase0/AttestationPhase0.java @@ -13,15 +13,11 @@ package tech.pegasys.teku.spec.datastructures.operations.versions.phase0; -import com.google.common.collect.Sets; -import java.util.Collection; -import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.ssz.containers.Container3; import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.type.SszSignature; @@ -47,16 +43,6 @@ public AttestationPhase0Schema getSchema() { return (AttestationPhase0Schema) super.getSchema(); } - @Override - public UInt64 getEarliestSlotForForkChoiceProcessing(final Spec spec) { - return getData().getEarliestSlotForForkChoice(spec); - } - - @Override - public Collection getDependentBlockRoots() { - return Sets.newHashSet(getData().getTarget().getRoot(), getData().getBeaconBlockRoot()); - } - @Override public SszBitlist getAggregationBits() { return getField0(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/util/AttestationUtilElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/util/AttestationUtilElectra.java index 943536e7bf6..7404783ed84 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/util/AttestationUtilElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/util/AttestationUtilElectra.java @@ -13,19 +13,28 @@ package tech.pegasys.teku.spec.logic.versions.electra.util; +import static com.google.common.base.Preconditions.checkArgument; + 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.async.SafeFuture; 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.attestation.ValidatableAttestation; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlockSummary; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; +import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation; +import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestationSchema; +import tech.pegasys.teku.spec.datastructures.state.Fork; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.util.AttestationProcessingResult; 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.util.AsyncBLSSignatureVerifier; import tech.pegasys.teku.spec.logic.versions.deneb.util.AttestationUtilDeneb; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; @@ -93,4 +102,61 @@ public AttestationData getGenericAttestationData( final UInt64 committeeIndex) { return super.getGenericAttestationData(slot, state, block, UInt64.ZERO); } + + @Override + public IndexedAttestation getIndexedAttestation( + final BeaconState state, final Attestation attestation) { + if (attestation.isSingleAttestation()) { + final IndexedAttestationSchema indexedAttestationSchema = + schemaDefinitions.getIndexedAttestationSchema(); + + return indexedAttestationSchema.create( + indexedAttestationSchema + .getAttestingIndicesSchema() + .of(attestation.getValidatorIndex().orElseThrow()), + attestation.getData(), + attestation.getAggregateSignature()); + } + return super.getIndexedAttestation(state, attestation); + } + + @Override + public SafeFuture isValidIndexedAttestationAsync( + final Fork fork, + final BeaconState state, + final ValidatableAttestation attestation, + final AsyncBLSSignatureVerifier blsSignatureVerifier) { + + return super.isValidIndexedAttestationAsync(fork, state, attestation, blsSignatureVerifier) + .thenPeek( + result -> { + if (result.isSuccessful() + && attestation.getAttestation().isSingleAttestation() + && attestation.getSingleAttestationAggregationBits().isEmpty()) { + attestation.setSingleAttestationAggregationBits( + getSingleAttestationAggregationBits(state, attestation.getAttestation())); + } + }); + } + + private SszBitlist getSingleAttestationAggregationBits( + final BeaconState state, final Attestation attestation) { + checkArgument(attestation.isSingleAttestation(), "Expecting single attestation"); + + final IntList committee = + beaconStateAccessors.getBeaconCommittee( + state, attestation.getData().getSlot(), attestation.getFirstCommitteeIndex()); + + int validatorIndex = attestation.getValidatorIndex().orElseThrow().intValue(); + + int validatorCommitteeBit = committee.indexOf(validatorIndex); + + checkArgument( + validatorCommitteeBit >= 0, + "Validator index %s is not part of the committee %s", + validatorIndex, + attestation.getFirstCommitteeIndex()); + + return attestation.getSchema().createAggregationBitsOf(validatorCommitteeBit); + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java index 4d6ecfd09eb..561c2323c3c 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java @@ -57,6 +57,8 @@ import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation; import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestationSchema; import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof.SignedAggregateAndProofSchema; +import tech.pegasys.teku.spec.datastructures.operations.SingleAttestation; +import tech.pegasys.teku.spec.datastructures.operations.SingleAttestationSchema; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttesterSlashingElectraSchema; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.IndexedAttestationElectraSchema; @@ -107,6 +109,8 @@ public class SchemaDefinitionsElectra extends SchemaDefinitionsDeneb { pendingPartialWithdrawalSchema; private final PendingConsolidation.PendingConsolidationSchema pendingConsolidationSchema; + private final SingleAttestationSchema singleAttestationSchema; + public SchemaDefinitionsElectra(final SpecConfigElectra specConfig) { super(specConfig); @@ -118,6 +122,8 @@ public SchemaDefinitionsElectra(final SpecConfigElectra specConfig) { new AttesterSlashingElectraSchema(indexedAttestationSchema) .castTypeToAttesterSlashingSchema(); + this.singleAttestationSchema = new SingleAttestationSchema(); + this.attestationSchema = new AttestationElectraSchema( maxValidatorsPerAttestation, specConfig.getMaxCommitteesPerSlot()) @@ -351,6 +357,10 @@ public PendingBalanceDeposit.PendingBalanceDepositSchema getPendingBalanceDeposi return beaconStateSchema.getPendingPartialWithdrawalsSchema(); } + public AttestationSchema getSingleAttestationSchema() { + return singleAttestationSchema; + } + public PendingPartialWithdrawal.PendingPartialWithdrawalSchema getPendingPartialWithdrawalSchema() { return pendingPartialWithdrawalSchema; diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java index 27303e656bd..ed64af01d5c 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java @@ -103,18 +103,18 @@ public AttestationData getAttestationData() { * @return True if the attestation was added, false otherwise */ public boolean add(final ValidatableAttestation attestation) { - if (includedValidators.isSuperSetOf(attestation.getAttestation())) { + final ValidatableAttestation converted = attestation.convertFromSingleAttestationIfRequired(); + if (includedValidators.isSuperSetOf(converted.getAttestation())) { // All attestation bits have already been included on chain return false; } if (committeeShufflingSeed.isEmpty()) { - committeeShufflingSeed = attestation.getCommitteeShufflingSeed(); + committeeShufflingSeed = converted.getCommitteeShufflingSeed(); } return attestationsByValidatorCount .computeIfAbsent( - attestation.getAttestation().getAggregationBits().getBitCount(), - count -> new HashSet<>()) - .add(attestation); + converted.getAttestation().getAggregationBits().getBitCount(), count -> new HashSet<>()) + .add(converted); } /** diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java index f652f597585..041d1f9cb07 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java @@ -72,6 +72,11 @@ public SafeFuture validate( } private InternalValidationResult singleAttestationChecks(final Attestation attestation) { + // if it is a SingleAttestation type we are guaranteed to be a valid single attestation + if (attestation.isSingleAttestation()) { + return InternalValidationResult.ACCEPT; + } + // The attestation is unaggregated -- that is, it has exactly one participating validator // (len([bit for bit in attestation.aggregation_bits if bit == 0b1]) == 1). final int bitCount = attestation.getAggregationBits().getBitCount(); @@ -167,15 +172,17 @@ SafeFuture singleOrAggregateAttestationChecks attestation.getFirstCommitteeIndex(), receivedOnSubnetId.getAsInt())); } - // [REJECT] The number of aggregation bits matches the committee size - final IntList committee = - spec.getBeaconCommittee( - state, data.getSlot(), attestation.getFirstCommitteeIndex()); - if (committee.size() != attestation.getAggregationBits().size()) { - return completedFuture( - InternalValidationResultWithState.reject( - "Aggregation bit size %s is greater than committee size %s", - attestation.getAggregationBits().size(), committee.size())); + if (!attestation.isSingleAttestation()) { + // [REJECT] The number of aggregation bits matches the committee size + final IntList committee = + spec.getBeaconCommittee( + state, data.getSlot(), attestation.getFirstCommitteeIndex()); + if (committee.size() != attestation.getAggregationBits().size()) { + return completedFuture( + InternalValidationResultWithState.reject( + "Aggregation bit size %s is greater than committee size %s", + attestation.getAggregationBits().size(), committee.size())); + } } return spec.isValidIndexedAttestation( diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/subnets/AttestationSubnetSubscriptions.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/subnets/AttestationSubnetSubscriptions.java index de1231d38c0..356a0e3036f 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/subnets/AttestationSubnetSubscriptions.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/subnets/AttestationSubnetSubscriptions.java @@ -30,6 +30,8 @@ import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationSchema; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; +import tech.pegasys.teku.spec.schemas.SchemaDefinitions; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; import tech.pegasys.teku.statetransition.util.DebugDataDumper; import tech.pegasys.teku.storage.client.RecentChainData; @@ -58,8 +60,14 @@ public AttestationSubnetSubscriptions( this.recentChainData = recentChainData; this.processor = processor; this.forkInfo = forkInfo; + final SchemaDefinitions schemaDefinitions = + spec.atEpoch(forkInfo.getFork().getEpoch()).getSchemaDefinitions(); attestationSchema = - spec.atEpoch(forkInfo.getFork().getEpoch()).getSchemaDefinitions().getAttestationSchema(); + schemaDefinitions + .toVersionElectra() + .>map( + SchemaDefinitionsElectra::getSingleAttestationSchema) + .orElse(schemaDefinitions.getAttestationSchema()); this.debugDataDumper = debugDataDumper; } From 683218331b20dfa08284bf77f8822dc0b5ebe64d Mon Sep 17 00:00:00 2001 From: Enrico Del Fante Date: Wed, 25 Sep 2024 18:28:10 +0200 Subject: [PATCH 2/3] OnchainAttestation --- .../PostAggregateAndProofsV2Test.java | 4 +- .../attestation/ValidatableAttestation.java | 58 ++++++++++++------- .../BeaconBlockBodySchemaElectraImpl.java | 4 +- ...indedBeaconBlockBodySchemaElectraImpl.java | 4 +- .../operations/Attestation.java | 4 ++ .../operations/AttestationSchema.java | 4 +- ...onElectra.java => OnchainAttestation.java} | 19 +++--- ...ema.java => OnchainAttestationSchema.java} | 24 ++++---- .../schemas/SchemaDefinitionsElectra.java | 23 ++++---- .../MatchingDataAttestationGroup.java | 5 +- 10 files changed, 85 insertions(+), 64 deletions(-) rename ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/{AttestationElectra.java => OnchainAttestation.java} (85%) rename ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/{AttestationElectraSchema.java => OnchainAttestationSchema.java} (84%) diff --git a/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/PostAggregateAndProofsV2Test.java b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/PostAggregateAndProofsV2Test.java index f2274bf4c1b..854036f167c 100644 --- a/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/PostAggregateAndProofsV2Test.java +++ b/data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/PostAggregateAndProofsV2Test.java @@ -44,7 +44,7 @@ import tech.pegasys.teku.spec.TestSpecContext; import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectra; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestation; import tech.pegasys.teku.spec.datastructures.operations.versions.phase0.AttestationPhase0; import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -111,7 +111,7 @@ void shouldReadRequestBody() throws IOException { ((SignedAggregateAndProof) ((List) requestBody).get(0)) .getMessage() .getAggregate()) - .isInstanceOf(AttestationElectra.class); + .isInstanceOf(OnchainAttestation.class); } else { assertThat( ((SignedAggregateAndProof) ((List) requestBody).get(0)) diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java index 909b06c2e7f..f4cf48ab881 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java @@ -27,13 +27,15 @@ import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.SpecVersion; import tech.pegasys.teku.spec.constants.Domain; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation; import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectra; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestation; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; public class ValidatableAttestation { @@ -54,29 +56,41 @@ public class ValidatableAttestation { private volatile Optional singleAttestationAggregationBits = Optional.empty(); - public ValidatableAttestation convertFromSingleAttestationIfRequired() { - if (!attestation.isSingleAttestation()) { + public ValidatableAttestation convertToOnchainAttestationIfRequired() { + + if (attestation.isOnchainAttestation()) { return this; } - final AttestationElectraSchema attestationElectraSchema = - spec.atSlot(attestation.getData().getSlot()) - .getSchemaDefinitions() - .getAttestationSchema() - .toVersionElectra() - .orElseThrow(); - - final AttestationElectra convertedAttestation = - attestationElectraSchema.create( - singleAttestationAggregationBits.orElseThrow(), - attestation.getData(), - attestation.getAggregateSignature(), - attestationElectraSchema - .getCommitteeBitsSchema() - .orElseThrow() - .ofBits(attestation.getFirstCommitteeIndex().intValue())); - - return from(spec, convertedAttestation); + if (attestation.isSingleAttestation()) { + final OnchainAttestationSchema attestationElectraSchema = + spec.atSlot(attestation.getData().getSlot()) + .getSchemaDefinitions() + .toVersionElectra() + .orElseThrow() + .getOnchainAttestationAttestationSchema(); + + final OnchainAttestation convertedAttestation = + attestationElectraSchema.create( + singleAttestationAggregationBits.orElseThrow(), + attestation.getData(), + attestation.getAggregateSignature(), + attestationElectraSchema + .getCommitteeBitsSchema() + .orElseThrow() + .ofBits(attestation.getFirstCommitteeIndex().intValue())); + + return from(spec, convertedAttestation); + } + + // if we are going to change Attestation in Electra (ie adding committee_index) + // we could directly check the version here instead of looking up in the spec version + final SpecVersion specVersion = spec.atSlot(attestation.getData().getSlot()); + if (specVersion.getMilestone().isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)) { + // TODO convert + } + + return this; } public static ValidatableAttestation from(final Spec spec, final Attestation attestation) { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BeaconBlockBodySchemaElectraImpl.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BeaconBlockBodySchemaElectraImpl.java index ced354e0051..f083ae31e84 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BeaconBlockBodySchemaElectraImpl.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BeaconBlockBodySchemaElectraImpl.java @@ -42,7 +42,7 @@ import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChangeSchema; import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.datastructures.type.SszSignatureSchema; @@ -118,7 +118,7 @@ public static BeaconBlockBodySchemaElectraImpl create( namedSchema( BlockBodyFields.ATTESTATIONS, SszListSchema.create( - new AttestationElectraSchema( + new OnchainAttestationSchema( maxValidatorsPerAttestation, specConfig.getMaxCommitteesPerSlot()) .castTypeToAttestationSchema(), specConfig.getMaxAttestationsElectra())), diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BlindedBeaconBlockBodySchemaElectraImpl.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BlindedBeaconBlockBodySchemaElectraImpl.java index e971d364431..c3d5d24da5c 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BlindedBeaconBlockBodySchemaElectraImpl.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/blocks/blockbody/versions/electra/BlindedBeaconBlockBodySchemaElectraImpl.java @@ -41,7 +41,7 @@ import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChangeSchema; import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.datastructures.type.SszSignatureSchema; @@ -117,7 +117,7 @@ public static BlindedBeaconBlockBodySchemaElectraImpl create( namedSchema( BlockBodyFields.ATTESTATIONS, SszListSchema.create( - new AttestationElectraSchema( + new OnchainAttestationSchema( maxValidatorsPerAttestation, specConfig.getMaxCommitteesPerSlot()) .castTypeToAttestationSchema(), specConfig.getMaxAttestationsElectra())), diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java index 03be364170b..13ef3fc1f1a 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java @@ -75,6 +75,10 @@ default boolean isSingleAttestation() { return false; } + default boolean isOnchainAttestation() { + return false; + } + default Optional getValidatorIndex() { return Optional.empty(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java index 7c7569d5598..d503fa455d0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationSchema.java @@ -21,7 +21,7 @@ import tech.pegasys.teku.infrastructure.ssz.schema.SszContainerSchema; import tech.pegasys.teku.infrastructure.ssz.schema.collections.SszBitlistSchema; import tech.pegasys.teku.infrastructure.ssz.schema.collections.SszBitvectorSchema; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; public interface AttestationSchema extends SszContainerSchema { @@ -55,7 +55,7 @@ default AttestationSchema castTypeToAttestationSchema() { return (AttestationSchema) this; } - default Optional toVersionElectra() { + default Optional toVersionElectra() { return Optional.empty(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestation.java similarity index 85% rename from ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java rename to ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestation.java index cebb76f5cb3..9be6a695810 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestation.java @@ -25,16 +25,16 @@ import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.type.SszSignature; -public class AttestationElectra - extends Container4 +public class OnchainAttestation + extends Container4 implements Attestation { - public AttestationElectra(final AttestationElectraSchema type, final TreeNode backingNode) { + public OnchainAttestation(final OnchainAttestationSchema type, final TreeNode backingNode) { super(type, backingNode); } - public AttestationElectra( - final AttestationElectraSchema schema, + public OnchainAttestation( + final OnchainAttestationSchema schema, final SszBitlist aggregationBits, final AttestationData data, final BLSSignature signature, @@ -43,8 +43,8 @@ public AttestationElectra( } @Override - public AttestationElectraSchema getSchema() { - return (AttestationElectraSchema) super.getSchema(); + public OnchainAttestationSchema getSchema() { + return (OnchainAttestationSchema) super.getSchema(); } @Override @@ -82,4 +82,9 @@ public UInt64 getFirstCommitteeIndex() { public boolean requiresCommitteeBits() { return true; } + + @Override + public boolean isOnchainAttestation() { + return true; + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectraSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestationSchema.java similarity index 84% rename from ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectraSchema.java rename to ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestationSchema.java index 1f6597738d1..de0c9622457 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/AttestationElectraSchema.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/versions/electra/OnchainAttestationSchema.java @@ -30,15 +30,15 @@ import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.datastructures.type.SszSignatureSchema; -public class AttestationElectraSchema +public class OnchainAttestationSchema extends ContainerSchema4< - AttestationElectra, SszBitlist, AttestationData, SszSignature, SszBitvector> - implements AttestationSchema { + OnchainAttestation, SszBitlist, AttestationData, SszSignature, SszBitvector> + implements AttestationSchema { - public AttestationElectraSchema( + public OnchainAttestationSchema( final long maxValidatorsPerAttestation, final long maxCommitteePerSlot) { super( - "AttestationElectra", + "OnchainAttestation", namedSchema("aggregation_bits", SszBitlistSchema.create(maxValidatorsPerAttestation)), namedSchema("data", AttestationData.SSZ_SCHEMA), namedSchema("signature", SszSignatureSchema.INSTANCE), @@ -56,8 +56,8 @@ public Optional> getCommitteeBitsSchema() { } @Override - public AttestationElectra createFromBackingNode(final TreeNode node) { - return new AttestationElectra(this, node); + public OnchainAttestation createFromBackingNode(final TreeNode node) { + return new OnchainAttestation(this, node); } @Override @@ -67,20 +67,20 @@ public Attestation create( final BLSSignature signature, final Supplier committeeBits) { final SszBitvector suppliedCommitteeBits = committeeBits.get(); - checkNotNull(suppliedCommitteeBits, "committeeBits must be provided in Electra"); - return new AttestationElectra(this, aggregationBits, data, signature, suppliedCommitteeBits); + checkNotNull(suppliedCommitteeBits, "committeeBits must be provided"); + return new OnchainAttestation(this, aggregationBits, data, signature, suppliedCommitteeBits); } - public AttestationElectra create( + public OnchainAttestation create( final SszBitlist aggregationBits, final AttestationData data, final BLSSignature signature, final SszBitvector committeeBits) { - return new AttestationElectra(this, aggregationBits, data, signature, committeeBits); + return new OnchainAttestation(this, aggregationBits, data, signature, committeeBits); } @Override - public Optional toVersionElectra() { + public Optional toVersionElectra() { return Optional.of(this); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java index 561c2323c3c..44a25db7b67 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java @@ -50,7 +50,6 @@ import tech.pegasys.teku.spec.datastructures.execution.versions.electra.WithdrawalRequest; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.WithdrawalRequestSchema; import tech.pegasys.teku.spec.datastructures.operations.AggregateAndProof.AggregateAndProofSchema; -import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationSchema; import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashingSchema; @@ -59,9 +58,9 @@ import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof.SignedAggregateAndProofSchema; import tech.pegasys.teku.spec.datastructures.operations.SingleAttestation; import tech.pegasys.teku.spec.datastructures.operations.SingleAttestationSchema; -import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttestationElectraSchema; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.AttesterSlashingElectraSchema; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.IndexedAttestationElectraSchema; +import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconStateSchema; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateSchemaElectra; @@ -73,7 +72,7 @@ public class SchemaDefinitionsElectra extends SchemaDefinitionsDeneb { private final IndexedAttestationSchema indexedAttestationSchema; private final AttesterSlashingSchema attesterSlashingSchema; - private final AttestationSchema attestationSchema; + private final OnchainAttestationSchema onchainAttestationAttestationSchema; private final SignedAggregateAndProofSchema signedAggregateAndProofSchema; private final AggregateAndProofSchema aggregateAndProofSchema; @@ -124,11 +123,10 @@ public SchemaDefinitionsElectra(final SpecConfigElectra specConfig) { this.singleAttestationSchema = new SingleAttestationSchema(); - this.attestationSchema = - new AttestationElectraSchema( - maxValidatorsPerAttestation, specConfig.getMaxCommitteesPerSlot()) - .castTypeToAttestationSchema(); - this.aggregateAndProofSchema = new AggregateAndProofSchema(attestationSchema); + this.onchainAttestationAttestationSchema = + new OnchainAttestationSchema( + maxValidatorsPerAttestation, specConfig.getMaxCommitteesPerSlot()); + this.aggregateAndProofSchema = new AggregateAndProofSchema(onchainAttestationAttestationSchema); this.signedAggregateAndProofSchema = new SignedAggregateAndProofSchema(aggregateAndProofSchema); this.executionPayloadSchemaElectra = new ExecutionPayloadSchemaElectra(specConfig); @@ -208,11 +206,6 @@ public AggregateAndProofSchema getAggregateAndProofSchema() { return aggregateAndProofSchema; } - @Override - public AttestationSchema getAttestationSchema() { - return attestationSchema; - } - @Override public IndexedAttestationSchema getIndexedAttestationSchema() { return indexedAttestationSchema; @@ -361,6 +354,10 @@ public AttestationSchema getSingleAttestationSchema() { return singleAttestationSchema; } + public OnchainAttestationSchema getOnchainAttestationAttestationSchema() { + return onchainAttestationAttestationSchema; + } + public PendingPartialWithdrawal.PendingPartialWithdrawalSchema getPendingPartialWithdrawalSchema() { return pendingPartialWithdrawalSchema; diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java index ed64af01d5c..4a6b386e9db 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/MatchingDataAttestationGroup.java @@ -103,13 +103,14 @@ public AttestationData getAttestationData() { * @return True if the attestation was added, false otherwise */ public boolean add(final ValidatableAttestation attestation) { - final ValidatableAttestation converted = attestation.convertFromSingleAttestationIfRequired(); + final ValidatableAttestation converted = attestation.convertToOnchainAttestationIfRequired(); if (includedValidators.isSuperSetOf(converted.getAttestation())) { // All attestation bits have already been included on chain return false; } if (committeeShufflingSeed.isEmpty()) { - committeeShufflingSeed = converted.getCommitteeShufflingSeed(); + // converted do not maintain shuffling, we need to take it from the original + committeeShufflingSeed = attestation.getCommitteeShufflingSeed(); } return attestationsByValidatorCount .computeIfAbsent( From 832427414cad352b9eed804babd9544141954edc Mon Sep 17 00:00:00 2001 From: Enrico Del Fante Date: Fri, 27 Sep 2024 11:34:30 +0200 Subject: [PATCH 3/3] fix some versioning bugs --- .../attestation/ValidatableAttestation.java | 65 +++++++++++++------ .../operations/SingleAttestation.java | 3 +- .../validation/AttestationValidator.java | 2 +- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java index f4cf48ab881..2b297d8fbe6 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/attestation/ValidatableAttestation.java @@ -27,8 +27,6 @@ import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.SpecVersion; import tech.pegasys.teku.spec.constants.Domain; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; @@ -37,6 +35,7 @@ import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestation; import tech.pegasys.teku.spec.datastructures.operations.versions.electra.OnchainAttestationSchema; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; public class ValidatableAttestation { private final Spec spec; @@ -62,37 +61,61 @@ public ValidatableAttestation convertToOnchainAttestationIfRequired() { return this; } + final Optional attestationElectraSchema = + spec.atSlot(attestation.getData().getSlot()) + .getSchemaDefinitions() + .toVersionElectra() + .map(SchemaDefinitionsElectra::getOnchainAttestationAttestationSchema); + if (attestation.isSingleAttestation()) { - final OnchainAttestationSchema attestationElectraSchema = - spec.atSlot(attestation.getData().getSlot()) - .getSchemaDefinitions() - .toVersionElectra() - .orElseThrow() - .getOnchainAttestationAttestationSchema(); final OnchainAttestation convertedAttestation = - attestationElectraSchema.create( - singleAttestationAggregationBits.orElseThrow(), - attestation.getData(), - attestation.getAggregateSignature(), - attestationElectraSchema - .getCommitteeBitsSchema() - .orElseThrow() - .ofBits(attestation.getFirstCommitteeIndex().intValue())); + attestationElectraSchema + .orElseThrow() + .create( + singleAttestationAggregationBits.orElseThrow(), + attestation.getData(), + attestation.getAggregateSignature(), + attestationElectraSchema + .orElseThrow() + .getCommitteeBitsSchema() + .orElseThrow() + .ofBits(attestation.getFirstCommitteeIndex().intValue())); return from(spec, convertedAttestation); } - // if we are going to change Attestation in Electra (ie adding committee_index) - // we could directly check the version here instead of looking up in the spec version - final SpecVersion specVersion = spec.atSlot(attestation.getData().getSlot()); - if (specVersion.getMilestone().isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)) { - // TODO convert + if (attestationElectraSchema.isPresent()) { + // this is a good old Attestation (single committee aggregation) + final OnchainAttestation convertedAttestation = + attestationElectraSchema + .get() + .create( + attestation.getAggregationBits(), + attestation.getData(), + attestation.getAggregateSignature(), + attestationElectraSchema + .get() + .getCommitteeBitsSchema() + .orElseThrow() + .ofBits(attestation.getFirstCommitteeIndex().intValue())); + + return from(spec, convertedAttestation); } return this; } + public ValidatableAttestation convertToAttestationIfRequired() { + if (!attestation.isOnchainAttestation()) { + // we are at phase0 + return this; + } + + // TODO convert onchain attestation to Attestation (committee aggregation) + throw new UnsupportedOperationException("not yet implemented"); + } + public static ValidatableAttestation from(final Spec spec, final Attestation attestation) { return new ValidatableAttestation( spec, attestation, Optional.empty(), OptionalInt.empty(), false); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java index 4510bd31d99..d499eddabf0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/SingleAttestation.java @@ -69,7 +69,8 @@ public BLSSignature getAggregateSignature() { @Override public boolean requiresCommitteeBits() { - return false; + // we signal that to process this attestation committeeBits handling is required + return true; } @Override diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java index 041d1f9cb07..fe115c9a0e0 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AttestationValidator.java @@ -123,7 +123,7 @@ SafeFuture singleOrAggregateAttestationChecks return completedFuture(InternalValidationResultWithState.saveForFuture()); } - if (attestation.requiresCommitteeBits()) { + if (attestation.getCommitteeBits().isPresent()) { // [REJECT] len(committee_indices) == 1, where committee_indices = // get_committee_indices(attestation) if (attestation.getCommitteeBitsRequired().getBitCount() != 1) {