@lodestar/beacon-node 1.44.0-dev.1f0e8b5a5f → 1.44.0-dev.2defe36dd3
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/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +1 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +5 -7
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +1 -1
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +1 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.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/validation/executionPayloadBid.js +22 -5
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +1 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/pool/index.ts +1 -0
- package/src/api/impl/validator/index.ts +5 -7
- package/src/chain/blocks/importBlock.ts +1 -0
- package/src/chain/blocks/importExecutionPayload.ts +1 -1
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +1 -1
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/validation/executionPayloadBid.ts +23 -5
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/network/processor/gossipHandlers.ts +1 -0
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.44.0-dev.
|
|
14
|
+
"version": "1.44.0-dev.2defe36dd3",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -135,17 +135,17 @@
|
|
|
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.44.0-dev.
|
|
139
|
-
"@lodestar/config": "^1.44.0-dev.
|
|
140
|
-
"@lodestar/db": "^1.44.0-dev.
|
|
141
|
-
"@lodestar/fork-choice": "^1.44.0-dev.
|
|
142
|
-
"@lodestar/logger": "^1.44.0-dev.
|
|
143
|
-
"@lodestar/params": "^1.44.0-dev.
|
|
144
|
-
"@lodestar/reqresp": "^1.44.0-dev.
|
|
145
|
-
"@lodestar/state-transition": "^1.44.0-dev.
|
|
146
|
-
"@lodestar/types": "^1.44.0-dev.
|
|
147
|
-
"@lodestar/utils": "^1.44.0-dev.
|
|
148
|
-
"@lodestar/validator": "^1.44.0-dev.
|
|
138
|
+
"@lodestar/api": "^1.44.0-dev.2defe36dd3",
|
|
139
|
+
"@lodestar/config": "^1.44.0-dev.2defe36dd3",
|
|
140
|
+
"@lodestar/db": "^1.44.0-dev.2defe36dd3",
|
|
141
|
+
"@lodestar/fork-choice": "^1.44.0-dev.2defe36dd3",
|
|
142
|
+
"@lodestar/logger": "^1.44.0-dev.2defe36dd3",
|
|
143
|
+
"@lodestar/params": "^1.44.0-dev.2defe36dd3",
|
|
144
|
+
"@lodestar/reqresp": "^1.44.0-dev.2defe36dd3",
|
|
145
|
+
"@lodestar/state-transition": "^1.44.0-dev.2defe36dd3",
|
|
146
|
+
"@lodestar/types": "^1.44.0-dev.2defe36dd3",
|
|
147
|
+
"@lodestar/utils": "^1.44.0-dev.2defe36dd3",
|
|
148
|
+
"@lodestar/validator": "^1.44.0-dev.2defe36dd3",
|
|
149
149
|
"@multiformats/multiaddr": "^13.0.1",
|
|
150
150
|
"datastore-core": "^11.0.2",
|
|
151
151
|
"datastore-fs": "^11.0.2",
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"@libp2p/interface-internal": "^3.0.13",
|
|
169
169
|
"@libp2p/logger": "^6.2.2",
|
|
170
170
|
"@libp2p/utils": "^7.0.13",
|
|
171
|
-
"@lodestar/spec-test-util": "^1.44.0-dev.
|
|
171
|
+
"@lodestar/spec-test-util": "^1.44.0-dev.2defe36dd3",
|
|
172
172
|
"@types/js-yaml": "^4.0.5",
|
|
173
173
|
"@types/qs": "^6.9.7",
|
|
174
174
|
"@types/tmp": "^0.2.3",
|
|
@@ -186,5 +186,5 @@
|
|
|
186
186
|
"beacon",
|
|
187
187
|
"blockchain"
|
|
188
188
|
],
|
|
189
|
-
"gitHead": "
|
|
189
|
+
"gitHead": "14e0154c3e2b6c61607a1840c888b70493dbfae9"
|
|
190
190
|
}
|
|
@@ -328,6 +328,7 @@ export function getBeaconPoolApi({
|
|
|
328
328
|
|
|
329
329
|
chain.forkChoice.notifyPtcMessages(
|
|
330
330
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
331
|
+
payloadAttestationMessage.data.slot,
|
|
331
332
|
validatorCommitteeIndices,
|
|
332
333
|
payloadAttestationMessage.data.payloadPresent,
|
|
333
334
|
payloadAttestationMessage.data.blobDataAvailable
|
|
@@ -1112,23 +1112,21 @@ export function getValidatorApi(
|
|
|
1112
1112
|
notWhileSyncing();
|
|
1113
1113
|
await waitForSlot(slot);
|
|
1114
1114
|
|
|
1115
|
-
const block = chain.forkChoice.
|
|
1115
|
+
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1116
1116
|
if (!block) {
|
|
1117
|
-
|
|
1117
|
+
// No block is seen at slot. Return 404 so vc can skip casting payload attestation.
|
|
1118
|
+
throw new ApiError(404, `No canonical block found at slot=${slot}`);
|
|
1118
1119
|
}
|
|
1119
1120
|
|
|
1120
|
-
const blockIsForSlot = block.slot === slot;
|
|
1121
1121
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1122
1122
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
1123
1123
|
// into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
|
|
1124
1124
|
// the input's creation time.
|
|
1125
1125
|
const payloadDueSec = config.getPayloadDueMs() / 1000;
|
|
1126
1126
|
const payloadPresent =
|
|
1127
|
-
|
|
1128
|
-
payloadInput !== undefined &&
|
|
1129
|
-
payloadInput.hasPayloadEnvelope() &&
|
|
1127
|
+
payloadInput?.hasPayloadEnvelope() === true &&
|
|
1130
1128
|
chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec) < payloadDueSec;
|
|
1131
|
-
const blobDataAvailable =
|
|
1129
|
+
const blobDataAvailable = payloadInput?.hasAllData() === true;
|
|
1132
1130
|
|
|
1133
1131
|
return {
|
|
1134
1132
|
data: {
|
|
@@ -263,6 +263,7 @@ export async function importBlock(
|
|
|
263
263
|
if (ptcIndices.length > 0) {
|
|
264
264
|
this.forkChoice.notifyPtcMessages(
|
|
265
265
|
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
266
|
+
payloadAttestation.data.slot,
|
|
266
267
|
ptcIndices,
|
|
267
268
|
payloadAttestation.data.payloadPresent,
|
|
268
269
|
payloadAttestation.data.blobDataAvailable
|
|
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
|
|
|
129
129
|
|
|
130
130
|
// 3. Regenerate state for envelope verification
|
|
131
131
|
const blockState = await this.regen
|
|
132
|
-
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.
|
|
132
|
+
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
|
|
133
133
|
.catch(() =>
|
|
134
134
|
// only happen at the 1st batch of skipped slot checkpoint sync
|
|
135
135
|
this.regen.getClosestHeadState(protoBlock)
|
|
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
|
|
|
5
5
|
NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
|
|
6
6
|
PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
|
|
7
7
|
UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
8
|
+
INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
|
|
8
9
|
INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
|
|
9
10
|
INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
|
|
10
11
|
INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
|
|
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
|
|
|
18
19
|
blockRoot: RootHex;
|
|
19
20
|
}
|
|
20
21
|
| {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
|
|
22
|
+
| {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
|
|
21
23
|
| {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
|
|
22
24
|
| {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
|
|
23
25
|
| {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};
|
|
@@ -170,7 +170,7 @@ export class PrepareNextSlotScheduler {
|
|
|
170
170
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
171
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
172
172
|
// Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
|
|
173
|
-
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead)) {
|
|
173
|
+
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
|
|
174
174
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
175
175
|
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
176
176
|
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
@@ -278,7 +278,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
278
278
|
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
279
279
|
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority
|
|
280
280
|
// signalled the blob data is not available, forcing a build on EMPTY (reorg).
|
|
281
|
-
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock);
|
|
281
|
+
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
|
|
282
282
|
if (isBuildingOnFull) {
|
|
283
283
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
284
284
|
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
@@ -17,10 +17,12 @@ export enum RegenCaller {
|
|
|
17
17
|
predictProposerHead = "predictProposerHead",
|
|
18
18
|
produceAttestationData = "produceAttestationData",
|
|
19
19
|
processBlocksInEpoch = "processBlocksInEpoch",
|
|
20
|
+
importExecutionPayload = "importExecutionPayload",
|
|
20
21
|
validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
|
|
21
22
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
23
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
24
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
25
|
+
validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
|
|
24
26
|
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
25
27
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
26
28
|
restApi = "restApi",
|
|
@@ -35,10 +35,6 @@ async function validateExecutionPayloadBid(
|
|
|
35
35
|
const bid = signedExecutionPayloadBid.message;
|
|
36
36
|
const parentBlockRootHex = toRootHex(bid.parentBlockRoot);
|
|
37
37
|
const parentBlockHashHex = toRootHex(bid.parentBlockHash);
|
|
38
|
-
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.validateGossipExecutionPayloadBid);
|
|
39
|
-
if (!isStatePostGloas(state)) {
|
|
40
|
-
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
41
|
-
}
|
|
42
38
|
|
|
43
39
|
// [IGNORE] `bid.slot` is the current slot or the next slot.
|
|
44
40
|
const currentSlot = chain.clock.currentSlot;
|
|
@@ -111,9 +107,31 @@ async function validateExecutionPayloadBid(
|
|
|
111
107
|
});
|
|
112
108
|
}
|
|
113
109
|
|
|
110
|
+
// Use the bid's parent branch state for builder checks
|
|
111
|
+
const state = await chain.regen
|
|
112
|
+
.getBlockSlotState(parentBlock, bid.slot, {dontTransferCache: true}, RegenCaller.validateGossipExecutionPayloadBid)
|
|
113
|
+
.catch(() => {
|
|
114
|
+
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
115
|
+
code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
116
|
+
parentBlockRoot: parentBlockRootHex,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!isStatePostGloas(state)) {
|
|
121
|
+
throw new Error(`Expected gloas+ state for execution payload bid validation, got fork=${state.forkName}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
114
124
|
// [REJECT] `bid.builder_index` is a valid/active builder index -- i.e.
|
|
115
125
|
// `is_active_builder(state, bid.builder_index)` returns `True`.
|
|
116
|
-
|
|
126
|
+
let builder: gloas.Builder;
|
|
127
|
+
try {
|
|
128
|
+
builder = state.getBuilder(bid.builderIndex);
|
|
129
|
+
} catch {
|
|
130
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
131
|
+
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
132
|
+
builderIndex: bid.builderIndex,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
117
135
|
if (!isActiveBuilder(builder, state.finalizedCheckpoint.epoch)) {
|
|
118
136
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
119
137
|
code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE,
|
|
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
8
8
|
import {toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
11
12
|
|
|
12
13
|
export type PayloadAttestationValidationResult = {
|
|
13
14
|
attDataRootHex: RootHex;
|
|
@@ -61,22 +62,43 @@ async function validatePayloadAttestationMessage(
|
|
|
61
62
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
62
63
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
63
64
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
64
|
-
|
|
65
|
+
const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
|
|
66
|
+
if (!block) {
|
|
65
67
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
66
68
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
67
69
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
// [IGNORE] The block referenced by `data.beacon_block_root` is at slot `data.slot`,
|
|
74
|
+
// i.e. the block has `block.slot == data.slot`.
|
|
75
|
+
if (block.slot !== data.slot) {
|
|
76
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
77
|
+
code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT,
|
|
78
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
79
|
+
blockSlot: block.slot,
|
|
80
|
+
slot: data.slot,
|
|
81
|
+
});
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
// [REJECT] The message's block `data.beacon_block_root` passes validation.
|
|
77
85
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
78
86
|
// it is possible that the block didn't pass the validation
|
|
79
87
|
|
|
88
|
+
// Use the referenced block's branch state for the PTC committee check
|
|
89
|
+
const state = await chain.regen
|
|
90
|
+
.getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
|
|
91
|
+
.catch(() => {
|
|
92
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
93
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
94
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!isStatePostGloas(state)) {
|
|
99
|
+
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
80
102
|
// [REJECT] The message's validator index is within the payload committee in
|
|
81
103
|
// `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
|
|
82
104
|
// processing the block up to the current slot as determined by the fork choice.
|
|
@@ -1214,6 +1214,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1214
1214
|
}
|
|
1215
1215
|
chain.forkChoice.notifyPtcMessages(
|
|
1216
1216
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1217
|
+
payloadAttestationMessage.data.slot,
|
|
1217
1218
|
validationResult.validatorCommitteeIndices,
|
|
1218
1219
|
payloadAttestationMessage.data.payloadPresent,
|
|
1219
1220
|
payloadAttestationMessage.data.blobDataAvailable
|