@lodestar/beacon-node 1.50.0-dev.335476095e → 1.50.0-dev.3acad901ec
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/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +23 -3
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +26 -18
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +6 -0
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +5 -0
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +13 -23
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.d.ts.map +1 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js +2 -1
- package/lib/chain/archiveStore/utils/pruneHistory.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +7 -3
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/chain.d.ts +3 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +13 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/interface.d.ts +1 -0
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +4 -2
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +30 -8
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/options.d.ts +1 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts +7 -0
- package/lib/chain/seenCache/seenGossipBlockInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenGossipBlockInput.js +21 -0
- package/lib/chain/seenCache/seenGossipBlockInput.js.map +1 -1
- package/lib/chain/shufflingCache.d.ts +1 -0
- package/lib/chain/shufflingCache.d.ts.map +1 -1
- package/lib/chain/shufflingCache.js +4 -0
- package/lib/chain/shufflingCache.js.map +1 -1
- package/lib/db/beacon.d.ts +2 -0
- package/lib/db/beacon.d.ts.map +1 -1
- package/lib/db/beacon.js +3 -0
- package/lib/db/beacon.js.map +1 -1
- package/lib/db/buckets.d.ts +2 -1
- package/lib/db/buckets.d.ts.map +1 -1
- package/lib/db/buckets.js +1 -0
- package/lib/db/buckets.js.map +1 -1
- package/lib/db/interface.d.ts +2 -0
- package/lib/db/interface.d.ts.map +1 -1
- package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
- package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
- package/lib/db/repositories/earliestAvailableSlot.d.ts +11 -0
- package/lib/db/repositories/earliestAvailableSlot.d.ts.map +1 -0
- package/lib/db/repositories/earliestAvailableSlot.js +19 -0
- package/lib/db/repositories/earliestAvailableSlot.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/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +59 -63
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/node/nodejs.d.ts +3 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +2 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +1 -0
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +1 -0
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +5 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +44 -20
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +8 -0
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +32 -9
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/package.json +16 -16
- package/src/api/impl/beacon/state/index.ts +31 -3
- package/src/api/impl/debug/index.ts +28 -19
- package/src/api/impl/validator/index.ts +8 -0
- package/src/chain/archiveStore/archiveStore.ts +18 -24
- package/src/chain/archiveStore/utils/pruneHistory.ts +4 -1
- package/src/chain/blocks/importBlock.ts +9 -3
- package/src/chain/chain.ts +26 -4
- package/src/chain/interface.ts +1 -0
- package/src/chain/opPools/aggregatedAttestationPool.ts +34 -10
- package/src/chain/options.ts +1 -1
- package/src/chain/seenCache/seenGossipBlockInput.ts +23 -0
- package/src/chain/shufflingCache.ts +5 -0
- package/src/db/beacon.ts +3 -0
- package/src/db/buckets.ts +1 -0
- package/src/db/interface.ts +2 -0
- package/src/db/repositories/earliestAvailableSlot.ts +19 -0
- package/src/metrics/metrics/lodestar.ts +4 -0
- package/src/network/processor/gossipHandlers.ts +90 -76
- package/src/node/nodejs.ts +4 -0
- package/src/sync/range/batch.ts +1 -0
- package/src/sync/range/chain.ts +1 -0
- package/src/sync/unknownBlock.ts +57 -21
- package/src/sync/utils/downloadByRange.ts +47 -8
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -73,7 +73,11 @@ type AdvancePendingBlockResult =
|
|
|
73
73
|
| "removed";
|
|
74
74
|
|
|
75
75
|
class UnknownBlockRateLimitedError extends Error {
|
|
76
|
-
constructor(
|
|
76
|
+
constructor(
|
|
77
|
+
message: string,
|
|
78
|
+
/** Earliest backoff expiry seen when the download was deferred, it is retried once that has passed */
|
|
79
|
+
readonly retryAt: number
|
|
80
|
+
) {
|
|
77
81
|
super(message);
|
|
78
82
|
this.name = "UnknownBlockRateLimitedError";
|
|
79
83
|
}
|
|
@@ -849,17 +853,20 @@ export class BlockInputSync {
|
|
|
849
853
|
}
|
|
850
854
|
};
|
|
851
855
|
|
|
852
|
-
|
|
856
|
+
/**
|
|
857
|
+
* Returns false if there is no backoff left to wait for, in which case no retry is scheduled.
|
|
858
|
+
*/
|
|
859
|
+
private scheduleRateLimitBackoffRetry(): boolean {
|
|
853
860
|
this.clearRateLimitBackoffTimer();
|
|
854
861
|
|
|
855
862
|
if (!this.subscribedToNetworkEvents || (this.pendingBlocks.size === 0 && this.pendingPayloads.size === 0)) {
|
|
856
|
-
return;
|
|
863
|
+
return false;
|
|
857
864
|
}
|
|
858
865
|
|
|
859
866
|
const now = Date.now();
|
|
860
867
|
const retryAt = this.peerBalancer.getNextRateLimitRetryAt();
|
|
861
868
|
if (retryAt === null) {
|
|
862
|
-
return;
|
|
869
|
+
return false;
|
|
863
870
|
}
|
|
864
871
|
|
|
865
872
|
this.rateLimitBackoffTimeout = setTimeout(
|
|
@@ -870,6 +877,7 @@ export class BlockInputSync {
|
|
|
870
877
|
},
|
|
871
878
|
Math.max(0, retryAt - now)
|
|
872
879
|
);
|
|
880
|
+
return true;
|
|
873
881
|
}
|
|
874
882
|
|
|
875
883
|
private clearRateLimitBackoffTimer(): void {
|
|
@@ -1081,7 +1089,11 @@ export class BlockInputSync {
|
|
|
1081
1089
|
pendingBlock.status = PendingBlockInputStatus.pending;
|
|
1082
1090
|
}
|
|
1083
1091
|
this.logger.debug("Deferring unknown block download due to peer rate limit", logCtx, res.err);
|
|
1084
|
-
this.scheduleRateLimitBackoffRetry()
|
|
1092
|
+
if (!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now()) {
|
|
1093
|
+
// The backoff this download waited for expired in the meantime, a timer is only armed if another
|
|
1094
|
+
// peer is still in backoff and would retry it later than needed
|
|
1095
|
+
this.triggerUnknownBlockSearch();
|
|
1096
|
+
}
|
|
1085
1097
|
return;
|
|
1086
1098
|
}
|
|
1087
1099
|
|
|
@@ -1364,6 +1376,14 @@ export class BlockInputSync {
|
|
|
1364
1376
|
if (!isPendingPayloadEnvelope(payload)) {
|
|
1365
1377
|
payload.status = PendingPayloadInputStatus.pending;
|
|
1366
1378
|
}
|
|
1379
|
+
if (
|
|
1380
|
+
res.err instanceof UnknownBlockRateLimitedError &&
|
|
1381
|
+
(!this.scheduleRateLimitBackoffRetry() || res.err.retryAt <= Date.now())
|
|
1382
|
+
) {
|
|
1383
|
+
// The backoff this download waited for expired in the meantime, a timer is only armed if another
|
|
1384
|
+
// peer is still in backoff and would retry it later than needed
|
|
1385
|
+
this.triggerUnknownBlockSearch();
|
|
1386
|
+
}
|
|
1367
1387
|
}
|
|
1368
1388
|
|
|
1369
1389
|
private async processPayload(pendingPayload: PendingPayloadInput): Promise<void> {
|
|
@@ -1488,16 +1508,21 @@ export class BlockInputSync {
|
|
|
1488
1508
|
const pendingColumns = payloadInput?.hasAllData()
|
|
1489
1509
|
? new Set<number>()
|
|
1490
1510
|
: new Set(payloadInput?.getMissingSampledColumnMeta().missing ?? []);
|
|
1511
|
+
// Same `now` for both calls, a backoff expiring in between would be reported as no peer available
|
|
1512
|
+
const now = Date.now();
|
|
1491
1513
|
// prefer peers that gossiped this payload root to us (#9923)
|
|
1492
1514
|
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1493
1515
|
pendingColumns,
|
|
1494
1516
|
excludedPeers,
|
|
1495
|
-
cacheItem.peerIdStrings
|
|
1517
|
+
cacheItem.peerIdStrings,
|
|
1518
|
+
now
|
|
1496
1519
|
);
|
|
1497
1520
|
if (peerMeta === null) {
|
|
1498
|
-
|
|
1521
|
+
const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
|
|
1522
|
+
if (retryAt !== null) {
|
|
1499
1523
|
throw new UnknownBlockRateLimitedError(
|
|
1500
|
-
`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited
|
|
1524
|
+
`Error fetching payload by root slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
|
|
1525
|
+
retryAt
|
|
1501
1526
|
);
|
|
1502
1527
|
}
|
|
1503
1528
|
|
|
@@ -1636,9 +1661,11 @@ export class BlockInputSync {
|
|
|
1636
1661
|
);
|
|
1637
1662
|
this.metrics?.blockInputSync.payloadFetchPeers.set({result: FetchResult.FailureMaxAttempts}, i - 1);
|
|
1638
1663
|
|
|
1639
|
-
|
|
1664
|
+
const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
|
|
1665
|
+
if (retryAt !== null) {
|
|
1640
1666
|
throw new UnknownBlockRateLimitedError(
|
|
1641
|
-
`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited
|
|
1667
|
+
`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts: peers are rate-limited`,
|
|
1668
|
+
retryAt
|
|
1642
1669
|
);
|
|
1643
1670
|
}
|
|
1644
1671
|
throw Error(`Error fetching payload with slot=${slot} root=${rootHex} after ${i - 1} attempts.`);
|
|
@@ -1730,16 +1757,21 @@ export class BlockInputSync {
|
|
|
1730
1757
|
isPendingBlockInput(cacheItem) && isBlockInputColumns(cacheItem.blockInput)
|
|
1731
1758
|
? new Set(cacheItem.blockInput.getMissingSampledColumnMeta().missing)
|
|
1732
1759
|
: defaultPendingColumns;
|
|
1760
|
+
// Same `now` for both calls, a backoff expiring in between would be reported as no peer available
|
|
1761
|
+
const now = Date.now();
|
|
1733
1762
|
// prefer peers that gossiped this block root to us (#9923)
|
|
1734
1763
|
const peerMeta = this.peerBalancer.bestPeerForPendingColumns(
|
|
1735
1764
|
pendingColumns,
|
|
1736
1765
|
excludedPeers,
|
|
1737
|
-
cacheItem.peerIdStrings
|
|
1766
|
+
cacheItem.peerIdStrings,
|
|
1767
|
+
now
|
|
1738
1768
|
);
|
|
1739
1769
|
if (peerMeta === null) {
|
|
1740
|
-
|
|
1770
|
+
const retryAt = this.peerBalancer.getNextRateLimitRetryAt(pendingColumns, excludedPeers, now);
|
|
1771
|
+
if (retryAt !== null) {
|
|
1741
1772
|
throw new UnknownBlockRateLimitedError(
|
|
1742
|
-
`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited
|
|
1773
|
+
`Error fetching UnknownBlockRoot slot=${slot} root=${rootHex} after ${i}: peers with needed columns are rate-limited`,
|
|
1774
|
+
retryAt
|
|
1743
1775
|
);
|
|
1744
1776
|
}
|
|
1745
1777
|
|
|
@@ -1845,8 +1877,9 @@ export class BlockInputSync {
|
|
|
1845
1877
|
|
|
1846
1878
|
const message = `Error fetching BlockInput with slot=${slot} root=${rootHex} after ${i - 1} attempts.`;
|
|
1847
1879
|
|
|
1848
|
-
|
|
1849
|
-
|
|
1880
|
+
const retryAt = deferredByRateLimit ? this.peerBalancer.getNextRateLimitRetryAt() : null;
|
|
1881
|
+
if (retryAt !== null) {
|
|
1882
|
+
throw new UnknownBlockRateLimitedError(`${message} Peers are rate-limited.`, retryAt);
|
|
1850
1883
|
}
|
|
1851
1884
|
|
|
1852
1885
|
if (!isPendingBlockInput(cacheItem)) {
|
|
@@ -2037,8 +2070,11 @@ export class UnknownBlockPeerBalancer {
|
|
|
2037
2070
|
this.rateLimitedUntilByPeer.set(peerId, rateLimitedUntilMs);
|
|
2038
2071
|
}
|
|
2039
2072
|
|
|
2040
|
-
getNextRateLimitRetryAt(
|
|
2041
|
-
|
|
2073
|
+
getNextRateLimitRetryAt(
|
|
2074
|
+
pendingColumns?: Set<number>,
|
|
2075
|
+
excludedPeers?: Set<PeerIdStr>,
|
|
2076
|
+
now = Date.now()
|
|
2077
|
+
): number | null {
|
|
2042
2078
|
let retryAt: number | null = null;
|
|
2043
2079
|
|
|
2044
2080
|
for (const [peerId, rateLimitedUntil] of this.rateLimitedUntilByPeer.entries()) {
|
|
@@ -2077,9 +2113,10 @@ export class UnknownBlockPeerBalancer {
|
|
|
2077
2113
|
bestPeerForPendingColumns(
|
|
2078
2114
|
pendingColumns: Set<number>,
|
|
2079
2115
|
excludedPeers: Set<PeerIdStr>,
|
|
2080
|
-
preferredPeers?: Set<PeerIdStr
|
|
2116
|
+
preferredPeers?: Set<PeerIdStr>,
|
|
2117
|
+
now = Date.now()
|
|
2081
2118
|
): PeerSyncMeta | null {
|
|
2082
|
-
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers);
|
|
2119
|
+
const eligiblePeers = this.filterPeers(pendingColumns, excludedPeers, now);
|
|
2083
2120
|
if (eligiblePeers.length === 0) {
|
|
2084
2121
|
return null;
|
|
2085
2122
|
}
|
|
@@ -2120,8 +2157,7 @@ export class UnknownBlockPeerBalancer {
|
|
|
2120
2157
|
return totalActiveRequests;
|
|
2121
2158
|
}
|
|
2122
2159
|
|
|
2123
|
-
private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr
|
|
2124
|
-
const now = Date.now();
|
|
2160
|
+
private filterPeers(pendingDataColumns: Set<number>, excludedPeers: Set<PeerIdStr>, now: number): PeerIdStr[] {
|
|
2125
2161
|
let maxColumnCount = 0;
|
|
2126
2162
|
const considerPeers: {peerId: PeerIdStr; columnCount: number}[] = [];
|
|
2127
2163
|
for (const [peerId, syncMeta] of this.peersMeta.entries()) {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
deneb,
|
|
17
17
|
fulu,
|
|
18
18
|
gloas,
|
|
19
|
+
isGloasBeaconBlock,
|
|
19
20
|
isGloasDataColumnSidecar,
|
|
20
21
|
phase0,
|
|
21
22
|
} from "@lodestar/types";
|
|
@@ -62,6 +63,7 @@ export type DownloadByRangeRequests = {
|
|
|
62
63
|
export type ParentPayloadCommitments = {
|
|
63
64
|
blockRoot: Uint8Array;
|
|
64
65
|
blockRootHex: RootHex;
|
|
66
|
+
blockHash: Uint8Array;
|
|
65
67
|
kzgCommitments: deneb.BlobKzgCommitments;
|
|
66
68
|
};
|
|
67
69
|
|
|
@@ -1171,6 +1173,8 @@ export enum DownloadByRangeErrorCode {
|
|
|
1171
1173
|
|
|
1172
1174
|
/** Envelope beaconBlockRoot does not match the block's root */
|
|
1173
1175
|
INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
|
|
1176
|
+
/** Envelope payload.blockHash does not match the block's bid */
|
|
1177
|
+
INVALID_ENVELOPE_BLOCK_HASH = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BLOCK_HASH",
|
|
1174
1178
|
|
|
1175
1179
|
/** Block segment + envelopes failed chain-segment linearity / FULL-chain checks */
|
|
1176
1180
|
INVALID_CHAIN_SEGMENT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_CHAIN_SEGMENT",
|
|
@@ -1272,6 +1276,12 @@ export type DownloadByRangeErrorType =
|
|
|
1272
1276
|
expected: string;
|
|
1273
1277
|
actual: string;
|
|
1274
1278
|
}
|
|
1279
|
+
| {
|
|
1280
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH;
|
|
1281
|
+
slot: Slot;
|
|
1282
|
+
expected: string;
|
|
1283
|
+
actual: string;
|
|
1284
|
+
}
|
|
1275
1285
|
| {
|
|
1276
1286
|
code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT;
|
|
1277
1287
|
slot: Slot;
|
|
@@ -1295,47 +1305,76 @@ export function validateEnvelopesByRangeResponse(
|
|
|
1295
1305
|
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[],
|
|
1296
1306
|
parentPayloadCommitments?: ParentPayloadCommitments
|
|
1297
1307
|
): Map<Slot, gloas.SignedExecutionPayloadEnvelope> {
|
|
1298
|
-
// Build a map of slot ->
|
|
1299
|
-
const
|
|
1308
|
+
// Build a map of slot -> block root and bid block hash for all blocks in the batch
|
|
1309
|
+
const batchBlocksBySlot = new Map<Slot, {blockRoot: Uint8Array; block: SignedBeaconBlock | null}>();
|
|
1300
1310
|
if (batchBlocks) {
|
|
1301
1311
|
for (const blockInput of batchBlocks) {
|
|
1302
|
-
|
|
1312
|
+
batchBlocksBySlot.set(blockInput.slot, {
|
|
1313
|
+
blockRoot: fromHex(blockInput.blockRootHex),
|
|
1314
|
+
block: blockInput.hasBlock() ? blockInput.getBlock() : null,
|
|
1315
|
+
});
|
|
1303
1316
|
}
|
|
1304
1317
|
}
|
|
1305
1318
|
for (const {block, blockRoot} of validatedBlocks) {
|
|
1306
|
-
|
|
1319
|
+
batchBlocksBySlot.set(block.message.slot, {blockRoot, block});
|
|
1307
1320
|
}
|
|
1308
1321
|
|
|
1309
1322
|
const payloadEnvelopeMap = new Map<Slot, gloas.SignedExecutionPayloadEnvelope>();
|
|
1310
1323
|
|
|
1311
1324
|
for (const payloadEnvelope of payloadEnvelopes) {
|
|
1312
1325
|
const slot = payloadEnvelope.message.payload.slotNumber;
|
|
1313
|
-
const
|
|
1326
|
+
const batchBlock = batchBlocksBySlot.get(slot);
|
|
1314
1327
|
|
|
1315
|
-
if (
|
|
1328
|
+
if (batchBlock === undefined) {
|
|
1316
1329
|
// Keep the requested dangling-parent envelope only when its beaconBlockRoot matches
|
|
1317
1330
|
// exactly. All other unrelated envelopes are dropped.
|
|
1318
1331
|
if (
|
|
1319
1332
|
parentPayloadCommitments !== undefined &&
|
|
1320
1333
|
byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, parentPayloadCommitments.blockRoot)
|
|
1321
1334
|
) {
|
|
1335
|
+
assertEnvelopeBlockHash(payloadEnvelope, parentPayloadCommitments.blockHash);
|
|
1322
1336
|
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1323
1337
|
}
|
|
1324
1338
|
continue;
|
|
1325
1339
|
}
|
|
1326
1340
|
|
|
1327
1341
|
// Verify beaconBlockRoot matches the block's root
|
|
1328
|
-
if (!byteArrayEquals(payloadEnvelope.message.beaconBlockRoot,
|
|
1342
|
+
if (!byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, batchBlock.blockRoot)) {
|
|
1329
1343
|
throw new DownloadByRangeError({
|
|
1330
1344
|
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BEACON_BLOCK_ROOT,
|
|
1331
1345
|
slot,
|
|
1332
|
-
expected: toRootHex(
|
|
1346
|
+
expected: toRootHex(batchBlock.blockRoot),
|
|
1333
1347
|
actual: toRootHex(payloadEnvelope.message.beaconBlockRoot),
|
|
1334
1348
|
});
|
|
1335
1349
|
}
|
|
1336
1350
|
|
|
1351
|
+
if (batchBlock.block !== null && isGloasBeaconBlock(batchBlock.block.message)) {
|
|
1352
|
+
assertEnvelopeBlockHash(
|
|
1353
|
+
payloadEnvelope,
|
|
1354
|
+
batchBlock.block.message.body.signedExecutionPayloadBid.message.blockHash
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1337
1358
|
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1338
1359
|
}
|
|
1339
1360
|
|
|
1340
1361
|
return payloadEnvelopeMap;
|
|
1341
1362
|
}
|
|
1363
|
+
|
|
1364
|
+
/**
|
|
1365
|
+
* Rejected before it is cached. In-batch, `assertLinearChainSegment` fails on a wrong hash without evicting the
|
|
1366
|
+
* envelope. For the dangling parent, the import evicts the parent's entry and range sync cannot recreate it.
|
|
1367
|
+
*/
|
|
1368
|
+
function assertEnvelopeBlockHash(
|
|
1369
|
+
payloadEnvelope: gloas.SignedExecutionPayloadEnvelope,
|
|
1370
|
+
bidBlockHash: Uint8Array
|
|
1371
|
+
): void {
|
|
1372
|
+
if (!byteArrayEquals(payloadEnvelope.message.payload.blockHash, bidBlockHash)) {
|
|
1373
|
+
throw new DownloadByRangeError({
|
|
1374
|
+
code: DownloadByRangeErrorCode.INVALID_ENVELOPE_BLOCK_HASH,
|
|
1375
|
+
slot: payloadEnvelope.message.payload.slotNumber,
|
|
1376
|
+
expected: toRootHex(bidBlockHash),
|
|
1377
|
+
actual: toRootHex(payloadEnvelope.message.payload.blockHash),
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
}
|