Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/beacon-node/src/chain/validation/aggregateAndProof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ async function validateAggregateAndProof(
// and non-gossip sources) (a client MAY queue attestations for processing once block is retrieved).
// Lighthouse doesn't check maxSkipSlots option here but Lodestar wants to be more strict
// to be more DOS protection

// [REJECT] The aggregate attestation's target block is an ancestor of the block named in the LMD vote
// -- i.e. `get_checkpoint_block(store, aggregate.data.beacon_block_root, aggregate.data.target.epoch) == aggregate.data.target.root`
const attHeadBlock = verifyHeadBlockAndTargetRoot(
chain,
attData.beaconBlockRoot,
Expand All @@ -148,10 +151,17 @@ async function validateAggregateAndProof(
RegenCaller.validateGossipAttestation
);

// [REJECT] The committee index is within the expected range
// -- i.e. data.index < get_committee_count_per_slot(state, data.target.epoch)
const committeeIndices = cachedAttData
? cachedAttData.committeeIndices
: getCommitteeIndices(shuffling, attSlot, attIndex);

// [IGNORE] The number of aggregation bits matches the committee size
// -- i.e. `len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, index))`.
if (aggregate.aggregationBits.bitLen !== committeeIndices.length) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.WRONG_NUMBER_OF_AGGREGATION_BITS});
}
const attestingIndices = aggregate.aggregationBits.intersectValues(committeeIndices);
const indexedAttestation: phase0.IndexedAttestation = {
attestingIndices,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {toHexString} from "@chainsafe/ssz";
import {BitArray, toHexString} from "@chainsafe/ssz";
import {describe, it} from "vitest";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {phase0, ssz} from "@lodestar/types";
Expand Down Expand Up @@ -138,6 +138,18 @@ describe("chain / validation / aggregateAndProof", () => {
await expectError(chain, signedAggregateAndProof, AttestationErrorCode.AGGREGATOR_NOT_IN_COMMITTEE);
});

it("WRONG_NUMBER_OF_AGGREGATION_BITS", async () => {
const attIndex = 1;
const {chain, signedAggregateAndProof} = getValidData({attIndex});
const {aggregationBits} = signedAggregateAndProof.message.aggregate;
signedAggregateAndProof.message.aggregate.aggregationBits = new BitArray(
aggregationBits.uint8Array,
aggregationBits.bitLen + 1
);

await expectError(chain, signedAggregateAndProof, AttestationErrorCode.WRONG_NUMBER_OF_AGGREGATION_BITS);
});

it("INVALID_SIGNATURE - selection proof sig", async () => {
const bitIndex = 1;
const {chain, signedAggregateAndProof} = getValidData({bitIndex});
Expand Down