@lodestar/beacon-node 1.42.0-dev.5f2fffc2ce → 1.42.0-dev.5f9f015475
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 +35 -16
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/types.d.ts +3 -3
- package/lib/chain/blocks/blockInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +18 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +48 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -0
- package/lib/chain/blocks/importExecutionPayload.js +159 -0
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.js +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/index.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +80 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +248 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +29 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js +11 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.js.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +15 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +46 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -0
- package/lib/chain/blocks/types.d.ts +7 -0
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +12 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +40 -0
- package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -0
- package/lib/chain/chain.d.ts +7 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +28 -3
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +12 -2
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +3 -1
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +0 -10
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/interface.d.ts +6 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/utils.js +1 -1
- package/lib/chain/opPools/utils.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +6 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +2 -2
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +38 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +76 -0
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -0
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +30 -19
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/lightClientFinalityUpdate.js +1 -1
- package/lib/chain/validation/lightClientFinalityUpdate.js.map +1 -1
- package/lib/chain/validation/lightClientOptimisticUpdate.js +1 -1
- package/lib/chain/validation/lightClientOptimisticUpdate.js.map +1 -1
- package/lib/chain/validation/syncCommittee.d.ts +2 -2
- package/lib/chain/validation/syncCommittee.d.ts.map +1 -1
- package/lib/chain/validation/syncCommittee.js +12 -11
- package/lib/chain/validation/syncCommittee.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +2 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +4 -1
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -2
- package/lib/metrics/metrics/lodestar.d.ts +28 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +74 -0
- 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 +15 -0
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/network.js +2 -2
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/extractSlotRootFns.d.ts.map +1 -1
- package/lib/network/processor/extractSlotRootFns.js +14 -4
- package/lib/network/processor/extractSlotRootFns.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +38 -8
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
- package/lib/sync/backfill/backfill.d.ts +1 -1
- package/lib/sync/backfill/backfill.js +1 -1
- package/lib/sync/constants.d.ts +1 -1
- package/lib/sync/constants.js +1 -1
- package/lib/sync/unknownBlock.d.ts +2 -8
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -40
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/sszBytes.d.ts +4 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +69 -12
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +36 -17
- package/src/chain/blocks/blockInput/types.ts +3 -3
- package/src/chain/blocks/importBlock.ts +36 -2
- package/src/chain/blocks/importExecutionPayload.ts +241 -0
- package/src/chain/blocks/payloadEnvelopeInput/index.ts +2 -0
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +336 -0
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +33 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +61 -0
- package/src/chain/blocks/types.ts +8 -0
- package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +55 -0
- package/src/chain/chain.ts +37 -3
- package/src/chain/errors/executionPayloadEnvelope.ts +6 -2
- package/src/chain/forkChoice/index.ts +0 -10
- package/src/chain/interface.ts +6 -3
- package/src/chain/opPools/utils.ts +1 -1
- package/src/chain/produceBlock/computeNewStateRoot.ts +6 -1
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/seenCache/index.ts +1 -1
- package/src/chain/seenCache/seenGossipBlockInput.ts +2 -2
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +106 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +38 -25
- package/src/chain/validation/lightClientFinalityUpdate.ts +1 -1
- package/src/chain/validation/lightClientOptimisticUpdate.ts +1 -1
- package/src/chain/validation/syncCommittee.ts +15 -14
- package/src/chain/validatorMonitor.ts +11 -1
- package/src/execution/engine/interface.ts +2 -2
- package/src/metrics/metrics/lodestar.ts +77 -0
- package/src/network/gossip/encoding.ts +16 -0
- package/src/network/network.ts +2 -2
- package/src/network/processor/extractSlotRootFns.ts +18 -5
- package/src/network/processor/gossipHandlers.ts +44 -7
- package/src/network/reqresp/ReqRespBeaconNode.ts +1 -1
- package/src/sync/backfill/backfill.ts +1 -1
- package/src/sync/constants.ts +1 -1
- package/src/sync/unknownBlock.ts +9 -49
- package/src/util/sszBytes.ts +90 -10
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts +0 -15
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.d.ts.map +0 -1
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js +0 -28
- package/lib/chain/seenCache/seenExecutionPayloadEnvelope.js.map +0 -1
- package/src/chain/seenCache/seenExecutionPayloadEnvelope.ts +0 -34
package/src/network/network.ts
CHANGED
|
@@ -721,7 +721,7 @@ export class Network implements INetwork {
|
|
|
721
721
|
|
|
722
722
|
try {
|
|
723
723
|
// messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
|
|
724
|
-
// https://github.com/ethereum/consensus-specs/blob/
|
|
724
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#sync-committee
|
|
725
725
|
await this.waitForSyncMessageCutoff(finalityUpdate.signatureSlot);
|
|
726
726
|
await this.publishLightClientFinalityUpdate(finalityUpdate);
|
|
727
727
|
} catch (e) {
|
|
@@ -738,7 +738,7 @@ export class Network implements INetwork {
|
|
|
738
738
|
|
|
739
739
|
try {
|
|
740
740
|
// messages SHOULD be broadcast after SYNC_MESSAGE_DUE_BPS of slot has transpired
|
|
741
|
-
// https://github.com/ethereum/consensus-specs/blob/
|
|
741
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#sync-committee
|
|
742
742
|
await this.waitForSyncMessageCutoff(optimisticUpdate.signatureSlot);
|
|
743
743
|
await this.publishLightClientOptimisticUpdate(optimisticUpdate);
|
|
744
744
|
} catch (e) {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {ForkName} from "@lodestar/params";
|
|
1
|
+
import {ForkName, isForkPostGloas} from "@lodestar/params";
|
|
2
2
|
import {SlotOptionalRoot, SlotRootHex} from "@lodestar/types";
|
|
3
3
|
import {
|
|
4
|
+
getBeaconBlockRootFromDataColumnSidecarSerialized,
|
|
5
|
+
getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized,
|
|
4
6
|
getBlockRootFromBeaconAttestationSerialized,
|
|
5
7
|
getBlockRootFromSignedAggregateAndProofSerialized,
|
|
6
8
|
getSlotFromBeaconAttestationSerialized,
|
|
7
9
|
getSlotFromBlobSidecarSerialized,
|
|
8
10
|
getSlotFromDataColumnSidecarSerialized,
|
|
11
|
+
getSlotFromExecutionPayloadEnvelopeSerialized,
|
|
9
12
|
getSlotFromSignedAggregateAndProofSerialized,
|
|
10
13
|
getSlotFromSignedBeaconBlockSerialized,
|
|
11
14
|
} from "../../util/sszBytes.js";
|
|
@@ -52,13 +55,23 @@ export function createExtractBlockSlotRootFns(): ExtractSlotRootFns {
|
|
|
52
55
|
}
|
|
53
56
|
return {slot};
|
|
54
57
|
},
|
|
55
|
-
[GossipType.data_column_sidecar]: (data: Uint8Array): SlotOptionalRoot | null => {
|
|
56
|
-
const slot = getSlotFromDataColumnSidecarSerialized(data);
|
|
57
|
-
|
|
58
|
+
[GossipType.data_column_sidecar]: (data: Uint8Array, fork: ForkName): SlotOptionalRoot | null => {
|
|
59
|
+
const slot = getSlotFromDataColumnSidecarSerialized(data, fork);
|
|
58
60
|
if (slot === null) {
|
|
59
61
|
return null;
|
|
60
62
|
}
|
|
61
|
-
|
|
63
|
+
|
|
64
|
+
const root = isForkPostGloas(fork) ? getBeaconBlockRootFromDataColumnSidecarSerialized(data) : null;
|
|
65
|
+
return root !== null ? {slot, root} : {slot};
|
|
66
|
+
},
|
|
67
|
+
[GossipType.execution_payload]: (data: Uint8Array): SlotRootHex | null => {
|
|
68
|
+
const slot = getSlotFromExecutionPayloadEnvelopeSerialized(data);
|
|
69
|
+
const root = getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(data);
|
|
70
|
+
|
|
71
|
+
if (slot === null || root === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return {slot, root};
|
|
62
75
|
},
|
|
63
76
|
};
|
|
64
77
|
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
IBlockInput,
|
|
31
31
|
isBlockInputColumns,
|
|
32
32
|
} from "../../chain/blocks/blockInput/index.js";
|
|
33
|
+
import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
33
34
|
import {BlobSidecarValidation} from "../../chain/blocks/types.js";
|
|
34
35
|
import {ChainEvent} from "../../chain/emitter.js";
|
|
35
36
|
import {
|
|
@@ -42,6 +43,8 @@ import {
|
|
|
42
43
|
BlockGossipError,
|
|
43
44
|
DataColumnSidecarErrorCode,
|
|
44
45
|
DataColumnSidecarGossipError,
|
|
46
|
+
ExecutionPayloadEnvelopeError,
|
|
47
|
+
ExecutionPayloadEnvelopeErrorCode,
|
|
45
48
|
GossipAction,
|
|
46
49
|
GossipActionError,
|
|
47
50
|
SyncCommitteeError,
|
|
@@ -616,6 +619,13 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
616
619
|
});
|
|
617
620
|
});
|
|
618
621
|
}
|
|
622
|
+
|
|
623
|
+
// TODO GLOAS: In Gloas, also add column to PayloadEnvelopeInput and notify the payload processor:
|
|
624
|
+
// const payloadInput = chain.seenPayloadEnvelopeInput.get(blockRootHex);
|
|
625
|
+
// if (payloadInput) {
|
|
626
|
+
// payloadInput.addColumn({columnSidecar, source: BlockInputSource.gossip, seenTimestampSec, peerIdStr});
|
|
627
|
+
// chain.processExecutionPayload(payloadInput, {validSignature: true});
|
|
628
|
+
// }
|
|
619
629
|
},
|
|
620
630
|
|
|
621
631
|
[GossipType.beacon_aggregate_and_proof]: async ({
|
|
@@ -767,9 +777,9 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
767
777
|
const {serializedData} = gossipData;
|
|
768
778
|
const syncCommittee = sszDeserialize(topic, serializedData);
|
|
769
779
|
const {subnet} = topic;
|
|
770
|
-
let
|
|
780
|
+
let indicesInSubcommittee: number[] = [0];
|
|
771
781
|
try {
|
|
772
|
-
|
|
782
|
+
indicesInSubcommittee = (await validateGossipSyncCommittee(chain, syncCommittee, subnet)).indicesInSubcommittee;
|
|
773
783
|
} catch (e) {
|
|
774
784
|
if (e instanceof SyncCommitteeError && e.action === GossipAction.REJECT) {
|
|
775
785
|
chain.persistInvalidSszValue(ssz.altair.SyncCommitteeMessage, syncCommittee, "gossip_reject");
|
|
@@ -777,11 +787,12 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
777
787
|
throw e;
|
|
778
788
|
}
|
|
779
789
|
|
|
780
|
-
// Handler
|
|
781
|
-
|
|
790
|
+
// Handler — add for ALL positions this validator holds in the subcommittee
|
|
782
791
|
try {
|
|
783
|
-
const
|
|
784
|
-
|
|
792
|
+
for (const indexInSubcommittee of indicesInSubcommittee) {
|
|
793
|
+
const insertOutcome = chain.syncCommitteeMessagePool.add(subnet, syncCommittee, indexInSubcommittee);
|
|
794
|
+
metrics?.opPool.syncCommitteeMessagePoolInsertOutcome.inc({insertOutcome});
|
|
795
|
+
}
|
|
785
796
|
} catch (e) {
|
|
786
797
|
logger.debug("Error adding to syncCommittee pool", {subnet}, e as Error);
|
|
787
798
|
}
|
|
@@ -826,17 +837,43 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
826
837
|
[GossipType.execution_payload]: async ({
|
|
827
838
|
gossipData,
|
|
828
839
|
topic,
|
|
840
|
+
peerIdStr,
|
|
829
841
|
seenTimestampSec,
|
|
830
842
|
}: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
|
|
831
843
|
const {serializedData} = gossipData;
|
|
832
844
|
const executionPayloadEnvelope = sszDeserialize(topic, serializedData);
|
|
845
|
+
// TODO GLOAS: handle BLOCK_ROOT_UNKNOWN error to trigger sync
|
|
833
846
|
await validateGossipExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
|
|
834
847
|
|
|
835
848
|
const slot = executionPayloadEnvelope.message.slot;
|
|
836
849
|
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|
|
837
850
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
851
|
+
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, executionPayloadEnvelope);
|
|
852
|
+
|
|
853
|
+
const blockRootHex = toRootHex(executionPayloadEnvelope.message.beaconBlockRoot);
|
|
854
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
855
|
+
|
|
856
|
+
if (!payloadInput) {
|
|
857
|
+
// This shouldn't happen because beacon block should have been imported and thus payload input should have been created.
|
|
858
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
859
|
+
code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
|
|
860
|
+
blockRoot: blockRootHex,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
chain.serializedCache.set(executionPayloadEnvelope, serializedData);
|
|
865
|
+
|
|
866
|
+
payloadInput.addPayloadEnvelope({
|
|
867
|
+
envelope: executionPayloadEnvelope,
|
|
868
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
869
|
+
seenTimestampSec,
|
|
870
|
+
peerIdStr,
|
|
871
|
+
});
|
|
838
872
|
|
|
839
|
-
// TODO GLOAS:
|
|
873
|
+
// TODO GLOAS: Emit execution_payload_gossip event for gossip receipt.
|
|
874
|
+
chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
|
|
875
|
+
chain.logger.debug("Error processing execution payload from gossip", {slot, root: blockRootHex}, e as Error);
|
|
876
|
+
});
|
|
840
877
|
},
|
|
841
878
|
[GossipType.payload_attestation_message]: async ({
|
|
842
879
|
gossipData,
|
|
@@ -58,7 +58,7 @@ export type ReqRespBeaconNodeOpts = ReqRespOpts & {disableLightClientServer?: bo
|
|
|
58
58
|
* Implementation of Ethereum Consensus p2p Req/Resp domain.
|
|
59
59
|
* For the spec that this code is based on, see:
|
|
60
60
|
* https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#the-reqresp-domain
|
|
61
|
-
* https://github.com/ethereum/consensus-specs/blob/
|
|
61
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/altair/light-client/p2p-interface.md#the-reqresp-domain
|
|
62
62
|
*/
|
|
63
63
|
export class ReqRespBeaconNode extends ReqResp {
|
|
64
64
|
private readonly metadataController: MetadataController;
|
|
@@ -127,7 +127,7 @@ export class BackfillSync extends (EventEmitter as {new (): BackfillSyncEmitter}
|
|
|
127
127
|
private wsValidated = false;
|
|
128
128
|
|
|
129
129
|
/**
|
|
130
|
-
* From https://github.com/ethereum/consensus-specs/blob/
|
|
130
|
+
* From https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/phase0/weak-subjectivity.md
|
|
131
131
|
*
|
|
132
132
|
*
|
|
133
133
|
* If
|
package/src/sync/constants.ts
CHANGED
|
@@ -59,7 +59,7 @@ export const BATCH_BUFFER_SIZE = Math.ceil(10 / EPOCHS_PER_BATCH);
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Maximum number of concurrent requests to perform with a SyncChain.
|
|
62
|
-
* This is according to the spec https://github.com/ethereum/consensus-specs/blob/
|
|
62
|
+
* This is according to the spec https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/phase0/p2p-interface.md
|
|
63
63
|
*/
|
|
64
64
|
export const MAX_CONCURRENT_REQUESTS = 2;
|
|
65
65
|
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -481,7 +481,7 @@ export class BlockInputSync {
|
|
|
481
481
|
* From a set of shuffled peers:
|
|
482
482
|
* - fetch the block
|
|
483
483
|
* - from deneb, fetch all missing blobs
|
|
484
|
-
* - from peerDAS, fetch sampled
|
|
484
|
+
* - from peerDAS, fetch sampled columns
|
|
485
485
|
* TODO: this means we only have block root, and nothing else. Consider to reflect this in the function name
|
|
486
486
|
* prefulu, will attempt a max of `MAX_ATTEMPTS_PER_BLOCK` on different peers, postfulu we may attempt more as defined in `getMaxDownloadAttempts()` function
|
|
487
487
|
* Also verifies the received block root + returns the peer that provided the block for future downscoring.
|
|
@@ -489,10 +489,7 @@ export class BlockInputSync {
|
|
|
489
489
|
private async fetchBlockInput(cacheItem: BlockInputSyncCacheItem): Promise<PendingBlockInput> {
|
|
490
490
|
const rootHex = getBlockInputSyncCacheItemRootHex(cacheItem);
|
|
491
491
|
const excludedPeers = new Set<PeerIdStr>();
|
|
492
|
-
const defaultPendingColumns =
|
|
493
|
-
this.config.getForkSeq(this.chain.clock.currentSlot) >= ForkSeq.fulu
|
|
494
|
-
? new Set(this.network.custodyConfig.sampledColumns)
|
|
495
|
-
: null;
|
|
492
|
+
const defaultPendingColumns = new Set(this.network.custodyConfig.sampledColumns);
|
|
496
493
|
|
|
497
494
|
const fetchStartSec = Date.now() / 1000;
|
|
498
495
|
let slot = isPendingBlockInput(cacheItem) ? cacheItem.blockInput.slot : undefined;
|
|
@@ -506,14 +503,10 @@ export class BlockInputSync {
|
|
|
506
503
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
507
504
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
508
505
|
: defaultPendingColumns;
|
|
509
|
-
// pendingDataColumns is null pre-fulu
|
|
510
506
|
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(pendingColumns, excludedPeers);
|
|
511
507
|
if (peerMeta === null) {
|
|
512
508
|
// no more peer with needed columns to try, throw error
|
|
513
|
-
|
|
514
|
-
if (pendingColumns) {
|
|
515
|
-
message += ` with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`;
|
|
516
|
-
}
|
|
509
|
+
const message = `Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: cannot find peer with needed columns=${prettyPrintIndices(Array.from(pendingColumns))}`;
|
|
517
510
|
this.metrics?.blockInputSync.fetchTimeSec.observe(
|
|
518
511
|
{result: FetchResult.FailureTriedAllPeers},
|
|
519
512
|
Date.now() / 1000 - fetchStartSec
|
|
@@ -650,7 +643,7 @@ export class BlockInputSync {
|
|
|
650
643
|
// TODO(fulu): why is this commented out here?
|
|
651
644
|
//
|
|
652
645
|
// this.knownBadBlocks.add(block.blockRootHex);
|
|
653
|
-
// for (const peerIdStr of block.
|
|
646
|
+
// for (const peerIdStr of block.peerIdStrings) {
|
|
654
647
|
// // TODO: Refactor peerRpcScores to work with peerIdStr only
|
|
655
648
|
// this.network.reportPeer(peerIdStr, PeerAction.LowToleranceError, "BadBlockByRoot");
|
|
656
649
|
// }
|
|
@@ -729,11 +722,11 @@ export class UnknownBlockPeerBalancer {
|
|
|
729
722
|
}
|
|
730
723
|
|
|
731
724
|
/**
|
|
732
|
-
* called from
|
|
725
|
+
* called from fetchBlockInput() where we only have block root and nothing else
|
|
733
726
|
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
734
727
|
* pendingColumns is empty for prefulu, or the 1st time we we download a block by root
|
|
735
728
|
*/
|
|
736
|
-
bestPeerForPendingColumns(pendingColumns: Set<number
|
|
729
|
+
bestPeerForPendingColumns(pendingColumns: Set<number>, excludedPeers: Set<PeerIdStr>): PeerSyncMeta | null {
|
|
737
730
|
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
738
731
|
if (eligiblePeers.length === 0) {
|
|
739
732
|
return null;
|
|
@@ -750,37 +743,6 @@ export class UnknownBlockPeerBalancer {
|
|
|
750
743
|
return this.peersMeta.get(bestPeerId) ?? null;
|
|
751
744
|
}
|
|
752
745
|
|
|
753
|
-
/**
|
|
754
|
-
* called from fetchUnavailableBlockInput() where we have either BlockInput or NullBlockInput
|
|
755
|
-
* excludedPeers are the peers that we requested already so we don't want to try again
|
|
756
|
-
*/
|
|
757
|
-
bestPeerForBlockInput(blockInput: IBlockInput, excludedPeers: Set<PeerIdStr>): PeerSyncMeta | null {
|
|
758
|
-
const eligiblePeers: PeerIdStr[] = [];
|
|
759
|
-
|
|
760
|
-
if (isBlockInputColumns(blockInput)) {
|
|
761
|
-
const pendingDataColumns: Set<number> = new Set(blockInput.getMissingSampledColumnMeta().missing);
|
|
762
|
-
// there could be no pending column in case when block is still missing
|
|
763
|
-
eligiblePeers.push(...this.filterPeers(pendingDataColumns, excludedPeers));
|
|
764
|
-
} else {
|
|
765
|
-
// prefulu
|
|
766
|
-
eligiblePeers.push(...this.filterPeers(null, excludedPeers));
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
if (eligiblePeers.length === 0) {
|
|
770
|
-
return null;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
const sortedEligiblePeers = sortBy(
|
|
774
|
-
shuffle(eligiblePeers),
|
|
775
|
-
// prefer peers with least active req
|
|
776
|
-
(peerId) => this.activeRequests.get(peerId) ?? 0
|
|
777
|
-
);
|
|
778
|
-
|
|
779
|
-
const bestPeerId = sortedEligiblePeers[0];
|
|
780
|
-
this.onRequest(bestPeerId);
|
|
781
|
-
return this.peersMeta.get(bestPeerId) ?? null;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
746
|
/**
|
|
785
747
|
* Consumers don't need to call this method directly, it is called internally by bestPeer*() methods
|
|
786
748
|
* make this public for testing
|
|
@@ -804,8 +766,7 @@ export class UnknownBlockPeerBalancer {
|
|
|
804
766
|
return totalActiveRequests;
|
|
805
767
|
}
|
|
806
768
|
|
|
807
|
-
|
|
808
|
-
private filterPeers(pendingDataColumns: Set<number> | null, excludedPeers: Set<PeerIdStr>): PeerIdStr[] {
|
|
769
|
+
private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr>): PeerIdStr[] {
|
|
809
770
|
let maxColumnCount = 0;
|
|
810
771
|
const considerPeers: {peerId: PeerIdStr; columnCount: number}[] = [];
|
|
811
772
|
for (const [peerId, syncMeta] of this.peersMeta.entries()) {
|
|
@@ -820,13 +781,12 @@ export class UnknownBlockPeerBalancer {
|
|
|
820
781
|
continue;
|
|
821
782
|
}
|
|
822
783
|
|
|
823
|
-
if (pendingDataColumns
|
|
824
|
-
// prefulu, no pending columns
|
|
784
|
+
if (pendingDataColumns.size === 0) {
|
|
825
785
|
considerPeers.push({peerId, columnCount: 0});
|
|
826
786
|
continue;
|
|
827
787
|
}
|
|
828
788
|
|
|
829
|
-
//
|
|
789
|
+
// find peers that have custody columns that we need
|
|
830
790
|
const {custodyColumns: peerColumns} = syncMeta;
|
|
831
791
|
// check if the peer has all needed columns
|
|
832
792
|
// get match
|
package/src/util/sszBytes.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ForkSeq,
|
|
9
9
|
MAX_COMMITTEES_PER_SLOT,
|
|
10
10
|
isForkPostElectra,
|
|
11
|
+
isForkPostGloas,
|
|
11
12
|
} from "@lodestar/params";
|
|
12
13
|
import {BLSSignature, CommitteeIndex, RootHex, Slot, ValidatorIndex, ssz} from "@lodestar/types";
|
|
13
14
|
|
|
@@ -398,23 +399,102 @@ export function getSlotFromBlobSidecarSerialized(data: Uint8Array): Slot | null
|
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
/**
|
|
402
|
+
* Pre-Gloas DataColumnSidecar:
|
|
401
403
|
* {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
404
|
+
* index: ColumnIndex [fixed - 8 bytes],
|
|
405
|
+
* column: DataColumn (offset - 4 bytes),
|
|
406
|
+
* kzgCommitments: (offset - 4 bytes),
|
|
407
|
+
* kzgProofs: (offset - 4 bytes),
|
|
408
|
+
* signedBlockHeader: (offset - 4 bytes) -> slot at variable offset after fixed header
|
|
409
|
+
* kzgCommitmentsInclusionProof: (offset - 4 bytes),
|
|
410
|
+
* }
|
|
411
|
+
* Post-Gloas DataColumnSidecar:
|
|
412
|
+
* {
|
|
413
|
+
* index: ColumnIndex [8 bytes],
|
|
414
|
+
* column: DataColumn (offset - 4 bytes),
|
|
415
|
+
* kzgProofs: (offset - 4 bytes),
|
|
416
|
+
* slot: Slot [8 bytes] - at offset 16,
|
|
417
|
+
* beaconBlockRoot: Root [32 bytes] - at offset 24,
|
|
418
|
+
* }
|
|
419
|
+
*/
|
|
420
|
+
const SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR_PRE_GLOAS = 20;
|
|
421
|
+
const SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR_POST_GLOAS = 16;
|
|
422
|
+
const BEACON_BLOCK_ROOT_POSITION_IN_GLOAS_DATA_COLUMN_SIDECAR = 24;
|
|
423
|
+
|
|
424
|
+
export function getSlotFromDataColumnSidecarSerialized(data: Uint8Array, fork: ForkName): Slot | null {
|
|
425
|
+
const offset = isForkPostGloas(fork)
|
|
426
|
+
? SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR_POST_GLOAS
|
|
427
|
+
: SLOT_BYTES_POSITION_IN_SIGNED_DATA_COLUMN_SIDECAR_PRE_GLOAS;
|
|
428
|
+
|
|
429
|
+
if (data.length < offset + SLOT_SIZE) {
|
|
430
|
+
return null;
|
|
408
431
|
}
|
|
432
|
+
|
|
433
|
+
return getSlotFromOffset(data, offset);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function getBeaconBlockRootFromDataColumnSidecarSerialized(data: Uint8Array): RootHex | null {
|
|
437
|
+
if (data.length < BEACON_BLOCK_ROOT_POSITION_IN_GLOAS_DATA_COLUMN_SIDECAR + ROOT_SIZE) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
blockRootBuf.set(
|
|
442
|
+
data.subarray(
|
|
443
|
+
BEACON_BLOCK_ROOT_POSITION_IN_GLOAS_DATA_COLUMN_SIDECAR,
|
|
444
|
+
BEACON_BLOCK_ROOT_POSITION_IN_GLOAS_DATA_COLUMN_SIDECAR + ROOT_SIZE
|
|
445
|
+
)
|
|
446
|
+
);
|
|
447
|
+
return "0x" + blockRootBuf.toString("hex");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* SignedExecutionPayloadEnvelope SSZ Layout:
|
|
452
|
+
* ├─ 4 bytes: message offset (points to byte 100)
|
|
453
|
+
* ├─ 96 bytes: signature
|
|
454
|
+
* └─ ExecutionPayloadEnvelope (starts at byte 100):
|
|
455
|
+
* ├─ 4 bytes: payload offset
|
|
456
|
+
* ├─ 4 bytes: executionRequests offset
|
|
457
|
+
* ├─ 8 bytes: builderIndex (offset 108-115)
|
|
458
|
+
* ├─ 32 bytes: beaconBlockRoot (offset 116-147)
|
|
459
|
+
* ├─ 8 bytes: slot (offset 148-155)
|
|
460
|
+
* └─ 32 bytes: stateRoot (offset 156-187)
|
|
409
461
|
*/
|
|
462
|
+
const SIGNED_EXECUTION_PAYLOAD_ENVELOPE_MESSAGE_OFFSET = 4;
|
|
463
|
+
const SIGNED_EXECUTION_PAYLOAD_ENVELOPE_SIGNATURE_SIZE = 96;
|
|
464
|
+
const EXECUTION_PAYLOAD_ENVELOPE_PAYLOAD_OFFSET = 4;
|
|
465
|
+
const EXECUTION_PAYLOAD_ENVELOPE_REQUESTS_OFFSET = 4;
|
|
466
|
+
const EXECUTION_PAYLOAD_ENVELOPE_BUILDER_INDEX_SIZE = 8;
|
|
467
|
+
|
|
468
|
+
const BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
469
|
+
SIGNED_EXECUTION_PAYLOAD_ENVELOPE_MESSAGE_OFFSET +
|
|
470
|
+
SIGNED_EXECUTION_PAYLOAD_ENVELOPE_SIGNATURE_SIZE +
|
|
471
|
+
EXECUTION_PAYLOAD_ENVELOPE_PAYLOAD_OFFSET +
|
|
472
|
+
EXECUTION_PAYLOAD_ENVELOPE_REQUESTS_OFFSET +
|
|
473
|
+
EXECUTION_PAYLOAD_ENVELOPE_BUILDER_INDEX_SIZE; // 116
|
|
474
|
+
|
|
475
|
+
const SLOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
476
|
+
BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + ROOT_SIZE; // 148
|
|
477
|
+
|
|
478
|
+
export function getSlotFromExecutionPayloadEnvelopeSerialized(data: Uint8Array): Slot | null {
|
|
479
|
+
if (data.length < SLOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + SLOT_SIZE) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
410
482
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
483
|
+
return getSlotFromOffset(data, SLOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export function getBeaconBlockRootFromExecutionPayloadEnvelopeSerialized(data: Uint8Array): RootHex | null {
|
|
487
|
+
if (data.length < BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + ROOT_SIZE) {
|
|
414
488
|
return null;
|
|
415
489
|
}
|
|
416
490
|
|
|
417
|
-
|
|
491
|
+
blockRootBuf.set(
|
|
492
|
+
data.subarray(
|
|
493
|
+
BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE,
|
|
494
|
+
BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + ROOT_SIZE
|
|
495
|
+
)
|
|
496
|
+
);
|
|
497
|
+
return "0x" + blockRootBuf.toString("hex");
|
|
418
498
|
}
|
|
419
499
|
|
|
420
500
|
/**
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { RootHex, Slot } from "@lodestar/types";
|
|
2
|
-
/**
|
|
3
|
-
* Cache to prevent processing multiple execution payload envelopes for the same block root.
|
|
4
|
-
* Only one builder qualifies to submit an execution payload for a given slot.
|
|
5
|
-
* We only keep track of envelopes of unfinalized slots.
|
|
6
|
-
* [IGNORE] The node has not seen another valid `SignedExecutionPayloadEnvelope` for this block root.
|
|
7
|
-
*/
|
|
8
|
-
export declare class SeenExecutionPayloadEnvelopes {
|
|
9
|
-
private readonly slotByBlockRoot;
|
|
10
|
-
private finalizedSlot;
|
|
11
|
-
isKnown(blockRoot: RootHex): boolean;
|
|
12
|
-
add(blockRoot: RootHex, slot: Slot): void;
|
|
13
|
-
prune(finalizedSlot: Slot): void;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=seenExecutionPayloadEnvelope.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenExecutionPayloadEnvelope.d.ts","sourceRoot":"","sources":["../../../src/chain/seenCache/seenExecutionPayloadEnvelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,6BAA6B;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4B;IAC5D,OAAO,CAAC,aAAa,CAAW;IAEhC,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAEnC;IAED,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAMxC;IAED,KAAK,CAAC,aAAa,EAAE,IAAI,GAAG,IAAI,CAQ/B;CACF"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cache to prevent processing multiple execution payload envelopes for the same block root.
|
|
3
|
-
* Only one builder qualifies to submit an execution payload for a given slot.
|
|
4
|
-
* We only keep track of envelopes of unfinalized slots.
|
|
5
|
-
* [IGNORE] The node has not seen another valid `SignedExecutionPayloadEnvelope` for this block root.
|
|
6
|
-
*/
|
|
7
|
-
export class SeenExecutionPayloadEnvelopes {
|
|
8
|
-
slotByBlockRoot = new Map();
|
|
9
|
-
finalizedSlot = 0;
|
|
10
|
-
isKnown(blockRoot) {
|
|
11
|
-
return this.slotByBlockRoot.has(blockRoot);
|
|
12
|
-
}
|
|
13
|
-
add(blockRoot, slot) {
|
|
14
|
-
if (slot < this.finalizedSlot) {
|
|
15
|
-
throw Error(`slot ${slot} < finalizedSlot ${this.finalizedSlot}`);
|
|
16
|
-
}
|
|
17
|
-
this.slotByBlockRoot.set(blockRoot, slot);
|
|
18
|
-
}
|
|
19
|
-
prune(finalizedSlot) {
|
|
20
|
-
this.finalizedSlot = finalizedSlot;
|
|
21
|
-
for (const [blockRoot, slot] of this.slotByBlockRoot.entries()) {
|
|
22
|
-
if (slot < finalizedSlot) {
|
|
23
|
-
this.slotByBlockRoot.delete(blockRoot);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=seenExecutionPayloadEnvelope.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"seenExecutionPayloadEnvelope.js","sourceRoot":"","sources":["../../../src/chain/seenCache/seenExecutionPayloadEnvelope.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,6BAA6B;IACvB,eAAe,GAAG,IAAI,GAAG,EAAiB,CAAC;IACpD,aAAa,GAAS,CAAC,CAAC;IAEhC,OAAO,CAAC,SAAkB,EAAW;QACnC,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAAA,CAC5C;IAED,GAAG,CAAC,SAAkB,EAAE,IAAU,EAAQ;QACxC,IAAI,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC,QAAQ,IAAI,oBAAoB,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAAA,CAC3C;IAED,KAAK,CAAC,aAAmB,EAAQ;QAC/B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,KAAK,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YAC/D,IAAI,IAAI,GAAG,aAAa,EAAE,CAAC;gBACzB,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;IAAA,CACF;CACF"}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {RootHex, Slot} from "@lodestar/types";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cache to prevent processing multiple execution payload envelopes for the same block root.
|
|
5
|
-
* Only one builder qualifies to submit an execution payload for a given slot.
|
|
6
|
-
* We only keep track of envelopes of unfinalized slots.
|
|
7
|
-
* [IGNORE] The node has not seen another valid `SignedExecutionPayloadEnvelope` for this block root.
|
|
8
|
-
*/
|
|
9
|
-
export class SeenExecutionPayloadEnvelopes {
|
|
10
|
-
private readonly slotByBlockRoot = new Map<RootHex, Slot>();
|
|
11
|
-
private finalizedSlot: Slot = 0;
|
|
12
|
-
|
|
13
|
-
isKnown(blockRoot: RootHex): boolean {
|
|
14
|
-
return this.slotByBlockRoot.has(blockRoot);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
add(blockRoot: RootHex, slot: Slot): void {
|
|
18
|
-
if (slot < this.finalizedSlot) {
|
|
19
|
-
throw Error(`slot ${slot} < finalizedSlot ${this.finalizedSlot}`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
this.slotByBlockRoot.set(blockRoot, slot);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
prune(finalizedSlot: Slot): void {
|
|
26
|
-
this.finalizedSlot = finalizedSlot;
|
|
27
|
-
|
|
28
|
-
for (const [blockRoot, slot] of this.slotByBlockRoot.entries()) {
|
|
29
|
-
if (slot < finalizedSlot) {
|
|
30
|
-
this.slotByBlockRoot.delete(blockRoot);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|