@lodestar/beacon-node 1.45.0-dev.f535421f29 → 1.45.0-dev.f6c521a123
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/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +6 -43
- package/lib/api/impl/beacon/pool/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 +48 -5
- package/lib/api/impl/beacon/state/index.js.map +1 -1
- package/lib/api/impl/beacon/state/utils.d.ts +13 -3
- package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
- package/lib/api/impl/beacon/state/utils.js +48 -4
- 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 +67 -62
- 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 +14 -3
- package/lib/api/rest/base.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +4 -22
- package/lib/chain/blocks/importBlock.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/verifyBlocksExecutionPayloads.js +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.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 +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +27 -5
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/blockError.d.ts +48 -6
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +25 -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 +3 -2
- 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/opPools/proposerPreferencesPool.d.ts +3 -2
- package/lib/chain/opPools/proposerPreferencesPool.d.ts.map +1 -1
- package/lib/chain/opPools/proposerPreferencesPool.js +4 -1
- package/lib/chain/opPools/proposerPreferencesPool.js.map +1 -1
- package/lib/chain/options.d.ts +5 -0
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- 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/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -12
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +0 -1
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +0 -1
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +10 -6
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +10 -6
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +53 -4
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +13 -12
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +29 -0
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +14 -5
- package/lib/chain/validation/proposerPreferences.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/core/metrics.d.ts +65 -0
- package/lib/network/core/metrics.d.ts.map +1 -1
- package/lib/network/core/metrics.js +94 -0
- package/lib/network/core/metrics.js.map +1 -1
- package/lib/network/discv5/utils.d.ts.map +1 -1
- package/lib/network/discv5/utils.js +12 -3
- package/lib/network/discv5/utils.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 +22 -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 +4 -3
- package/lib/network/gossip/gossipsub.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +831 -226
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +47 -4
- package/lib/network/gossip/topic.js.map +1 -1
- package/lib/network/peers/peerManager.d.ts.map +1 -1
- package/lib/network/peers/peerManager.js +10 -0
- package/lib/network/peers/peerManager.js.map +1 -1
- package/lib/network/peers/score/score.d.ts +3 -1
- package/lib/network/peers/score/score.d.ts.map +1 -1
- package/lib/network/peers/score/score.js +6 -1
- package/lib/network/peers/score/score.js.map +1 -1
- package/lib/network/peers/score/store.d.ts.map +1 -1
- package/lib/network/peers/score/store.js +6 -1
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.d.ts.map +1 -1
- package/lib/network/peers/utils/prioritizePeers.js +14 -0
- package/lib/network/peers/utils/prioritizePeers.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +18 -9
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +4 -2
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +7 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +7 -2
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/util/graffiti.d.ts +15 -0
- package/lib/util/graffiti.d.ts.map +1 -1
- package/lib/util/graffiti.js +55 -0
- package/lib/util/graffiti.js.map +1 -1
- package/package.json +23 -21
- package/src/api/impl/beacon/blocks/index.ts +3 -2
- package/src/api/impl/beacon/index.ts +1 -1
- package/src/api/impl/beacon/pool/index.ts +5 -53
- package/src/api/impl/beacon/state/index.ts +55 -3
- package/src/api/impl/beacon/state/utils.ts +68 -4
- 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 +75 -67
- package/src/api/rest/base.ts +17 -3
- package/src/chain/blocks/importBlock.ts +2 -24
- package/src/chain/blocks/utils/chainSegment.ts +16 -5
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +1 -1
- 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 +38 -5
- package/src/chain/errors/blockError.ts +44 -7
- package/src/chain/errors/executionPayloadEnvelope.ts +9 -0
- package/src/chain/interface.ts +2 -2
- 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/opPools/proposerPreferencesPool.ts +6 -2
- package/src/chain/options.ts +6 -0
- package/src/chain/prepareNextSlot.ts +11 -7
- package/src/chain/produceBlock/produceBlockBody.ts +9 -17
- package/src/chain/seenCache/index.ts +0 -1
- package/src/chain/validation/aggregateAndProof.ts +12 -6
- package/src/chain/validation/attestation.ts +12 -6
- package/src/chain/validation/block.ts +69 -4
- package/src/chain/validation/executionPayloadBid.ts +16 -11
- package/src/chain/validation/executionPayloadEnvelope.ts +37 -0
- package/src/chain/validation/proposerPreferences.ts +15 -5
- 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/core/metrics.ts +94 -0
- package/src/network/discv5/utils.ts +11 -3
- package/src/network/gossip/encoding.ts +20 -7
- package/src/network/gossip/gossipsub.ts +9 -3
- package/src/network/gossip/topic.ts +58 -4
- package/src/network/peers/peerManager.ts +11 -0
- package/src/network/peers/score/score.ts +8 -1
- package/src/network/peers/score/store.ts +8 -1
- package/src/network/peers/utils/prioritizePeers.ts +15 -0
- package/src/network/processor/gossipHandlers.ts +18 -8
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +4 -2
- package/src/sync/sync.ts +10 -0
- package/src/sync/unknownBlock.ts +8 -2
- package/src/util/graffiti.ts +78 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +0 -16
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +0 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +0 -26
- package/lib/chain/seenCache/seenProposerPreferences.js.map +0 -1
- package/src/chain/seenCache/seenProposerPreferences.ts +0 -32
|
@@ -2,7 +2,6 @@ import {BitArray} from "@chainsafe/ssz";
|
|
|
2
2
|
import {routes} from "@lodestar/api";
|
|
3
3
|
import {
|
|
4
4
|
AncestorStatus,
|
|
5
|
-
EpochDifference,
|
|
6
5
|
ExecutionStatus,
|
|
7
6
|
ForkChoiceError,
|
|
8
7
|
ForkChoiceErrorCode,
|
|
@@ -21,7 +20,6 @@ import {
|
|
|
21
20
|
IBeaconStateView,
|
|
22
21
|
RootCache,
|
|
23
22
|
computeEpochAtSlot,
|
|
24
|
-
computeStartSlotAtEpoch,
|
|
25
23
|
computeTimeAtSlot,
|
|
26
24
|
isStartSlotOfEpoch,
|
|
27
25
|
isStatePostAltair,
|
|
@@ -124,18 +122,13 @@ export async function importBlock(
|
|
|
124
122
|
executionStatus = parentBlock.executionStatus;
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
// getBeaconProposerOrNull will return null if head state is more than one epoch away
|
|
128
|
-
// from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
|
|
129
|
-
const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
|
|
130
|
-
|
|
131
125
|
const blockSummary = this.forkChoice.onBlock(
|
|
132
126
|
block.message,
|
|
133
127
|
postState,
|
|
134
128
|
blockDelaySec,
|
|
135
129
|
currentSlot,
|
|
136
130
|
executionStatus,
|
|
137
|
-
dataAvailabilityStatus
|
|
138
|
-
expectedProposerIndex
|
|
131
|
+
dataAvailabilityStatus
|
|
139
132
|
);
|
|
140
133
|
|
|
141
134
|
// This adds the state necessary to process the next block
|
|
@@ -306,21 +299,6 @@ export async function importBlock(
|
|
|
306
299
|
// Set head state as strong reference
|
|
307
300
|
this.regen.updateHeadState(newHead, postState);
|
|
308
301
|
|
|
309
|
-
try {
|
|
310
|
-
this.emitter.emit(routes.events.EventType.head, {
|
|
311
|
-
block: newHead.blockRoot,
|
|
312
|
-
epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(newHead.slot)) === newHead.slot,
|
|
313
|
-
slot: newHead.slot,
|
|
314
|
-
state: newHead.stateRoot,
|
|
315
|
-
previousDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.previous),
|
|
316
|
-
currentDutyDependentRoot: this.forkChoice.getDependentRoot(newHead, EpochDifference.current),
|
|
317
|
-
executionOptimistic: isOptimisticBlock(newHead),
|
|
318
|
-
});
|
|
319
|
-
} catch (e) {
|
|
320
|
-
// getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
|
|
321
|
-
this.logger.debug("Error emitting head event", {slot: newHead.slot, root: newHead.blockRoot}, e as Error);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
302
|
const delaySec = this.clock.secFromSlot(newHead.slot);
|
|
325
303
|
this.logger.verbose("New chain head", {
|
|
326
304
|
slot: newHead.slot,
|
|
@@ -426,7 +404,7 @@ export async function importBlock(
|
|
|
426
404
|
}
|
|
427
405
|
} catch (e) {
|
|
428
406
|
if (isStartSlotOfEpoch(proposalSlot)) {
|
|
429
|
-
notOverrideFcuReason = NotReorgedReason.
|
|
407
|
+
notOverrideFcuReason = NotReorgedReason.AtEpochBoundary;
|
|
430
408
|
} else {
|
|
431
409
|
this.logger.warn("Unable to get beacon proposer. Do not override fcu.", {proposalSlot}, e as Error);
|
|
432
410
|
}
|
|
@@ -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);
|
|
@@ -201,7 +201,7 @@ export async function verifyBlockExecutionPayload(
|
|
|
201
201
|
invalidateFromParentBlockHash: toRootHex(executionPayloadEnabled.parentHash),
|
|
202
202
|
};
|
|
203
203
|
const execError = new BlockError(block, {
|
|
204
|
-
code: BlockErrorCode.
|
|
204
|
+
code: BlockErrorCode.EXECUTION_ENGINE_INVALID,
|
|
205
205
|
execStatus: execResult.status,
|
|
206
206
|
errorMessage: execResult.validationError ?? "",
|
|
207
207
|
});
|
|
@@ -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
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Type} from "@chainsafe/ssz";
|
|
4
|
+
import {routes} from "@lodestar/api";
|
|
4
5
|
import {BeaconConfig} from "@lodestar/config";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
CheckpointWithHex,
|
|
8
|
+
EpochDifference,
|
|
9
|
+
ForkChoiceStateGetter,
|
|
10
|
+
IForkChoice,
|
|
11
|
+
ProtoBlock,
|
|
12
|
+
UpdateHeadOpt,
|
|
13
|
+
} from "@lodestar/fork-choice";
|
|
6
14
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
7
15
|
import {
|
|
8
16
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
@@ -75,6 +83,7 @@ import {ImportPayloadOpts} from "./blocks/types.js";
|
|
|
75
83
|
import {persistBlockInput} from "./blocks/writeBlockInputToDb.js";
|
|
76
84
|
import {persistPayloadEnvelopeInput} from "./blocks/writePayloadEnvelopeInputToDb.js";
|
|
77
85
|
import {BlsMultiThreadWorkerPool, BlsSingleThreadVerifier, IBlsVerifier} from "./bls/index.js";
|
|
86
|
+
import {BuilderCircuitBreaker} from "./builderCircuitBreaker.js";
|
|
78
87
|
import {ColumnReconstructionTracker} from "./ColumnReconstructionTracker.js";
|
|
79
88
|
import {ChainEvent, ChainEventEmitter} from "./emitter.js";
|
|
80
89
|
import {ForkchoiceCaller, initializeForkChoice} from "./forkChoice/index.js";
|
|
@@ -107,7 +116,6 @@ import {
|
|
|
107
116
|
SeenExecutionPayloadBids,
|
|
108
117
|
SeenPayloadAttesters,
|
|
109
118
|
SeenPayloadEnvelopeInput,
|
|
110
|
-
SeenProposerPreferences,
|
|
111
119
|
SeenSyncCommitteeMessages,
|
|
112
120
|
} from "./seenCache/index.js";
|
|
113
121
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -152,6 +160,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
152
160
|
readonly genesisValidatorsRoot: Root;
|
|
153
161
|
readonly executionEngine: IExecutionEngine;
|
|
154
162
|
readonly executionBuilder?: IExecutionBuilder;
|
|
163
|
+
readonly builderCircuitBreaker: BuilderCircuitBreaker;
|
|
155
164
|
// Expose config for convenience in modularized functions
|
|
156
165
|
readonly config: BeaconConfig;
|
|
157
166
|
readonly custodyConfig: CustodyConfig;
|
|
@@ -189,7 +198,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
189
198
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
190
199
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
191
200
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
192
|
-
readonly seenProposerPreferences = new SeenProposerPreferences();
|
|
193
201
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
194
202
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
195
203
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -427,6 +435,11 @@ export class BeaconChain implements IBeaconChain {
|
|
|
427
435
|
|
|
428
436
|
this.forkChoice = forkChoice;
|
|
429
437
|
|
|
438
|
+
this.builderCircuitBreaker = new BuilderCircuitBreaker(
|
|
439
|
+
{faultInspectionWindow: opts.faultInspectionWindow, allowedFaults: opts.allowedFaults},
|
|
440
|
+
{forkChoice, logger, metrics}
|
|
441
|
+
);
|
|
442
|
+
|
|
430
443
|
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
431
444
|
config,
|
|
432
445
|
clock,
|
|
@@ -1174,7 +1187,27 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1174
1187
|
const timer = this.metrics?.forkChoice.findHead.startTimer({caller});
|
|
1175
1188
|
|
|
1176
1189
|
try {
|
|
1177
|
-
|
|
1190
|
+
const prevHead = this.forkChoice.getHead();
|
|
1191
|
+
const head = this.forkChoice.updateAndGetHead({mode: UpdateHeadOpt.GetCanonicalHead}).head;
|
|
1192
|
+
|
|
1193
|
+
if (head.blockRoot !== prevHead.blockRoot) {
|
|
1194
|
+
try {
|
|
1195
|
+
this.emitter.emit(routes.events.EventType.head, {
|
|
1196
|
+
block: head.blockRoot,
|
|
1197
|
+
epochTransition: computeStartSlotAtEpoch(computeEpochAtSlot(head.slot)) === head.slot,
|
|
1198
|
+
slot: head.slot,
|
|
1199
|
+
state: head.stateRoot,
|
|
1200
|
+
previousDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.previous),
|
|
1201
|
+
currentDutyDependentRoot: this.forkChoice.getDependentRoot(head, EpochDifference.current),
|
|
1202
|
+
executionOptimistic: isOptimisticBlock(head),
|
|
1203
|
+
});
|
|
1204
|
+
} catch (e) {
|
|
1205
|
+
// getDependentRoot() may fail with error: "No block for root" as we can see in holesky non-finality issue
|
|
1206
|
+
this.logger.debug("Error emitting head event", {slot: head.slot, root: head.blockRoot}, e as Error);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
return head;
|
|
1178
1211
|
} catch (e) {
|
|
1179
1212
|
this.metrics?.forkChoice.errors.inc({entrypoint: UpdateHeadOpt.GetCanonicalHead});
|
|
1180
1213
|
throw e;
|
|
@@ -1204,6 +1237,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1204
1237
|
const secFromSlot = this.clock.secFromSlot(slot);
|
|
1205
1238
|
|
|
1206
1239
|
try {
|
|
1240
|
+
// Do not emit head event here, when proposing we rely on the one emitted when importing our own block
|
|
1207
1241
|
const {head, isHeadTimely, notReorgedReason} = this.forkChoice.updateAndGetHead({
|
|
1208
1242
|
mode: UpdateHeadOpt.GetProposerHead,
|
|
1209
1243
|
secFromSlot,
|
|
@@ -1473,7 +1507,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1473
1507
|
this.payloadAttestationPool.prune(slot);
|
|
1474
1508
|
this.executionPayloadBidPool.prune(slot);
|
|
1475
1509
|
this.seenExecutionPayloadBids.prune(slot);
|
|
1476
|
-
this.seenProposerPreferences.prune(slot);
|
|
1477
1510
|
this.proposerPreferencesPool.prune(slot);
|
|
1478
1511
|
this.seenAttestationDatas.onSlot(slot);
|
|
1479
1512
|
this.reprocessController.onSlot(slot);
|
|
@@ -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",
|
|
@@ -61,16 +66,32 @@ export enum BlockErrorCode {
|
|
|
61
66
|
TRANSACTIONS_TOO_BIG = "BLOCK_ERROR_TRANSACTIONS_TOO_BIG",
|
|
62
67
|
/** Execution engine is unavailable, syncing, or api call errored. Peers must not be downscored on this code */
|
|
63
68
|
EXECUTION_ENGINE_ERROR = "BLOCK_ERROR_EXECUTION_ERROR",
|
|
69
|
+
/**
|
|
70
|
+
* Execution engine returned a definitive INVALID verdict on the payload. Unlike
|
|
71
|
+
* EXECUTION_ENGINE_ERROR this is evidence about the data itself, not a local malfunction, so
|
|
72
|
+
* whoever served the block MAY be downscored. Mirrors PayloadErrorCode.EXECUTION_ENGINE_INVALID.
|
|
73
|
+
*/
|
|
74
|
+
EXECUTION_ENGINE_INVALID = "BLOCK_ERROR_EXECUTION_INVALID",
|
|
64
75
|
/** The blobs are unavailable */
|
|
65
76
|
DATA_UNAVAILABLE = "BLOCK_ERROR_DATA_UNAVAILABLE",
|
|
66
77
|
/** Block contains too many kzg commitments */
|
|
67
78
|
TOO_MANY_KZG_COMMITMENTS = "BLOCK_ERROR_TOO_MANY_KZG_COMMITMENTS",
|
|
68
79
|
/** Bid parent block root does not match block parent root */
|
|
69
80
|
BID_PARENT_ROOT_MISMATCH = "BLOCK_ERROR_BID_PARENT_ROOT_MISMATCH",
|
|
81
|
+
/** A block body operation or parent execution request list exceeds its per-block limit */
|
|
82
|
+
TOO_MANY_BLOCK_OPERATIONS = "BLOCK_ERROR_TOO_MANY_BLOCK_OPERATIONS",
|
|
70
83
|
/** The parent block's execution payload has been verified as invalid */
|
|
71
84
|
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
|
|
72
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* [gloas] The first block of a chain segment references a parent execution payload
|
|
87
|
+
* (bid.parent_block_hash) unknown to fork-choice (segment boundary).
|
|
88
|
+
*/
|
|
73
89
|
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
|
|
90
|
+
/**
|
|
91
|
+
* [gloas] A block in the middle of a chain segment has a bid.parent_block_hash that does not chain onto the
|
|
92
|
+
* previous block's execution payload (broken payload link inside the segment).
|
|
93
|
+
*/
|
|
94
|
+
NON_LINEAR_PAYLOAD_ROOTS = "BLOCK_ERROR_NON_LINEAR_PAYLOAD_ROOTS",
|
|
74
95
|
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
|
|
75
96
|
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
|
|
76
97
|
}
|
|
@@ -80,9 +101,18 @@ type ExecutionErrorStatus = Exclude<
|
|
|
80
101
|
ExecutionPayloadStatus.VALID | ExecutionPayloadStatus.ACCEPTED | ExecutionPayloadStatus.SYNCING
|
|
81
102
|
>;
|
|
82
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Statuses where the execution engine could NOT render a verdict on the payload: it is unreachable
|
|
106
|
+
* (UNAVAILABLE), errored (ELERROR), or returned a malformed/client-specific response
|
|
107
|
+
* (INVALID_BLOCK_HASH, dropped from engine_newPayloadV2+). None of these are evidence about the
|
|
108
|
+
* data, so peers must not be downscored on them. A definitive INVALID carries
|
|
109
|
+
* BlockErrorCode.EXECUTION_ENGINE_INVALID instead.
|
|
110
|
+
*/
|
|
111
|
+
type ExecutionEngineErrorStatus = Exclude<ExecutionErrorStatus, ExecutionPayloadStatus.INVALID>;
|
|
112
|
+
|
|
83
113
|
export type BlockErrorType =
|
|
84
114
|
| {code: BlockErrorCode.PRESTATE_MISSING; error: Error}
|
|
85
|
-
| {code: BlockErrorCode.
|
|
115
|
+
| {code: BlockErrorCode.PARENT_BLOCK_UNKNOWN; parentRoot: RootHex}
|
|
86
116
|
| {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
|
|
87
117
|
| {code: BlockErrorCode.STATE_ROOT_MISMATCH}
|
|
88
118
|
| {code: BlockErrorCode.GENESIS_BLOCK}
|
|
@@ -114,12 +144,19 @@ export type BlockErrorType =
|
|
|
114
144
|
| {code: BlockErrorCode.TOO_MUCH_GAS_USED; gasUsed: number; gasLimit: number}
|
|
115
145
|
| {code: BlockErrorCode.SAME_PARENT_HASH; blockHash: RootHex}
|
|
116
146
|
| {code: BlockErrorCode.TRANSACTIONS_TOO_BIG; size: number; max: number}
|
|
117
|
-
| {code: BlockErrorCode.EXECUTION_ENGINE_ERROR; execStatus:
|
|
147
|
+
| {code: BlockErrorCode.EXECUTION_ENGINE_ERROR; execStatus: ExecutionEngineErrorStatus; errorMessage: string}
|
|
148
|
+
| {
|
|
149
|
+
code: BlockErrorCode.EXECUTION_ENGINE_INVALID;
|
|
150
|
+
execStatus: ExecutionPayloadStatus.INVALID;
|
|
151
|
+
errorMessage: string;
|
|
152
|
+
}
|
|
118
153
|
| {code: BlockErrorCode.DATA_UNAVAILABLE}
|
|
119
154
|
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
|
|
120
155
|
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
|
|
156
|
+
| {code: BlockErrorCode.TOO_MANY_BLOCK_OPERATIONS; name: string; count: number; limit: number}
|
|
121
157
|
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
|
|
122
|
-
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex}
|
|
158
|
+
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex}
|
|
159
|
+
| {code: BlockErrorCode.NON_LINEAR_PAYLOAD_ROOTS; parentBlockHash: RootHex; expectedBlockHash: RootHex};
|
|
123
160
|
|
|
124
161
|
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
|
|
125
162
|
|
|
@@ -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";
|
|
@@ -61,7 +62,6 @@ import {
|
|
|
61
62
|
SeenContributionAndProof,
|
|
62
63
|
SeenExecutionPayloadBids,
|
|
63
64
|
SeenPayloadAttesters,
|
|
64
|
-
SeenProposerPreferences,
|
|
65
65
|
SeenSyncCommitteeMessages,
|
|
66
66
|
} from "./seenCache/index.js";
|
|
67
67
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -96,6 +96,7 @@ export interface IBeaconChain {
|
|
|
96
96
|
readonly earliestAvailableSlot: Slot;
|
|
97
97
|
readonly executionEngine: IExecutionEngine;
|
|
98
98
|
readonly executionBuilder?: IExecutionBuilder;
|
|
99
|
+
readonly builderCircuitBreaker: BuilderCircuitBreaker;
|
|
99
100
|
// Expose config for convenience in modularized functions
|
|
100
101
|
readonly config: BeaconConfig;
|
|
101
102
|
readonly custodyConfig: CustodyConfig;
|
|
@@ -133,7 +134,6 @@ export interface IBeaconChain {
|
|
|
133
134
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
134
135
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
135
136
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
136
|
-
readonly seenProposerPreferences: SeenProposerPreferences;
|
|
137
137
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
138
138
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
139
139
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -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,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
1
|
+
import {RootHex, Slot, ValidatorIndex, gloas} from "@lodestar/types";
|
|
2
2
|
import {toRootHex} from "@lodestar/utils";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -17,11 +17,15 @@ import {toRootHex} from "@lodestar/utils";
|
|
|
17
17
|
export class ProposerPreferencesPool {
|
|
18
18
|
private readonly bySlot = new Map<Slot, Map<RootHex, gloas.SignedProposerPreferences>>();
|
|
19
19
|
|
|
20
|
-
/** Lookup for bid validation: matches `(bid.slot,
|
|
20
|
+
/** Lookup for bid validation: matches `(bid.slot, get_shuffling_dependent_root(store, bid.parent_block_root, epoch))`. */
|
|
21
21
|
get(slot: Slot, dependentRootHex: RootHex): gloas.SignedProposerPreferences | null {
|
|
22
22
|
return this.bySlot.get(slot)?.get(dependentRootHex) ?? null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
isKnown(proposalSlot: Slot, dependentRoot: RootHex, validatorIndex: ValidatorIndex): boolean {
|
|
26
|
+
return this.get(proposalSlot, dependentRoot)?.message.validatorIndex === validatorIndex;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
add(signed: gloas.SignedProposerPreferences): void {
|
|
26
30
|
const {proposalSlot, dependentRoot} = signed.message;
|
|
27
31
|
const rootHex = toRootHex(dependentRoot);
|