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