@lodestar/beacon-node 1.43.0-dev.e341cdc614 → 1.43.0-dev.e5b13221e5
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 +1 -4
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +0 -4
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +9 -18
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +6 -2
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +20 -10
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +29 -19
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +8 -8
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +4 -3
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
- package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
- package/lib/execution/engine/http.d.ts.map +1 -1
- package/lib/execution/engine/http.js +21 -14
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +1 -0
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +6 -0
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +20 -0
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +18 -0
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +750 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/network.js +1 -1
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +3 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +16 -3
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/blocks/index.ts +1 -4
- package/src/api/impl/validator/index.ts +0 -4
- package/src/chain/blocks/importExecutionPayload.ts +9 -19
- package/src/chain/forkChoice/index.ts +6 -2
- package/src/chain/prepareNextSlot.ts +23 -10
- package/src/chain/produceBlock/produceBlockBody.ts +40 -14
- package/src/chain/stateCache/persistentCheckpointsCache.ts +4 -1
- package/src/chain/validation/executionPayloadEnvelope.ts +8 -8
- package/src/chain/validation/payloadAttestationMessage.ts +5 -3
- package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
- package/src/execution/engine/http.ts +21 -14
- package/src/execution/engine/interface.ts +1 -0
- package/src/execution/engine/mock.ts +8 -1
- package/src/execution/engine/types.ts +41 -0
- package/src/network/network.ts +1 -1
- package/src/network/processor/gossipHandlers.ts +3 -3
- package/src/node/nodejs.ts +4 -2
- package/src/util/sszBytes.ts +21 -3
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/ChainSafe/lodestar/issues"
|
|
13
13
|
},
|
|
14
|
-
"version": "1.43.0-dev.
|
|
14
|
+
"version": "1.43.0-dev.e5b13221e5",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@chainsafe/prometheus-gc-stats": "^1.0.0",
|
|
119
119
|
"@chainsafe/pubkey-index-map": "^3.0.0",
|
|
120
120
|
"@chainsafe/snappy-wasm": "^0.5.0",
|
|
121
|
-
"@chainsafe/ssz": "^1.
|
|
121
|
+
"@chainsafe/ssz": "^1.4.0",
|
|
122
122
|
"@chainsafe/threads": "^1.11.3",
|
|
123
123
|
"@crate-crypto/node-eth-kzg": "0.9.1",
|
|
124
124
|
"@fastify/bearer-auth": "^10.0.1",
|
|
@@ -135,18 +135,18 @@
|
|
|
135
135
|
"@libp2p/peer-id": "^6.0.4",
|
|
136
136
|
"@libp2p/prometheus-metrics": "^5.0.14",
|
|
137
137
|
"@libp2p/tcp": "^11.0.13",
|
|
138
|
-
"@lodestar/api": "^1.43.0-dev.
|
|
139
|
-
"@lodestar/config": "^1.43.0-dev.
|
|
140
|
-
"@lodestar/db": "^1.43.0-dev.
|
|
141
|
-
"@lodestar/fork-choice": "^1.43.0-dev.
|
|
142
|
-
"@lodestar/light-client": "^1.43.0-dev.
|
|
143
|
-
"@lodestar/logger": "^1.43.0-dev.
|
|
144
|
-
"@lodestar/params": "^1.43.0-dev.
|
|
145
|
-
"@lodestar/reqresp": "^1.43.0-dev.
|
|
146
|
-
"@lodestar/state-transition": "^1.43.0-dev.
|
|
147
|
-
"@lodestar/types": "^1.43.0-dev.
|
|
148
|
-
"@lodestar/utils": "^1.43.0-dev.
|
|
149
|
-
"@lodestar/validator": "^1.43.0-dev.
|
|
138
|
+
"@lodestar/api": "^1.43.0-dev.e5b13221e5",
|
|
139
|
+
"@lodestar/config": "^1.43.0-dev.e5b13221e5",
|
|
140
|
+
"@lodestar/db": "^1.43.0-dev.e5b13221e5",
|
|
141
|
+
"@lodestar/fork-choice": "^1.43.0-dev.e5b13221e5",
|
|
142
|
+
"@lodestar/light-client": "^1.43.0-dev.e5b13221e5",
|
|
143
|
+
"@lodestar/logger": "^1.43.0-dev.e5b13221e5",
|
|
144
|
+
"@lodestar/params": "^1.43.0-dev.e5b13221e5",
|
|
145
|
+
"@lodestar/reqresp": "^1.43.0-dev.e5b13221e5",
|
|
146
|
+
"@lodestar/state-transition": "^1.43.0-dev.e5b13221e5",
|
|
147
|
+
"@lodestar/types": "^1.43.0-dev.e5b13221e5",
|
|
148
|
+
"@lodestar/utils": "^1.43.0-dev.e5b13221e5",
|
|
149
|
+
"@lodestar/validator": "^1.43.0-dev.e5b13221e5",
|
|
150
150
|
"@multiformats/multiaddr": "^13.0.1",
|
|
151
151
|
"datastore-core": "^11.0.2",
|
|
152
152
|
"datastore-fs": "^11.0.2",
|
|
@@ -169,7 +169,7 @@
|
|
|
169
169
|
"@libp2p/interface-internal": "^3.0.13",
|
|
170
170
|
"@libp2p/logger": "^6.2.2",
|
|
171
171
|
"@libp2p/utils": "^7.0.13",
|
|
172
|
-
"@lodestar/spec-test-util": "^1.43.0-dev.
|
|
172
|
+
"@lodestar/spec-test-util": "^1.43.0-dev.e5b13221e5",
|
|
173
173
|
"@types/js-yaml": "^4.0.5",
|
|
174
174
|
"@types/qs": "^6.9.7",
|
|
175
175
|
"@types/tmp": "^0.2.3",
|
|
@@ -186,5 +186,5 @@
|
|
|
186
186
|
"beacon",
|
|
187
187
|
"blockchain"
|
|
188
188
|
],
|
|
189
|
-
"gitHead": "
|
|
189
|
+
"gitHead": "52a0608b862b2938272884c58061fa9da66b0b11"
|
|
190
190
|
}
|
|
@@ -651,11 +651,10 @@ export function getBeaconBlockApi({
|
|
|
651
651
|
async publishExecutionPayloadEnvelope({signedExecutionPayloadEnvelope}) {
|
|
652
652
|
const seenTimestampSec = Date.now() / 1000;
|
|
653
653
|
const envelope = signedExecutionPayloadEnvelope.message;
|
|
654
|
-
const slot = envelope.
|
|
654
|
+
const slot = envelope.payload.slotNumber;
|
|
655
655
|
const fork = config.getForkName(slot);
|
|
656
656
|
const blockRootHex = toRootHex(envelope.beaconBlockRoot);
|
|
657
657
|
const blockHashHex = toRootHex(envelope.payload.blockHash);
|
|
658
|
-
const stateRootHex = toRootHex(envelope.stateRoot);
|
|
659
658
|
|
|
660
659
|
if (!isForkPostGloas(fork)) {
|
|
661
660
|
throw new ApiError(400, `publishExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
@@ -740,7 +739,6 @@ export function getBeaconBlockApi({
|
|
|
740
739
|
slot,
|
|
741
740
|
blockRoot: blockRootHex,
|
|
742
741
|
blockHash: blockHashHex,
|
|
743
|
-
stateRoot: stateRootHex,
|
|
744
742
|
builderIndex: envelope.builderIndex,
|
|
745
743
|
isSelfBuild,
|
|
746
744
|
dataColumns: dataColumnSidecars.length,
|
|
@@ -768,7 +766,6 @@ export function getBeaconBlockApi({
|
|
|
768
766
|
builderIndex: envelope.builderIndex,
|
|
769
767
|
blockHash: blockHashHex,
|
|
770
768
|
blockRoot: blockRootHex,
|
|
771
|
-
stateRoot: stateRootHex,
|
|
772
769
|
});
|
|
773
770
|
|
|
774
771
|
const sentPeersArr = await publishPromise;
|
|
@@ -1648,10 +1648,6 @@ export function getValidatorApi(
|
|
|
1648
1648
|
executionRequests: executionRequests,
|
|
1649
1649
|
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
1650
1650
|
beaconBlockRoot,
|
|
1651
|
-
slot,
|
|
1652
|
-
// TODO GLOAS: stateRoot is no longer computed during block production.
|
|
1653
|
-
// This field will be removed when we implement defer payload processing
|
|
1654
|
-
stateRoot: ZERO_HASH,
|
|
1655
1651
|
};
|
|
1656
1652
|
|
|
1657
1653
|
logger.info("Produced execution payload envelope", {
|
|
@@ -2,7 +2,7 @@ import {routes} from "@lodestar/api";
|
|
|
2
2
|
import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
3
|
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
4
4
|
import {getExecutionPayloadEnvelopeSignatureSet, isStatePostGloas} from "@lodestar/state-transition";
|
|
5
|
-
import {
|
|
5
|
+
import {fromHex, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {ExecutionPayloadStatus} from "../../execution/index.js";
|
|
7
7
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
8
8
|
import {BeaconChain} from "../chain.js";
|
|
@@ -92,15 +92,15 @@ export async function importExecutionPayload(
|
|
|
92
92
|
const envelope = signedEnvelope.message;
|
|
93
93
|
const blockRootHex = payloadInput.blockRootHex;
|
|
94
94
|
const blockHashHex = payloadInput.getBlockHashHex();
|
|
95
|
-
const fork = this.config.getForkName(envelope.
|
|
95
|
+
const fork = this.config.getForkName(envelope.payload.slotNumber);
|
|
96
96
|
|
|
97
97
|
// 1. Emit `execution_payload_available` event at the start of import. At this point the payload input
|
|
98
98
|
// is already complete, so the payload and required data are available for payload attestation.
|
|
99
99
|
// This event is only about availability, not validity of the execution payload, hence we can emit
|
|
100
100
|
// it before getting a response from the execution client on whether the payload is valid or not.
|
|
101
|
-
if (this.clock.currentSlot - envelope.
|
|
101
|
+
if (this.clock.currentSlot - envelope.payload.slotNumber < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
102
102
|
this.emitter.emit(routes.events.EventType.executionPayloadAvailable, {
|
|
103
|
-
slot: envelope.
|
|
103
|
+
slot: envelope.payload.slotNumber,
|
|
104
104
|
blockRoot: blockRootHex,
|
|
105
105
|
});
|
|
106
106
|
}
|
|
@@ -213,22 +213,16 @@ export async function importExecutionPayload(
|
|
|
213
213
|
});
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
// 5c.
|
|
216
|
+
// 5c. Compute post-payload state root
|
|
217
217
|
const postPayloadState = postPayloadResult.postPayloadState;
|
|
218
218
|
const postPayloadStateRoot = postPayloadState.hashTreeRoot();
|
|
219
|
-
if (!byteArrayEquals(envelope.stateRoot, postPayloadStateRoot)) {
|
|
220
|
-
throw new PayloadError({
|
|
221
|
-
code: PayloadErrorCode.STATE_TRANSITION_ERROR,
|
|
222
|
-
message: `Envelope state root mismatch expected=${toRootHex(envelope.stateRoot)} actual=${toRootHex(postPayloadStateRoot)}`,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
219
|
|
|
226
220
|
// 6. Persist payload envelope to hot DB (performed asynchronously to avoid blocking)
|
|
227
221
|
this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
|
|
228
222
|
if (!isQueueErrorAborted(e)) {
|
|
229
223
|
this.logger.error(
|
|
230
224
|
"Error pushing payload envelope to unfinalized write queue",
|
|
231
|
-
{slot: envelope.
|
|
225
|
+
{slot: envelope.payload.slotNumber, blockRoot: blockRootHex},
|
|
232
226
|
e as Error
|
|
233
227
|
);
|
|
234
228
|
}
|
|
@@ -256,26 +250,22 @@ export async function importExecutionPayload(
|
|
|
256
250
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
257
251
|
}
|
|
258
252
|
|
|
259
|
-
const stateRootHex = toRootHex(envelope.stateRoot);
|
|
260
|
-
|
|
261
253
|
// 10. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
262
|
-
if (this.clock.currentSlot - envelope.
|
|
254
|
+
if (this.clock.currentSlot - envelope.payload.slotNumber < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
263
255
|
this.emitter.emit(routes.events.EventType.executionPayload, {
|
|
264
|
-
slot: envelope.
|
|
256
|
+
slot: envelope.payload.slotNumber,
|
|
265
257
|
builderIndex: envelope.builderIndex,
|
|
266
258
|
blockHash: blockHashHex,
|
|
267
259
|
blockRoot: blockRootHex,
|
|
268
|
-
stateRoot: stateRootHex,
|
|
269
260
|
// TODO GLOAS: revisit once we support optimistic import
|
|
270
261
|
executionOptimistic: false,
|
|
271
262
|
});
|
|
272
263
|
}
|
|
273
264
|
|
|
274
265
|
this.logger.verbose("Execution payload imported", {
|
|
275
|
-
slot: envelope.
|
|
266
|
+
slot: envelope.payload.slotNumber,
|
|
276
267
|
builderIndex: envelope.builderIndex,
|
|
277
268
|
blockRoot: blockRootHex,
|
|
278
269
|
blockHash: blockHashHex,
|
|
279
|
-
stateRoot: stateRootHex,
|
|
280
270
|
});
|
|
281
271
|
}
|
|
@@ -150,7 +150,9 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
150
150
|
|
|
151
151
|
...(isStatePostBellatrix(state) && state.isExecutionStateType && state.isMergeTransitionComplete
|
|
152
152
|
? {
|
|
153
|
-
executionPayloadBlockHash:
|
|
153
|
+
executionPayloadBlockHash: isStatePostGloas(state)
|
|
154
|
+
? toRootHex(state.latestBlockHash)
|
|
155
|
+
: toRootHex(state.latestExecutionPayloadHeader.blockHash),
|
|
154
156
|
// TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
|
|
155
157
|
// latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
|
|
156
158
|
executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
|
|
@@ -247,7 +249,9 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
247
249
|
unfinalizedState.isExecutionStateType &&
|
|
248
250
|
unfinalizedState.isMergeTransitionComplete
|
|
249
251
|
? {
|
|
250
|
-
executionPayloadBlockHash:
|
|
252
|
+
executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
|
|
253
|
+
? toRootHex(unfinalizedState.latestBlockHash)
|
|
254
|
+
: toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
|
|
251
255
|
// TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
|
|
252
256
|
// latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
|
|
253
257
|
executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
computeEpochAtSlot,
|
|
9
9
|
computeTimeAtSlot,
|
|
10
10
|
isStatePostBellatrix,
|
|
11
|
+
isStatePostGloas,
|
|
11
12
|
} from "@lodestar/state-transition";
|
|
12
|
-
import {Slot} from "@lodestar/types";
|
|
13
|
+
import {Bytes32, Slot} from "@lodestar/types";
|
|
13
14
|
import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
|
|
14
15
|
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
|
|
15
16
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
@@ -81,6 +82,8 @@ export class PrepareNextSlotScheduler {
|
|
|
81
82
|
// calling updateHead() here before we produce a block to reduce reorg possibility
|
|
82
83
|
const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
|
|
83
84
|
const {slot: headSlot, blockRoot: headRoot} = headBlock;
|
|
85
|
+
// may be updated below if we predict a proposer-boost-reorg
|
|
86
|
+
let updatedHeadRoot = headRoot;
|
|
84
87
|
|
|
85
88
|
// PS: previously this was comparing slots, but that gave no leway on the skipped
|
|
86
89
|
// slots on epoch bounday. Making it more fluid.
|
|
@@ -123,7 +126,6 @@ export class PrepareNextSlotScheduler {
|
|
|
123
126
|
const proposerIndex = prepareState.getBeaconProposer(prepareSlot);
|
|
124
127
|
const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
|
|
125
128
|
let updatedPrepareState = prepareState;
|
|
126
|
-
let updatedHeadRoot = headRoot;
|
|
127
129
|
|
|
128
130
|
if (feeRecipient) {
|
|
129
131
|
// If we are proposing next slot, we need to predict if we can proposer-boost-reorg or not
|
|
@@ -156,25 +158,39 @@ export class PrepareNextSlotScheduler {
|
|
|
156
158
|
this.logger.error("Builder disabled as the check status api failed", {prepareSlot}, e as Error);
|
|
157
159
|
});
|
|
158
160
|
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
164
|
+
throw new Error("Expected Bellatrix state for payload attributes");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let parentBlockHash: Bytes32;
|
|
168
|
+
if (isStatePostGloas(updatedPrepareState)) {
|
|
169
|
+
parentBlockHash = this.chain.forkChoice.shouldExtendPayload(updatedHeadRoot)
|
|
170
|
+
? updatedPrepareState.latestExecutionPayloadBid.blockHash
|
|
171
|
+
: updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
|
|
172
|
+
} else {
|
|
173
|
+
parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
|
|
174
|
+
}
|
|
159
175
|
|
|
176
|
+
if (feeRecipient) {
|
|
160
177
|
const preparationTime =
|
|
161
178
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
162
179
|
this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
|
|
163
|
-
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
164
|
-
throw new Error("Expected Bellatrix state for payload preparation");
|
|
165
|
-
}
|
|
166
180
|
|
|
167
181
|
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice);
|
|
168
182
|
const finalizedBlockHash =
|
|
169
183
|
this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
184
|
+
|
|
170
185
|
// awaiting here instead of throwing an async call because there is no other task
|
|
171
|
-
// left for scheduler and this gives nice
|
|
186
|
+
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
172
187
|
// try/catch wrapper here.
|
|
173
188
|
await prepareExecutionPayload(
|
|
174
189
|
this.chain,
|
|
175
190
|
this.logger,
|
|
176
191
|
fork as ForkPostBellatrix, // State is of execution type
|
|
177
192
|
fromHex(updatedHeadRoot),
|
|
193
|
+
parentBlockHash,
|
|
178
194
|
safeBlockHash,
|
|
179
195
|
finalizedBlockHash,
|
|
180
196
|
updatedPrepareState,
|
|
@@ -187,10 +203,6 @@ export class PrepareNextSlotScheduler {
|
|
|
187
203
|
});
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
191
|
-
throw new Error("Expected Bellatrix state for payload attributes");
|
|
192
|
-
}
|
|
193
|
-
|
|
194
206
|
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
|
|
195
207
|
|
|
196
208
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event
|
|
@@ -202,6 +214,7 @@ export class PrepareNextSlotScheduler {
|
|
|
202
214
|
prepareState: updatedPrepareState,
|
|
203
215
|
prepareSlot,
|
|
204
216
|
parentBlockRoot: fromHex(headRoot),
|
|
217
|
+
parentBlockHash,
|
|
205
218
|
// The likely consumers of this API are builders and will anyway ignore the
|
|
206
219
|
// feeRecipient, so just pass zero hash for now till a real use case arises
|
|
207
220
|
feeRecipient: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
IBeaconStateView,
|
|
20
20
|
type IBeaconStateViewBellatrix,
|
|
21
21
|
computeTimeAtSlot,
|
|
22
|
-
isParentBlockFull,
|
|
23
22
|
isStatePostBellatrix,
|
|
24
23
|
isStatePostCapella,
|
|
25
24
|
isStatePostGloas,
|
|
@@ -47,8 +46,9 @@ import {
|
|
|
47
46
|
electra,
|
|
48
47
|
fulu,
|
|
49
48
|
gloas,
|
|
49
|
+
ssz,
|
|
50
50
|
} from "@lodestar/types";
|
|
51
|
-
import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
51
|
+
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
52
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
53
53
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
54
54
|
import {
|
|
@@ -214,11 +214,15 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// Get execution payload from EL
|
|
217
|
+
const parentBlockHash = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot))
|
|
218
|
+
? currentState.latestExecutionPayloadBid.blockHash
|
|
219
|
+
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
217
220
|
const prepareRes = await prepareExecutionPayload(
|
|
218
221
|
this,
|
|
219
222
|
this.logger,
|
|
220
223
|
fork,
|
|
221
224
|
parentBlockRoot,
|
|
225
|
+
parentBlockHash,
|
|
222
226
|
safeBlockHash,
|
|
223
227
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
224
228
|
currentState,
|
|
@@ -255,8 +259,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
255
259
|
|
|
256
260
|
// Create self-build execution payload bid
|
|
257
261
|
const bid: gloas.ExecutionPayloadBid = {
|
|
258
|
-
parentBlockHash
|
|
259
|
-
parentBlockRoot
|
|
262
|
+
parentBlockHash,
|
|
263
|
+
parentBlockRoot,
|
|
260
264
|
blockHash: executionPayload.blockHash,
|
|
261
265
|
prevRandao: currentState.getRandaoMix(currentState.epoch),
|
|
262
266
|
feeRecipient: executionPayload.feeRecipient,
|
|
@@ -266,6 +270,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
266
270
|
value: 0,
|
|
267
271
|
executionPayment: 0,
|
|
268
272
|
blobKzgCommitments: blobsBundle.commitments,
|
|
273
|
+
executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
|
|
269
274
|
};
|
|
270
275
|
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
271
276
|
message: bid,
|
|
@@ -334,6 +339,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
334
339
|
this.logger,
|
|
335
340
|
fork,
|
|
336
341
|
parentBlockRoot,
|
|
342
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
337
343
|
safeBlockHash,
|
|
338
344
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
339
345
|
currentState,
|
|
@@ -442,6 +448,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
442
448
|
this.logger,
|
|
443
449
|
fork,
|
|
444
450
|
parentBlockRoot,
|
|
451
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
445
452
|
safeBlockHash,
|
|
446
453
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
447
454
|
currentState,
|
|
@@ -607,17 +614,17 @@ export async function prepareExecutionPayload(
|
|
|
607
614
|
logger: Logger,
|
|
608
615
|
fork: ForkPostBellatrix,
|
|
609
616
|
parentBlockRoot: Root,
|
|
617
|
+
parentBlockHash: Bytes32,
|
|
610
618
|
safeBlockHash: RootHex,
|
|
611
619
|
finalizedBlockHash: RootHex,
|
|
612
620
|
state: IBeaconStateViewBellatrix,
|
|
613
621
|
suggestedFeeRecipient: string
|
|
614
622
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
615
|
-
const parentHash = state.latestBlockHash;
|
|
616
623
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
617
624
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
618
625
|
|
|
619
626
|
const payloadIdCached = chain.executionEngine.payloadIdCache.get({
|
|
620
|
-
headBlockHash: toRootHex(
|
|
627
|
+
headBlockHash: toRootHex(parentBlockHash),
|
|
621
628
|
finalizedBlockHash,
|
|
622
629
|
timestamp: numToQuantity(timestamp),
|
|
623
630
|
prevRandao: toHex(prevRandao),
|
|
@@ -646,12 +653,13 @@ export async function prepareExecutionPayload(
|
|
|
646
653
|
prepareState: state,
|
|
647
654
|
prepareSlot: state.slot,
|
|
648
655
|
parentBlockRoot,
|
|
656
|
+
parentBlockHash,
|
|
649
657
|
feeRecipient: suggestedFeeRecipient,
|
|
650
658
|
});
|
|
651
659
|
|
|
652
660
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
653
661
|
fork,
|
|
654
|
-
toRootHex(
|
|
662
|
+
toRootHex(parentBlockHash),
|
|
655
663
|
safeBlockHash,
|
|
656
664
|
finalizedBlockHash,
|
|
657
665
|
attributes
|
|
@@ -703,20 +711,30 @@ export function getPayloadAttributesForSSE(
|
|
|
703
711
|
prepareState,
|
|
704
712
|
prepareSlot,
|
|
705
713
|
parentBlockRoot,
|
|
714
|
+
parentBlockHash,
|
|
706
715
|
feeRecipient,
|
|
707
|
-
}: {
|
|
716
|
+
}: {
|
|
717
|
+
prepareState: IBeaconStateViewBellatrix;
|
|
718
|
+
prepareSlot: Slot;
|
|
719
|
+
parentBlockRoot: Root;
|
|
720
|
+
parentBlockHash: Bytes32;
|
|
721
|
+
feeRecipient: string;
|
|
722
|
+
}
|
|
708
723
|
): SSEPayloadAttributes {
|
|
709
|
-
const parentHash = prepareState.latestBlockHash;
|
|
710
724
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
711
725
|
prepareState,
|
|
712
726
|
prepareSlot,
|
|
713
727
|
parentBlockRoot,
|
|
728
|
+
parentBlockHash,
|
|
714
729
|
feeRecipient,
|
|
715
730
|
});
|
|
716
731
|
|
|
717
732
|
let parentBlockNumber: number;
|
|
718
733
|
if (isForkPostGloas(fork)) {
|
|
719
|
-
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
734
|
+
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
735
|
+
toRootHex(parentBlockRoot),
|
|
736
|
+
toRootHex(parentBlockHash)
|
|
737
|
+
);
|
|
720
738
|
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
721
739
|
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
722
740
|
}
|
|
@@ -730,7 +748,7 @@ export function getPayloadAttributesForSSE(
|
|
|
730
748
|
proposalSlot: prepareSlot,
|
|
731
749
|
parentBlockNumber,
|
|
732
750
|
parentBlockRoot,
|
|
733
|
-
parentBlockHash
|
|
751
|
+
parentBlockHash,
|
|
734
752
|
payloadAttributes,
|
|
735
753
|
};
|
|
736
754
|
return ssePayloadAttributes;
|
|
@@ -745,11 +763,13 @@ function preparePayloadAttributes(
|
|
|
745
763
|
prepareState,
|
|
746
764
|
prepareSlot,
|
|
747
765
|
parentBlockRoot,
|
|
766
|
+
parentBlockHash,
|
|
748
767
|
feeRecipient,
|
|
749
768
|
}: {
|
|
750
769
|
prepareState: IBeaconStateViewBellatrix;
|
|
751
770
|
prepareSlot: Slot;
|
|
752
771
|
parentBlockRoot: Root;
|
|
772
|
+
parentBlockHash: Bytes32;
|
|
753
773
|
feeRecipient: string;
|
|
754
774
|
}
|
|
755
775
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
@@ -766,13 +786,15 @@ function preparePayloadAttributes(
|
|
|
766
786
|
throw new Error("Expected Capella state for withdrawals");
|
|
767
787
|
}
|
|
768
788
|
|
|
769
|
-
if (isStatePostGloas(prepareState)
|
|
789
|
+
if (isStatePostGloas(prepareState)) {
|
|
790
|
+
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
770
791
|
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
771
792
|
// already deducted from CL balances but never credited on the EL (the envelope
|
|
772
793
|
// was not delivered). The next payload must carry those same withdrawals to
|
|
773
794
|
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
774
|
-
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
775
|
-
prepareState.
|
|
795
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals = isExtendingPayload
|
|
796
|
+
? prepareState.getExpectedWithdrawals().expectedWithdrawals
|
|
797
|
+
: prepareState.payloadExpectedWithdrawals;
|
|
776
798
|
} else {
|
|
777
799
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
778
800
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
@@ -784,6 +806,10 @@ function preparePayloadAttributes(
|
|
|
784
806
|
(payloadAttributes as deneb.SSEPayloadAttributes["payloadAttributes"]).parentBeaconBlockRoot = parentBlockRoot;
|
|
785
807
|
}
|
|
786
808
|
|
|
809
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
810
|
+
(payloadAttributes as gloas.SSEPayloadAttributes["payloadAttributes"]).slotNumber = prepareSlot;
|
|
811
|
+
}
|
|
812
|
+
|
|
787
813
|
return payloadAttributes;
|
|
788
814
|
}
|
|
789
815
|
|
|
@@ -226,7 +226,10 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
|
|
226
226
|
}
|
|
227
227
|
sszTimer?.();
|
|
228
228
|
const timer = this.metrics?.cpStateCache.stateReloadDuration.startTimer();
|
|
229
|
-
|
|
229
|
+
// preload validators and balances for faster state transition
|
|
230
|
+
const newCachedState = seedState.loadOtherState(stateBytes, validatorsBytes, {
|
|
231
|
+
preloadValidatorsAndBalances: true,
|
|
232
|
+
});
|
|
230
233
|
// hashTreeRoot() calls the commit() inside
|
|
231
234
|
// there is no modification inside the state, it's just that we want to compute and cache all roots
|
|
232
235
|
const stateRoot = toRootHex(newCachedState.hashTreeRoot());
|
|
@@ -53,7 +53,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
53
53
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
54
54
|
code: ExecutionPayloadEnvelopeErrorCode.ENVELOPE_ALREADY_KNOWN,
|
|
55
55
|
blockRoot: blockRootHex,
|
|
56
|
-
slot:
|
|
56
|
+
slot: payload.slotNumber,
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -65,13 +65,13 @@ async function validateExecutionPayloadEnvelope(
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `
|
|
68
|
+
// [IGNORE] The envelope is from a slot greater than or equal to the latest finalized slot -- i.e. validate that `payload.slotNumber >= compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
|
|
69
69
|
const finalizedCheckpoint = chain.forkChoice.getFinalizedCheckpoint();
|
|
70
70
|
const finalizedSlot = computeStartSlotAtEpoch(finalizedCheckpoint.epoch);
|
|
71
|
-
if (
|
|
71
|
+
if (payload.slotNumber < finalizedSlot) {
|
|
72
72
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
73
73
|
code: ExecutionPayloadEnvelopeErrorCode.BELONG_TO_FINALIZED_BLOCK,
|
|
74
|
-
envelopeSlot:
|
|
74
|
+
envelopeSlot: payload.slotNumber,
|
|
75
75
|
finalizedSlot,
|
|
76
76
|
});
|
|
77
77
|
}
|
|
@@ -80,11 +80,11 @@ async function validateExecutionPayloadEnvelope(
|
|
|
80
80
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
81
81
|
// it is possible that the block didn't pass the validation
|
|
82
82
|
|
|
83
|
-
// [REJECT] `block.slot` equals `
|
|
84
|
-
if (block.slot !==
|
|
83
|
+
// [REJECT] `block.slot` equals `payload.slotNumber`.
|
|
84
|
+
if (block.slot !== payload.slotNumber) {
|
|
85
85
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
86
86
|
code: ExecutionPayloadEnvelopeErrorCode.SLOT_MISMATCH,
|
|
87
|
-
envelopeSlot:
|
|
87
|
+
envelopeSlot: payload.slotNumber,
|
|
88
88
|
blockSlot: block.slot,
|
|
89
89
|
});
|
|
90
90
|
}
|
|
@@ -114,7 +114,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
114
114
|
throw new ExecutionPayloadEnvelopeError(GossipAction.IGNORE, {
|
|
115
115
|
code: ExecutionPayloadEnvelopeErrorCode.UNKNOWN_BLOCK_STATE,
|
|
116
116
|
blockRoot: blockRootHex,
|
|
117
|
-
slot:
|
|
117
|
+
slot: payload.slotNumber,
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
if (!isStatePostGloas(blockState)) {
|
|
@@ -18,7 +18,8 @@ export async function validateApiPayloadAttestationMessage(
|
|
|
18
18
|
chain: IBeaconChain,
|
|
19
19
|
payloadAttestationMessage: gloas.PayloadAttestationMessage
|
|
20
20
|
): Promise<PayloadAttestationValidationResult> {
|
|
21
|
-
|
|
21
|
+
const prioritizeBls = true;
|
|
22
|
+
return validatePayloadAttestationMessage(chain, payloadAttestationMessage, prioritizeBls);
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export async function validateGossipPayloadAttestationMessage(
|
|
@@ -30,7 +31,8 @@ export async function validateGossipPayloadAttestationMessage(
|
|
|
30
31
|
|
|
31
32
|
async function validatePayloadAttestationMessage(
|
|
32
33
|
chain: IBeaconChain,
|
|
33
|
-
payloadAttestationMessage: gloas.PayloadAttestationMessage
|
|
34
|
+
payloadAttestationMessage: gloas.PayloadAttestationMessage,
|
|
35
|
+
prioritizeBls = false
|
|
34
36
|
): Promise<PayloadAttestationValidationResult> {
|
|
35
37
|
const {data, validatorIndex} = payloadAttestationMessage;
|
|
36
38
|
const epoch = computeEpochAtSlot(data.slot);
|
|
@@ -102,7 +104,7 @@ async function validatePayloadAttestationMessage(
|
|
|
102
104
|
payloadAttestationMessage.signature
|
|
103
105
|
);
|
|
104
106
|
|
|
105
|
-
if (!(await chain.bls.verifySignatureSets([signatureSet]))) {
|
|
107
|
+
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true, priority: prioritizeBls}))) {
|
|
106
108
|
throw new PayloadAttestationError(GossipAction.REJECT, {
|
|
107
109
|
code: PayloadAttestationErrorCode.INVALID_SIGNATURE,
|
|
108
110
|
});
|
|
@@ -19,7 +19,7 @@ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot,
|
|
|
19
19
|
* Id is the slot from the envelope
|
|
20
20
|
*/
|
|
21
21
|
getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
|
|
22
|
-
return value.message.
|
|
22
|
+
return value.message.payload.slotNumber;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
encodeKey(id: Slot): Uint8Array {
|
|
@@ -216,13 +216,15 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
216
216
|
executionRequests?: ExecutionRequests
|
|
217
217
|
): Promise<ExecutePayloadResponse> {
|
|
218
218
|
const method =
|
|
219
|
-
ForkSeq[fork] >= ForkSeq.
|
|
220
|
-
? "
|
|
221
|
-
: ForkSeq[fork] >= ForkSeq.
|
|
222
|
-
? "
|
|
223
|
-
: ForkSeq[fork] >= ForkSeq.
|
|
224
|
-
? "
|
|
225
|
-
:
|
|
219
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
220
|
+
? "engine_newPayloadV5"
|
|
221
|
+
: ForkSeq[fork] >= ForkSeq.electra
|
|
222
|
+
? "engine_newPayloadV4"
|
|
223
|
+
: ForkSeq[fork] >= ForkSeq.deneb
|
|
224
|
+
? "engine_newPayloadV3"
|
|
225
|
+
: ForkSeq[fork] >= ForkSeq.capella
|
|
226
|
+
? "engine_newPayloadV2"
|
|
227
|
+
: "engine_newPayloadV1";
|
|
226
228
|
|
|
227
229
|
const serializedExecutionPayload = serializeExecutionPayload(fork, executionPayload);
|
|
228
230
|
|
|
@@ -244,7 +246,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
244
246
|
}
|
|
245
247
|
const serializedExecutionRequests = serializeExecutionRequests(executionRequests);
|
|
246
248
|
engineRequest = {
|
|
247
|
-
method: "engine_newPayloadV4",
|
|
249
|
+
method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
|
|
248
250
|
params: [
|
|
249
251
|
serializedExecutionPayload,
|
|
250
252
|
serializedVersionedHashes,
|
|
@@ -348,11 +350,13 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
348
350
|
// Once on capella, should this need to be permanently switched to v2 when payload attrs
|
|
349
351
|
// not provided
|
|
350
352
|
const method =
|
|
351
|
-
ForkSeq[fork] >= ForkSeq.
|
|
352
|
-
? "
|
|
353
|
-
: ForkSeq[fork] >= ForkSeq.
|
|
354
|
-
? "
|
|
355
|
-
:
|
|
353
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
354
|
+
? "engine_forkchoiceUpdatedV4"
|
|
355
|
+
: ForkSeq[fork] >= ForkSeq.deneb
|
|
356
|
+
? "engine_forkchoiceUpdatedV3"
|
|
357
|
+
: ForkSeq[fork] >= ForkSeq.capella
|
|
358
|
+
? "engine_forkchoiceUpdatedV2"
|
|
359
|
+
: "engine_forkchoiceUpdatedV1";
|
|
356
360
|
const payloadAttributesRpc = payloadAttributes ? serializePayloadAttributes(payloadAttributes) : undefined;
|
|
357
361
|
// If we are just fcUing and not asking execution for payload, retry is not required
|
|
358
362
|
// and we can move on, as the next fcU will be issued soon on the new slot
|
|
@@ -438,9 +442,12 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
438
442
|
case ForkName.electra:
|
|
439
443
|
method = "engine_getPayloadV4";
|
|
440
444
|
break;
|
|
441
|
-
|
|
445
|
+
case ForkName.fulu:
|
|
442
446
|
method = "engine_getPayloadV5";
|
|
443
447
|
break;
|
|
448
|
+
default:
|
|
449
|
+
method = "engine_getPayloadV6";
|
|
450
|
+
break;
|
|
444
451
|
}
|
|
445
452
|
const payloadResponse = await this.rpc.fetchWithRetries<
|
|
446
453
|
EngineApiRpcReturnTypes[typeof method],
|