@lodestar/beacon-node 1.45.0-dev.77f7452c30 → 1.45.0-dev.7acc183c2b
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 +3 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/index.d.ts +1 -1
- package/lib/api/impl/beacon/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/index.js.map +1 -1
- package/lib/api/impl/beacon/state/index.d.ts +1 -1
- package/lib/api/impl/beacon/state/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/index.js +2 -2
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +2 -1
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +9 -1
- package/lib/api/impl/beacon/state/utils.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +2 -2
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +2 -2
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/proof/index.d.ts +1 -1
- package/lib/api/impl/proof/index.d.ts.map +1 -1
- package/lib/api/impl/proof/index.js +2 -2
- package/lib/api/impl/proof/index.js.map +1 -1
- package/lib/api/impl/utils.d.ts +23 -0
- package/lib/api/impl/utils.d.ts.map +1 -1
- package/lib/api/impl/utils.js +44 -1
- package/lib/api/impl/utils.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts +0 -14
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +21 -56
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/rest/base.d.ts.map +1 -1
- package/lib/api/rest/base.js +8 -0
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +13 -5
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +1 -1
- 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/builderCircuitBreaker.d.ts +31 -0
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -0
- package/lib/chain/builderCircuitBreaker.js +59 -0
- package/lib/chain/builderCircuitBreaker.js.map +1 -0
- package/lib/chain/chain.d.ts +2 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +3 -0
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +29 -5
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +19 -4
- package/lib/chain/errors/blockError.js.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.d.ts +11 -0
- package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadEnvelope.js +2 -0
- package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -0
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/lightClient/index.d.ts.map +1 -1
- package/lib/chain/lightClient/index.js +11 -6
- package/lib/chain/lightClient/index.js.map +1 -1
- package/lib/chain/lightClient/proofs.d.ts.map +1 -1
- package/lib/chain/lightClient/proofs.js +28 -2
- package/lib/chain/lightClient/proofs.js.map +1 -1
- package/lib/chain/lightClient/types.d.ts +3 -1
- package/lib/chain/lightClient/types.d.ts.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/options.d.ts +4 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +12 -7
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +55 -4
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +30 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.d.ts.map +1 -1
- package/lib/chain/validation/signatureSets/aggregateAndProof.js +2 -3
- package/lib/chain/validation/signatureSets/aggregateAndProof.js.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.d.ts.map +1 -1
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js +42 -7
- package/lib/db/repositories/lightclientSyncCommitteeWitness.js.map +1 -1
- package/lib/execution/builder/http.d.ts +18 -0
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +20 -11
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +6 -0
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +18 -0
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/network/gossip/encoding.d.ts +2 -2
- package/lib/network/gossip/encoding.d.ts.map +1 -1
- package/lib/network/gossip/encoding.js +15 -9
- package/lib/network/gossip/encoding.js.map +1 -1
- package/lib/network/gossip/gossipsub.d.ts.map +1 -1
- package/lib/network/gossip/gossipsub.js +2 -2
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +74 -231
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +24 -1
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +6 -5
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/sync/unknownBlock.js +1 -1
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/beacon/index.ts +1 -1
- package/src/api/impl/beacon/state/index.ts +3 -2
- package/src/api/impl/beacon/state/utils.ts +11 -0
- package/src/api/impl/debug/index.ts +3 -2
- package/src/api/impl/lodestar/index.ts +2 -2
- package/src/api/impl/proof/index.ts +2 -2
- package/src/api/impl/utils.ts +51 -1
- package/src/api/impl/validator/index.ts +21 -62
- package/src/api/rest/base.ts +9 -0
- package/src/chain/blocks/utils/chainSegment.ts +16 -5
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +1 -1
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/builderCircuitBreaker.ts +84 -0
- package/src/chain/chain.ts +7 -0
- package/src/chain/errors/blockError.ts +23 -6
- package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
- package/src/chain/interface.ts +2 -0
- package/src/chain/lightClient/index.ts +12 -7
- package/src/chain/lightClient/proofs.ts +36 -1
- package/src/chain/lightClient/types.ts +3 -1
- package/src/chain/opPools/aggregatedAttestationPool.ts +1 -1
- package/src/chain/options.ts +4 -0
- package/src/chain/prepareNextSlot.ts +11 -7
- package/src/chain/validation/block.ts +72 -4
- package/src/chain/validation/executionPayloadEnvelope.ts +39 -0
- package/src/chain/validation/signatureSets/aggregateAndProof.ts +3 -3
- package/src/db/repositories/lightclientSyncCommitteeWitness.ts +57 -9
- package/src/execution/builder/http.ts +30 -17
- package/src/metrics/metrics/beacon.ts +19 -0
- package/src/network/gossip/encoding.ts +13 -7
- package/src/network/gossip/gossipsub.ts +7 -2
- package/src/network/gossip/topic.ts +31 -0
- package/src/network/processor/gossipHandlers.ts +6 -5
- package/src/sync/unknownBlock.ts +1 -1
|
@@ -82,7 +82,6 @@ import {ZERO_HASH} from "../../../constants/index.js";
|
|
|
82
82
|
import {BuilderStatus, NoBidReceived} from "../../../execution/builder/http.js";
|
|
83
83
|
import {validateGossipFnRetryUnknownRoot} from "../../../network/processor/gossipHandlers.js";
|
|
84
84
|
import {CommitteeSubscription} from "../../../network/subnets/index.js";
|
|
85
|
-
import {SyncState} from "../../../sync/index.js";
|
|
86
85
|
import {callInNextEventLoop} from "../../../util/eventLoop.js";
|
|
87
86
|
import {isOptimisticBlock} from "../../../util/forkChoice.js";
|
|
88
87
|
import {getBlockGraffiti, toGraffitiBytes} from "../../../util/graffiti.js";
|
|
@@ -91,23 +90,9 @@ import {ApiOptions} from "../../options.js";
|
|
|
91
90
|
import {getStateResponseWithRegen} from "../beacon/state/utils.js";
|
|
92
91
|
import {ApiError, FailureList, IndexedError, NodeIsSyncing, OnlySupportedByDVT} from "../errors.js";
|
|
93
92
|
import {ApiModules} from "../types.js";
|
|
93
|
+
import {notWhileSyncing} from "../utils.js";
|
|
94
94
|
import {computeSubnetForCommitteesAtSlot, getPubkeysForIndices, selectBlockProductionSource} from "./utils.js";
|
|
95
95
|
|
|
96
|
-
/**
|
|
97
|
-
* If the node is within this many epochs from the head, we declare it to be synced regardless of
|
|
98
|
-
* the network sync state.
|
|
99
|
-
*
|
|
100
|
-
* This helps prevent attacks where nodes can convince us that we're syncing some non-existent
|
|
101
|
-
* finalized head.
|
|
102
|
-
*
|
|
103
|
-
* TODO: Lighthouse uses 8 for the attack described above. However, 8 kills Lodestar since validators
|
|
104
|
-
* can trigger regen to fast-forward head state 8 epochs to be immediately invalidated as sync sets
|
|
105
|
-
* a new head. Then the checkpoint state cache grows unbounded with very different states (because
|
|
106
|
-
* they are 8 epochs apart) and causes an OOM. Research a proper solution once regen and the state
|
|
107
|
-
* caches are better.
|
|
108
|
-
*/
|
|
109
|
-
export const SYNC_TOLERANCE_EPOCHS = 1;
|
|
110
|
-
|
|
111
96
|
/**
|
|
112
97
|
* Cutoff time to wait from start of the slot for execution and builder block production apis to resolve.
|
|
113
98
|
* Post this time, race execution and builder to pick whatever resolves first.
|
|
@@ -341,36 +326,6 @@ export function getValidatorApi(
|
|
|
341
326
|
return null;
|
|
342
327
|
}
|
|
343
328
|
|
|
344
|
-
/**
|
|
345
|
-
* Reject any request while the node is syncing
|
|
346
|
-
*/
|
|
347
|
-
function notWhileSyncing(): void {
|
|
348
|
-
// Consider node synced before or close to genesis
|
|
349
|
-
if (chain.clock.currentSlot < SLOTS_PER_EPOCH) {
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const syncState = sync.state;
|
|
354
|
-
switch (syncState) {
|
|
355
|
-
case SyncState.SyncingFinalized:
|
|
356
|
-
case SyncState.SyncingHead: {
|
|
357
|
-
const currentSlot = chain.clock.currentSlot;
|
|
358
|
-
const headSlot = chain.forkChoice.getHead().slot;
|
|
359
|
-
if (currentSlot - headSlot > SYNC_TOLERANCE_EPOCHS * SLOTS_PER_EPOCH) {
|
|
360
|
-
throw new NodeIsSyncing(`headSlot ${headSlot} currentSlot ${currentSlot}`);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
case SyncState.Synced:
|
|
367
|
-
return;
|
|
368
|
-
|
|
369
|
-
case SyncState.Stalled:
|
|
370
|
-
throw new NodeIsSyncing("waiting for peers");
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
329
|
/**
|
|
375
330
|
* Post merge, the CL and EL could be out of step in the sync, and could result in
|
|
376
331
|
* Syncing status of the chain head. To be precise:
|
|
@@ -573,7 +528,7 @@ export function getValidatorApi(
|
|
|
573
528
|
builderBoostFactor?: bigint,
|
|
574
529
|
{feeRecipient, builderSelection, strictFeeRecipientCheck}: routes.validator.ExtraProduceBlockOpts = {}
|
|
575
530
|
): Promise<ProduceBlindedBlockOrBlockContentsRes> {
|
|
576
|
-
notWhileSyncing();
|
|
531
|
+
notWhileSyncing(chain, sync.state);
|
|
577
532
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
578
533
|
|
|
579
534
|
const parentBlock = chain.getProposerHead(slot);
|
|
@@ -917,7 +872,7 @@ export function getValidatorApi(
|
|
|
917
872
|
throw new ApiError(400, `produceBlockV4 not supported for pre-gloas fork=${fork}`);
|
|
918
873
|
}
|
|
919
874
|
|
|
920
|
-
notWhileSyncing();
|
|
875
|
+
notWhileSyncing(chain, sync.state);
|
|
921
876
|
await waitForSlot(slot);
|
|
922
877
|
|
|
923
878
|
const parentBlock = chain.getProposerHead(slot);
|
|
@@ -938,7 +893,10 @@ export function getValidatorApi(
|
|
|
938
893
|
// support when it is implemented.
|
|
939
894
|
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
940
895
|
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
941
|
-
const
|
|
896
|
+
const circuitBreakerActive = chain.builderCircuitBreaker.isActive(slot);
|
|
897
|
+
const builderBid = circuitBreakerActive
|
|
898
|
+
? null
|
|
899
|
+
: chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
942
900
|
|
|
943
901
|
const logCtx = {
|
|
944
902
|
slot,
|
|
@@ -946,6 +904,7 @@ export function getValidatorApi(
|
|
|
946
904
|
parentBlockRoot: parentBlockRootHex,
|
|
947
905
|
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
948
906
|
fork,
|
|
907
|
+
circuitBreakerActive,
|
|
949
908
|
...(builderBid !== null
|
|
950
909
|
? {
|
|
951
910
|
bidValue: builderBid.message.value,
|
|
@@ -1035,7 +994,7 @@ export function getValidatorApi(
|
|
|
1035
994
|
},
|
|
1036
995
|
|
|
1037
996
|
async produceAttestationData({committeeIndex, slot}) {
|
|
1038
|
-
notWhileSyncing();
|
|
997
|
+
notWhileSyncing(chain, sync.state);
|
|
1039
998
|
|
|
1040
999
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1041
1000
|
|
|
@@ -1116,7 +1075,7 @@ export function getValidatorApi(
|
|
|
1116
1075
|
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
1117
1076
|
}
|
|
1118
1077
|
|
|
1119
|
-
notWhileSyncing();
|
|
1078
|
+
notWhileSyncing(chain, sync.state);
|
|
1120
1079
|
await waitForSlot(slot);
|
|
1121
1080
|
|
|
1122
1081
|
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
@@ -1201,7 +1160,7 @@ export function getValidatorApi(
|
|
|
1201
1160
|
},
|
|
1202
1161
|
|
|
1203
1162
|
async getProposerDuties({epoch}, _context, opts?: {v2?: boolean}) {
|
|
1204
|
-
notWhileSyncing();
|
|
1163
|
+
notWhileSyncing(chain, sync.state);
|
|
1205
1164
|
|
|
1206
1165
|
const currentEpoch = currentEpochWithDisparity();
|
|
1207
1166
|
const nextEpoch = currentEpoch + 1;
|
|
@@ -1248,7 +1207,7 @@ export function getValidatorApi(
|
|
|
1248
1207
|
// requested epoch is within that range, we can use the head state at current epoch
|
|
1249
1208
|
state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
1250
1209
|
} else {
|
|
1251
|
-
const res = await getStateResponseWithRegen(chain, startSlot);
|
|
1210
|
+
const res = await getStateResponseWithRegen(chain, sync, startSlot);
|
|
1252
1211
|
|
|
1253
1212
|
state = res.state instanceof Uint8Array ? chain.getHeadState().loadOtherState(res.state) : res.state;
|
|
1254
1213
|
|
|
@@ -1342,7 +1301,7 @@ export function getValidatorApi(
|
|
|
1342
1301
|
},
|
|
1343
1302
|
|
|
1344
1303
|
async getAttesterDuties({epoch, indices}) {
|
|
1345
|
-
notWhileSyncing();
|
|
1304
|
+
notWhileSyncing(chain, sync.state);
|
|
1346
1305
|
|
|
1347
1306
|
if (indices.length === 0) {
|
|
1348
1307
|
throw new ApiError(400, "No validator to get attester duties");
|
|
@@ -1402,7 +1361,7 @@ export function getValidatorApi(
|
|
|
1402
1361
|
},
|
|
1403
1362
|
|
|
1404
1363
|
async getPtcDuties({epoch, indices}) {
|
|
1405
|
-
notWhileSyncing();
|
|
1364
|
+
notWhileSyncing(chain, sync.state);
|
|
1406
1365
|
|
|
1407
1366
|
if (indices.length === 0) {
|
|
1408
1367
|
throw new ApiError(400, "No validator to get PTC duties");
|
|
@@ -1464,7 +1423,7 @@ export function getValidatorApi(
|
|
|
1464
1423
|
* @param validatorIndices an array of the validator indices for which to obtain the duties.
|
|
1465
1424
|
*/
|
|
1466
1425
|
async getSyncCommitteeDuties({epoch, indices}) {
|
|
1467
|
-
notWhileSyncing();
|
|
1426
|
+
notWhileSyncing(chain, sync.state);
|
|
1468
1427
|
|
|
1469
1428
|
if (indices.length === 0) {
|
|
1470
1429
|
throw new ApiError(400, "No validator to get attester duties");
|
|
@@ -1511,7 +1470,7 @@ export function getValidatorApi(
|
|
|
1511
1470
|
},
|
|
1512
1471
|
|
|
1513
1472
|
async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) {
|
|
1514
|
-
notWhileSyncing();
|
|
1473
|
+
notWhileSyncing(chain, sync.state);
|
|
1515
1474
|
|
|
1516
1475
|
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1517
1476
|
|
|
@@ -1534,7 +1493,7 @@ export function getValidatorApi(
|
|
|
1534
1493
|
},
|
|
1535
1494
|
|
|
1536
1495
|
async publishAggregateAndProofsV2({signedAggregateAndProofs}) {
|
|
1537
|
-
notWhileSyncing();
|
|
1496
|
+
notWhileSyncing(chain, sync.state);
|
|
1538
1497
|
|
|
1539
1498
|
const seenTimestampSec = Date.now() / 1000;
|
|
1540
1499
|
const failures: FailureList = [];
|
|
@@ -1595,7 +1554,7 @@ export function getValidatorApi(
|
|
|
1595
1554
|
* https://github.com/ethereum/beacon-APIs/pull/137
|
|
1596
1555
|
*/
|
|
1597
1556
|
async publishContributionAndProofs({contributionAndProofs}) {
|
|
1598
|
-
notWhileSyncing();
|
|
1557
|
+
notWhileSyncing(chain, sync.state);
|
|
1599
1558
|
|
|
1600
1559
|
const failures: FailureList = [];
|
|
1601
1560
|
|
|
@@ -1644,7 +1603,7 @@ export function getValidatorApi(
|
|
|
1644
1603
|
},
|
|
1645
1604
|
|
|
1646
1605
|
async prepareBeaconCommitteeSubnet({subscriptions}) {
|
|
1647
|
-
notWhileSyncing();
|
|
1606
|
+
notWhileSyncing(chain, sync.state);
|
|
1648
1607
|
|
|
1649
1608
|
await network.prepareBeaconCommitteeSubnets(
|
|
1650
1609
|
subscriptions.map(({validatorIndex, slot, isAggregator, committeesAtSlot, committeeIndex}) => ({
|
|
@@ -1677,7 +1636,7 @@ export function getValidatorApi(
|
|
|
1677
1636
|
* https://github.com/ethereum/beacon-APIs/pull/136
|
|
1678
1637
|
*/
|
|
1679
1638
|
async prepareSyncCommitteeSubnets({subscriptions}) {
|
|
1680
|
-
notWhileSyncing();
|
|
1639
|
+
notWhileSyncing(chain, sync.state);
|
|
1681
1640
|
|
|
1682
1641
|
// A `validatorIndex` can be in multiple subnets, so compute the CommitteeSubscription with double for loop
|
|
1683
1642
|
const subs: CommitteeSubscription[] = [];
|
|
@@ -1821,7 +1780,7 @@ export function getValidatorApi(
|
|
|
1821
1780
|
throw new ApiError(400, `getExecutionPayloadEnvelope not supported for pre-gloas fork=${fork}`);
|
|
1822
1781
|
}
|
|
1823
1782
|
|
|
1824
|
-
notWhileSyncing();
|
|
1783
|
+
notWhileSyncing(chain, sync.state);
|
|
1825
1784
|
await waitForSlot(slot);
|
|
1826
1785
|
|
|
1827
1786
|
const blockRootHex = toRootHex(beaconBlockRoot);
|
package/src/api/rest/base.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {fastifyCors} from "@fastify/cors";
|
|
|
3
3
|
import {FastifyError, FastifyInstance, FastifyRequest, errorCodes, fastify} from "fastify";
|
|
4
4
|
import {parse as parseQueryString} from "qs";
|
|
5
5
|
import {addSszContentTypeParser} from "@lodestar/api/server";
|
|
6
|
+
import {NUMBER_OF_COLUMNS} from "@lodestar/params";
|
|
6
7
|
import {ErrorAborted, Gauge, Histogram, Logger} from "@lodestar/utils";
|
|
7
8
|
import {isLocalhostIP} from "../../util/ip.js";
|
|
8
9
|
import {ApiError, FailureList, IndexedError, NodeIsSyncing} from "../impl/errors.js";
|
|
@@ -55,6 +56,13 @@ const INVALID_MEDIA_TYPE_CODE = errorCodes.FST_ERR_CTP_INVALID_MEDIA_TYPE().code
|
|
|
55
56
|
*/
|
|
56
57
|
const SCHEMA_VALIDATION_ERROR_CODE = errorCodes.FST_ERR_VALIDATION().code;
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Cap for array query params, set to the largest array any beacon-API query can carry:
|
|
61
|
+
* a full data-column custody set (`getDebugDataColumnSidecars` `indices`). `qs` turns
|
|
62
|
+
* longer arrays into an object, which then fails schema validation.
|
|
63
|
+
*/
|
|
64
|
+
const QUERY_STRING_ARRAY_LIMIT = NUMBER_OF_COLUMNS;
|
|
65
|
+
|
|
58
66
|
/**
|
|
59
67
|
* REST API powered by `fastify` server.
|
|
60
68
|
*/
|
|
@@ -82,6 +90,7 @@ export class RestApiServer {
|
|
|
82
90
|
// be OpenAPI spec compliant and results are inconsistent, see https://github.com/ljharb/qs/issues/331.
|
|
83
91
|
// The schema validation will catch this and throw an error as parsed query string results in an object.
|
|
84
92
|
parseArrays: false,
|
|
93
|
+
arrayLimit: QUERY_STRING_ARRAY_LIMIT,
|
|
85
94
|
}),
|
|
86
95
|
},
|
|
87
96
|
bodyLimit: opts.bodyLimit,
|
|
@@ -86,11 +86,22 @@ export function assertLinearChainSegment(
|
|
|
86
86
|
// Maybe the previous slot's FULL envelope was orphaned — try falling back.
|
|
87
87
|
// If even prevExecHash doesn't match, the segment is non-linear.
|
|
88
88
|
if (bidParentHash !== prevExecHash) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
// i === 0 compares against the seeded fork-choice parent (segment boundary); i > 0
|
|
90
|
+
// compares against the in-segment predecessor's payload (broken link inside the segment).
|
|
91
|
+
throw new BlockError(
|
|
92
|
+
block,
|
|
93
|
+
i === 0
|
|
94
|
+
? {
|
|
95
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
96
|
+
parentRoot: toRootHex(block.message.parentRoot),
|
|
97
|
+
parentBlockHash: bidParentHash,
|
|
98
|
+
}
|
|
99
|
+
: {
|
|
100
|
+
code: BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS,
|
|
101
|
+
parentBlockHash: bidParentHash,
|
|
102
|
+
expectedBlockHash: currentExecHash,
|
|
103
|
+
}
|
|
104
|
+
);
|
|
94
105
|
}
|
|
95
106
|
if (lastFullSlot !== null && payloadEnvelopes !== null) {
|
|
96
107
|
const orphanedInput = payloadEnvelopes.get(lastFullSlot);
|
|
@@ -94,7 +94,7 @@ export function verifyBlocksSanityChecks(
|
|
|
94
94
|
const parentRoot = toRootHex(block.message.parentRoot);
|
|
95
95
|
const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
96
96
|
if (!parentBlockDefaultStatus) {
|
|
97
|
-
throw new BlockError(block, {code: BlockErrorCode.
|
|
97
|
+
throw new BlockError(block, {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN, parentRoot});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
parentBlock = parentBlockDefaultStatus;
|
|
@@ -100,8 +100,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// Verify consistency with expected withdrawals
|
|
103
|
-
const payloadWithdrawalsRoot = ssz.
|
|
104
|
-
const expectedWithdrawalsRoot = ssz.
|
|
103
|
+
const payloadWithdrawalsRoot = ssz.gloas.Withdrawals.hashTreeRoot(payload.withdrawals);
|
|
104
|
+
const expectedWithdrawalsRoot = ssz.gloas.Withdrawals.hashTreeRoot(state.payloadExpectedWithdrawals);
|
|
105
105
|
if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
|
|
106
106
|
throw new Error(
|
|
107
107
|
`Withdrawals mismatch between payload and expected payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {IForkChoice} from "@lodestar/fork-choice";
|
|
2
|
+
import {Slot} from "@lodestar/types";
|
|
3
|
+
import {Logger} from "@lodestar/utils";
|
|
4
|
+
import {getFaultInspectionParams} from "../execution/builder/http.js";
|
|
5
|
+
import {Metrics} from "../metrics/index.js";
|
|
6
|
+
|
|
7
|
+
export type BuilderCircuitBreakerOpts = {
|
|
8
|
+
faultInspectionWindow?: number;
|
|
9
|
+
allowedFaults?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type BuilderCircuitBreakerModules = {
|
|
13
|
+
forkChoice: IForkChoice;
|
|
14
|
+
logger: Logger;
|
|
15
|
+
metrics: Metrics | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Post-gloas circuit breaker for builder bids. The beacon block is produced by the proposer
|
|
20
|
+
* regardless of bid source, so missed blocks are not a useful builder health signal. Instead
|
|
21
|
+
* count blocks whose payload was never revealed and stop selecting builder bids while the
|
|
22
|
+
* non-reveal rate in the fault inspection window is too high.
|
|
23
|
+
*/
|
|
24
|
+
export class BuilderCircuitBreaker {
|
|
25
|
+
readonly faultInspectionWindow: number;
|
|
26
|
+
readonly allowedFaults: number;
|
|
27
|
+
|
|
28
|
+
private active = false;
|
|
29
|
+
private lastUpdatedSlot = -1;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
opts: BuilderCircuitBreakerOpts,
|
|
33
|
+
private readonly modules: BuilderCircuitBreakerModules
|
|
34
|
+
) {
|
|
35
|
+
const {faultInspectionWindow, allowedFaults} = getFaultInspectionParams(opts);
|
|
36
|
+
this.faultInspectionWindow = faultInspectionWindow;
|
|
37
|
+
this.allowedFaults = allowedFaults;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Whether builder bids must be ignored for a block produced at clockSlot */
|
|
41
|
+
isActive(clockSlot: Slot): boolean {
|
|
42
|
+
this.update(clockSlot);
|
|
43
|
+
return this.active;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
update(clockSlot: Slot): void {
|
|
47
|
+
if (clockSlot <= this.lastUpdatedSlot) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this.lastUpdatedSlot = clockSlot;
|
|
51
|
+
|
|
52
|
+
// Exclude clockSlot itself, its payload reveal may still be in flight
|
|
53
|
+
const {blocksPresent, payloadsRevealed} = this.modules.forkChoice.getPayloadRevealCounts(
|
|
54
|
+
Math.max(clockSlot - this.faultInspectionWindow, 0),
|
|
55
|
+
clockSlot - 1
|
|
56
|
+
);
|
|
57
|
+
const faults = blocksPresent - payloadsRevealed;
|
|
58
|
+
|
|
59
|
+
const wasActive = this.active;
|
|
60
|
+
// Scale the fault budget by blocks present so sparse windows still trigger on high non-reveal rates
|
|
61
|
+
this.active = faults * this.faultInspectionWindow > this.allowedFaults * blocksPresent;
|
|
62
|
+
|
|
63
|
+
this.modules.metrics?.builderCircuitBreaker.active.set(this.active ? 1 : 0);
|
|
64
|
+
this.modules.metrics?.builderCircuitBreaker.faults.set(faults);
|
|
65
|
+
this.modules.metrics?.builderCircuitBreaker.blocksPresent.set(blocksPresent);
|
|
66
|
+
this.modules.metrics?.builderCircuitBreaker.payloadsRevealed.set(payloadsRevealed);
|
|
67
|
+
|
|
68
|
+
const logCtx = {
|
|
69
|
+
blocksPresent,
|
|
70
|
+
faults,
|
|
71
|
+
faultInspectionWindow: this.faultInspectionWindow,
|
|
72
|
+
allowedFaults: this.allowedFaults,
|
|
73
|
+
};
|
|
74
|
+
if (this.active !== wasActive) {
|
|
75
|
+
if (this.active) {
|
|
76
|
+
this.modules.logger.warn("Builder circuit breaker activated, ignoring builder bids", logCtx);
|
|
77
|
+
} else {
|
|
78
|
+
this.modules.logger.info("Builder circuit breaker deactivated", logCtx);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
this.modules.logger.verbose("Builder circuit breaker status", {active: this.active, ...logCtx});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -75,6 +75,7 @@ import {ImportPayloadOpts} from "./blocks/types.js";
|
|
|
75
75
|
import {persistBlockInput} from "./blocks/writeBlockInputToDb.js";
|
|
76
76
|
import {persistPayloadEnvelopeInput} from "./blocks/writePayloadEnvelopeInputToDb.js";
|
|
77
77
|
import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
|
|
78
|
+
import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
|
|
78
79
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
79
80
|
import {ChainEvent, ChainEventEmitter} from "./emitter.js";
|
|
80
81
|
import {ForkchoiceCaller, initializeForkChoice} from "./forkChoice/index.js";
|
|
@@ -151,6 +152,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
151
152
|
readonly genesisValidatorsRoot: Root;
|
|
152
153
|
readonly executionEngine: IExecutionEngine;
|
|
153
154
|
readonly executionBuilder?: IExecutionBuilder;
|
|
155
|
+
readonly builderCircuitBreaker: BuilderCircuitBreaker;
|
|
154
156
|
// Expose config for convenience in modularized functions
|
|
155
157
|
readonly config: BeaconConfig;
|
|
156
158
|
readonly custodyConfig: CustodyConfig;
|
|
@@ -425,6 +427,11 @@ export class BeaconChain implements IBeaconChain {
|
|
|
425
427
|
|
|
426
428
|
this.forkChoice = forkChoice;
|
|
427
429
|
|
|
430
|
+
this.builderCircuitBreaker = new BuilderCircuitBreaker(
|
|
431
|
+
{faultInspectionWindow: opts.faultInspectionWindow, allowedFaults: opts.allowedFaults},
|
|
432
|
+
{forkChoice, logger, metrics}
|
|
433
|
+
);
|
|
434
|
+
|
|
428
435
|
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
429
436
|
config,
|
|
430
437
|
clock,
|
|
@@ -8,8 +8,10 @@ import {GossipActionError} from "./gossipValidation.js";
|
|
|
8
8
|
export enum BlockErrorCode {
|
|
9
9
|
/** The prestate cannot be fetched */
|
|
10
10
|
PRESTATE_MISSING = "BLOCK_ERROR_PRESTATE_MISSING",
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The first block of a chain segment references a parentRoot unknown to fork-choice.
|
|
13
|
+
*/
|
|
14
|
+
PARENT_BLOCK_UNKNOWN = "BLOCK_ERROR_PARENT_BLOCK_UNKNOWN",
|
|
13
15
|
/** The block slot is greater than the present slot. */
|
|
14
16
|
FUTURE_SLOT = "BLOCK_ERROR_FUTURE_SLOT",
|
|
15
17
|
/** The block state_root does not match the generated state. */
|
|
@@ -38,7 +40,10 @@ export enum BlockErrorCode {
|
|
|
38
40
|
NOT_FINALIZED_DESCENDANT = "BLOCK_ERROR_NOT_FINALIZED_DESCENDANT",
|
|
39
41
|
/** The provided block is from an later slot than its parent. */
|
|
40
42
|
NOT_LATER_THAN_PARENT = "BLOCK_ERROR_NOT_LATER_THAN_PARENT",
|
|
41
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* A block in the middle of a chain segment does not set its parentRoot to the previous block's root
|
|
45
|
+
* (broken parent-root link inside the segment).
|
|
46
|
+
*/
|
|
42
47
|
NON_LINEAR_PARENT_ROOTS = "BLOCK_ERROR_NON_LINEAR_PARENT_ROOTS",
|
|
43
48
|
/** The slots of the blocks in the chain segment were not strictly increasing. */
|
|
44
49
|
NON_LINEAR_SLOTS = "BLOCK_ERROR_NON_LINEAR_SLOTS",
|
|
@@ -67,10 +72,20 @@ export enum BlockErrorCode {
|
|
|
67
72
|
TOO_MANY_KZG_COMMITMENTS = "BLOCK_ERROR_TOO_MANY_KZG_COMMITMENTS",
|
|
68
73
|
/** Bid parent block root does not match block parent root */
|
|
69
74
|
BID_PARENT_ROOT_MISMATCH = "BLOCK_ERROR_BID_PARENT_ROOT_MISMATCH",
|
|
75
|
+
/** A block body operation or parent execution request list exceeds its per-block limit */
|
|
76
|
+
TOO_MANY_BLOCK_OPERATIONS = "BLOCK_ERROR_TOO_MANY_BLOCK_OPERATIONS",
|
|
70
77
|
/** The parent block's execution payload has been verified as invalid */
|
|
71
78
|
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
|
|
72
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* [gloas] The first block of a chain segment references a parent execution payload
|
|
81
|
+
* (bid.parent_block_hash) unknown to fork-choice (segment boundary).
|
|
82
|
+
*/
|
|
73
83
|
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
|
|
84
|
+
/**
|
|
85
|
+
* [gloas] A block in the middle of a chain segment has a bid.parent_block_hash that does not chain onto the
|
|
86
|
+
* previous block's execution payload (broken payload link inside the segment).
|
|
87
|
+
*/
|
|
88
|
+
NON_LINEAR_PAYLOAD_ROOTS = "BLOCK_ERROR_NON_LINEAR_PAYLOAD_ROOTS",
|
|
74
89
|
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
|
|
75
90
|
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
|
|
76
91
|
}
|
|
@@ -82,7 +97,7 @@ type ExecutionErrorStatus = Exclude<
|
|
|
82
97
|
|
|
83
98
|
export type BlockErrorType =
|
|
84
99
|
| {code: BlockErrorCode.PRESTATE_MISSING; error: Error}
|
|
85
|
-
| {code: BlockErrorCode.
|
|
100
|
+
| {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN; parentRoot: RootHex}
|
|
86
101
|
| {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
|
|
87
102
|
| {code: BlockErrorCode.STATE_ROOT_MISMATCH}
|
|
88
103
|
| {code: BlockErrorCode.GENESIS_BLOCK}
|
|
@@ -118,8 +133,10 @@ export type BlockErrorType =
|
|
|
118
133
|
| {code: BlockErrorCode.DATA_UNAVAILABLE}
|
|
119
134
|
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
|
|
120
135
|
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
|
|
136
|
+
| {code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS; name: string; count: number; limit: number}
|
|
121
137
|
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
|
|
122
|
-
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex}
|
|
138
|
+
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex}
|
|
139
|
+
| {code: BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS; parentBlockHash: RootHex; expectedBlockHash: RootHex};
|
|
123
140
|
|
|
124
141
|
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
|
|
125
142
|
|
|
@@ -12,6 +12,8 @@ export enum ExecutionPayloadEnvelopeErrorCode {
|
|
|
12
12
|
BUILDER_INDEX_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BUILDER_INDEX_MISMATCH",
|
|
13
13
|
BLOCK_HASH_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_HASH_MISMATCH",
|
|
14
14
|
EXECUTION_REQUESTS_ROOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_ROOT_MISMATCH",
|
|
15
|
+
EXECUTION_REQUESTS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_COUNT_EXCEEDED",
|
|
16
|
+
WITHDRAWALS_COUNT_EXCEEDED = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_WITHDRAWALS_COUNT_EXCEEDED",
|
|
15
17
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_SIGNATURE",
|
|
16
18
|
PAYLOAD_ENVELOPE_INPUT_MISSING = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PAYLOAD_ENVELOPE_INPUT_MISSING",
|
|
17
19
|
}
|
|
@@ -42,6 +44,13 @@ export type ExecutionPayloadEnvelopeErrorType =
|
|
|
42
44
|
envelopeRequestsRoot: RootHex;
|
|
43
45
|
bidRequestsRoot: RootHex;
|
|
44
46
|
}
|
|
47
|
+
| {
|
|
48
|
+
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_COUNT_EXCEEDED;
|
|
49
|
+
name: string;
|
|
50
|
+
count: number;
|
|
51
|
+
limit: number;
|
|
52
|
+
}
|
|
53
|
+
| {code: ExecutionPayloadEnvelopeErrorCode.WITHDRAWALS_COUNT_EXCEEDED; count: number; limit: number}
|
|
45
54
|
| {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
|
|
46
55
|
| {code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING; blockRoot: RootHex};
|
|
47
56
|
|
package/src/chain/interface.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {BeaconProposerCache, ProposerPreparationData} from "./beaconProposerCach
|
|
|
35
35
|
import {IBlockInput} from "./blocks/blockInput/index.js";
|
|
36
36
|
import {ImportBlockOpts, ImportPayloadOpts} from "./blocks/types.js";
|
|
37
37
|
import {IBlsVerifier} from "./bls/index.js";
|
|
38
|
+
import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
|
|
38
39
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
39
40
|
import {ChainEventEmitter} from "./emitter.js";
|
|
40
41
|
import {ForkchoiceCaller} from "./forkChoice/index.js";
|
|
@@ -95,6 +96,7 @@ export interface IBeaconChain {
|
|
|
95
96
|
readonly earliestAvailableSlot: Slot;
|
|
96
97
|
readonly executionEngine: IExecutionEngine;
|
|
97
98
|
readonly executionBuilder?: IExecutionBuilder;
|
|
99
|
+
readonly builderCircuitBreaker: BuilderCircuitBreaker;
|
|
98
100
|
// Expose config for convenience in modularized functions
|
|
99
101
|
readonly config: BeaconConfig;
|
|
100
102
|
readonly custodyConfig: CustodyConfig;
|
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
MIN_SYNC_COMMITTEE_PARTICIPANTS,
|
|
11
11
|
SLOTS_PER_EPOCH,
|
|
12
12
|
SYNC_COMMITTEE_SIZE,
|
|
13
|
-
forkPostAltair,
|
|
14
|
-
highestFork,
|
|
15
13
|
isForkPostElectra,
|
|
14
|
+
isForkPostGloas,
|
|
16
15
|
} from "@lodestar/params";
|
|
17
16
|
import {
|
|
18
17
|
type IBeaconStateViewAltair,
|
|
@@ -44,7 +43,6 @@ import {
|
|
|
44
43
|
electra,
|
|
45
44
|
phase0,
|
|
46
45
|
ssz,
|
|
47
|
-
sszTypesFor,
|
|
48
46
|
} from "@lodestar/types";
|
|
49
47
|
import {Logger, MapDef, byteArrayEquals, pruneSetToMax, toRootHex} from "@lodestar/utils";
|
|
50
48
|
import {ZERO_HASH} from "../../constants/index.js";
|
|
@@ -230,8 +228,8 @@ export class LightClientServer {
|
|
|
230
228
|
this.signal = signal;
|
|
231
229
|
|
|
232
230
|
this.zero = {
|
|
233
|
-
// Assign the
|
|
234
|
-
finalizedHeader:
|
|
231
|
+
// Assign the highest pre-Gloas light-client header because post-Gloas light-client updates are skipped for now.
|
|
232
|
+
finalizedHeader: ssz.electra.LightClientHeader.defaultValue(),
|
|
235
233
|
// Electra finalityBranch has fixed length of 5 whereas altair has 4. The fifth element will be ignored
|
|
236
234
|
// when serializing as altair LightClientUpdate
|
|
237
235
|
finalityBranch: ssz.electra.LightClientUpdate.fields.finalityBranch.defaultValue(),
|
|
@@ -658,10 +656,17 @@ export class LightClientServer {
|
|
|
658
656
|
|
|
659
657
|
const attestedFork = this.config.getForkName(attestedHeader.beacon.slot);
|
|
660
658
|
const numWitness = syncCommitteeWitness.witness.length;
|
|
661
|
-
if (
|
|
659
|
+
if (
|
|
660
|
+
isForkPostGloas(attestedFork) &&
|
|
661
|
+
(syncCommitteeWitness.currentSyncCommitteeBranch === undefined ||
|
|
662
|
+
syncCommitteeWitness.nextSyncCommitteeBranch === undefined)
|
|
663
|
+
) {
|
|
664
|
+
throw Error("Expected post-Gloas sync committee branches");
|
|
665
|
+
}
|
|
666
|
+
if (!isForkPostGloas(attestedFork) && isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS_ELECTRA) {
|
|
662
667
|
throw Error(`Expected ${NUM_WITNESS_ELECTRA} witnesses in post-Electra numWitness=${numWitness}`);
|
|
663
668
|
}
|
|
664
|
-
if (!isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS) {
|
|
669
|
+
if (!isForkPostGloas(attestedFork) && !isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS) {
|
|
665
670
|
throw Error(`Expected ${NUM_WITNESS} witnesses in pre-Electra numWitness=${numWitness}`);
|
|
666
671
|
}
|
|
667
672
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {Tree} from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import {
|
|
3
3
|
BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
|
|
4
|
+
CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
4
5
|
FINALIZED_ROOT_GINDEX,
|
|
5
6
|
FINALIZED_ROOT_GINDEX_ELECTRA,
|
|
7
|
+
FINALIZED_ROOT_GINDEX_GLOAS,
|
|
6
8
|
ForkName,
|
|
7
9
|
ForkPostBellatrix,
|
|
10
|
+
NEXT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
8
11
|
isForkPostElectra,
|
|
12
|
+
isForkPostGloas,
|
|
9
13
|
} from "@lodestar/params";
|
|
10
14
|
import {BeaconStateAllForks, CachedBeaconStateAllForks} from "@lodestar/state-transition";
|
|
11
15
|
import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
|
|
@@ -17,6 +21,24 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
|
|
|
17
21
|
let currentSyncCommitteeRoot: Uint8Array;
|
|
18
22
|
let nextSyncCommitteeRoot: Uint8Array;
|
|
19
23
|
|
|
24
|
+
if (isForkPostGloas(fork)) {
|
|
25
|
+
const tree = new Tree(state.node);
|
|
26
|
+
const currentSyncCommitteeGindex = BigInt(CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS);
|
|
27
|
+
const nextSyncCommitteeGindex = BigInt(NEXT_SYNC_COMMITTEE_GINDEX_GLOAS);
|
|
28
|
+
|
|
29
|
+
currentSyncCommitteeRoot = tree.getRoot(currentSyncCommitteeGindex);
|
|
30
|
+
nextSyncCommitteeRoot = tree.getRoot(nextSyncCommitteeGindex);
|
|
31
|
+
witness = [];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
witness,
|
|
35
|
+
currentSyncCommitteeRoot,
|
|
36
|
+
nextSyncCommitteeRoot,
|
|
37
|
+
currentSyncCommitteeBranch: tree.getSingleProof(currentSyncCommitteeGindex),
|
|
38
|
+
nextSyncCommitteeBranch: tree.getSingleProof(nextSyncCommitteeGindex),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
if (isForkPostElectra(fork)) {
|
|
21
43
|
const n2 = n1.left;
|
|
22
44
|
const n5 = n2.right;
|
|
@@ -60,17 +82,30 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
|
|
|
60
82
|
}
|
|
61
83
|
|
|
62
84
|
export function getNextSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
|
|
85
|
+
if (syncCommitteesWitness.nextSyncCommitteeBranch) {
|
|
86
|
+
return syncCommitteesWitness.nextSyncCommitteeBranch;
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
// Witness branch is sorted by descending gindex
|
|
64
90
|
return [syncCommitteesWitness.currentSyncCommitteeRoot, ...syncCommitteesWitness.witness];
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
|
|
94
|
+
if (syncCommitteesWitness.currentSyncCommitteeBranch) {
|
|
95
|
+
return syncCommitteesWitness.currentSyncCommitteeBranch;
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
// Witness branch is sorted by descending gindex
|
|
69
99
|
return [syncCommitteesWitness.nextSyncCommitteeRoot, ...syncCommitteesWitness.witness];
|
|
70
100
|
}
|
|
71
101
|
|
|
72
102
|
export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
|
|
73
|
-
const
|
|
103
|
+
const fork = state.config.getForkName(state.slot);
|
|
104
|
+
const finalizedRootGindex = isForkPostGloas(fork)
|
|
105
|
+
? FINALIZED_ROOT_GINDEX_GLOAS
|
|
106
|
+
: state.epochCtx.isPostElectra()
|
|
107
|
+
? FINALIZED_ROOT_GINDEX_ELECTRA
|
|
108
|
+
: FINALIZED_ROOT_GINDEX;
|
|
74
109
|
return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
|
|
75
110
|
}
|
|
76
111
|
|
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export type SyncCommitteeWitness = {
|
|
29
|
-
/**
|
|
29
|
+
/** Shared witness for pre-Gloas forks where current and next sync committees are siblings. */
|
|
30
30
|
witness: Uint8Array[];
|
|
31
31
|
currentSyncCommitteeRoot: Uint8Array;
|
|
32
32
|
nextSyncCommitteeRoot: Uint8Array;
|
|
33
|
+
currentSyncCommitteeBranch?: Uint8Array[];
|
|
34
|
+
nextSyncCommitteeBranch?: Uint8Array[];
|
|
33
35
|
};
|
|
@@ -355,7 +355,7 @@ export class AggregatedAttestationPool {
|
|
|
355
355
|
// after all committees are processed, we have a list of sameAttDataCons
|
|
356
356
|
for (const consolidation of sameAttDataCons) {
|
|
357
357
|
// Score attestations by profitability to maximize proposer reward
|
|
358
|
-
const flags = getAttestationParticipationStatus(
|
|
358
|
+
const {flags} = getAttestationParticipationStatus(
|
|
359
359
|
ForkSeq[fork],
|
|
360
360
|
consolidation.attData,
|
|
361
361
|
inclusionDistance,
|