@lodestar/beacon-node 1.47.0-dev.404f68958c → 1.47.0-dev.742db200d1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  2. package/lib/api/impl/lodestar/index.js +11 -12
  3. package/lib/api/impl/lodestar/index.js.map +1 -1
  4. package/lib/api/impl/validator/index.d.ts.map +1 -1
  5. package/lib/api/impl/validator/index.js +11 -21
  6. package/lib/api/impl/validator/index.js.map +1 -1
  7. package/lib/api/impl/validator/utils.d.ts +5 -0
  8. package/lib/api/impl/validator/utils.d.ts.map +1 -1
  9. package/lib/api/impl/validator/utils.js +18 -12
  10. package/lib/api/impl/validator/utils.js.map +1 -1
  11. package/lib/chain/blocks/index.js +1 -1
  12. package/lib/chain/blocks/index.js.map +1 -1
  13. package/lib/chain/chain.d.ts +1 -1
  14. package/lib/chain/chain.d.ts.map +1 -1
  15. package/lib/chain/chain.js +2 -1
  16. package/lib/chain/chain.js.map +1 -1
  17. package/lib/chain/errors/blockError.d.ts +1 -0
  18. package/lib/chain/errors/blockError.d.ts.map +1 -1
  19. package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -0
  20. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  21. package/lib/chain/produceBlock/produceBlockBody.js +6 -1
  22. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  23. package/lib/chain/seenCache/seenBlockProposers.d.ts +5 -1
  24. package/lib/chain/seenCache/seenBlockProposers.d.ts.map +1 -1
  25. package/lib/chain/seenCache/seenBlockProposers.js +10 -3
  26. package/lib/chain/seenCache/seenBlockProposers.js.map +1 -1
  27. package/lib/chain/validation/block.d.ts.map +1 -1
  28. package/lib/chain/validation/block.js +21 -7
  29. package/lib/chain/validation/block.js.map +1 -1
  30. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  31. package/lib/network/processor/gossipHandlers.js +25 -3
  32. package/lib/network/processor/gossipHandlers.js.map +1 -1
  33. package/lib/sync/unknownBlock.d.ts.map +1 -1
  34. package/lib/sync/unknownBlock.js +17 -5
  35. package/lib/sync/unknownBlock.js.map +1 -1
  36. package/package.json +14 -14
  37. package/src/api/impl/lodestar/index.ts +11 -12
  38. package/src/api/impl/validator/index.ts +21 -26
  39. package/src/api/impl/validator/utils.ts +30 -15
  40. package/src/chain/blocks/index.ts +1 -1
  41. package/src/chain/chain.ts +2 -0
  42. package/src/chain/errors/blockError.ts +1 -1
  43. package/src/chain/produceBlock/produceBlockBody.ts +13 -0
  44. package/src/chain/seenCache/seenBlockProposers.ts +13 -3
  45. package/src/chain/validation/block.ts +21 -7
  46. package/src/network/processor/gossipHandlers.ts +26 -3
  47. package/src/sync/unknownBlock.ts +18 -5
@@ -89,13 +89,20 @@ export async function validateGossipBlock(
89
89
 
90
90
  // [IGNORE] The block is the first block with valid signature received for the proposer for the slot, signed_beacon_block.message.slot.
91
91
  const proposerIndex = block.proposerIndex;
92
- const hasBlockRoot = chain.seenBlockProposers.hasBlockRoot(blockSlot, proposerIndex, blockRoot);
93
92
  if (chain.seenBlockProposers.isKnown(blockSlot, proposerIndex)) {
94
- if (!hasBlockRoot && !chain.seenBlockProposers.isEquivocating(blockSlot, proposerIndex)) {
95
- await verifyBlockProposerSignature(chain, signedBlock, blockRoot, {verifyOnMainThread: false});
96
- chain.seenBlockProposers.observeBlockRoot(blockSlot, proposerIndex, blockRoot, signedBlockHeader);
93
+ if (chain.seenBlockProposers.isRepeatProposal(blockSlot, proposerIndex, blockRoot)) {
94
+ const hasBlockRoot = chain.seenBlockProposers.hasBlockRoot(blockSlot, proposerIndex, blockRoot);
95
+ if (!hasBlockRoot && !chain.seenBlockProposers.isEquivocating(blockSlot, proposerIndex)) {
96
+ await verifyBlockProposerSignature(chain, signedBlock, blockRoot, {verifyOnMainThread: false});
97
+ chain.seenBlockProposers.observeBlockRoot(blockSlot, proposerIndex, blockRoot, signedBlockHeader);
98
+ }
99
+ throw new BlockGossipError(GossipAction.IGNORE, {
100
+ code: BlockErrorCode.REPEAT_PROPOSAL,
101
+ proposerIndex,
102
+ root: blockRoot,
103
+ });
97
104
  }
98
- throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.REPEAT_PROPOSAL, proposerIndex});
105
+ throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
99
106
  }
100
107
 
101
108
  // [REJECT] The current finalized_checkpoint is an ancestor of block -- i.e.
@@ -301,10 +308,17 @@ export async function validateGossipBlock(
301
308
 
302
309
  // Check again after all async validation and the early-block delay so concurrent proposals cannot both pass
303
310
  if (chain.seenBlockProposers.isKnown(blockSlot, proposerIndex)) {
304
- throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.REPEAT_PROPOSAL, proposerIndex});
311
+ if (chain.seenBlockProposers.isRepeatProposal(blockSlot, proposerIndex, blockRoot)) {
312
+ throw new BlockGossipError(GossipAction.IGNORE, {
313
+ code: BlockErrorCode.REPEAT_PROPOSAL,
314
+ proposerIndex,
315
+ root: blockRoot,
316
+ });
317
+ }
318
+ throw new BlockGossipError(GossipAction.IGNORE, {code: BlockErrorCode.ALREADY_KNOWN, root: blockRoot});
305
319
  }
306
320
 
307
- chain.seenBlockProposers.add(blockSlot, proposerIndex);
321
+ chain.seenBlockProposers.add(blockSlot, proposerIndex, blockRoot);
308
322
 
309
323
  return {skippedSlots};
310
324
  }
@@ -713,9 +713,32 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
713
713
  const {serializedData} = gossipData;
714
714
 
715
715
  const signedBlock = sszDeserialize(topic, serializedData);
716
- const blockInput = await validateBeaconBlock(signedBlock, topic.boundary.fork, peerIdStr, seenTimestampSec);
717
- chain.serializedCache.set(signedBlock, serializedData);
718
- handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
716
+ try {
717
+ const blockInput = await validateBeaconBlock(signedBlock, topic.boundary.fork, peerIdStr, seenTimestampSec);
718
+ chain.serializedCache.set(signedBlock, serializedData);
719
+ handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
720
+ } catch (e) {
721
+ // Spec: IGNORE the block, ie not to re-publish to peers
722
+ // but we should still import an equivocating (REPEAT_PROPOSAL) block into fork choice because we don't
723
+ // know if this block (or the 1st known block with same slot) will become canonical yet
724
+ if (
725
+ e instanceof BlockGossipError &&
726
+ e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
727
+ // this is make sure the block's proposer signature was verified, it should be true anyway
728
+ chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
729
+ ) {
730
+ // blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
731
+ const blockInput = chain.seenBlockInputCache.get(e.type.root);
732
+ if (blockInput) {
733
+ chain.serializedCache.set(signedBlock, serializedData);
734
+ // this is technically not a valid gossip block but gossip validation is a cheap subset of checks
735
+ // this runs the full state transition, so importing an equivocating-but-valid block here is safe.
736
+ handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
737
+ }
738
+ }
739
+ // rethrow so gossipValidatorFn maps IGNORE -> TopicValidatorResult.Ignore (message not forwarded)
740
+ throw e;
741
+ }
719
742
  },
720
743
 
721
744
  [GossipType.blob_sidecar]: async ({
@@ -1,6 +1,6 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkSeq} from "@lodestar/params";
3
+ import {ForkSeq, isForkPostGloas} from "@lodestar/params";
4
4
  import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
5
5
  import {computeTimeAtSlot} from "@lodestar/state-transition";
6
6
  import {RootHex, Slot, gloas} from "@lodestar/types";
@@ -764,9 +764,10 @@ export class BlockInputSync {
764
764
  // this prevents unbundling attack
765
765
  // see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
766
766
  const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
767
+ const blockRootHex = pendingBlock.blockInput.blockRootHex;
767
768
  const logCtx = {
768
769
  slot: blockSlot,
769
- root: pendingBlock.blockInput.blockRootHex,
770
+ root: blockRootHex,
770
771
  delaySec: this.chain.clock.secFromSlot(blockSlot),
771
772
  };
772
773
  this.logger.verbose("Processing downloaded block", logCtx);
@@ -780,7 +781,7 @@ export class BlockInputSync {
780
781
  // eligible for proposer boost to prevent unbundling attack
781
782
  this.logger.verbose("Avoid proposer boost for this block of known proposer", {
782
783
  slot: blockSlot,
783
- root: pendingBlock.blockInput.blockRootHex,
784
+ root: blockRootHex,
784
785
  proposerIndex,
785
786
  });
786
787
  await sleep(proposerBoostWindowMs);
@@ -807,12 +808,24 @@ export class BlockInputSync {
807
808
  if (!res.err) {
808
809
  this.logger.verbose("Processed block from unknown sync", logCtx);
809
810
  // no need to update status to "processed", delete anyway
810
- this.pendingBlocks.delete(pendingBlock.blockInput.blockRootHex);
811
+ this.pendingBlocks.delete(blockRootHex);
812
+
813
+ if (isForkPostGloas(fork)) {
814
+ const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
815
+ if (!payloadInput) {
816
+ this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
817
+ } else {
818
+ // Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
819
+ // The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
820
+ this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
821
+ }
822
+ }
823
+
811
824
  // Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
812
825
  // are advanced through the same dependency checks as every other pending item.
813
826
  this.triggerUnknownBlockSearch();
814
827
  } else {
815
- const errorData = {slot: pendingBlock.blockInput.slot, root: pendingBlock.blockInput.blockRootHex};
828
+ const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
816
829
  if (res.err instanceof BlockError) {
817
830
  switch (res.err.type.code) {
818
831
  // This cases are already handled with `{ignoreIfKnown: true}`