Skip to content

Commit

Permalink
Use attestation head state to validate attestations (#3532)
Browse files Browse the repository at this point in the history
  • Loading branch information
twoeths authored Dec 17, 2021
1 parent 3d05dd6 commit 53f31a7
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 21 deletions.
6 changes: 3 additions & 3 deletions packages/lodestar/src/chain/errors/attestationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ export enum AttestationErrorCode {
*/
COMMITTEE_INDEX_OUT_OF_RANGE = "ATTESTATION_ERROR_COMMITTEE_INDEX_OUT_OF_RANGE",
/**
* Missing attestation pre-state.
* Missing attestation head state
*/
MISSING_ATTESTATION_TARGET_STATE = "ATTESTATION_ERROR_MISSING_ATTESTATION_TARGET_STATE",
MISSING_ATTESTATION_HEAD_STATE = "ATTESTATION_ERROR_MISSING_ATTESTATION_HEAD_STATE",
/**
* Invalid aggregator.
*/
Expand Down Expand Up @@ -160,7 +160,7 @@ export type AttestationErrorType =
| {code: AttestationErrorCode.INVALID_TARGET_ROOT; targetRoot: RootHex; expected: string | null}
| {code: AttestationErrorCode.TARGET_BLOCK_NOT_AN_ANCESTOR_OF_LMD_BLOCK}
| {code: AttestationErrorCode.COMMITTEE_INDEX_OUT_OF_RANGE; index: number}
| {code: AttestationErrorCode.MISSING_ATTESTATION_TARGET_STATE; error: Error}
| {code: AttestationErrorCode.MISSING_ATTESTATION_HEAD_STATE; error: Error}
| {code: AttestationErrorCode.INVALID_AGGREGATOR}
| {code: AttestationErrorCode.INVALID_INDEXED_ATTESTATION};

Expand Down
18 changes: 9 additions & 9 deletions packages/lodestar/src/chain/validation/aggregateAndProof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ export async function validateGossipAggregateAndProof(

// [IGNORE] The block being voted for (attestation.data.beacon_block_root) has been seen (via both gossip
// and non-gossip sources) (a client MAY queue attestations for processing once block is retrieved).
verifyHeadBlockAndTargetRoot(chain, attData.beaconBlockRoot, attTarget.root, attEpoch);
const attHeadBlock = verifyHeadBlockAndTargetRoot(chain, attData.beaconBlockRoot, attTarget.root, attEpoch);

// [REJECT] The current finalized_checkpoint is an ancestor of the block defined by aggregate.data.beacon_block_root
// -- i.e. get_ancestor(store, aggregate.data.beacon_block_root, compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == store.finalized_checkpoint.root
// > Altready check in `chain.forkChoice.hasBlock(attestation.data.beaconBlockRoot)`

const targetState = await chain.regen
.getCheckpointState(attTarget, RegenCaller.validateGossipAggregateAndProof)
const attHeadState = await chain.regen
.getState(attHeadBlock.stateRoot, RegenCaller.validateGossipAggregateAndProof)
.catch((e: Error) => {
throw new AttestationError(GossipAction.REJECT, {
code: AttestationErrorCode.MISSING_ATTESTATION_TARGET_STATE,
code: AttestationErrorCode.MISSING_ATTESTATION_HEAD_STATE,
error: e as Error,
});
});

const committeeIndices = getCommitteeIndices(targetState, attSlot, attData.index);
const committeeIndices = getCommitteeIndices(attHeadState, attSlot, attData.index);
const attestingIndices = zipIndexesCommitteeBits(committeeIndices, aggregate.aggregationBits);
const indexedAttestation: phase0.IndexedAttestation = {
attestingIndices: attestingIndices as List<number>,
Expand Down Expand Up @@ -102,11 +102,11 @@ export async function validateGossipAggregateAndProof(
// by the validator with index aggregate_and_proof.aggregator_index.
// [REJECT] The aggregator signature, signed_aggregate_and_proof.signature, is valid.
// [REJECT] The signature of aggregate is valid.
const aggregator = targetState.index2pubkey[aggregateAndProof.aggregatorIndex];
const aggregator = attHeadState.index2pubkey[aggregateAndProof.aggregatorIndex];
const signatureSets = [
getSelectionProofSignatureSet(targetState, attSlot, aggregator, signedAggregateAndProof),
getAggregateAndProofSignatureSet(targetState, attEpoch, aggregator, signedAggregateAndProof),
allForks.getIndexedAttestationSignatureSet(targetState, indexedAttestation),
getSelectionProofSignatureSet(attHeadState, attSlot, aggregator, signedAggregateAndProof),
getAggregateAndProofSignatureSet(attHeadState, attEpoch, aggregator, signedAggregateAndProof),
allForks.getIndexedAttestationSignatureSet(attHeadState, indexedAttestation),
];
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true}))) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.INVALID_SIGNATURE});
Expand Down
17 changes: 9 additions & 8 deletions packages/lodestar/src/chain/validation/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export async function validateGossipAttestation(

// [IGNORE] The block being voted for (attestation.data.beacon_block_root) has been seen (via both gossip
// and non-gossip sources) (a client MAY queue attestations for processing once block is retrieved).
verifyHeadBlockAndTargetRoot(chain, attData.beaconBlockRoot, attTarget.root, attEpoch);
const attHeadBlock = verifyHeadBlockAndTargetRoot(chain, attData.beaconBlockRoot, attTarget.root, attEpoch);

// [REJECT] The block being voted for (attestation.data.beacon_block_root) passes validation.
// > Altready check in `verifyHeadBlockAndTargetRoot()`
Expand All @@ -85,19 +85,19 @@ export async function validateGossipAttestation(
// --i.e. get_ancestor(store, attestation.data.beacon_block_root, compute_start_slot_at_epoch(attestation.data.target.epoch)) == attestation.data.target.root
// > Altready check in `verifyHeadBlockAndTargetRoot()`

const attestationTargetState = await chain.regen
.getCheckpointState(attTarget, RegenCaller.validateGossipAttestation)
const attHeadState = await chain.regen
.getState(attHeadBlock.stateRoot, RegenCaller.validateGossipAttestation)
.catch((e: Error) => {
throw new AttestationError(GossipAction.REJECT, {
code: AttestationErrorCode.MISSING_ATTESTATION_TARGET_STATE,
code: AttestationErrorCode.MISSING_ATTESTATION_HEAD_STATE,
error: e as Error,
});
});

// [REJECT] The committee index is within the expected range
// -- i.e. data.index < get_committee_count_per_slot(state, data.target.epoch)
const attIndex = attData.index;
const committeeIndices = getCommitteeIndices(attestationTargetState, attSlot, attIndex);
const committeeIndices = getCommitteeIndices(attHeadState, attSlot, attIndex);
const validatorIndex = committeeIndices[bitIndex];

// [REJECT] The number of aggregation bits matches the committee size
Expand All @@ -116,7 +116,7 @@ export async function validateGossipAttestation(
// -- i.e. compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, attestation.data.index) == subnet_id,
// where committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch),
// which may be pre-computed along with the committee information for the signature check.
const expectedSubnet = computeSubnetForSlot(attestationTargetState, attSlot, attIndex);
const expectedSubnet = computeSubnetForSlot(attHeadState, attSlot, attIndex);
if (subnet !== null && subnet !== expectedSubnet) {
throw new AttestationError(GossipAction.REJECT, {
code: AttestationErrorCode.INVALID_SUBNET_ID,
Expand All @@ -141,7 +141,7 @@ export async function validateGossipAttestation(
data: attData,
signature: attestation.signature,
};
const signatureSet = getIndexedAttestationSignatureSet(attestationTargetState, indexedAttestation);
const signatureSet = getIndexedAttestationSignatureSet(attHeadState, indexedAttestation);
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true}))) {
throw new AttestationError(GossipAction.REJECT, {code: AttestationErrorCode.INVALID_SIGNATURE});
}
Expand Down Expand Up @@ -206,9 +206,10 @@ export function verifyHeadBlockAndTargetRoot(
beaconBlockRoot: Root,
targetRoot: Root,
attestationEpoch: Epoch
): void {
): IProtoBlock {
const headBlock = verifyHeadBlockIsKnown(chain, beaconBlockRoot);
verifyAttestationTargetRoot(headBlock, targetRoot, attestationEpoch);
return headBlock;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/lodestar/test/utils/validationData/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function getAttestationValidData(

// Add state to regen
const regen = ({
getCheckpointState: async () => (state as unknown) as CachedBeaconState<allForks.BeaconState>,
getState: async () => (state as unknown) as CachedBeaconState<allForks.BeaconState>,
} as Partial<IStateRegenerator>) as IStateRegenerator;

const chain = ({
Expand Down

0 comments on commit 53f31a7

Please sign in to comment.