diff --git a/packages/beacon-node/src/chain/blocks/importBlock.ts b/packages/beacon-node/src/chain/blocks/importBlock.ts index 74b89ec590a5..ab6e4774a0ba 100644 --- a/packages/beacon-node/src/chain/blocks/importBlock.ts +++ b/packages/beacon-node/src/chain/blocks/importBlock.ts @@ -2,7 +2,6 @@ import {BitArray} from "@chainsafe/ssz"; import {routes} from "@lodestar/api"; import { AncestorStatus, - EpochDifference, ExecutionStatus, ForkChoiceError, ForkChoiceErrorCode, @@ -21,7 +20,6 @@ import { IBeaconStateView, RootCache, computeEpochAtSlot, - computeStartSlotAtEpoch, computeTimeAtSlot, isStartSlotOfEpoch, isStatePostAltair, @@ -301,21 +299,6 @@ export async function importBlock( // Set head state as strong reference this.regen.updateHeadState(newHead, postState); - try { - this.emitter.emit(routes.events.EventType.head, { - block: newHead.blockRoot, - epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(newHead.slot)) === newHead.slot, - slot: newHead.slot, - state: newHead.stateRoot, - previousDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.previous), - currentDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.current), - executionOptimistic: isOptimisticBlock(newHead), - }); - } catch (e) { - // getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue - this.logger.debug("Error emitting head event", {slot: newHead.slot, root: newHead.blockRoot}, e as Error); - } - const delaySec = this.clock.secFromSlot(newHead.slot); this.logger.verbose("New chain head", { slot: newHead.slot, diff --git a/packages/beacon-node/src/chain/chain.ts b/packages/beacon-node/src/chain/chain.ts index ec6177685753..c349b76054c8 100644 --- a/packages/beacon-node/src/chain/chain.ts +++ b/packages/beacon-node/src/chain/chain.ts @@ -1,8 +1,16 @@ import path from "node:path"; import {PrivateKey} from "@libp2p/interface"; import {Type} from "@chainsafe/ssz"; +import {routes} from "@lodestar/api"; import {BeaconConfig} from "@lodestar/config"; -import {CheckpointWithHex, ForkChoiceStateGetter, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice"; +import { + CheckpointWithHex, + EpochDifference, + ForkChoiceStateGetter, + IForkChoice, + ProtoBlock, + UpdateHeadOpt, +} from "@lodestar/fork-choice"; import {LoggerNode} from "@lodestar/logger/node"; import { EFFECTIVE_BALANCE_INCREMENT, @@ -1179,7 +1187,27 @@ export class BeaconChain implements IBeaconChain { const timer = this.metrics?.forkChoice.findHead.startTimer({caller}); try { - return this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head; + const prevHead = this.forkChoice.getHead(); + const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head; + + if (head.blockRoot !== prevHead.blockRoot) { + try { + this.emitter.emit(routes.events.EventType.head, { + block: head.blockRoot, + epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot, + slot: head.slot, + state: head.stateRoot, + previousDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.previous), + currentDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.current), + executionOptimistic: isOptimisticBlock(head), + }); + } catch (e) { + // getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue + this.logger.debug("Error emitting head event", {slot: head.slot, root: head.blockRoot}, e as Error); + } + } + + return head; } catch (e) { this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetCanonicalHead}); throw e; @@ -1209,6 +1237,7 @@ export class BeaconChain implements IBeaconChain { const secFromSlot = this.clock.secFromSlot(slot); try { + // Do not emit head event here, when proposing we rely on the one emitted when importing our own block const {head, isHeadTimely, notReorgedReason} = this.forkChoice.updateAndGetHead({ mode: UpdateHeadOpt.GetProposerHead, secFromSlot,