@lodestar/beacon-node 1.43.0-dev.07875b3e0c → 1.43.0-dev.1213f9c92d
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 +6 -5
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +66 -1
- 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 +3 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +19 -6
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +43 -19
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +5 -3
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +31 -10
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +4 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -2
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +81 -12
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +5 -3
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +51 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts +5 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +19 -4
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +8 -1
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -0
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +33 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +13 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -0
- package/lib/chain/interface.d.ts +5 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +26 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +20 -11
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +10 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +53 -16
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -0
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -0
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +15 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +25 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +1 -0
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +8 -0
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/validation/proposerPreferences.js +69 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/interface.d.ts +7 -1
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/interface.js +1 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
- package/lib/network/gossip/scoringParameters.js +12 -1
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +8 -0
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +6 -0
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +5 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +14 -16
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +5 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +6 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/node/notifier.js +7 -1
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +84 -33
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +6 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +26 -7
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +17 -6
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/types.d.ts +34 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +34 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +22 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +602 -53
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +46 -10
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +147 -24
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +6 -2
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.js +0 -9
- package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
- package/package.json +16 -15
- package/src/api/impl/beacon/blocks/index.ts +8 -5
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +80 -0
- package/src/chain/blocks/importBlock.ts +3 -2
- package/src/chain/blocks/importExecutionPayload.ts +57 -21
- package/src/chain/blocks/index.ts +54 -14
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +5 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
- package/src/chain/blocks/types.ts +2 -2
- package/src/chain/blocks/utils/chainSegment.ts +106 -17
- package/src/chain/blocks/verifyBlock.ts +68 -9
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +28 -3
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +39 -0
- package/src/chain/interface.ts +9 -1
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +23 -11
- package/src/chain/produceBlock/produceBlockBody.ts +62 -15
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +13 -3
- package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
- package/src/chain/validation/block.ts +1 -0
- package/src/chain/validation/proposerPreferences.ts +91 -0
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/gossip/interface.ts +6 -0
- package/src/network/gossip/scoringParameters.ts +14 -1
- package/src/network/gossip/topic.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +23 -17
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
- package/src/node/notifier.ts +8 -1
- package/src/sync/range/batch.ts +142 -38
- package/src/sync/range/chain.ts +37 -9
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +760 -57
- package/src/sync/utils/downloadByRange.ts +256 -39
- package/src/sync/utils/downloadByRoot.ts +12 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
|
@@ -1,29 +1,118 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
2
|
+
import {ProtoBlock} from "@lodestar/fork-choice";
|
|
3
|
+
import {Slot, isGloasBeaconBlock, ssz} from "@lodestar/types";
|
|
4
|
+
import {toRootHex} from "@lodestar/utils";
|
|
3
5
|
import {BlockError, BlockErrorCode} from "../../errors/index.js";
|
|
4
6
|
import {IBlockInput} from "../blockInput/types.js";
|
|
7
|
+
import {PayloadEnvelopeInput} from "../payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
8
|
+
|
|
9
|
+
export type OrphanedPayloadEnvelope = {
|
|
10
|
+
slot: Slot;
|
|
11
|
+
payloadEnvelopeInput: PayloadEnvelopeInput;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ChainSegmentResult = {warnings: OrphanedPayloadEnvelope[] | null};
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
|
-
* Assert this chain segment of blocks is linear with slot numbers and hashes
|
|
17
|
+
* Assert this chain segment of blocks is linear with slot numbers and hashes,
|
|
18
|
+
* and that the provided envelopes are consistent with their respective blocks.
|
|
19
|
+
*
|
|
20
|
+
* Must be called after verifyBlocksSanityChecks so that parentBlock (from forkchoice)
|
|
21
|
+
* is available to seed the execution hash chain.
|
|
22
|
+
*
|
|
23
|
+
* For each block:
|
|
24
|
+
* - Verifies parent root + slot linearity
|
|
25
|
+
* - For gloas: verifies bid.parentBlockHash matches the tracked execution hash; if not, the
|
|
26
|
+
* previous FULL envelope is treated as orphaned (segment continues as if previous slot was EMPTY)
|
|
27
|
+
* - If an envelope exists for this slot: verifies it references this block's root
|
|
28
|
+
* - Advances the tracked execution hash (FULL if envelope present, EMPTY if not)
|
|
8
29
|
*/
|
|
30
|
+
export function assertLinearChainSegment(
|
|
31
|
+
config: ChainForkConfig,
|
|
32
|
+
blocks: IBlockInput[],
|
|
33
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
34
|
+
parentBlock: ProtoBlock
|
|
35
|
+
): ChainSegmentResult {
|
|
36
|
+
const warnings: OrphanedPayloadEnvelope[] = [];
|
|
9
37
|
|
|
10
|
-
|
|
11
|
-
|
|
38
|
+
// Track the expected execution payload block hash through the segment.
|
|
39
|
+
// Starts from the known forkchoice parent's execution hash.
|
|
40
|
+
// - FULL variant (envelope present for slot): advances to envelope.payload.blockHash
|
|
41
|
+
// - EMPTY variant (no envelope for slot): execution hash is unchanged
|
|
42
|
+
// null only for pre-merge parents, which cannot precede gloas blocks.
|
|
43
|
+
let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
|
|
44
|
+
// Track the execution hash before the last FULL advancement so we can recover
|
|
45
|
+
// if the next block reveals that envelope was orphaned.
|
|
46
|
+
let prevExecHash: string | null = currentExecHash;
|
|
47
|
+
// The slot whose envelope last advanced currentExecHash (for warning context).
|
|
48
|
+
let lastFullSlot: Slot | null = null;
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
12
51
|
const block = blocks[i].getBlock();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
const slot = block.message.slot;
|
|
53
|
+
|
|
54
|
+
if (i > 0) {
|
|
55
|
+
const prevBlock = blocks[i - 1].getBlock();
|
|
56
|
+
// Ensure parent root matches the previous block's root
|
|
57
|
+
if (
|
|
58
|
+
!ssz.Root.equals(
|
|
59
|
+
config.getForkTypes(prevBlock.message.slot).BeaconBlock.hashTreeRoot(prevBlock.message),
|
|
60
|
+
block.message.parentRoot
|
|
61
|
+
)
|
|
62
|
+
) {
|
|
63
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS});
|
|
64
|
+
}
|
|
65
|
+
// Ensure slots are strictly increasing
|
|
66
|
+
if (slot <= prevBlock.message.slot) {
|
|
67
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_SLOTS});
|
|
68
|
+
}
|
|
23
69
|
}
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
70
|
+
|
|
71
|
+
if (isGloasBeaconBlock(block.message) && currentExecHash !== null) {
|
|
72
|
+
// Verify the bid's parentBlockHash matches the tracked execution hash.
|
|
73
|
+
// This ensures the block was built on the correct FULL or EMPTY variant of its parent.
|
|
74
|
+
const bidParentHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
75
|
+
if (bidParentHash !== currentExecHash) {
|
|
76
|
+
// Maybe the previous slot's FULL envelope was orphaned — try falling back.
|
|
77
|
+
// If even prevExecHash doesn't match, the segment is non-linear.
|
|
78
|
+
if (bidParentHash !== prevExecHash) {
|
|
79
|
+
throw new BlockError(block, {
|
|
80
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
81
|
+
parentRoot: toRootHex(block.message.parentRoot),
|
|
82
|
+
parentBlockHash: bidParentHash,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (lastFullSlot !== null && payloadEnvelopes !== null) {
|
|
86
|
+
const orphanedInput = payloadEnvelopes.get(lastFullSlot);
|
|
87
|
+
if (orphanedInput != null) {
|
|
88
|
+
warnings.push({slot: lastFullSlot, payloadEnvelopeInput: orphanedInput});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
currentExecHash = prevExecHash;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const payloadInput = payloadEnvelopes?.get(slot) ?? null;
|
|
95
|
+
const payloadEnvelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : null;
|
|
96
|
+
if (payloadEnvelope !== null) {
|
|
97
|
+
// Verify the envelope references this block's root
|
|
98
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message));
|
|
99
|
+
const envelopeBlockRoot = toRootHex(payloadEnvelope.message.beaconBlockRoot);
|
|
100
|
+
if (blockRoot !== envelopeBlockRoot) {
|
|
101
|
+
throw new BlockError(block, {
|
|
102
|
+
code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH,
|
|
103
|
+
envelopeBlockRoot,
|
|
104
|
+
blockRoot,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// FULL variant: save state before advancing, then advance
|
|
109
|
+
prevExecHash = currentExecHash;
|
|
110
|
+
lastFullSlot = slot;
|
|
111
|
+
currentExecHash = toRootHex(payloadEnvelope.message.payload.blockHash);
|
|
112
|
+
}
|
|
113
|
+
// EMPTY variant: currentExecHash unchanged
|
|
27
114
|
}
|
|
28
115
|
}
|
|
116
|
+
|
|
117
|
+
return {warnings: warnings.length > 0 ? warnings : null};
|
|
29
118
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
-
import {ForkName, isForkPostFulu} from "@lodestar/params";
|
|
2
|
+
import {ForkName, ForkSeq, isForkPostFulu} from "@lodestar/params";
|
|
3
3
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
4
|
-
import {IndexedAttestation, deneb} from "@lodestar/types";
|
|
4
|
+
import {IndexedAttestation, Slot, deneb} from "@lodestar/types";
|
|
5
|
+
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
5
6
|
import type {BeaconChain} from "../chain.js";
|
|
6
7
|
import {BlockError, BlockErrorCode} from "../errors/index.js";
|
|
7
8
|
import {BlockProcessOpts} from "../options.js";
|
|
8
9
|
import {RegenCaller} from "../regen/index.js";
|
|
9
10
|
import {DAType, IBlockInput} from "./blockInput/index.js";
|
|
11
|
+
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
10
12
|
import {ImportBlockOpts} from "./types.js";
|
|
11
13
|
import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js";
|
|
12
14
|
import {ELECTRA_GIRAFFE_BANNER} from "./utils/giraffeBanner.js";
|
|
@@ -16,6 +18,7 @@ import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js";
|
|
|
16
18
|
import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js";
|
|
17
19
|
import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js";
|
|
18
20
|
import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js";
|
|
21
|
+
import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* Verifies 1 or more blocks are fully valid; from a linear sequence of blocks.
|
|
@@ -32,12 +35,14 @@ export async function verifyBlocksInEpoch(
|
|
|
32
35
|
this: BeaconChain,
|
|
33
36
|
parentBlock: ProtoBlock,
|
|
34
37
|
blockInputs: IBlockInput[],
|
|
38
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
35
39
|
opts: BlockProcessOpts & ImportBlockOpts
|
|
36
40
|
): Promise<{
|
|
37
41
|
postStates: IBeaconStateView[];
|
|
38
42
|
proposerBalanceDeltas: number[];
|
|
39
43
|
segmentExecStatus: SegmentExecStatus;
|
|
40
|
-
|
|
44
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
45
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
41
46
|
indexedAttestationsByBlock: IndexedAttestation[][];
|
|
42
47
|
}> {
|
|
43
48
|
const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
|
|
@@ -110,17 +115,59 @@ export async function verifyBlocksInEpoch(
|
|
|
110
115
|
});
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
// Pick the data-availability source by fork:
|
|
119
|
+
// - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
|
|
120
|
+
// - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
|
|
121
|
+
const daAvailabilityPromise: Promise<{
|
|
122
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
123
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
124
|
+
availableTime: number;
|
|
125
|
+
}> =
|
|
126
|
+
fork >= ForkSeq.gloas
|
|
127
|
+
? (async () => {
|
|
128
|
+
const payloadInputsForDa: PayloadEnvelopeInput[] = [];
|
|
129
|
+
for (const input of blockInputs) {
|
|
130
|
+
const pi = payloadEnvelopes?.get(input.slot);
|
|
131
|
+
if (pi !== undefined) payloadInputsForDa.push(pi);
|
|
132
|
+
}
|
|
133
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
|
|
134
|
+
payloadInputsForDa,
|
|
135
|
+
abortController.signal
|
|
136
|
+
);
|
|
137
|
+
const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
|
|
138
|
+
for (let i = 0; i < payloadInputsForDa.length; i++) {
|
|
139
|
+
payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
// post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
|
|
143
|
+
blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
|
|
144
|
+
payloadDAStatuses,
|
|
145
|
+
availableTime,
|
|
146
|
+
};
|
|
147
|
+
})()
|
|
148
|
+
: (async () => {
|
|
149
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
|
|
150
|
+
blockInputs,
|
|
151
|
+
abortController.signal
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
blockDAStatuses: dataAvailabilityStatuses,
|
|
155
|
+
payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
|
|
156
|
+
availableTime,
|
|
157
|
+
};
|
|
158
|
+
})();
|
|
159
|
+
|
|
113
160
|
// batch all I/O operations to reduce overhead
|
|
114
161
|
const [
|
|
115
162
|
segmentExecStatus,
|
|
116
|
-
{
|
|
163
|
+
{blockDAStatuses, payloadDAStatuses, availableTime},
|
|
117
164
|
{postStates, proposerBalanceDeltas, verifyStateTime},
|
|
118
165
|
{verifySignaturesTime},
|
|
119
166
|
] = await Promise.all([
|
|
120
167
|
verifyExecutionPayloadsPromise,
|
|
121
168
|
|
|
122
|
-
// data availability
|
|
123
|
-
|
|
169
|
+
// data availability (fork-specific; see daAvailabilityPromise above)
|
|
170
|
+
daAvailabilityPromise,
|
|
124
171
|
|
|
125
172
|
// Run state transition only
|
|
126
173
|
// TODO: Ensure it yields to allow flushing to workers and engine API
|
|
@@ -149,6 +196,9 @@ export async function verifyBlocksInEpoch(
|
|
|
149
196
|
opts
|
|
150
197
|
)
|
|
151
198
|
: Promise.resolve({verifySignaturesTime: Date.now()}),
|
|
199
|
+
|
|
200
|
+
// TODO GLOAS: can verify payload signatures in batch too
|
|
201
|
+
// maybe chain with the above verifyBlocksSignatures()
|
|
152
202
|
]);
|
|
153
203
|
|
|
154
204
|
if (opts.verifyOnly !== true) {
|
|
@@ -200,7 +250,9 @@ export async function verifyBlocksInEpoch(
|
|
|
200
250
|
blockInputs.length === 1 &&
|
|
201
251
|
// gossip blocks have seenTimestampSec
|
|
202
252
|
opts.seenTimestampSec !== undefined &&
|
|
253
|
+
// PreData (pre-deneb) and NoData (gloas) carry no blob data on the block — skip metric
|
|
203
254
|
blockInputs[0].type !== DAType.PreData &&
|
|
255
|
+
blockInputs[0].type !== DAType.NoData &&
|
|
204
256
|
executionStatuses[0] === ExecutionStatus.Valid
|
|
205
257
|
) {
|
|
206
258
|
// Find the max time when the block was actually verified
|
|
@@ -209,8 +261,8 @@ export async function verifyBlocksInEpoch(
|
|
|
209
261
|
this.metrics?.gossipBlock.receivedToFullyVerifiedTime.observe(recvTofullyVerifedTime);
|
|
210
262
|
|
|
211
263
|
const verifiedToBlobsAvailabiltyTime = Math.max(availableTime - fullyVerifiedTime, 0) / 1000;
|
|
212
|
-
const block = blockInputs[0].getBlock()
|
|
213
|
-
const numBlobs = block.
|
|
264
|
+
const block = blockInputs[0].getBlock();
|
|
265
|
+
const numBlobs = getBlobKzgCommitments(blockInputs[0].forkName, block as deneb.SignedBeaconBlock).length;
|
|
214
266
|
|
|
215
267
|
this.metrics?.gossipBlock.verifiedToBlobsAvailabiltyTime.observe({numBlobs}, verifiedToBlobsAvailabiltyTime);
|
|
216
268
|
this.logger.verbose("Verified blockInput fully with blobs availability", {
|
|
@@ -229,7 +281,14 @@ export async function verifyBlocksInEpoch(
|
|
|
229
281
|
);
|
|
230
282
|
}
|
|
231
283
|
|
|
232
|
-
return {
|
|
284
|
+
return {
|
|
285
|
+
postStates,
|
|
286
|
+
blockDAStatuses,
|
|
287
|
+
payloadDAStatuses,
|
|
288
|
+
proposerBalanceDeltas,
|
|
289
|
+
segmentExecStatus,
|
|
290
|
+
indexedAttestationsByBlock,
|
|
291
|
+
};
|
|
233
292
|
} finally {
|
|
234
293
|
abortController.abort();
|
|
235
294
|
}
|
|
@@ -90,15 +90,24 @@ export function verifyBlocksSanityChecks(
|
|
|
90
90
|
} else {
|
|
91
91
|
// When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice.
|
|
92
92
|
const parentRoot = toRootHex(block.message.parentRoot);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parentRoot,
|
|
96
|
-
toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
97
|
-
)
|
|
98
|
-
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
99
|
-
if (!parentBlock) {
|
|
93
|
+
const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
94
|
+
if (!parentBlockDefaultStatus) {
|
|
100
95
|
throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
|
|
101
96
|
}
|
|
97
|
+
|
|
98
|
+
parentBlock = parentBlockDefaultStatus;
|
|
99
|
+
if (isGloasBeaconBlock(block.message)) {
|
|
100
|
+
const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
101
|
+
const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
|
|
102
|
+
if (!parentBlockWithPayload) {
|
|
103
|
+
throw new BlockError(block, {
|
|
104
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
105
|
+
parentRoot,
|
|
106
|
+
parentBlockHash,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
parentBlock = parentBlockWithPayload;
|
|
110
|
+
}
|
|
102
111
|
// Parent is known to the fork-choice
|
|
103
112
|
parentBlockSlot = parentBlock.slot;
|
|
104
113
|
}
|
|
@@ -32,8 +32,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
32
32
|
const payload = envelope.payload;
|
|
33
33
|
|
|
34
34
|
// Verify consistency with the beacon block.
|
|
35
|
-
// Compute header root on a
|
|
36
|
-
const headerValue =
|
|
35
|
+
// Compute header root on a clone of latestBlockHeader to avoid mutating state.
|
|
36
|
+
const headerValue = ssz.phase0.BeaconBlockHeader.clone(state.latestBlockHeader);
|
|
37
37
|
if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
|
|
38
38
|
headerValue.stateRoot = state.hashTreeRoot();
|
|
39
39
|
}
|
|
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
|
|
|
28
28
|
await Promise.all(promises);
|
|
29
29
|
|
|
30
30
|
const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
|
|
31
|
-
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
|
|
32
|
-
payloadInput.
|
|
31
|
+
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
|
|
32
|
+
if (payloadInput.daOutOfRange) {
|
|
33
|
+
return DataAvailabilityStatus.OutOfRange;
|
|
34
|
+
}
|
|
35
|
+
return payloadInput.getBlobKzgCommitments().length === 0
|
|
33
36
|
? DataAvailabilityStatus.NotRequired
|
|
34
|
-
: DataAvailabilityStatus.Available
|
|
35
|
-
);
|
|
37
|
+
: DataAvailabilityStatus.Available;
|
|
38
|
+
});
|
|
36
39
|
|
|
37
40
|
return {dataAvailabilityStatuses, availableTime};
|
|
38
41
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
ValidatorIndex,
|
|
40
40
|
Wei,
|
|
41
41
|
deneb,
|
|
42
|
+
electra,
|
|
42
43
|
gloas,
|
|
43
44
|
isBlindedBeaconBlock,
|
|
44
45
|
phase0,
|
|
@@ -105,6 +106,7 @@ import {
|
|
|
105
106
|
SeenExecutionPayloadBids,
|
|
106
107
|
SeenPayloadAttesters,
|
|
107
108
|
SeenPayloadEnvelopeInput,
|
|
109
|
+
SeenProposerPreferences,
|
|
108
110
|
SeenSyncCommitteeMessages,
|
|
109
111
|
} from "./seenCache/index.js";
|
|
110
112
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -185,6 +187,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
185
187
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
186
188
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
187
189
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
190
|
+
readonly seenProposerPreferences = new SeenProposerPreferences();
|
|
188
191
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
189
192
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
190
193
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -333,6 +336,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
333
336
|
logger,
|
|
334
337
|
});
|
|
335
338
|
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
339
|
+
config,
|
|
340
|
+
clock,
|
|
336
341
|
chainEvents: emitter,
|
|
337
342
|
signal,
|
|
338
343
|
serializedCache: this.serializedCache,
|
|
@@ -886,6 +891,21 @@ export class BeaconChain implements IBeaconChain {
|
|
|
886
891
|
);
|
|
887
892
|
}
|
|
888
893
|
|
|
894
|
+
async getParentExecutionRequests(
|
|
895
|
+
parentBlockSlot: Slot,
|
|
896
|
+
parentBlockRootHex: RootHex
|
|
897
|
+
): Promise<electra.ExecutionRequests> {
|
|
898
|
+
// at the fork boundary, parent is pre-gloas
|
|
899
|
+
if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
|
|
900
|
+
return ssz.electra.ExecutionRequests.defaultValue();
|
|
901
|
+
}
|
|
902
|
+
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
903
|
+
if (envelope === null) {
|
|
904
|
+
throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
|
|
905
|
+
}
|
|
906
|
+
return envelope.message.executionRequests;
|
|
907
|
+
}
|
|
908
|
+
|
|
889
909
|
async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
|
|
890
910
|
const fork = this.config.getForkName(blockSlot);
|
|
891
911
|
|
|
@@ -1082,11 +1102,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1082
1102
|
}
|
|
1083
1103
|
|
|
1084
1104
|
async processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void> {
|
|
1085
|
-
return this.blockProcessor.processBlocksJob([block], opts);
|
|
1105
|
+
return this.blockProcessor.processBlocksJob([block], null, opts);
|
|
1086
1106
|
}
|
|
1087
1107
|
|
|
1088
|
-
async processChainSegment(
|
|
1089
|
-
|
|
1108
|
+
async processChainSegment(
|
|
1109
|
+
blocks: IBlockInput[],
|
|
1110
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
1111
|
+
opts?: ImportBlockOpts
|
|
1112
|
+
): Promise<void> {
|
|
1113
|
+
await this.blockProcessor.processBlocksJob(blocks, payloadEnvelopes, opts);
|
|
1090
1114
|
}
|
|
1091
1115
|
|
|
1092
1116
|
async processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void> {
|
|
@@ -1417,6 +1441,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1417
1441
|
this.payloadAttestationPool.prune(slot);
|
|
1418
1442
|
this.executionPayloadBidPool.prune(slot);
|
|
1419
1443
|
this.seenExecutionPayloadBids.prune(slot);
|
|
1444
|
+
this.seenProposerPreferences.prune(slot);
|
|
1420
1445
|
this.seenAttestationDatas.onSlot(slot);
|
|
1421
1446
|
this.reprocessController.onSlot(slot);
|
|
1422
1447
|
|
package/src/chain/emitter.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {routes} from "@lodestar/api";
|
|
|
4
4
|
import {CheckpointWithHex} from "@lodestar/fork-choice";
|
|
5
5
|
import {IBeaconStateView} from "@lodestar/state-transition";
|
|
6
6
|
import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
7
|
-
import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
|
|
8
7
|
import {PeerIdStr} from "../util/peerId.js";
|
|
9
8
|
import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
|
|
10
9
|
import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
@@ -60,10 +59,6 @@ export enum ChainEvent {
|
|
|
60
59
|
* Post-gloas, missing parent could be a SignedBeaconBlock and/or a SignedExecutionPayloadEnvelope
|
|
61
60
|
*/
|
|
62
61
|
blockUnknownParent = "blockUnknownParent",
|
|
63
|
-
/**
|
|
64
|
-
* Trigger BlockInputSync to find a SignedBeaconBlock given a SignedExecutionPayloadEnvelop received
|
|
65
|
-
*/
|
|
66
|
-
envelopeUnknownBlock = "envelopeUnknownBlock",
|
|
67
62
|
/**
|
|
68
63
|
* Trigger BlockInputSync to find a SignedBeaconBlock with specified block root.
|
|
69
64
|
*/
|
|
@@ -92,11 +87,6 @@ type ApiEvents = {[K in routes.events.EventType]: (data: routes.events.EventData
|
|
|
92
87
|
|
|
93
88
|
export type ChainEventData = {
|
|
94
89
|
[ChainEvent.blockUnknownParent]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
95
|
-
[ChainEvent.envelopeUnknownBlock]: {
|
|
96
|
-
envelope: SignedExecutionPayloadEnvelope;
|
|
97
|
-
peer?: PeerIdStr;
|
|
98
|
-
source: BlockInputSource;
|
|
99
|
-
};
|
|
100
90
|
[ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
101
91
|
[ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
102
92
|
[ChainEvent.incompletePayloadEnvelope]: {
|
|
@@ -124,7 +114,6 @@ export type IChainEvents = ApiEvents & {
|
|
|
124
114
|
// Sync events that are chain->chain. Initiated from network requests but do not cross the network
|
|
125
115
|
// barrier so are considered ChainEvent(s).
|
|
126
116
|
[ChainEvent.blockUnknownParent]: (data: ChainEventData[ChainEvent.blockUnknownParent]) => void;
|
|
127
|
-
[ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
|
|
128
117
|
[ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
|
|
129
118
|
[ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
|
|
130
119
|
[ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
|
|
@@ -74,6 +74,8 @@ export enum BlockErrorCode {
|
|
|
74
74
|
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
|
|
75
75
|
/** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
|
|
76
76
|
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
|
|
77
|
+
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
|
|
78
|
+
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
type ExecutionErrorStatus = Exclude<
|
|
@@ -107,6 +109,7 @@ export type BlockErrorType =
|
|
|
107
109
|
| {code: BlockErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
108
110
|
| {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS}
|
|
109
111
|
| {code: BlockErrorCode.NON_LINEAR_SLOTS}
|
|
112
|
+
| {code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH; envelopeBlockRoot: RootHex; blockRoot: RootHex}
|
|
110
113
|
| {code: BlockErrorCode.PER_BLOCK_PROCESSING_ERROR; error: Error}
|
|
111
114
|
| {code: BlockErrorCode.BEACON_CHAIN_ERROR; error: Error}
|
|
112
115
|
| {code: BlockErrorCode.KNOWN_BAD_BLOCK}
|
|
@@ -120,7 +123,7 @@ export type BlockErrorType =
|
|
|
120
123
|
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
|
|
121
124
|
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
|
|
122
125
|
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
|
|
123
|
-
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentBlockHash: RootHex};
|
|
126
|
+
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
|
|
124
127
|
|
|
125
128
|
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
|
|
126
129
|
|
|
@@ -8,6 +8,7 @@ export * from "./executionPayloadBid.js";
|
|
|
8
8
|
export * from "./executionPayloadEnvelope.js";
|
|
9
9
|
export * from "./gossipValidation.js";
|
|
10
10
|
export * from "./payloadAttestation.js";
|
|
11
|
+
export * from "./proposerPreferences.js";
|
|
11
12
|
export * from "./proposerSlashingError.js";
|
|
12
13
|
export * from "./syncCommitteeError.js";
|
|
13
14
|
export * from "./voluntaryExitError.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
+
import {GossipActionError} from "./gossipValidation.js";
|
|
3
|
+
|
|
4
|
+
export enum ProposerPreferencesErrorCode {
|
|
5
|
+
INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH",
|
|
6
|
+
PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED",
|
|
7
|
+
INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER",
|
|
8
|
+
ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN",
|
|
9
|
+
INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ProposerPreferencesErrorType =
|
|
13
|
+
| {
|
|
14
|
+
code: ProposerPreferencesErrorCode.INVALID_EPOCH;
|
|
15
|
+
proposalSlot: Slot;
|
|
16
|
+
currentEpoch: number;
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED;
|
|
20
|
+
proposalSlot: Slot;
|
|
21
|
+
currentSlot: Slot;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
code: ProposerPreferencesErrorCode.INVALID_PROPOSER;
|
|
25
|
+
proposalSlot: Slot;
|
|
26
|
+
validatorIndex: ValidatorIndex;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN;
|
|
30
|
+
proposalSlot: Slot;
|
|
31
|
+
validatorIndex: ValidatorIndex;
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
code: ProposerPreferencesErrorCode.INVALID_SIGNATURE;
|
|
35
|
+
proposalSlot: Slot;
|
|
36
|
+
validatorIndex: ValidatorIndex;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class ProposerPreferencesError extends GossipActionError<ProposerPreferencesErrorType> {}
|
package/src/chain/interface.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
altair,
|
|
19
19
|
capella,
|
|
20
20
|
deneb,
|
|
21
|
+
electra,
|
|
21
22
|
gloas,
|
|
22
23
|
phase0,
|
|
23
24
|
rewards,
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
SeenContributionAndProof,
|
|
61
62
|
SeenExecutionPayloadBids,
|
|
62
63
|
SeenPayloadAttesters,
|
|
64
|
+
SeenProposerPreferences,
|
|
63
65
|
SeenSyncCommitteeMessages,
|
|
64
66
|
} from "./seenCache/index.js";
|
|
65
67
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -130,6 +132,7 @@ export interface IBeaconChain {
|
|
|
130
132
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
131
133
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
132
134
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
135
|
+
readonly seenProposerPreferences: SeenProposerPreferences;
|
|
133
136
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
134
137
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
135
138
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -231,6 +234,7 @@ export interface IBeaconChain {
|
|
|
231
234
|
blockSlot: Slot,
|
|
232
235
|
blockRootHex: string
|
|
233
236
|
): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
|
|
237
|
+
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<electra.ExecutionRequests>;
|
|
234
238
|
|
|
235
239
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
236
240
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|
|
@@ -248,7 +252,11 @@ export interface IBeaconChain {
|
|
|
248
252
|
/** Process a block until complete */
|
|
249
253
|
processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void>;
|
|
250
254
|
/** Process a chain of blocks until complete */
|
|
251
|
-
processChainSegment(
|
|
255
|
+
processChainSegment(
|
|
256
|
+
blocks: IBlockInput[],
|
|
257
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
258
|
+
opts?: ImportBlockOpts
|
|
259
|
+
): Promise<void>;
|
|
252
260
|
|
|
253
261
|
/** Process execution payload envelope: verify, import to fork choice, and persist to DB */
|
|
254
262
|
processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Signature, aggregateSignatures} from "@chainsafe/blst";
|
|
2
2
|
import {BitArray} from "@chainsafe/ssz";
|
|
3
3
|
import {ChainForkConfig} from "@lodestar/config";
|
|
4
|
-
import {MAX_COMMITTEES_PER_SLOT, PTC_SIZE} from "@lodestar/params";
|
|
4
|
+
import {MAX_COMMITTEES_PER_SLOT, MAX_PAYLOAD_ATTESTATIONS, PTC_SIZE} from "@lodestar/params";
|
|
5
5
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
6
6
|
import {MapDef, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {Metrics} from "../../metrics/metrics.js";
|
|
@@ -95,13 +95,9 @@ export class PayloadAttestationPool {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Get payload attestations to be included in a block.
|
|
98
|
-
* Pick the top `
|
|
98
|
+
* Pick the top `MAX_PAYLOAD_ATTESTATIONS` aggregates with the most votes.
|
|
99
99
|
*/
|
|
100
|
-
getPayloadAttestationsForBlock(
|
|
101
|
-
beaconBlockRoot: BlockRootHex,
|
|
102
|
-
slot: Slot,
|
|
103
|
-
maxAttestation: number
|
|
104
|
-
): gloas.PayloadAttestation[] {
|
|
100
|
+
getPayloadAttestationsForBlock(beaconBlockRoot: BlockRootHex, slot: Slot): gloas.PayloadAttestation[] {
|
|
105
101
|
const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
|
|
106
102
|
|
|
107
103
|
if (!aggregateByDataRootByBlockRoot) {
|
|
@@ -119,7 +115,32 @@ export class PayloadAttestationPool {
|
|
|
119
115
|
return Array.from(aggregateByDataRoot.values())
|
|
120
116
|
.slice()
|
|
121
117
|
.sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
|
|
122
|
-
.slice(0,
|
|
118
|
+
.slice(0, MAX_PAYLOAD_ATTESTATIONS)
|
|
119
|
+
.map(fastToPayloadAttestation);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getAll(slot?: Slot): gloas.PayloadAttestation[] {
|
|
123
|
+
const aggregates: AggregateFast[] = [];
|
|
124
|
+
|
|
125
|
+
const addAggregates = (aggregateByDataRootByBlockRoot: Map<BlockRootHex, Map<DataRootHex, AggregateFast>>) => {
|
|
126
|
+
for (const aggregateByDataRoot of aggregateByDataRootByBlockRoot.values()) {
|
|
127
|
+
aggregates.push(...aggregateByDataRoot.values());
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (slot !== undefined) {
|
|
132
|
+
const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
|
|
133
|
+
if (aggregateByDataRootByBlockRoot) {
|
|
134
|
+
addAggregates(aggregateByDataRootByBlockRoot);
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
for (const aggregateByDataRootByBlockRoot of this.aggregateByDataRootByBlockRootBySlot.values()) {
|
|
138
|
+
addAggregates(aggregateByDataRootByBlockRoot);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return aggregates
|
|
143
|
+
.sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
|
|
123
144
|
.map(fastToPayloadAttestation);
|
|
124
145
|
}
|
|
125
146
|
|