From 5a665a5c50ebe0026f2550d73bfb8a879e5582dd Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Fri, 29 Nov 2024 10:45:10 +0100 Subject: [PATCH] fix: get attester index from single attestation bytes if cache is used --- .../src/chain/validation/attestation.ts | 16 +++++++++++++--- packages/beacon-node/src/util/sszBytes.ts | 16 +++++++++++++++- .../beacon-node/test/unit/util/sszBytes.test.ts | 6 ++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/beacon-node/src/chain/validation/attestation.ts b/packages/beacon-node/src/chain/validation/attestation.ts index 305cbba3fbde..d181ea4f26b5 100644 --- a/packages/beacon-node/src/chain/validation/attestation.ts +++ b/packages/beacon-node/src/chain/validation/attestation.ts @@ -43,8 +43,7 @@ import { getAggregationBitsFromAttestationSerialized, getAttDataFromSignedAggregateAndProofElectra, getAttDataFromSignedAggregateAndProofPhase0, - getBeaconAttestationGossipIndex, - getCommitteeBitsFromSignedAggregateAndProofElectra, + getAttesterIndexFromSingleAttestationSerialized, getCommitteeIndexFromSingleAttestationSerialized, getSignatureFromAttestationSerialized, } from "../../util/sszBytes.js"; @@ -420,7 +419,18 @@ async function validateAttestationNoSignatureCheck( }); } } else { - validatorIndex = (attestationOrCache.attestation as SingleAttestation).attesterIndex; + if (attestationOrCache.attestation) { + validatorIndex = (attestationOrCache.attestation as SingleAttestation).attesterIndex; + } else { + const attesterIndex = getAttesterIndexFromSingleAttestationSerialized(attestationOrCache.serializedData); + if (attesterIndex === null) { + throw new AttestationError(GossipAction.REJECT, { + code: AttestationErrorCode.INVALID_SERIALIZED_BYTES, + }); + } + validatorIndex = attesterIndex; + } + // [REJECT] The attester is a member of the committee -- i.e. // `attestation.attester_index in get_beacon_committee(state, attestation.data.slot, index)`. // If `aggregationBitsElectra` exists, that means we have already cached it. No need to check again diff --git a/packages/beacon-node/src/util/sszBytes.ts b/packages/beacon-node/src/util/sszBytes.ts index 8dca248b5830..ab2b59c68080 100644 --- a/packages/beacon-node/src/util/sszBytes.ts +++ b/packages/beacon-node/src/util/sszBytes.ts @@ -7,7 +7,7 @@ import { MAX_COMMITTEES_PER_SLOT, isForkPostElectra, } from "@lodestar/params"; -import {BLSSignature, CommitteeIndex, RootHex, Slot} from "@lodestar/types"; +import {BLSSignature, CommitteeIndex, RootHex, Slot, ValidatorIndex} from "@lodestar/types"; export type BlockRootHex = RootHex; // pre-electra, AttestationData is used to cache attestations @@ -54,6 +54,7 @@ const SIGNATURE_SIZE = 96; const SINGLE_ATTESTATION_ATTDATA_OFFSET = 8 + 8; const SINGLE_ATTESTATION_SLOT_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET; const SINGLE_ATTESTATION_COMMITTEE_INDEX_OFFSET = 0; +const SINGLE_ATTESTATION_ATTESTER_INDEX_OFFSET = 8; const SINGLE_ATTESTATION_BEACON_BLOCK_ROOT_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET + 8 + 8; const SINGLE_ATTESTATION_SIGNATURE_OFFSET = SINGLE_ATTESTATION_ATTDATA_OFFSET + ATTESTATION_DATA_SIZE; const SINGLE_ATTESTATION_SIZE = SINGLE_ATTESTATION_SIGNATURE_OFFSET + SIGNATURE_SIZE; @@ -201,6 +202,19 @@ export function getCommitteeIndexFromSingleAttestationSerialized( return getSlotFromOffset(data, VARIABLE_FIELD_OFFSET + SLOT_SIZE); } +/** + * Extract attester index from SingleAttestation serialized bytes. + * Return null if data is not long enough to extract index. + * TODO Electra: Rename getSlotFromOffset to reflect generic usage + */ +export function getAttesterIndexFromSingleAttestationSerialized(data: Uint8Array): ValidatorIndex | null { + if (data.length !== SINGLE_ATTESTATION_SIZE) { + return null; + } + + return getSlotFromOffset(data, SINGLE_ATTESTATION_ATTESTER_INDEX_OFFSET); +} + /** * Extract block root from SingleAttestation serialized bytes. * Return null if data is not long enough to extract block root. diff --git a/packages/beacon-node/test/unit/util/sszBytes.test.ts b/packages/beacon-node/test/unit/util/sszBytes.test.ts index ef6b713dccdb..6496c9e80619 100644 --- a/packages/beacon-node/test/unit/util/sszBytes.test.ts +++ b/packages/beacon-node/test/unit/util/sszBytes.test.ts @@ -6,6 +6,7 @@ import { RootHex, SingleAttestation, Slot, + ValidatorIndex, deneb, electra, isElectraSingleAttestation, @@ -21,6 +22,7 @@ import { getAttDataFromSignedAggregateAndProofElectra, getAttDataFromSignedAggregateAndProofPhase0, getAttDataFromSingleAttestationSerialized, + getAttesterIndexFromSingleAttestationSerialized, getBlockRootFromAttestationSerialized, getBlockRootFromSignedAggregateAndProofSerialized, getBlockRootFromSingleAttestationSerialized, @@ -49,6 +51,7 @@ describe("SinlgeAttestation SSZ serialized picking", () => { ...electraSingleAttestationFromValues( 4_000_000, 127, + 1, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 200_00, "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffff" @@ -68,6 +71,7 @@ describe("SinlgeAttestation SSZ serialized picking", () => { expect(getCommitteeIndexFromSingleAttestationSerialized(ForkName.electra, bytes)).toEqual( attestation.committeeIndex ); + expect(getAttesterIndexFromSingleAttestationSerialized(bytes)).toEqual(attestation.attesterIndex); expect(getBlockRootFromSingleAttestationSerialized(bytes)).toEqual(toRootHex(attestation.data.beaconBlockRoot)); // base64, not hex expect(getAttDataFromSingleAttestationSerialized(bytes)).toEqual( @@ -332,6 +336,7 @@ function phase0SingleAttestationFromValues( function electraSingleAttestationFromValues( slot: Slot, committeeIndex: CommitteeIndex, + attesterIndex: ValidatorIndex, blockRoot: RootHex, targetEpoch: Epoch, targetRoot: RootHex @@ -342,6 +347,7 @@ function electraSingleAttestationFromValues( attestation.data.target.epoch = targetEpoch; attestation.data.target.root = fromHex(targetRoot); attestation.committeeIndex = committeeIndex; + attestation.attesterIndex = attesterIndex; return attestation; }