refactor: rename variables / functions related to committee vs. validator indices (#7687)

- `validatorCommitteeIndices` should be the position index in the
committee
- `committeeValidatorIndices` should be the validator indices (eg.
1063664) of the committee members

---------

Co-authored-by: NC <17676176+ensi321@users.noreply.github.com>
This commit is contained in:
Nico Flaig
2025-08-29 07:46:42 +01:00
committed by GitHub
parent eb311a59bd
commit d368818b1e
7 changed files with 44 additions and 49 deletions

View File

@@ -112,7 +112,7 @@ export function getBeaconPoolApi({
// when a validator is configured with multiple beacon node urls, this attestation data may come from another beacon node
// and the block hasn't been in our forkchoice since we haven't seen / processing that block
// see https://github.com/ChainSafe/lodestar/issues/5098
const {indexedAttestation, subnet, attDataRootHex, committeeIndex, committeeValidatorIndex, committeeSize} =
const {indexedAttestation, subnet, attDataRootHex, committeeIndex, validatorCommitteeIndex, committeeSize} =
await validateGossipFnRetryUnknownRoot(validateFn, network, chain, slot, beaconBlockRoot);
if (network.shouldAggregate(subnet, slot)) {
@@ -120,7 +120,7 @@ export function getBeaconPoolApi({
committeeIndex,
attestation,
attDataRootHex,
committeeValidatorIndex,
validatorCommitteeIndex,
committeeSize,
priority
);

View File

@@ -1295,19 +1295,14 @@ export function getValidatorApi(
// when a validator is configured with multiple beacon node urls, this attestation may come from another beacon node
// and the block hasn't been in our forkchoice since we haven't seen / processing that block
// see https://github.com/ChainSafe/lodestar/issues/5098
const {indexedAttestation, committeeIndices, attDataRootHex} = await validateGossipFnRetryUnknownRoot(
validateFn,
network,
chain,
slot,
beaconBlockRoot
);
const {indexedAttestation, committeeValidatorIndices, attDataRootHex} =
await validateGossipFnRetryUnknownRoot(validateFn, network, chain, slot, beaconBlockRoot);
const insertOutcome = chain.aggregatedAttestationPool.add(
signedAggregateAndProof.message.aggregate,
attDataRootHex,
indexedAttestation.attestingIndices.length,
committeeIndices
committeeValidatorIndices
);
metrics?.opPool.aggregatedAttestationPool.apiInsertOutcome.inc({insertOutcome});

View File

@@ -111,7 +111,7 @@ export class AttestationPool {
committeeIndex: CommitteeIndex,
attestation: SingleAttestation,
attDataRootHex: RootHex,
committeeValidatorIndex: number,
validatorCommitteeIndex: number,
committeeSize: number,
priority?: boolean
): InsertOutcome {
@@ -154,10 +154,10 @@ export class AttestationPool {
const aggregate = aggregateByIndex.get(committeeIndex);
if (aggregate) {
// Aggregate mutating
return aggregateAttestationInto(aggregate, attestation, committeeValidatorIndex);
return aggregateAttestationInto(aggregate, attestation, validatorCommitteeIndex);
}
// Create new aggregate
aggregateByIndex.set(committeeIndex, attestationToAggregate(attestation, committeeValidatorIndex, committeeSize));
aggregateByIndex.set(committeeIndex, attestationToAggregate(attestation, validatorCommitteeIndex, committeeSize));
return InsertOutcome.NewData;
}
@@ -229,12 +229,12 @@ export class AttestationPool {
function aggregateAttestationInto(
aggregate: AggregateFast,
attestation: SingleAttestation,
committeeValidatorIndex: number
validatorCommitteeIndex: number
): InsertOutcome {
let bitIndex: number | null;
if (isElectraSingleAttestation(attestation)) {
bitIndex = committeeValidatorIndex;
bitIndex = validatorCommitteeIndex;
} else {
bitIndex = attestation.aggregationBits.getSingleTrueBit();
}
@@ -256,13 +256,13 @@ function aggregateAttestationInto(
*/
function attestationToAggregate(
attestation: SingleAttestation,
committeeValidatorIndex: number,
validatorCommitteeIndex: number,
committeeSize: number
): AggregateFast {
if (isElectraSingleAttestation(attestation)) {
return {
data: attestation.data,
aggregationBits: BitArray.fromSingleBit(committeeSize, committeeValidatorIndex),
aggregationBits: BitArray.fromSingleBit(committeeSize, validatorCommitteeIndex),
committeeBits: BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, attestation.committeeIndex),
signature: signatureFromBytesNoCheck(attestation.signature),
};

View File

@@ -11,7 +11,7 @@ import {IBeaconChain} from "../index.js";
import {RegenCaller} from "../regen/index.js";
import {
getAttestationDataSigningRoot,
getCommitteeIndices,
getCommitteeValidatorIndices,
getSeenAttDataKeyFromSignedAggregateAndProof,
getShufflingForAttestationVerification,
verifyHeadBlockAndTargetRoot,
@@ -21,7 +21,7 @@ import {getAggregateAndProofSignatureSet, getSelectionProofSignatureSet} from ".
export type AggregateAndProofValidationResult = {
indexedAttestation: IndexedAttestation;
committeeIndices: Uint32Array;
committeeValidatorIndices: Uint32Array;
attDataRootHex: RootHex;
};
@@ -175,16 +175,16 @@ async function validateAggregateAndProof(
// [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
const committeeValidatorIndices = cachedAttData
? cachedAttData.committeeValidatorIndices
: getCommitteeIndices(shuffling, attSlot, attIndex);
: getCommitteeValidatorIndices(shuffling, attSlot, attIndex);
// [REJECT] 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) {
if (aggregate.aggregationBits.bitLen !== committeeValidatorIndices.length) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.WRONG_NUMBER_OF_AGGREGATION_BITS});
}
const attestingIndices = aggregate.aggregationBits.intersectValues(committeeIndices);
const attestingIndices = aggregate.aggregationBits.intersectValues(committeeValidatorIndices);
const indexedAttestation: IndexedAttestation = {
attestingIndices,
@@ -202,13 +202,13 @@ async function validateAggregateAndProof(
// [REJECT] aggregate_and_proof.selection_proof selects the validator as an aggregator for the slot
// -- i.e. is_aggregator(state, aggregate.data.slot, aggregate.data.index, aggregate_and_proof.selection_proof) returns True.
if (!isAggregatorFromCommitteeLength(committeeIndices.length, aggregateAndProof.selectionProof)) {
if (!isAggregatorFromCommitteeLength(committeeValidatorIndices.length, aggregateAndProof.selectionProof)) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.INVALID_AGGREGATOR});
}
// [REJECT] The aggregator's validator index is within the committee
// -- i.e. aggregate_and_proof.aggregator_index in get_beacon_committee(state, aggregate.data.slot, aggregate.data.index).
if (!committeeIndices.includes(aggregateAndProof.aggregatorIndex)) {
if (!committeeValidatorIndices.includes(aggregateAndProof.aggregatorIndex)) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.AGGREGATOR_NOT_IN_COMMITTEE});
}
@@ -254,5 +254,5 @@ async function validateAggregateAndProof(
false
);
return {indexedAttestation, committeeIndices, attDataRootHex};
return {indexedAttestation, committeeValidatorIndices, attDataRootHex};
}

View File

@@ -69,7 +69,7 @@ export type AttestationValidationResult = {
subnet: SubnetID;
attDataRootHex: RootHex;
committeeIndex: CommitteeIndex;
committeeValidatorIndex: number;
validatorCommitteeIndex: number;
committeeSize: number;
};
@@ -335,7 +335,7 @@ async function validateAttestationNoSignatureCheck(
}
let aggregationBits: BitArray | null = null;
let committeeValidatorIndex: number | null = null;
let validatorCommitteeIndex: number | null = null;
if (!isForkPostElectra(fork)) {
// [REJECT] The attestation is unaggregated -- that is, it has exactly one participating validator
// (len([bit for bit in attestation.aggregation_bits if bit]) == 1, i.e. exactly 1 bit is set).
@@ -355,7 +355,7 @@ async function validateAttestationNoSignatureCheck(
code: AttestationErrorCode.NOT_EXACTLY_ONE_AGGREGATION_BIT_SET,
});
}
committeeValidatorIndex = bitIndex;
validatorCommitteeIndex = bitIndex;
}
let committeeValidatorIndices: Uint32Array;
@@ -404,7 +404,7 @@ async function validateAttestationNoSignatureCheck(
// [REJECT] The committee index is within the expected range
// -- i.e. data.index < get_committee_count_per_slot(state, data.target.epoch)
committeeValidatorIndices = getCommitteeIndices(shuffling, attSlot, committeeIndex);
committeeValidatorIndices = getCommitteeValidatorIndices(shuffling, attSlot, committeeIndex);
getSigningRoot = () => getAttestationDataSigningRoot(chain.config, attData);
expectedSubnet = computeSubnetForSlot(shuffling, attSlot, committeeIndex);
}
@@ -414,9 +414,9 @@ async function validateAttestationNoSignatureCheck(
if (!isForkPostElectra(fork)) {
// The validity of aggregation bits are already checked above
assert.notNull(aggregationBits);
assert.notNull(committeeValidatorIndex);
assert.notNull(validatorCommitteeIndex);
validatorIndex = committeeValidatorIndices[committeeValidatorIndex];
validatorIndex = committeeValidatorIndices[validatorCommitteeIndex];
// [REJECT] The number of aggregation bits matches the committee size
// -- i.e. len(attestation.aggregation_bits) == len(get_beacon_committee(state, data.slot, data.index)).
// > TODO: Is this necessary? Lighthouse does not do this check.
@@ -441,8 +441,8 @@ async function validateAttestationNoSignatureCheck(
// [REJECT] The attester is a member of the committee -- i.e.
// `attestation.attester_index in get_beacon_committee(state, attestation.data.slot, index)`.
// Position of the validator in its committee
committeeValidatorIndex = committeeValidatorIndices.indexOf(validatorIndex);
if (committeeValidatorIndex === -1) {
validatorCommitteeIndex = committeeValidatorIndices.indexOf(validatorIndex);
if (validatorCommitteeIndex === -1) {
throw new AttestationError(GossipAction.REJECT, {
code: AttestationErrorCode.ATTESTER_NOT_IN_COMMITTEE,
});
@@ -557,7 +557,7 @@ async function validateAttestationNoSignatureCheck(
signatureSet,
validatorIndex,
committeeIndex,
committeeValidatorIndex,
validatorCommitteeIndex,
committeeSize: committeeValidatorIndices.length,
};
}
@@ -797,10 +797,10 @@ function verifyAttestationTargetRoot(headBlock: ProtoBlock, targetRoot: Root, at
}
/**
* Get a list of indices of validators in the given committee
* Get a list of validator indices in the given committee
* attestationIndex - Index of the committee in shuffling.committees
*/
export function getCommitteeIndices(
export function getCommitteeValidatorIndices(
shuffling: EpochShuffling,
attestationSlot: Slot,
attestationIndex: number

View File

@@ -666,7 +666,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
}
// Handler
const {indexedAttestation, committeeIndices, attDataRootHex} = validationResult;
const {indexedAttestation, committeeValidatorIndices, attDataRootHex} = validationResult;
chain.validatorMonitor?.registerGossipAggregatedAttestation(
seenTimestampSec,
signedAggregateAndProof,
@@ -678,7 +678,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
aggregatedAttestation,
attDataRootHex,
indexedAttestation.attestingIndices.length,
committeeIndices
committeeValidatorIndices
);
metrics?.opPool.aggregatedAttestationPool.gossipInsertOutcome.inc({insertOutcome});
@@ -892,7 +892,7 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
attDataRootHex,
attestation,
committeeIndex,
committeeValidatorIndex,
validatorCommitteeIndex,
committeeSize,
} = validationResult.result;
chain.validatorMonitor?.registerGossipUnaggregatedAttestation(
@@ -909,7 +909,7 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
committeeIndex,
attestation,
attDataRootHex,
committeeValidatorIndex,
validatorCommitteeIndex,
committeeSize
);
metrics?.opPool.attestationPool.gossipInsertOutcome.inc({insertOutcome});

View File

@@ -24,7 +24,7 @@ describe("AttestationPool", () => {
const clockStub = getMockedClock();
vi.spyOn(clockStub, "secFromSlot").mockReturnValue(0);
const committeeValidatorIndex = 0;
const validatorCommitteeIndex = 0;
const committeeSize = 128;
const cutOffSecFromSlot = (2 / 3) * config.SECONDS_PER_SLOT;
@@ -40,7 +40,7 @@ describe("AttestationPool", () => {
signature: validSignature,
};
const electraAttestation: electra.Attestation = {
aggregationBits: BitArray.fromSingleBit(committeeSize, committeeValidatorIndex),
aggregationBits: BitArray.fromSingleBit(committeeSize, validatorCommitteeIndex),
data: electraAttestationData,
signature: validSignature,
committeeBits: BitArray.fromSingleBit(MAX_COMMITTEES_PER_SLOT, electraSingleAttestation.committeeIndex),
@@ -68,7 +68,7 @@ describe("AttestationPool", () => {
committeeIndex,
electraSingleAttestation,
attDataRootHex,
committeeValidatorIndex,
validatorCommitteeIndex,
committeeSize
);
@@ -79,7 +79,7 @@ describe("AttestationPool", () => {
it("add correct phase0 attestation", () => {
const committeeIndex = null;
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(phase0Attestation.data));
const outcome = pool.add(committeeIndex, phase0Attestation, attDataRootHex, committeeValidatorIndex, committeeSize);
const outcome = pool.add(committeeIndex, phase0Attestation, attDataRootHex, validatorCommitteeIndex, committeeSize);
expect(outcome).equal(InsertOutcome.NewData);
expect(pool.getAggregate(phase0AttestationData.slot, attDataRootHex, committeeIndex)).toEqual(phase0Attestation);
@@ -93,7 +93,7 @@ describe("AttestationPool", () => {
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(electraSingleAttestation.data));
expect(() =>
pool.add(committeeIndex, electraSingleAttestation, attDataRootHex, committeeValidatorIndex, committeeSize)
pool.add(committeeIndex, electraSingleAttestation, attDataRootHex, validatorCommitteeIndex, committeeSize)
).toThrow();
expect(pool.getAggregate(electraAttestationData.slot, attDataRootHex, committeeIndex)).toBeNull();
});
@@ -101,7 +101,7 @@ describe("AttestationPool", () => {
it("add phase0 attestation with committee index", () => {
const committeeIndex = 0;
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(phase0Attestation.data));
const outcome = pool.add(committeeIndex, phase0Attestation, attDataRootHex, committeeValidatorIndex, committeeSize);
const outcome = pool.add(committeeIndex, phase0Attestation, attDataRootHex, validatorCommitteeIndex, committeeSize);
expect(outcome).equal(InsertOutcome.NewData);
expect(pool.getAggregate(phase0AttestationData.slot, attDataRootHex, committeeIndex)).toEqual(phase0Attestation);
@@ -119,7 +119,7 @@ describe("AttestationPool", () => {
};
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(electraAttestationDataWithPhase0Slot));
expect(() => pool.add(0, singleAttestation, attDataRootHex, committeeValidatorIndex, committeeSize)).toThrow();
expect(() => pool.add(0, singleAttestation, attDataRootHex, validatorCommitteeIndex, committeeSize)).toThrow();
});
it("add phase0 attestation with electra slot", () => {
@@ -134,6 +134,6 @@ describe("AttestationPool", () => {
};
const attDataRootHex = toHexString(ssz.phase0.AttestationData.hashTreeRoot(phase0AttestationDataWithElectraSlot));
expect(() => pool.add(0, attestation, attDataRootHex, committeeValidatorIndex, committeeSize)).toThrow();
expect(() => pool.add(0, attestation, attDataRootHex, validatorCommitteeIndex, committeeSize)).toThrow();
});
});