Skip to content

Commit

Permalink
update p2p validations
Browse files Browse the repository at this point in the history
  • Loading branch information
g11tech committed Jul 12, 2023
1 parent b85838a commit c46c53b
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 21 deletions.
3 changes: 2 additions & 1 deletion packages/beacon-node/src/api/impl/beacon/pool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ export function getBeaconPoolApi({
await Promise.all(
attestations.map(async (attestation, i) => {
try {
const fork = chain.config.getForkName(chain.clock.currentSlot);
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const validateFn = () => validateGossipAttestation(chain, {attestation, serializedData: null}, null);
const validateFn = () => validateGossipAttestation(fork, chain, {attestation, serializedData: null}, null);
const {slot, beaconBlockRoot} = attestation.data;
// 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
Expand Down
2 changes: 2 additions & 0 deletions packages/beacon-node/src/api/impl/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ export function getValidatorApi({

const seenTimestampSec = Date.now() / 1000;
const errors: Error[] = [];
const fork = chain.config.getForkName(chain.clock.currentSlot);

await Promise.all(
signedAggregateAndProofs.map(async (signedAggregateAndProof, i) => {
Expand All @@ -556,6 +557,7 @@ export function getValidatorApi({
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const validateFn = () =>
validateGossipAggregateAndProof(
fork,
chain,
signedAggregateAndProof,
true // skip known attesters check
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/chain/errors/attestationError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export type AttestationErrorType =
| {code: AttestationErrorCode.NOT_EXACTLY_ONE_AGGREGATION_BIT_SET}
| {code: AttestationErrorCode.PRIOR_ATTESTATION_KNOWN; validatorIndex: ValidatorIndex; epoch: Epoch}
| {code: AttestationErrorCode.FUTURE_EPOCH; attestationEpoch: Epoch; currentEpoch: Epoch}
| {code: AttestationErrorCode.PAST_EPOCH; attestationEpoch: Epoch; currentEpoch: Epoch}
| {code: AttestationErrorCode.PAST_EPOCH; attestationEpoch: Epoch; previousEpoch: Epoch}
| {code: AttestationErrorCode.ATTESTS_TO_FUTURE_BLOCK; block: Slot; attestation: Slot}
| {code: AttestationErrorCode.INVALID_SUBNET_ID; received: number; expected: number}
| {code: AttestationErrorCode.WRONG_NUMBER_OF_AGGREGATION_BITS}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {toHexString} from "@chainsafe/ssz";
import {ForkName} from "@lodestar/params";
import {phase0, RootHex, ssz, ValidatorIndex} from "@lodestar/types";
import {
computeEpochAtSlot,
Expand Down Expand Up @@ -26,6 +27,7 @@ export type AggregateAndProofValidationResult = {
};

export async function validateGossipAggregateAndProof(
fork: ForkName,
chain: IBeaconChain,
signedAggregateAndProof: phase0.SignedAggregateAndProof,
skipValidationKnownAttesters = false,
Expand Down Expand Up @@ -66,7 +68,7 @@ export async function validateGossipAggregateAndProof(
// [IGNORE] aggregate.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance)
// -- i.e. aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot
// (a client MAY queue future aggregates for processing at the appropriate slot).
verifyPropagationSlotRange(chain, attSlot);
verifyPropagationSlotRange(fork, chain, attSlot);
}

// [IGNORE] The aggregate is the first valid aggregate received for the aggregator with
Expand Down
63 changes: 47 additions & 16 deletions packages/beacon-node/src/chain/validation/attestation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {phase0, Epoch, Root, Slot, RootHex, ssz} from "@lodestar/types";
import {ProtoBlock} from "@lodestar/fork-choice";
import {ATTESTATION_SUBNET_COUNT, SLOTS_PER_EPOCH} from "@lodestar/params";
import {ATTESTATION_SUBNET_COUNT, SLOTS_PER_EPOCH, ForkName, ForkSeq} from "@lodestar/params";
import {toHexString} from "@chainsafe/ssz";
import {
computeEpochAtSlot,
Expand Down Expand Up @@ -51,6 +51,7 @@ const SHUFFLING_LOOK_AHEAD_EPOCHS = 1;
* This is to avoid deserializing similar attestation multiple times which could help the gc
*/
export async function validateGossipAttestation(
fork: ForkName,
chain: IBeaconChain,
attestationOrBytes: AttestationOrBytes,
/** Optional, to allow verifying attestations through API with unknown subnet */
Expand Down Expand Up @@ -113,7 +114,7 @@ export async function validateGossipAttestation(
// [IGNORE] attestation.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (within a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance)
// -- i.e. attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot
// (a client MAY queue future attestations for processing at the appropriate slot).
verifyPropagationSlotRange(chain, attestationOrCache.attestation.data.slot);
verifyPropagationSlotRange(fork, chain, attestationOrCache.attestation.data.slot);
}

// [REJECT] The attestation is unaggregated -- that is, it has exactly one participating validator
Expand Down Expand Up @@ -310,29 +311,59 @@ export async function validateGossipAttestation(
* Accounts for `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
* Note: We do not queue future attestations for later processing
*/
export function verifyPropagationSlotRange(chain: IBeaconChain, attestationSlot: Slot): void {
export function verifyPropagationSlotRange(fork: ForkName, chain: IBeaconChain, attestationSlot: Slot): void {
// slot with future tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC
const latestPermissibleSlot = chain.clock.slotWithFutureTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC);
const earliestPermissibleSlot = Math.max(
// slot with past tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC
// ATTESTATION_PROPAGATION_SLOT_RANGE = SLOTS_PER_EPOCH
chain.clock.slotWithPastTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC) - SLOTS_PER_EPOCH,
0
);
if (attestationSlot < earliestPermissibleSlot) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.PAST_SLOT,
earliestPermissibleSlot,
attestationSlot,
});
}
if (attestationSlot > latestPermissibleSlot) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.FUTURE_SLOT,
latestPermissibleSlot,
attestationSlot,
});
}

const earliestPermissibleSlot = Math.max(
// slot with past tolerance of MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC
// ATTESTATION_PROPAGATION_SLOT_RANGE = SLOTS_PER_EPOCH
chain.clock.slotWithPastTolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY_SEC) - SLOTS_PER_EPOCH,
0
);

// Post deneb the attestations are valid for current as well as previous epoch
// while pre deneb they are valid for ATTESTATION_PROPAGATION_SLOT_RANGE
//
// see: https://github.com/ethereum/consensus-specs/pull/3360
if (ForkSeq[fork] < ForkSeq.deneb) {
if (attestationSlot < earliestPermissibleSlot) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.PAST_SLOT,
earliestPermissibleSlot,
attestationSlot,
});
}
} else {
const attestationEpoch = computeEpochAtSlot(attestationSlot);

// upper bound for current epoch is same as epoch of latestPermissibleSlot
const latestPermissibleCurrentEpoch = computeEpochAtSlot(latestPermissibleSlot);
if (attestationEpoch > latestPermissibleCurrentEpoch) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.FUTURE_EPOCH,
currentEpoch: latestPermissibleCurrentEpoch,
attestationEpoch,
});
}

// lower bound for previous epoch is same as epoch of earliestPermissibleSlot
const earliestPermissiblePreviousEpoch = computeEpochAtSlot(earliestPermissibleSlot);
if (attestationEpoch < earliestPermissiblePreviousEpoch) {
throw new AttestationError(GossipAction.IGNORE, {
code: AttestationErrorCode.PAST_EPOCH,
previousEpoch: earliestPermissiblePreviousEpoch,
attestationEpoch,
});
}
}
}

/**
Expand Down
14 changes: 12 additions & 2 deletions packages/beacon-node/src/network/processor/gossipHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,16 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
[GossipType.beacon_aggregate_and_proof]: async ({serializedData}, topic, _peer, seenTimestampSec) => {
let validationResult: AggregateAndProofValidationResult;
const signedAggregateAndProof = sszDeserialize(topic, serializedData);
const {fork} = topic;

try {
validationResult = await validateGossipAggregateAndProof(chain, signedAggregateAndProof, false, serializedData);
validationResult = await validateGossipAggregateAndProof(
fork,
chain,
signedAggregateAndProof,
false,
serializedData
);
} catch (e) {
if (e instanceof AttestationError && e.action === GossipAction.REJECT) {
chain.persistInvalidSszValue(ssz.phase0.SignedAggregateAndProof, signedAggregateAndProof, "gossip_reject");
Expand Down Expand Up @@ -229,14 +236,17 @@ export function getGossipHandlers(modules: ValidatorFnsModules, options: GossipH
chain.emitter.emit(routes.events.EventType.attestation, signedAggregateAndProof.message.aggregate);
},

[GossipType.beacon_attestation]: async ({serializedData, msgSlot}, {subnet}, _peer, seenTimestampSec) => {
[GossipType.beacon_attestation]: async ({serializedData, msgSlot}, topic, _peer, seenTimestampSec) => {
if (msgSlot == undefined) {
throw Error("msgSlot is undefined for beacon_attestation topic");
}
const {subnet, fork} = topic;

// do not deserialize gossipSerializedData here, it's done in validateGossipAttestation only if needed
let validationResult: AttestationValidationResult;
try {
validationResult = await validateGossipAttestation(
fork,
chain,
{attestation: null, serializedData, attSlot: msgSlot},
subnet
Expand Down

0 comments on commit c46c53b

Please sign in to comment.