@lodestar/beacon-node 1.42.0-dev.4411584fd8 → 1.42.0-dev.70938e1eec
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/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/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/validatorMonitor.d.ts +2 -1
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +3 -0
- package/lib/chain/validatorMonitor.js.map +1 -1
- 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/topic.d.ts +727 -0
- package/lib/network/gossip/topic.d.ts.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 +31 -3
- package/lib/network/processor/gossipHandlers.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/produceBlock/computeNewStateRoot.ts +6 -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/validatorMonitor.ts +10 -0
- package/src/metrics/metrics/lodestar.ts +77 -0
- package/src/network/processor/extractSlotRootFns.ts +18 -5
- package/src/network/processor/gossipHandlers.ts +37 -1
- 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/chain/interface.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {IArchiveStore} from "./archiveStore/interface.js";
|
|
|
32
32
|
import {CheckpointBalancesCache} from "./balancesCache.js";
|
|
33
33
|
import {BeaconProposerCache, ProposerPreparationData} from "./beaconProposerCache.js";
|
|
34
34
|
import {IBlockInput} from "./blocks/blockInput/index.js";
|
|
35
|
-
import {ImportBlockOpts} from "./blocks/types.js";
|
|
35
|
+
import {ImportBlockOpts, ImportPayloadOpts} from "./blocks/types.js";
|
|
36
36
|
import {IBlsVerifier} from "./bls/index.js";
|
|
37
37
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
38
38
|
import {ChainEventEmitter} from "./emitter.js";
|
|
@@ -58,7 +58,6 @@ import {
|
|
|
58
58
|
SeenBlockProposers,
|
|
59
59
|
SeenContributionAndProof,
|
|
60
60
|
SeenExecutionPayloadBids,
|
|
61
|
-
SeenExecutionPayloadEnvelopes,
|
|
62
61
|
SeenPayloadAttesters,
|
|
63
62
|
SeenSyncCommitteeMessages,
|
|
64
63
|
} from "./seenCache/index.js";
|
|
@@ -66,6 +65,7 @@ import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
|
66
65
|
import {SeenAttestationDatas} from "./seenCache/seenAttestationData.js";
|
|
67
66
|
import {SeenBlockAttesters} from "./seenCache/seenBlockAttesters.js";
|
|
68
67
|
import {SeenBlockInput} from "./seenCache/seenGossipBlockInput.js";
|
|
68
|
+
import {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenCache/seenPayloadEnvelopeInput.js";
|
|
69
69
|
import {ShufflingCache} from "./shufflingCache.js";
|
|
70
70
|
import {ValidatorMonitor} from "./validatorMonitor.js";
|
|
71
71
|
|
|
@@ -128,13 +128,13 @@ export interface IBeaconChain {
|
|
|
128
128
|
readonly seenAggregators: SeenAggregators;
|
|
129
129
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
130
130
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
131
|
-
readonly seenExecutionPayloadEnvelopes: SeenExecutionPayloadEnvelopes;
|
|
132
131
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
133
132
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
134
133
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
135
134
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
136
135
|
readonly seenAttestationDatas: SeenAttestationDatas;
|
|
137
136
|
readonly seenBlockInputCache: SeenBlockInput;
|
|
137
|
+
readonly seenPayloadEnvelopeInputCache: SeenPayloadEnvelopeInput;
|
|
138
138
|
// Seen cache for liveness checks
|
|
139
139
|
readonly seenBlockAttesters: SeenBlockAttesters;
|
|
140
140
|
|
|
@@ -242,6 +242,9 @@ export interface IBeaconChain {
|
|
|
242
242
|
/** Process a chain of blocks until complete */
|
|
243
243
|
processChainSegment(blocks: IBlockInput[], opts?: ImportBlockOpts): Promise<void>;
|
|
244
244
|
|
|
245
|
+
/** Process execution payload envelope: verify, import to fork choice, and persist to DB */
|
|
246
|
+
processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void>;
|
|
247
|
+
|
|
245
248
|
getStatus(): Status;
|
|
246
249
|
|
|
247
250
|
recomputeForkChoiceHead(caller: ForkchoiceCaller): ProtoBlock;
|
|
@@ -73,7 +73,12 @@ export function computeEnvelopeStateRoot(
|
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
76
|
-
const postEnvelopeState = processExecutionPayloadEnvelope(postBlockState, signedEnvelope,
|
|
76
|
+
const postEnvelopeState = processExecutionPayloadEnvelope(postBlockState, signedEnvelope, {
|
|
77
|
+
// Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification
|
|
78
|
+
verifySignature: false,
|
|
79
|
+
// State root is being computed here, the envelope doesn't have it yet
|
|
80
|
+
verifyStateRoot: false,
|
|
81
|
+
// Preserve cache in source state, since the resulting state is not added to the state cache
|
|
77
82
|
dontTransferCache: true,
|
|
78
83
|
});
|
|
79
84
|
processEnvelopeTimer?.();
|
|
@@ -9,8 +9,10 @@ export enum RegenCaller {
|
|
|
9
9
|
processBlock = "processBlock",
|
|
10
10
|
produceBlock = "produceBlock",
|
|
11
11
|
validateGossipBlock = "validateGossipBlock",
|
|
12
|
+
validateGossipPayloadEnvelope = "validateGossipPayloadEnvelope",
|
|
12
13
|
validateGossipBlob = "validateGossipBlob",
|
|
13
14
|
validateGossipDataColumn = "validateGossipDataColumn",
|
|
15
|
+
validateGossipExecutionPayloadEnvelope = "validateGossipExecutionPayloadEnvelope",
|
|
14
16
|
precomputeEpoch = "precomputeEpoch",
|
|
15
17
|
predictProposerHead = "predictProposerHead",
|
|
16
18
|
produceAttestationData = "produceAttestationData",
|
|
@@ -3,5 +3,5 @@ export {SeenBlockProposers} from "./seenBlockProposers.js";
|
|
|
3
3
|
export {SeenSyncCommitteeMessages} from "./seenCommittee.js";
|
|
4
4
|
export {SeenContributionAndProof} from "./seenCommitteeContribution.js";
|
|
5
5
|
export {SeenExecutionPayloadBids} from "./seenExecutionPayloadBids.js";
|
|
6
|
-
export {SeenExecutionPayloadEnvelopes} from "./seenExecutionPayloadEnvelope.js";
|
|
7
6
|
export {SeenBlockInput} from "./seenGossipBlockInput.js";
|
|
7
|
+
export {PayloadEnvelopeInput, SeenPayloadEnvelopeInput} from "./seenPayloadEnvelopeInput.js";
|
|
@@ -180,7 +180,7 @@ export class SeenBlockInput {
|
|
|
180
180
|
blockInput = this.blockInputs.get(parentRootHex ?? "");
|
|
181
181
|
parentRootHex = blockInput?.parentRootHex;
|
|
182
182
|
}
|
|
183
|
-
this.logger?.debug(
|
|
183
|
+
this.logger?.debug("BlockInputCache.prune deleted cached BlockInputs", {deletedCount});
|
|
184
184
|
this.pruneToMaxSize();
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -193,7 +193,7 @@ export class SeenBlockInput {
|
|
|
193
193
|
this.evictBlockInput(blockInput);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
|
-
this.logger?.debug(
|
|
196
|
+
this.logger?.debug("BlockInputCache.onFinalized deleted cached BlockInputs", {deletedCount});
|
|
197
197
|
this.pruneToMaxSize();
|
|
198
198
|
};
|
|
199
199
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
2
|
+
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
+
import {RootHex} from "@lodestar/types";
|
|
4
|
+
import {Logger} from "@lodestar/utils";
|
|
5
|
+
import {Metrics} from "../../metrics/metrics.js";
|
|
6
|
+
import {SerializedCache} from "../../util/serializedCache.js";
|
|
7
|
+
import {CreateFromBlockProps, PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
8
|
+
import {ChainEvent, ChainEventEmitter} from "../emitter.js";
|
|
9
|
+
|
|
10
|
+
export type {PayloadEnvelopeInputState} from "../blocks/payloadEnvelopeInput/index.js";
|
|
11
|
+
export {PayloadEnvelopeInput} from "../blocks/payloadEnvelopeInput/index.js";
|
|
12
|
+
|
|
13
|
+
export type SeenPayloadEnvelopeInputModules = {
|
|
14
|
+
chainEvents: ChainEventEmitter;
|
|
15
|
+
signal: AbortSignal;
|
|
16
|
+
serializedCache: SerializedCache;
|
|
17
|
+
metrics: Metrics | null;
|
|
18
|
+
logger?: Logger;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cache for tracking PayloadEnvelopeInput instances, keyed by beacon block root.
|
|
23
|
+
*
|
|
24
|
+
* Created during block import when a block is processed.
|
|
25
|
+
* Pruned on finalization and after payload is written to DB.
|
|
26
|
+
*/
|
|
27
|
+
export class SeenPayloadEnvelopeInput {
|
|
28
|
+
private readonly chainEvents: ChainEventEmitter;
|
|
29
|
+
private readonly signal: AbortSignal;
|
|
30
|
+
private readonly serializedCache: SerializedCache;
|
|
31
|
+
private readonly metrics: Metrics | null;
|
|
32
|
+
private readonly logger?: Logger;
|
|
33
|
+
private payloadInputs = new Map<RootHex, PayloadEnvelopeInput>();
|
|
34
|
+
|
|
35
|
+
constructor({chainEvents, signal, serializedCache, metrics, logger}: SeenPayloadEnvelopeInputModules) {
|
|
36
|
+
this.chainEvents = chainEvents;
|
|
37
|
+
this.signal = signal;
|
|
38
|
+
this.serializedCache = serializedCache;
|
|
39
|
+
this.metrics = metrics;
|
|
40
|
+
this.logger = logger;
|
|
41
|
+
|
|
42
|
+
if (metrics) {
|
|
43
|
+
metrics.seenCache.payloadEnvelopeInput.count.addCollect(() => {
|
|
44
|
+
metrics.seenCache.payloadEnvelopeInput.count.set(this.payloadInputs.size);
|
|
45
|
+
metrics.seenCache.payloadEnvelopeInput.serializedObjectRefs.set(
|
|
46
|
+
Array.from(this.payloadInputs.values()).reduce(
|
|
47
|
+
(count, payloadInput) => count + payloadInput.getSerializedCacheKeys().length,
|
|
48
|
+
0
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.chainEvents.on(ChainEvent.forkChoiceFinalized, this.onFinalized);
|
|
55
|
+
this.signal.addEventListener("abort", () => {
|
|
56
|
+
this.chainEvents.off(ChainEvent.forkChoiceFinalized, this.onFinalized);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private onFinalized = (checkpoint: CheckpointWithHex): void => {
|
|
61
|
+
// Prune all entries with slot < finalized slot
|
|
62
|
+
const finalizedSlot = computeStartSlotAtEpoch(checkpoint.epoch);
|
|
63
|
+
let deletedCount = 0;
|
|
64
|
+
for (const [, input] of this.payloadInputs) {
|
|
65
|
+
if (input.slot < finalizedSlot) {
|
|
66
|
+
this.evictPayloadInput(input);
|
|
67
|
+
deletedCount++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.logger?.debug("SeenPayloadEnvelopeInput.onFinalized deleted cached entries", {deletedCount});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
add(props: CreateFromBlockProps): PayloadEnvelopeInput {
|
|
74
|
+
if (this.payloadInputs.has(props.blockRootHex)) {
|
|
75
|
+
throw new Error(`PayloadEnvelopeInput already exists for block ${props.blockRootHex}`);
|
|
76
|
+
}
|
|
77
|
+
const input = PayloadEnvelopeInput.createFromBlock(props);
|
|
78
|
+
this.payloadInputs.set(props.blockRootHex, input);
|
|
79
|
+
this.metrics?.seenCache.payloadEnvelopeInput.created.inc();
|
|
80
|
+
return input;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
get(blockRootHex: RootHex): PayloadEnvelopeInput | undefined {
|
|
84
|
+
return this.payloadInputs.get(blockRootHex);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
has(blockRootHex: RootHex): boolean {
|
|
88
|
+
return this.payloadInputs.has(blockRootHex);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
prune(blockRootHex: RootHex): void {
|
|
92
|
+
const payloadInput = this.payloadInputs.get(blockRootHex);
|
|
93
|
+
if (payloadInput) {
|
|
94
|
+
this.evictPayloadInput(payloadInput);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
size(): number {
|
|
99
|
+
return this.payloadInputs.size;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private evictPayloadInput(payloadInput: PayloadEnvelopeInput): void {
|
|
103
|
+
this.serializedCache.delete(payloadInput.getSerializedCacheKeys());
|
|
104
|
+
this.payloadInputs.delete(payloadInput.blockRootHex);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {PayloadStatus} from "@lodestar/fork-choice";
|
|
2
2
|
import {
|
|
3
|
+
BeaconStateView,
|
|
3
4
|
CachedBeaconStateGloas,
|
|
4
5
|
computeStartSlotAtEpoch,
|
|
5
|
-
|
|
6
|
-
getExecutionPayloadEnvelopeSigningRoot,
|
|
6
|
+
getExecutionPayloadEnvelopeSignatureSet,
|
|
7
7
|
} from "@lodestar/state-transition";
|
|
8
8
|
import {gloas} from "@lodestar/types";
|
|
9
9
|
import {toRootHex} from "@lodestar/utils";
|
|
10
10
|
import {ExecutionPayloadEnvelopeError, ExecutionPayloadEnvelopeErrorCode, GossipAction} from "../errors/index.js";
|
|
11
11
|
import {IBeaconChain} from "../index.js";
|
|
12
|
+
import {RegenCaller} from "../regen/index.js";
|
|
12
13
|
|
|
13
14
|
export async function validateApiExecutionPayloadEnvelope(
|
|
14
15
|
chain: IBeaconChain,
|
|
@@ -47,7 +48,9 @@ async function validateExecutionPayloadEnvelope(
|
|
|
47
48
|
|
|
48
49
|
// [IGNORE] The node has not seen another valid
|
|
49
50
|
// `SignedExecutionPayloadEnvelope` for this block root from this builder.
|
|
50
|
-
|
|
51
|
+
const envelopeBlock = chain.forkChoice.getBlockHex(blockRootHex, PayloadStatus.FULL);
|
|
52
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
53
|
+
if (envelopeBlock || payloadInput?.hasPayloadEnvelope()) {
|
|
51
54
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
52
55
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
53
56
|
blockRoot: blockRootHex,
|
|
@@ -55,6 +58,14 @@ async function validateExecutionPayloadEnvelope(
|
|
|
55
58
|
});
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
if (!payloadInput) {
|
|
62
|
+
// PayloadEnvelopeInput should have been created during block import
|
|
63
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
64
|
+
code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
|
|
65
|
+
blockRoot: blockRootHex,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `envelope.slot >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
59
70
|
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
60
71
|
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
@@ -79,45 +90,47 @@ async function validateExecutionPayloadEnvelope(
|
|
|
79
90
|
});
|
|
80
91
|
}
|
|
81
92
|
|
|
82
|
-
if (block.builderIndex == null || block.blockHashFromBid == null) {
|
|
83
|
-
// This indicates this block is a pre-gloas block which is wrong
|
|
84
|
-
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
85
|
-
code: ExecutionPayloadEnvelopeErrorCode.CACHE_FAIL,
|
|
86
|
-
blockRoot: blockRootHex,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
93
|
// [REJECT] `envelope.builder_index == bid.builder_index`
|
|
91
|
-
if (envelope.builderIndex !==
|
|
94
|
+
if (envelope.builderIndex !== payloadInput.getBuilderIndex()) {
|
|
92
95
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
93
96
|
code: ExecutionPayloadEnvelopeErrorCode.BUILDER_INDEX_MISMATCH,
|
|
94
97
|
envelopeBuilderIndex: envelope.builderIndex,
|
|
95
|
-
bidBuilderIndex:
|
|
98
|
+
bidBuilderIndex: payloadInput.getBuilderIndex(),
|
|
96
99
|
});
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
// [REJECT] `payload.block_hash == bid.block_hash`
|
|
100
|
-
if (toRootHex(payload.blockHash) !==
|
|
103
|
+
if (toRootHex(payload.blockHash) !== payloadInput.getBlockHashHex()) {
|
|
101
104
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
102
105
|
code: ExecutionPayloadEnvelopeErrorCode.BLOCK_HASH_MISMATCH,
|
|
103
106
|
envelopeBlockHash: toRootHex(payload.blockHash),
|
|
104
|
-
bidBlockHash:
|
|
107
|
+
bidBlockHash: payloadInput.getBlockHashHex(),
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
// Get the post block state which is the pre-payload state to verify the builder's signature.
|
|
112
|
+
const blockState = await chain.regen
|
|
113
|
+
.getState(block.stateRoot, RegenCaller.validateGossipPayloadEnvelope)
|
|
114
|
+
.catch(() => {
|
|
115
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
116
|
+
code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
|
|
117
|
+
blockRoot: blockRootHex,
|
|
118
|
+
slot: envelope.slot,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const state = blockState as CachedBeaconStateGloas;
|
|
123
|
+
const signatureSet = getExecutionPayloadEnvelopeSignatureSet(
|
|
124
|
+
chain.config,
|
|
125
|
+
chain.pubkeyCache,
|
|
126
|
+
new BeaconStateView(state),
|
|
127
|
+
executionPayloadEnvelope,
|
|
128
|
+
payloadInput.proposerIndex
|
|
114
129
|
);
|
|
115
130
|
|
|
116
|
-
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
131
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
|
117
132
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
118
133
|
code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE,
|
|
119
134
|
});
|
|
120
135
|
}
|
|
121
|
-
|
|
122
|
-
chain.seenExecutionPayloadEnvelopes.add(blockRootHex, envelope.slot);
|
|
123
136
|
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ValidatorIndex,
|
|
24
24
|
altair,
|
|
25
25
|
deneb,
|
|
26
|
+
gloas,
|
|
26
27
|
} from "@lodestar/types";
|
|
27
28
|
import {LogData, LogHandler, LogLevel, Logger, MapDef, MapDefMax, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
28
29
|
import {GENESIS_SLOT} from "../constants/constants.js";
|
|
@@ -61,6 +62,11 @@ export type ValidatorMonitor = {
|
|
|
61
62
|
): void;
|
|
62
63
|
registerBeaconBlock(src: OpSource, delaySec: Seconds, block: BeaconBlock): void;
|
|
63
64
|
registerBlobSidecar(src: OpSource, seenTimestampSec: Seconds, blob: deneb.BlobSidecar): void;
|
|
65
|
+
registerExecutionPayloadEnvelope(
|
|
66
|
+
src: OpSource,
|
|
67
|
+
delaySec: Seconds,
|
|
68
|
+
envelope: gloas.SignedExecutionPayloadEnvelope
|
|
69
|
+
): void;
|
|
64
70
|
registerImportedBlock(block: BeaconBlock, data: {proposerBalanceDelta: number}): void;
|
|
65
71
|
onPoolSubmitUnaggregatedAttestation(
|
|
66
72
|
seenTimestampSec: number,
|
|
@@ -450,6 +456,10 @@ export function createValidatorMonitor(
|
|
|
450
456
|
//TODO: freetheblobs
|
|
451
457
|
},
|
|
452
458
|
|
|
459
|
+
registerExecutionPayloadEnvelope(_src, _delaySec, _envelope) {
|
|
460
|
+
// TODO GLOAS: implement execution payload envelope monitoring
|
|
461
|
+
},
|
|
462
|
+
|
|
453
463
|
registerImportedBlock(block, {proposerBalanceDelta}) {
|
|
454
464
|
const validator = validators.get(block.proposerIndex);
|
|
455
465
|
if (validator) {
|
|
@@ -3,6 +3,7 @@ import {NotReorgedReason} from "@lodestar/fork-choice";
|
|
|
3
3
|
import {ArchiveStoreTask} from "../../chain/archiveStore/archiveStore.js";
|
|
4
4
|
import {FrequencyStateArchiveStep} from "../../chain/archiveStore/strategies/frequencyStateArchiveStrategy.js";
|
|
5
5
|
import {BlockInputSource} from "../../chain/blocks/blockInput/index.js";
|
|
6
|
+
import {PayloadEnvelopeInputSource} from "../../chain/blocks/payloadEnvelopeInput/index.js";
|
|
6
7
|
import {JobQueueItemType} from "../../chain/bls/index.js";
|
|
7
8
|
import {AttestationErrorCode, BlockErrorCode} from "../../chain/errors/index.js";
|
|
8
9
|
import {
|
|
@@ -237,6 +238,56 @@ export function createLodestarMetrics(
|
|
|
237
238
|
}),
|
|
238
239
|
},
|
|
239
240
|
|
|
241
|
+
payloadEnvelopeProcessorQueue: {
|
|
242
|
+
length: register.gauge({
|
|
243
|
+
name: "lodestar_payload_envelope_processor_queue_length",
|
|
244
|
+
help: "Count of total payload envelope processor queue length",
|
|
245
|
+
}),
|
|
246
|
+
droppedJobs: register.gauge({
|
|
247
|
+
name: "lodestar_payload_envelope_processor_queue_dropped_jobs_total",
|
|
248
|
+
help: "Count of total payload envelope processor queue dropped jobs",
|
|
249
|
+
}),
|
|
250
|
+
jobTime: register.histogram({
|
|
251
|
+
name: "lodestar_payload_envelope_processor_queue_job_time_seconds",
|
|
252
|
+
help: "Time to process payload envelope processor queue job in seconds",
|
|
253
|
+
buckets: [0.01, 0.1, 1, 4, 12],
|
|
254
|
+
}),
|
|
255
|
+
jobWaitTime: register.histogram({
|
|
256
|
+
name: "lodestar_payload_envelope_processor_queue_job_wait_time_seconds",
|
|
257
|
+
help: "Time from job added to the payload envelope processor queue to starting in seconds",
|
|
258
|
+
buckets: [0.01, 0.1, 1, 4, 12],
|
|
259
|
+
}),
|
|
260
|
+
concurrency: register.gauge({
|
|
261
|
+
name: "lodestar_payload_envelope_processor_queue_concurrency",
|
|
262
|
+
help: "Current concurrency of payload envelope processor queue",
|
|
263
|
+
}),
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
unfinalizedPayloadEnvelopeWritesQueue: {
|
|
267
|
+
length: register.gauge({
|
|
268
|
+
name: "lodestar_unfinalized_payload_envelope_writes_queue_length",
|
|
269
|
+
help: "Count of total unfinalized payload envelope writes queue length",
|
|
270
|
+
}),
|
|
271
|
+
droppedJobs: register.gauge({
|
|
272
|
+
name: "lodestar_unfinalized_payload_envelope_writes_queue_dropped_jobs_total",
|
|
273
|
+
help: "Count of total unfinalized payload envelope writes queue dropped jobs",
|
|
274
|
+
}),
|
|
275
|
+
jobTime: register.histogram({
|
|
276
|
+
name: "lodestar_unfinalized_payload_envelope_writes_queue_job_time_seconds",
|
|
277
|
+
help: "Time to process unfinalized payload envelope writes queue job in seconds",
|
|
278
|
+
buckets: [0.01, 0.1, 1, 4, 12],
|
|
279
|
+
}),
|
|
280
|
+
jobWaitTime: register.histogram({
|
|
281
|
+
name: "lodestar_unfinalized_payload_envelope_writes_queue_job_wait_time_seconds",
|
|
282
|
+
help: "Time from job added to the unfinalized payload envelope writes queue to starting in seconds",
|
|
283
|
+
buckets: [0.01, 0.1, 1, 4, 12],
|
|
284
|
+
}),
|
|
285
|
+
concurrency: register.gauge({
|
|
286
|
+
name: "lodestar_unfinalized_payload_envelope_writes_queue_concurrency",
|
|
287
|
+
help: "Current concurrency of unfinalized payload envelope writes queue",
|
|
288
|
+
}),
|
|
289
|
+
},
|
|
290
|
+
|
|
240
291
|
engineHttpProcessorQueue: {
|
|
241
292
|
length: register.gauge({
|
|
242
293
|
name: "lodestar_engine_http_processor_queue_length",
|
|
@@ -925,6 +976,18 @@ export function createLodestarMetrics(
|
|
|
925
976
|
labelNames: ["reason"],
|
|
926
977
|
}),
|
|
927
978
|
},
|
|
979
|
+
importPayload: {
|
|
980
|
+
bySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
|
|
981
|
+
name: "lodestar_import_payload_by_source_total",
|
|
982
|
+
help: "Total number of imported execution payload envelopes by source",
|
|
983
|
+
labelNames: ["source"],
|
|
984
|
+
}),
|
|
985
|
+
columnsBySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
|
|
986
|
+
name: "lodestar_import_payload_columns_by_source_total",
|
|
987
|
+
help: "Total number of payload-attached columns (sampled columns for Gloas) by source",
|
|
988
|
+
labelNames: ["source"],
|
|
989
|
+
}),
|
|
990
|
+
},
|
|
928
991
|
engineNotifyNewPayloadResult: register.gauge<{result: ExecutionPayloadStatus}>({
|
|
929
992
|
name: "lodestar_execution_engine_notify_new_payload_result_total",
|
|
930
993
|
help: "The total result of calling notifyNewPayload execution engine api",
|
|
@@ -1495,6 +1558,20 @@ export function createLodestarMetrics(
|
|
|
1495
1558
|
help: "Number of BlockInputs created via a data column being seen first",
|
|
1496
1559
|
}),
|
|
1497
1560
|
},
|
|
1561
|
+
payloadEnvelopeInput: {
|
|
1562
|
+
count: register.gauge({
|
|
1563
|
+
name: "lodestar_seen_payload_envelope_input_cache_size",
|
|
1564
|
+
help: "Number of cached PayloadEnvelopeInputs",
|
|
1565
|
+
}),
|
|
1566
|
+
serializedObjectRefs: register.gauge({
|
|
1567
|
+
name: "lodestar_seen_payload_envelope_input_cache_serialized_object_refs",
|
|
1568
|
+
help: "Number of serialized-cache object refs retained by cached PayloadEnvelopeInputs",
|
|
1569
|
+
}),
|
|
1570
|
+
created: register.counter({
|
|
1571
|
+
name: "lodestar_seen_payload_envelope_input_cache_items_created_total",
|
|
1572
|
+
help: "Number of PayloadEnvelopeInputs created",
|
|
1573
|
+
}),
|
|
1574
|
+
},
|
|
1498
1575
|
},
|
|
1499
1576
|
|
|
1500
1577
|
processFinalizedCheckpoint: {
|
|
@@ -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 ({
|
|
@@ -826,17 +836,43 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
826
836
|
[GossipType.execution_payload]: async ({
|
|
827
837
|
gossipData,
|
|
828
838
|
topic,
|
|
839
|
+
peerIdStr,
|
|
829
840
|
seenTimestampSec,
|
|
830
841
|
}: GossipHandlerParamGeneric<GossipType.execution_payload>) => {
|
|
831
842
|
const {serializedData} = gossipData;
|
|
832
843
|
const executionPayloadEnvelope = sszDeserialize(topic, serializedData);
|
|
844
|
+
// TODO GLOAS: handle BLOCK_ROOT_UNKNOWN error to trigger sync
|
|
833
845
|
await validateGossipExecutionPayloadEnvelope(chain, executionPayloadEnvelope);
|
|
834
846
|
|
|
835
847
|
const slot = executionPayloadEnvelope.message.slot;
|
|
836
848
|
const delaySec = seenTimestampSec - computeTimeAtSlot(config, slot, chain.genesisTime);
|
|
837
849
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
850
|
+
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, executionPayloadEnvelope);
|
|
851
|
+
|
|
852
|
+
const blockRootHex = toRootHex(executionPayloadEnvelope.message.beaconBlockRoot);
|
|
853
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
854
|
+
|
|
855
|
+
if (!payloadInput) {
|
|
856
|
+
// This shouldn't happen because beacon block should have been imported and thus payload input should have been created.
|
|
857
|
+
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
858
|
+
code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING,
|
|
859
|
+
blockRoot: blockRootHex,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
838
862
|
|
|
839
|
-
|
|
863
|
+
chain.serializedCache.set(executionPayloadEnvelope, serializedData);
|
|
864
|
+
|
|
865
|
+
payloadInput.addPayloadEnvelope({
|
|
866
|
+
envelope: executionPayloadEnvelope,
|
|
867
|
+
source: PayloadEnvelopeInputSource.gossip,
|
|
868
|
+
seenTimestampSec,
|
|
869
|
+
peerIdStr,
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// TODO GLOAS: Emit execution_payload_gossip event for gossip receipt.
|
|
873
|
+
chain.processExecutionPayload(payloadInput, {validSignature: true}).catch((e) => {
|
|
874
|
+
chain.logger.debug("Error processing execution payload from gossip", {slot, root: blockRootHex}, e as Error);
|
|
875
|
+
});
|
|
840
876
|
},
|
|
841
877
|
[GossipType.payload_attestation_message]: async ({
|
|
842
878
|
gossipData,
|