@lodestar/beacon-node 1.48.0-dev.67344272c0 → 1.48.0-dev.68ce7b06a0
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.
- package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +4 -11
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +14 -0
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/config/index.d.ts.map +1 -1
- package/lib/api/impl/config/index.js +4 -1
- package/lib/api/impl/config/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +39 -39
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +4 -1
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.d.ts.map +1 -1
- package/lib/chain/archiveStore/historicalState/metrics.js +12 -1
- package/lib/chain/archiveStore/historicalState/metrics.js.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +9 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/archiveBlocks.js +37 -4
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts +5 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +12 -18
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +8 -4
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +83 -17
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +8 -0
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +26 -11
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/chain.d.ts +9 -4
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +82 -51
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +1 -1
- package/lib/chain/emitter.js +1 -1
- package/lib/chain/errors/blockError.d.ts +3 -5
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -2
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +4 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +0 -11
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +0 -2
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/errors/gossipValidation.d.ts +2 -1
- package/lib/chain/errors/gossipValidation.d.ts.map +1 -1
- package/lib/chain/errors/gossipValidation.js +1 -0
- package/lib/chain/errors/gossipValidation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +2 -0
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +4 -4
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/lightClient/index.d.ts +3 -3
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +21 -23
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts +2 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +5 -1
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +13 -13
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +46 -64
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +9 -50
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +56 -40
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +0 -29
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +5 -6
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +7 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +22 -5
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/builder/validateBid.d.ts.map +1 -1
- package/lib/execution/builder/validateBid.js +8 -5
- package/lib/execution/builder/validateBid.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +4 -1
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -3
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -5
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +0 -6
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -1
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +4 -20
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/network.js +2 -2
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +383 -279
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +49 -3
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +254 -114
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +6 -22
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.js +2 -1
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +24 -1
- package/lib/sync/range/range.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +8 -14
- package/src/api/impl/beacon/state/utils.ts +15 -0
- package/src/api/impl/config/index.ts +4 -1
- package/src/api/impl/validator/index.ts +44 -43
- package/src/chain/archiveStore/archiveStore.ts +6 -0
- package/src/chain/archiveStore/historicalState/metrics.ts +14 -1
- package/src/chain/archiveStore/utils/archiveBlocks.ts +39 -3
- package/src/chain/blocks/importBlock.ts +25 -20
- package/src/chain/blocks/index.ts +120 -23
- package/src/chain/blocks/types.ts +9 -0
- package/src/chain/blocks/utils/chainSegment.ts +26 -12
- package/src/chain/chain.ts +100 -61
- package/src/chain/emitter.ts +1 -1
- package/src/chain/errors/blockError.ts +3 -3
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/errors/executionPayloadEnvelope.ts +0 -9
- package/src/chain/errors/gossipValidation.ts +1 -0
- package/src/chain/forkChoice/index.ts +2 -0
- package/src/chain/interface.ts +4 -4
- package/src/chain/lightClient/index.ts +32 -36
- package/src/chain/lightClient/proofs.ts +10 -0
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/prepareNextSlot.ts +16 -16
- package/src/chain/produceBlock/produceBlockBody.ts +50 -71
- package/src/chain/validation/block.ts +9 -66
- package/src/chain/validation/executionPayloadBid.ts +62 -44
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -37
- package/src/chain/validation/payloadAttestationMessage.ts +8 -9
- package/src/chain/validatorMonitor.ts +24 -7
- package/src/execution/builder/validateBid.ts +10 -6
- package/src/execution/engine/mock.ts +4 -1
- package/src/metrics/metrics/lodestar.ts +6 -5
- package/src/network/gossip/encoding.ts +0 -6
- package/src/network/gossip/topic.ts +3 -25
- package/src/network/network.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +495 -345
- package/src/network/processor/index.ts +287 -165
- package/src/network/reqresp/protocols.ts +7 -24
- package/src/network/reqresp/rateLimit.ts +2 -1
- package/src/sync/range/range.ts +23 -1
|
@@ -87,6 +87,7 @@ import {OpSource} from "../../chain/validatorMonitor.js";
|
|
|
87
87
|
import {Metrics} from "../../metrics/index.js";
|
|
88
88
|
import {kzgCommitmentToVersionedHash} from "../../util/blobs.js";
|
|
89
89
|
import {getBlobKzgCommitments, getDataColumnSidecarSlot} from "../../util/dataColumns.js";
|
|
90
|
+
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
90
91
|
import {INetworkCore} from "../core/index.js";
|
|
91
92
|
import {NetworkEventBus} from "../events.js";
|
|
92
93
|
import {
|
|
@@ -248,7 +249,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
248
249
|
throw e;
|
|
249
250
|
}
|
|
250
251
|
|
|
251
|
-
chain.persistInvalidSszValue(
|
|
252
|
+
chain.persistInvalidSszValue(
|
|
253
|
+
forkTypes.SignedBeaconBlock,
|
|
254
|
+
signedBlock,
|
|
255
|
+
`gossip_reject_slot_${slot}`,
|
|
256
|
+
blockRootHex
|
|
257
|
+
);
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
// REJECT or unexpected (non-BlockGossipError) error: drop the optimistically-added entries from
|
|
@@ -717,8 +723,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
717
723
|
const signedBlock = sszDeserialize(topic, serializedData);
|
|
718
724
|
try {
|
|
719
725
|
const blockInput = await validateBeaconBlock(signedBlock, topic.boundary.fork, peerIdStr, seenTimestampSec);
|
|
720
|
-
|
|
721
|
-
|
|
726
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
727
|
+
callInNextEventLoop(() => {
|
|
728
|
+
try {
|
|
729
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
730
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
731
|
+
} catch (e) {
|
|
732
|
+
logger.debug(
|
|
733
|
+
"Error handling gossip block",
|
|
734
|
+
{slot: signedBlock.message.slot, root: blockInput.blockRootHex},
|
|
735
|
+
e as Error
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
722
739
|
} catch (e) {
|
|
723
740
|
// Spec: IGNORE the block, ie not to re-publish to peers
|
|
724
741
|
// but we should still import an equivocating (REPEAT_PROPOSAL) block into fork choice because we don't
|
|
@@ -732,6 +749,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
732
749
|
// blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
|
|
733
750
|
const blockInput = chain.seenBlockInputCache.get(e.type.root);
|
|
734
751
|
if (blockInput) {
|
|
752
|
+
// We're returning IGNORE (thrown below), so this block is not forwarded to peers. Unlike the
|
|
753
|
+
// happy path there is no rush to forward, so we don't need to wrap in callInNextEventLoop.
|
|
735
754
|
chain.serializedCache.set(signedBlock, serializedData);
|
|
736
755
|
// this is technically not a valid gossip block but gossip validation is a cheap subset of checks
|
|
737
756
|
// this runs the full state transition, so importing an equivocating-but-valid block here is safe.
|
|
@@ -758,29 +777,40 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
758
777
|
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_DENEB_BLOCK"});
|
|
759
778
|
}
|
|
760
779
|
const blockInput = await validateBeaconBlob(blobSidecar, topic.subnet, peerIdStr, seenTimestampSec);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
blockInput.waitForAllData(cutoffTimeMs).catch((_e) => {
|
|
770
|
-
chain.logger.debug(
|
|
771
|
-
"Waited for data after receiving gossip blob. Cut-off reached so attempting to fetch remainder of BlockInput",
|
|
772
|
-
{
|
|
780
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
781
|
+
callInNextEventLoop(() => {
|
|
782
|
+
try {
|
|
783
|
+
chain.serializedCache.set(blobSidecar, serializedData);
|
|
784
|
+
if (!blockInput.hasBlockAndAllData()) {
|
|
785
|
+
const cutoffTimeMs = getCutoffTimeMs(chain, blobSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
786
|
+
chain.logger.debug("Received gossip blob, waiting for full data availability", {
|
|
787
|
+
msToWait: cutoffTimeMs,
|
|
773
788
|
blobIndex: index,
|
|
774
789
|
...blockInput.getLogMeta(),
|
|
775
|
-
}
|
|
790
|
+
});
|
|
791
|
+
blockInput.waitForAllData(cutoffTimeMs).catch((_e) => {
|
|
792
|
+
chain.logger.debug(
|
|
793
|
+
"Waited for data after receiving gossip blob. Cut-off reached so attempting to fetch remainder of BlockInput",
|
|
794
|
+
{
|
|
795
|
+
blobIndex: index,
|
|
796
|
+
...blockInput.getLogMeta(),
|
|
797
|
+
}
|
|
798
|
+
);
|
|
799
|
+
chain.emitter.emit(ChainEvent.incompleteBlockInput, {
|
|
800
|
+
blockInput,
|
|
801
|
+
peer: peerIdStr,
|
|
802
|
+
source: BlockInputSource.gossip,
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
} catch (e) {
|
|
807
|
+
logger.debug(
|
|
808
|
+
"Error handling gossip blob",
|
|
809
|
+
{slot: blobSlot, root: blockInput.blockRootHex, index},
|
|
810
|
+
e as Error
|
|
776
811
|
);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
peer: peerIdStr,
|
|
780
|
-
source: BlockInputSource.gossip,
|
|
781
|
-
});
|
|
782
|
-
});
|
|
783
|
-
}
|
|
812
|
+
}
|
|
813
|
+
});
|
|
784
814
|
},
|
|
785
815
|
|
|
786
816
|
[GossipType.data_column_sidecar]: async ({
|
|
@@ -813,58 +843,69 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
813
843
|
peerIdStr,
|
|
814
844
|
seenTimestampSec
|
|
815
845
|
);
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
if (receivedColumns >= NUMBER_OF_COLUMNS / 2) {
|
|
836
|
-
chain.columnReconstructionTracker.triggerColumnReconstruction(payloadInput);
|
|
837
|
-
}
|
|
846
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
847
|
+
callInNextEventLoop(() => {
|
|
848
|
+
try {
|
|
849
|
+
chain.serializedCache.set(dataColumnSidecar, serializedData);
|
|
850
|
+
|
|
851
|
+
const payloadInputMeta = payloadInput.getLogMeta();
|
|
852
|
+
const {receivedColumns} = payloadInputMeta;
|
|
853
|
+
// it's not helpful to track every single column received
|
|
854
|
+
// instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
|
|
855
|
+
switch (receivedColumns) {
|
|
856
|
+
case 1:
|
|
857
|
+
case config.SAMPLES_PER_SLOT:
|
|
858
|
+
case 2 * config.SAMPLES_PER_SLOT:
|
|
859
|
+
case NUMBER_OF_COLUMNS / 4:
|
|
860
|
+
case NUMBER_OF_COLUMNS / 2:
|
|
861
|
+
case NUMBER_OF_COLUMNS:
|
|
862
|
+
metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
838
865
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
866
|
+
if (!payloadInput.hasComputedAllData()) {
|
|
867
|
+
// if we've received at least half of the columns, trigger reconstruction of the rest
|
|
868
|
+
if (receivedColumns >= NUMBER_OF_COLUMNS / 2) {
|
|
869
|
+
chain.columnReconstructionTracker.triggerColumnReconstruction(payloadInput);
|
|
870
|
+
}
|
|
844
871
|
|
|
845
|
-
|
|
846
|
-
// envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
|
|
847
|
-
// payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
|
|
848
|
-
|
|
849
|
-
if (!payloadInput.isComplete()) {
|
|
850
|
-
const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
851
|
-
// do not await here to not delay gossip validation
|
|
852
|
-
payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
|
|
853
|
-
chain.logger.debug(
|
|
854
|
-
"Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
|
|
855
|
-
{
|
|
872
|
+
chain.logger.debug("Received gossip data column, payload envelope input not yet complete", {
|
|
856
873
|
dataColumnIndex: index,
|
|
857
874
|
...payloadInputMeta,
|
|
858
|
-
}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// NOTE: we do NOT call chain.processExecutionPayload here. That is triggered only by
|
|
879
|
+
// envelope arrival (gossip or API). An in-flight importExecutionPayload is awaiting
|
|
880
|
+
// payloadInput.waitForAllData(); addColumn above will resolve it once hasAllData flips.
|
|
881
|
+
|
|
882
|
+
if (!payloadInput.isComplete()) {
|
|
883
|
+
const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
884
|
+
// do not await here to not delay gossip validation
|
|
885
|
+
payloadInput.waitForEnvelopeAndAllData(cutoffTimeMs).catch((_e) => {
|
|
886
|
+
chain.logger.debug(
|
|
887
|
+
"Waited for envelope and data after receiving gossip column. Cut-off reached so emitting incompletePayloadEnvelope",
|
|
888
|
+
{
|
|
889
|
+
dataColumnIndex: index,
|
|
890
|
+
...payloadInputMeta,
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
// TODO GLOAS: UnknownBlockSync to handle this event
|
|
894
|
+
chain.emitter.emit(ChainEvent.incompletePayloadEnvelope, {
|
|
895
|
+
payloadInput,
|
|
896
|
+
peer: peerIdStr,
|
|
897
|
+
source: BlockInputSource.gossip,
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
} catch (e) {
|
|
902
|
+
logger.debug(
|
|
903
|
+
"Error handling gossip data column",
|
|
904
|
+
{slot: dataColumnSlot, root: payloadInput.blockRootHex, index},
|
|
905
|
+
e as Error
|
|
859
906
|
);
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
payloadInput,
|
|
863
|
-
peer: peerIdStr,
|
|
864
|
-
source: BlockInputSource.gossip,
|
|
865
|
-
});
|
|
866
|
-
});
|
|
867
|
-
}
|
|
907
|
+
}
|
|
908
|
+
});
|
|
868
909
|
} else {
|
|
869
910
|
if (config.getForkSeq(dataColumnSlot) < ForkSeq.fulu) {
|
|
870
911
|
throw new GossipActionError(GossipAction.REJECT, {code: "PRE_FULU_BLOCK"});
|
|
@@ -887,54 +928,65 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
887
928
|
peerIdStr,
|
|
888
929
|
seenTimestampSec
|
|
889
930
|
);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
931
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
932
|
+
callInNextEventLoop(() => {
|
|
933
|
+
try {
|
|
934
|
+
chain.serializedCache.set(dataColumnSidecar, serializedData);
|
|
935
|
+
const blockInputMeta = blockInput.getLogMeta();
|
|
936
|
+
const {receivedColumns} = blockInputMeta;
|
|
937
|
+
// it's not helpful to track every single column received
|
|
938
|
+
// instead of that, track 1st, 8th, 16th 32th, 64th, and 128th column
|
|
939
|
+
switch (receivedColumns) {
|
|
940
|
+
case 1:
|
|
941
|
+
case config.SAMPLES_PER_SLOT:
|
|
942
|
+
case 2 * config.SAMPLES_PER_SLOT:
|
|
943
|
+
case NUMBER_OF_COLUMNS / 4:
|
|
944
|
+
case NUMBER_OF_COLUMNS / 2:
|
|
945
|
+
case NUMBER_OF_COLUMNS:
|
|
946
|
+
metrics?.dataColumns.elapsedTimeTillReceived.observe({receivedOrder: receivedColumns}, delaySec);
|
|
947
|
+
break;
|
|
948
|
+
}
|
|
905
949
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
950
|
+
if (!blockInput.hasComputedAllData()) {
|
|
951
|
+
// immediately attempt fetch of data columns from execution engine
|
|
952
|
+
chain.getBlobsTracker.triggerGetBlobs(blockInput);
|
|
953
|
+
// if we've received at least half of the columns, trigger reconstruction of the rest
|
|
954
|
+
if (blockInput.columnCount >= NUMBER_OF_COLUMNS / 2) {
|
|
955
|
+
chain.columnReconstructionTracker.triggerColumnReconstruction(blockInput);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
914
958
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
dataColumnIndex: index,
|
|
920
|
-
...blockInputMeta,
|
|
921
|
-
});
|
|
922
|
-
// do not await here to not delay gossip validation
|
|
923
|
-
blockInput.waitForBlockAndAllData(cutoffTimeMs).catch((_e) => {
|
|
924
|
-
chain.logger.debug(
|
|
925
|
-
"Waited for data after receiving gossip column. Cut-off reached so attempting to fetch remainder of BlockInput",
|
|
926
|
-
{
|
|
959
|
+
if (!blockInput.hasBlockAndAllData()) {
|
|
960
|
+
const cutoffTimeMs = getCutoffTimeMs(chain, dataColumnSlot, BLOCK_AVAILABILITY_CUTOFF_MS);
|
|
961
|
+
chain.logger.debug("Received gossip data column, waiting for full data availability", {
|
|
962
|
+
msToWait: cutoffTimeMs,
|
|
927
963
|
dataColumnIndex: index,
|
|
928
964
|
...blockInputMeta,
|
|
929
|
-
}
|
|
965
|
+
});
|
|
966
|
+
// do not await here to not delay gossip validation
|
|
967
|
+
blockInput.waitForBlockAndAllData(cutoffTimeMs).catch((_e) => {
|
|
968
|
+
chain.logger.debug(
|
|
969
|
+
"Waited for data after receiving gossip column. Cut-off reached so attempting to fetch remainder of BlockInput",
|
|
970
|
+
{
|
|
971
|
+
dataColumnIndex: index,
|
|
972
|
+
...blockInputMeta,
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
chain.emitter.emit(ChainEvent.incompleteBlockInput, {
|
|
976
|
+
blockInput,
|
|
977
|
+
peer: peerIdStr,
|
|
978
|
+
source: BlockInputSource.gossip,
|
|
979
|
+
});
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
} catch (e) {
|
|
983
|
+
logger.debug(
|
|
984
|
+
"Error handling gossip data column",
|
|
985
|
+
{slot: dataColumnSlot, root: blockInput.blockRootHex, index},
|
|
986
|
+
e as Error
|
|
930
987
|
);
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
peer: peerIdStr,
|
|
934
|
-
source: BlockInputSource.gossip,
|
|
935
|
-
});
|
|
936
|
-
});
|
|
937
|
-
}
|
|
988
|
+
}
|
|
989
|
+
});
|
|
938
990
|
}
|
|
939
991
|
},
|
|
940
992
|
|
|
@@ -961,36 +1013,41 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
961
1013
|
throw e;
|
|
962
1014
|
}
|
|
963
1015
|
|
|
964
|
-
// Handler
|
|
1016
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
965
1017
|
const {indexedAttestation, committeeValidatorIndices, attDataRootHex} = validationResult;
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1018
|
+
callInNextEventLoop(() => {
|
|
1019
|
+
try {
|
|
1020
|
+
chain.validatorMonitor?.registerGossipAggregatedAttestation(
|
|
1021
|
+
seenTimestampSec,
|
|
1022
|
+
signedAggregateAndProof,
|
|
1023
|
+
indexedAttestation
|
|
1024
|
+
);
|
|
1025
|
+
const aggregatedAttestation = signedAggregateAndProof.message.aggregate;
|
|
972
1026
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1027
|
+
const insertOutcome = chain.aggregatedAttestationPool.add(
|
|
1028
|
+
aggregatedAttestation,
|
|
1029
|
+
attDataRootHex,
|
|
1030
|
+
indexedAttestation.attestingIndices.length,
|
|
1031
|
+
committeeValidatorIndices
|
|
1032
|
+
);
|
|
1033
|
+
metrics?.opPool.aggregatedAttestationPool.gossipInsertOutcome.inc({insertOutcome});
|
|
980
1034
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1035
|
+
if (!options.dontSendGossipAttestationsToForkchoice) {
|
|
1036
|
+
chain.forkChoice.onAttestation(indexedAttestation, attDataRootHex);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
chain.emitter.emit(routes.events.EventType.attestation, signedAggregateAndProof.message.aggregate);
|
|
984
1040
|
} catch (e) {
|
|
985
1041
|
logger.debug(
|
|
986
|
-
"Error
|
|
987
|
-
{
|
|
1042
|
+
"Error handling gossip aggregate and proof",
|
|
1043
|
+
{
|
|
1044
|
+
slot: signedAggregateAndProof.message.aggregate.data.slot,
|
|
1045
|
+
root: toRootHex(signedAggregateAndProof.message.aggregate.data.beaconBlockRoot),
|
|
1046
|
+
},
|
|
988
1047
|
e as Error
|
|
989
1048
|
);
|
|
990
1049
|
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
chain.emitter.emit(routes.events.EventType.attestation, signedAggregateAndProof.message.aggregate);
|
|
1050
|
+
});
|
|
994
1051
|
},
|
|
995
1052
|
|
|
996
1053
|
[GossipType.attester_slashing]: async ({
|
|
@@ -1002,16 +1059,20 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1002
1059
|
const attesterSlashing = sszDeserialize(topic, serializedData);
|
|
1003
1060
|
await validateGossipAttesterSlashing(chain, attesterSlashing);
|
|
1004
1061
|
|
|
1005
|
-
// Handler
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1062
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1063
|
+
callInNextEventLoop(() => {
|
|
1064
|
+
try {
|
|
1065
|
+
chain.opPool.insertAttesterSlashing(fork, attesterSlashing);
|
|
1066
|
+
chain.forkChoice.onAttesterSlashing(attesterSlashing);
|
|
1067
|
+
chain.emitter.emit(routes.events.EventType.attesterSlashing, attesterSlashing);
|
|
1068
|
+
} catch (e) {
|
|
1069
|
+
logger.debug(
|
|
1070
|
+
"Error handling gossip attester slashing",
|
|
1071
|
+
{slot: attesterSlashing.attestation1.data.slot},
|
|
1072
|
+
e as Error
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1015
1076
|
},
|
|
1016
1077
|
|
|
1017
1078
|
[GossipType.proposer_slashing]: async ({
|
|
@@ -1022,15 +1083,22 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1022
1083
|
const proposerSlashing = sszDeserialize(topic, serializedData);
|
|
1023
1084
|
await validateGossipProposerSlashing(chain, proposerSlashing);
|
|
1024
1085
|
|
|
1025
|
-
// Handler
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1086
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1087
|
+
callInNextEventLoop(() => {
|
|
1088
|
+
try {
|
|
1089
|
+
chain.opPool.insertProposerSlashing(proposerSlashing);
|
|
1090
|
+
chain.emitter.emit(routes.events.EventType.proposerSlashing, proposerSlashing);
|
|
1091
|
+
} catch (e) {
|
|
1092
|
+
logger.debug(
|
|
1093
|
+
"Error handling gossip proposer slashing",
|
|
1094
|
+
{
|
|
1095
|
+
slot: proposerSlashing.signedHeader1.message.slot,
|
|
1096
|
+
proposerIndex: proposerSlashing.signedHeader1.message.proposerIndex,
|
|
1097
|
+
},
|
|
1098
|
+
e as Error
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1034
1102
|
},
|
|
1035
1103
|
|
|
1036
1104
|
[GossipType.voluntary_exit]: async ({gossipData, topic}: GossipHandlerParamGeneric<GossipType.voluntary_exit>) => {
|
|
@@ -1038,15 +1106,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1038
1106
|
const voluntaryExit = sszDeserialize(topic, serializedData);
|
|
1039
1107
|
await validateGossipVoluntaryExit(chain, voluntaryExit);
|
|
1040
1108
|
|
|
1041
|
-
// Handler
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1109
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1110
|
+
callInNextEventLoop(() => {
|
|
1111
|
+
try {
|
|
1112
|
+
chain.opPool.insertVoluntaryExit(voluntaryExit);
|
|
1113
|
+
chain.emitter.emit(routes.events.EventType.voluntaryExit, voluntaryExit);
|
|
1114
|
+
} catch (e) {
|
|
1115
|
+
logger.debug(
|
|
1116
|
+
"Error handling gossip voluntary exit",
|
|
1117
|
+
{epoch: voluntaryExit.message.epoch, validatorIndex: voluntaryExit.message.validatorIndex},
|
|
1118
|
+
e as Error
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1050
1122
|
},
|
|
1051
1123
|
|
|
1052
1124
|
[GossipType.sync_committee_contribution_and_proof]: async ({
|
|
@@ -1065,22 +1137,30 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1065
1137
|
throw e;
|
|
1066
1138
|
});
|
|
1067
1139
|
|
|
1068
|
-
// Handler
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1140
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1141
|
+
callInNextEventLoop(() => {
|
|
1142
|
+
try {
|
|
1143
|
+
chain.validatorMonitor?.registerGossipSyncContributionAndProof(
|
|
1144
|
+
contributionAndProof.message,
|
|
1145
|
+
syncCommitteeParticipantIndices
|
|
1146
|
+
);
|
|
1147
|
+
const insertOutcome = chain.syncContributionAndProofPool.add(
|
|
1148
|
+
contributionAndProof.message,
|
|
1149
|
+
syncCommitteeParticipantIndices.length
|
|
1150
|
+
);
|
|
1151
|
+
metrics?.opPool.syncContributionAndProofPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1152
|
+
chain.emitter.emit(routes.events.EventType.contributionAndProof, contributionAndProof);
|
|
1153
|
+
} catch (e) {
|
|
1154
|
+
logger.debug(
|
|
1155
|
+
"Error handling gossip contribution and proof",
|
|
1156
|
+
{
|
|
1157
|
+
slot: contributionAndProof.message.contribution.slot,
|
|
1158
|
+
subcommitteeIndex: contributionAndProof.message.contribution.subcommitteeIndex,
|
|
1159
|
+
},
|
|
1160
|
+
e as Error
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1084
1164
|
},
|
|
1085
1165
|
|
|
1086
1166
|
[GossipType.sync_committee]: async ({gossipData, topic}: GossipHandlerParamGeneric<GossipType.sync_committee>) => {
|
|
@@ -1097,15 +1177,22 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1097
1177
|
throw e;
|
|
1098
1178
|
}
|
|
1099
1179
|
|
|
1100
|
-
// Handler
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1180
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1181
|
+
// add for ALL positions this validator holds in the subcommittee
|
|
1182
|
+
callInNextEventLoop(() => {
|
|
1183
|
+
try {
|
|
1184
|
+
for (const indexInSubcommittee of indicesInSubcommittee) {
|
|
1185
|
+
const insertOutcome = chain.syncCommitteeMessagePool.add(subnet, syncCommittee, indexInSubcommittee);
|
|
1186
|
+
metrics?.opPool.syncCommitteeMessagePoolInsertOutcome.inc({insertOutcome});
|
|
1187
|
+
}
|
|
1188
|
+
} catch (e) {
|
|
1189
|
+
logger.debug(
|
|
1190
|
+
"Error handling gossip sync committee",
|
|
1191
|
+
{slot: syncCommittee.slot, subnet, validatorIndex: syncCommittee.validatorIndex},
|
|
1192
|
+
e as Error
|
|
1193
|
+
);
|
|
1105
1194
|
}
|
|
1106
|
-
}
|
|
1107
|
-
logger.debug("Error adding to syncCommittee pool", {subnet}, e as Error);
|
|
1108
|
-
}
|
|
1195
|
+
});
|
|
1109
1196
|
},
|
|
1110
1197
|
|
|
1111
1198
|
[GossipType.light_client_finality_update]: async ({
|
|
@@ -1135,14 +1222,19 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1135
1222
|
const blsToExecutionChange = sszDeserialize(topic, serializedData);
|
|
1136
1223
|
await validateGossipBlsToExecutionChange(chain, blsToExecutionChange);
|
|
1137
1224
|
|
|
1138
|
-
// Handler
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1225
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1226
|
+
callInNextEventLoop(() => {
|
|
1227
|
+
try {
|
|
1228
|
+
chain.opPool.insertBlsToExecutionChange(blsToExecutionChange);
|
|
1229
|
+
chain.emitter.emit(routes.events.EventType.blsToExecutionChange, blsToExecutionChange);
|
|
1230
|
+
} catch (e) {
|
|
1231
|
+
logger.debug(
|
|
1232
|
+
"Error handling gossip bls to execution change",
|
|
1233
|
+
{validatorIndex: blsToExecutionChange.message.validatorIndex},
|
|
1234
|
+
e as Error
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1146
1238
|
},
|
|
1147
1239
|
[GossipType.execution_payload]: async ({
|
|
1148
1240
|
gossipData,
|
|
@@ -1203,57 +1295,64 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1203
1295
|
});
|
|
1204
1296
|
}
|
|
1205
1297
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
source: PayloadEnvelopeInputSource.gossip,
|
|
1211
|
-
seenTimestampSec,
|
|
1212
|
-
peerIdStr,
|
|
1213
|
-
});
|
|
1298
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1299
|
+
callInNextEventLoop(() => {
|
|
1300
|
+
try {
|
|
1301
|
+
chain.serializedCache.set(signedEnvelope, serializedData);
|
|
1214
1302
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1303
|
+
payloadInput.addPayloadEnvelope({
|
|
1304
|
+
envelope: signedEnvelope,
|
|
1305
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
1306
|
+
seenTimestampSec,
|
|
1307
|
+
peerIdStr,
|
|
1308
|
+
});
|
|
1221
1309
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1310
|
+
chain.emitter.emit(routes.events.EventType.executionPayloadGossip, {
|
|
1311
|
+
slot,
|
|
1312
|
+
builderIndex: envelope.builderIndex,
|
|
1313
|
+
blockHash: toRootHex(envelope.payload.blockHash),
|
|
1314
|
+
blockRoot: blockRootHex,
|
|
1315
|
+
});
|
|
1225
1316
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1317
|
+
chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
|
|
1318
|
+
// Adjust verbosity based on error type
|
|
1319
|
+
let logLevel: LogLevel;
|
|
1320
|
+
|
|
1321
|
+
if (e instanceof PayloadError) {
|
|
1322
|
+
switch (e.type.code) {
|
|
1323
|
+
// BLOCK_NOT_IN_FORK_CHOICE should not happen, validateGossipExecutionPayloadEnvelope above
|
|
1324
|
+
// already verified the block is in fork choice
|
|
1325
|
+
case PayloadErrorCode.BLOCK_NOT_IN_FORK_CHOICE:
|
|
1326
|
+
case PayloadErrorCode.MISS_BLOCK_STATE:
|
|
1327
|
+
case PayloadErrorCode.EXECUTION_ENGINE_ERROR:
|
|
1328
|
+
// Errors might indicate an issue with our node or the connected EL client
|
|
1329
|
+
logLevel = LogLevel.error;
|
|
1330
|
+
break;
|
|
1331
|
+
// INVALID_SIGNATURE should not happen, signature is verified during gossip validation
|
|
1332
|
+
case PayloadErrorCode.INVALID_SIGNATURE:
|
|
1333
|
+
case PayloadErrorCode.ENVELOPE_VERIFICATION_ERROR:
|
|
1334
|
+
case PayloadErrorCode.EXECUTION_ENGINE_INVALID:
|
|
1335
|
+
core.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadGossipPayload");
|
|
1336
|
+
// Misbehaving peer, but could highlight an issue in another client
|
|
1337
|
+
logLevel = LogLevel.warn;
|
|
1338
|
+
break;
|
|
1339
|
+
}
|
|
1340
|
+
} else {
|
|
1341
|
+
// Any unexpected error
|
|
1234
1342
|
logLevel = LogLevel.error;
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
logLevel = LogLevel.error;
|
|
1343
|
+
}
|
|
1344
|
+
metrics?.gossipExecutionPayloadEnvelope.processPayloadErrors.inc({
|
|
1345
|
+
error: e instanceof PayloadError ? e.type.code : "NOT_PAYLOAD_ERROR",
|
|
1346
|
+
});
|
|
1347
|
+
chain.logger[logLevel](
|
|
1348
|
+
"Error processing execution payload from gossip",
|
|
1349
|
+
{slot, root: blockRootHex, peer: peerIdStr},
|
|
1350
|
+
e as Error
|
|
1351
|
+
);
|
|
1352
|
+
});
|
|
1353
|
+
} catch (e) {
|
|
1354
|
+
logger.debug("Error handling gossip execution payload", {slot, root: blockRootHex}, e as Error);
|
|
1248
1355
|
}
|
|
1249
|
-
metrics?.gossipExecutionPayloadEnvelope.processPayloadErrors.inc({
|
|
1250
|
-
error: e instanceof PayloadError ? e.type.code : "NOT_PAYLOAD_ERROR",
|
|
1251
|
-
});
|
|
1252
|
-
chain.logger[logLevel](
|
|
1253
|
-
"Error processing execution payload from gossip",
|
|
1254
|
-
{slot, peer: peerIdStr, root: blockRootHex},
|
|
1255
|
-
e as Error
|
|
1256
|
-
);
|
|
1257
1356
|
});
|
|
1258
1357
|
},
|
|
1259
1358
|
[GossipType.payload_attestation_message]: async ({
|
|
@@ -1265,30 +1364,49 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1265
1364
|
const payloadAttestationMessage = sszDeserialize(topic, serializedData);
|
|
1266
1365
|
const validationResult = await validateGossipPayloadAttestationMessage(chain, payloadAttestationMessage);
|
|
1267
1366
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1367
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1368
|
+
callInNextEventLoop(() => {
|
|
1369
|
+
try {
|
|
1370
|
+
const delaySec = chain.clock.secFromSlot(payloadAttestationMessage.data.slot, seenTimestampSec);
|
|
1371
|
+
metrics?.gossipPayloadAttestationMessage.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1372
|
+
const nextSlotProposer = chain.getHeadState().getBeaconProposer(payloadAttestationMessage.data.slot + 1);
|
|
1373
|
+
const isNextSlotProposer = chain.beaconProposerCache.get(nextSlotProposer) !== undefined;
|
|
1374
|
+
|
|
1375
|
+
if (isNextSlotProposer) {
|
|
1376
|
+
try {
|
|
1377
|
+
const insertOutcome = chain.payloadAttestationPool.add(
|
|
1378
|
+
payloadAttestationMessage,
|
|
1379
|
+
validationResult.attDataRootHex,
|
|
1380
|
+
validationResult.validatorCommitteeIndices
|
|
1381
|
+
);
|
|
1382
|
+
metrics?.opPool.payloadAttestationPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1383
|
+
} catch (e) {
|
|
1384
|
+
logger.debug("Error adding to payloadAttestation pool", {}, e as Error);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1270
1387
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
} catch (e) {
|
|
1279
|
-
logger.error("Error adding to payloadAttestation pool", {}, e as Error);
|
|
1280
|
-
}
|
|
1281
|
-
chain.forkChoice.notifyPtcMessages(
|
|
1282
|
-
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1283
|
-
payloadAttestationMessage.data.slot,
|
|
1284
|
-
validationResult.validatorCommitteeIndices,
|
|
1285
|
-
payloadAttestationMessage.data.payloadPresent,
|
|
1286
|
-
payloadAttestationMessage.data.blobDataAvailable
|
|
1287
|
-
);
|
|
1388
|
+
chain.forkChoice.notifyPtcMessages(
|
|
1389
|
+
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1390
|
+
payloadAttestationMessage.data.slot,
|
|
1391
|
+
validationResult.validatorCommitteeIndices,
|
|
1392
|
+
payloadAttestationMessage.data.payloadPresent,
|
|
1393
|
+
payloadAttestationMessage.data.blobDataAvailable
|
|
1394
|
+
);
|
|
1288
1395
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1396
|
+
chain.emitter.emit(routes.events.EventType.payloadAttestationMessage, {
|
|
1397
|
+
version: config.getForkName(payloadAttestationMessage.data.slot),
|
|
1398
|
+
data: payloadAttestationMessage,
|
|
1399
|
+
});
|
|
1400
|
+
} catch (e) {
|
|
1401
|
+
logger.debug(
|
|
1402
|
+
"Error handling gossip payload attestation message",
|
|
1403
|
+
{
|
|
1404
|
+
slot: payloadAttestationMessage.data.slot,
|
|
1405
|
+
root: toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1406
|
+
},
|
|
1407
|
+
e as Error
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1292
1410
|
});
|
|
1293
1411
|
},
|
|
1294
1412
|
[GossipType.execution_payload_bid]: async ({
|
|
@@ -1300,23 +1418,38 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1300
1418
|
const executionPayloadBid = sszDeserialize(topic, serializedData);
|
|
1301
1419
|
const {proposerIndex} = await validateGossipExecutionPayloadBid(chain, executionPayloadBid);
|
|
1302
1420
|
|
|
1303
|
-
//
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1421
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1422
|
+
callInNextEventLoop(() => {
|
|
1423
|
+
try {
|
|
1424
|
+
// this could be negative, because it's most likely the bid of next slot comes at this clock slot
|
|
1425
|
+
const elapsedSec = chain.clock.secFromSlot(executionPayloadBid.message.slot, seenTimestampSec);
|
|
1426
|
+
metrics?.gossipExecutionPayloadBid.elapsedTimeTillReceived.observe({source: OpSource.gossip}, elapsedSec);
|
|
1427
|
+
|
|
1428
|
+
// Handle valid payload bid by storing in a bid pool
|
|
1429
|
+
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid, Math.floor(elapsedSec * 1000));
|
|
1430
|
+
metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1431
|
+
|
|
1432
|
+
chain.validatorMonitor?.registerExecutionPayloadBid(
|
|
1433
|
+
OpSource.gossip,
|
|
1434
|
+
proposerIndex,
|
|
1435
|
+
executionPayloadBid.message
|
|
1436
|
+
);
|
|
1316
1437
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1438
|
+
chain.emitter.emit(routes.events.EventType.executionPayloadBid, {
|
|
1439
|
+
version: config.getForkName(executionPayloadBid.message.slot),
|
|
1440
|
+
data: executionPayloadBid,
|
|
1441
|
+
});
|
|
1442
|
+
} catch (e) {
|
|
1443
|
+
logger.debug(
|
|
1444
|
+
"Error handling gossip execution payload bid",
|
|
1445
|
+
{
|
|
1446
|
+
slot: executionPayloadBid.message.slot,
|
|
1447
|
+
root: toRootHex(executionPayloadBid.message.parentBlockRoot),
|
|
1448
|
+
proposerIndex,
|
|
1449
|
+
},
|
|
1450
|
+
e as Error
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1320
1453
|
});
|
|
1321
1454
|
},
|
|
1322
1455
|
[GossipType.proposer_preferences]: async ({
|
|
@@ -1327,9 +1460,20 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1327
1460
|
const signedProposerPreferences = sszDeserialize(topic, serializedData);
|
|
1328
1461
|
await validateGossipProposerPreferences(chain, signedProposerPreferences);
|
|
1329
1462
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1463
|
+
// Handler - deferred to next event loop so the validation result propagates first
|
|
1464
|
+
callInNextEventLoop(() => {
|
|
1465
|
+
try {
|
|
1466
|
+
chain.emitter.emit(routes.events.EventType.proposerPreferences, {
|
|
1467
|
+
version: config.getForkName(signedProposerPreferences.message.proposalSlot),
|
|
1468
|
+
data: signedProposerPreferences,
|
|
1469
|
+
});
|
|
1470
|
+
} catch (e) {
|
|
1471
|
+
logger.debug(
|
|
1472
|
+
"Error handling gossip proposer preferences",
|
|
1473
|
+
{proposalSlot: signedProposerPreferences.message.proposalSlot},
|
|
1474
|
+
e as Error
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1333
1477
|
});
|
|
1334
1478
|
},
|
|
1335
1479
|
};
|
|
@@ -1363,69 +1507,9 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
|
|
|
1363
1507
|
chain,
|
|
1364
1508
|
validationParams
|
|
1365
1509
|
);
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
continue;
|
|
1370
|
-
}
|
|
1371
|
-
// null means no error
|
|
1372
|
-
results.push(null);
|
|
1373
|
-
|
|
1374
|
-
// Handler
|
|
1375
|
-
const {
|
|
1376
|
-
indexedAttestation,
|
|
1377
|
-
attDataRootHex,
|
|
1378
|
-
attestation,
|
|
1379
|
-
committeeIndex,
|
|
1380
|
-
validatorCommitteeIndex,
|
|
1381
|
-
committeeSize,
|
|
1382
|
-
} = validationResult.result;
|
|
1383
|
-
chain.validatorMonitor?.registerGossipUnaggregatedAttestation(
|
|
1384
|
-
gossipHandlerParams[i].seenTimestampSec,
|
|
1385
|
-
indexedAttestation
|
|
1386
|
-
);
|
|
1387
|
-
|
|
1388
|
-
const {subnet} = validationResult.result;
|
|
1389
|
-
try {
|
|
1390
|
-
// Node may be subscribe to extra subnets (long-lived random subnets). For those, validate the messages
|
|
1391
|
-
// but don't add to attestation pool, to save CPU and RAM
|
|
1392
|
-
if (aggregatorTracker.shouldAggregate(subnet, indexedAttestation.data.slot)) {
|
|
1393
|
-
const insertOutcome = chain.attestationPool.add(
|
|
1394
|
-
committeeIndex,
|
|
1395
|
-
attestation,
|
|
1396
|
-
attDataRootHex,
|
|
1397
|
-
validatorCommitteeIndex,
|
|
1398
|
-
committeeSize
|
|
1399
|
-
);
|
|
1400
|
-
metrics?.opPool.attestationPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1401
|
-
}
|
|
1402
|
-
} catch (e) {
|
|
1403
|
-
logger.error("Error adding unaggregated attestation to pool", {subnet}, e as Error);
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
if (!options.dontSendGossipAttestationsToForkchoice) {
|
|
1407
|
-
try {
|
|
1408
|
-
chain.forkChoice.onAttestation(indexedAttestation, attDataRootHex);
|
|
1409
|
-
} catch (e) {
|
|
1410
|
-
logger.debug("Error adding gossip unaggregated attestation to forkchoice", {subnet}, e as Error);
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
if (isForkPostElectra(fork)) {
|
|
1415
|
-
chain.emitter.emit(
|
|
1416
|
-
routes.events.EventType.singleAttestation,
|
|
1417
|
-
attestation as SingleAttestation<ForkPostElectra>
|
|
1418
|
-
);
|
|
1419
|
-
} else {
|
|
1420
|
-
chain.emitter.emit(routes.events.EventType.attestation, attestation as SingleAttestation<ForkPreElectra>);
|
|
1421
|
-
chain.emitter.emit(
|
|
1422
|
-
routes.events.EventType.singleAttestation,
|
|
1423
|
-
toElectraSingleAttestation(
|
|
1424
|
-
attestation as SingleAttestation<ForkPreElectra>,
|
|
1425
|
-
indexedAttestation.attestingIndices[0]
|
|
1426
|
-
)
|
|
1427
|
-
);
|
|
1428
|
-
}
|
|
1510
|
+
// verdicts computed synchronously and returned immediately
|
|
1511
|
+
for (const validationResult of validationResults) {
|
|
1512
|
+
results.push(validationResult.err ? (validationResult.err as AttestationError) : null);
|
|
1429
1513
|
}
|
|
1430
1514
|
|
|
1431
1515
|
if (batchableBls) {
|
|
@@ -1434,6 +1518,72 @@ function getBatchHandlers(modules: ValidatorFnsModules, options: GossipHandlerOp
|
|
|
1434
1518
|
metrics?.gossipAttestation.attestationNonBatchCount.inc(attestationCount);
|
|
1435
1519
|
}
|
|
1436
1520
|
|
|
1521
|
+
// Handler - deferred to next event loop so the validation result propagates first.
|
|
1522
|
+
callInNextEventLoop(() => {
|
|
1523
|
+
for (const [i, validationResult] of validationResults.entries()) {
|
|
1524
|
+
if (validationResult.err) continue;
|
|
1525
|
+
const {
|
|
1526
|
+
indexedAttestation,
|
|
1527
|
+
attDataRootHex,
|
|
1528
|
+
attestation,
|
|
1529
|
+
committeeIndex,
|
|
1530
|
+
validatorCommitteeIndex,
|
|
1531
|
+
committeeSize,
|
|
1532
|
+
subnet,
|
|
1533
|
+
} = validationResult.result;
|
|
1534
|
+
try {
|
|
1535
|
+
chain.validatorMonitor?.registerGossipUnaggregatedAttestation(
|
|
1536
|
+
gossipHandlerParams[i].seenTimestampSec,
|
|
1537
|
+
indexedAttestation
|
|
1538
|
+
);
|
|
1539
|
+
|
|
1540
|
+
try {
|
|
1541
|
+
// Node may be subscribe to extra subnets (long-lived random subnets). For those, validate the messages
|
|
1542
|
+
// but don't add to attestation pool, to save CPU and RAM
|
|
1543
|
+
if (aggregatorTracker.shouldAggregate(subnet, indexedAttestation.data.slot)) {
|
|
1544
|
+
const insertOutcome = chain.attestationPool.add(
|
|
1545
|
+
committeeIndex,
|
|
1546
|
+
attestation,
|
|
1547
|
+
attDataRootHex,
|
|
1548
|
+
validatorCommitteeIndex,
|
|
1549
|
+
committeeSize
|
|
1550
|
+
);
|
|
1551
|
+
metrics?.opPool.attestationPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1552
|
+
}
|
|
1553
|
+
} catch (e) {
|
|
1554
|
+
logger.debug("Error adding gossip unaggregated attestation to pool", {subnet}, e as Error);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// Separate boundary: a pool insertion error above must not skip the fork-choice vote
|
|
1558
|
+
if (!options.dontSendGossipAttestationsToForkchoice) {
|
|
1559
|
+
try {
|
|
1560
|
+
chain.forkChoice.onAttestation(indexedAttestation, attDataRootHex);
|
|
1561
|
+
} catch (e) {
|
|
1562
|
+
logger.debug("Error adding gossip unaggregated attestation to forkchoice", {subnet}, e as Error);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
if (isForkPostElectra(fork)) {
|
|
1567
|
+
chain.emitter.emit(
|
|
1568
|
+
routes.events.EventType.singleAttestation,
|
|
1569
|
+
attestation as SingleAttestation<ForkPostElectra>
|
|
1570
|
+
);
|
|
1571
|
+
} else {
|
|
1572
|
+
chain.emitter.emit(routes.events.EventType.attestation, attestation as SingleAttestation<ForkPreElectra>);
|
|
1573
|
+
chain.emitter.emit(
|
|
1574
|
+
routes.events.EventType.singleAttestation,
|
|
1575
|
+
toElectraSingleAttestation(
|
|
1576
|
+
attestation as SingleAttestation<ForkPreElectra>,
|
|
1577
|
+
indexedAttestation.attestingIndices[0]
|
|
1578
|
+
)
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
} catch (e) {
|
|
1582
|
+
logger.debug("Error handling gossip attestation", {slot: indexedAttestation.data.slot, subnet}, e as Error);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1437
1587
|
return results;
|
|
1438
1588
|
},
|
|
1439
1589
|
};
|