@lodestar/beacon-node 1.47.0-dev.ca6d994e0e → 1.47.0-dev.cec7fd63e3
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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +12 -22
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/api/impl/validator/utils.d.ts +5 -0
- package/lib/api/impl/validator/utils.d.ts.map +1 -1
- package/lib/api/impl/validator/utils.js +18 -12
- package/lib/api/impl/validator/utils.js.map +1 -1
- package/lib/chain/builderCircuitBreaker.d.ts +6 -5
- package/lib/chain/builderCircuitBreaker.d.ts.map +1 -1
- package/lib/chain/builderCircuitBreaker.js +28 -15
- package/lib/chain/builderCircuitBreaker.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +2 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/options.d.ts +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +1 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -0
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +6 -1
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/execution/builder/http.d.ts +10 -7
- package/lib/execution/builder/http.d.ts.map +1 -1
- package/lib/execution/builder/http.js +11 -8
- package/lib/execution/builder/http.js.map +1 -1
- package/lib/metrics/metrics/beacon.d.ts +3 -3
- package/lib/metrics/metrics/beacon.d.ts.map +1 -1
- package/lib/metrics/metrics/beacon.js +9 -9
- package/lib/metrics/metrics/beacon.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +25 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +17 -5
- package/lib/sync/unknownBlock.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/validator/index.ts +22 -27
- package/src/api/impl/validator/utils.ts +30 -15
- package/src/chain/builderCircuitBreaker.ts +31 -17
- package/src/chain/chain.ts +2 -0
- package/src/chain/options.ts +1 -1
- package/src/chain/prepareNextSlot.ts +1 -1
- package/src/chain/produceBlock/produceBlockBody.ts +13 -0
- package/src/execution/builder/http.ts +11 -11
- package/src/metrics/metrics/beacon.ts +9 -9
- package/src/network/processor/gossipHandlers.ts +26 -3
- package/src/sync/unknownBlock.ts +18 -5
|
@@ -71,15 +71,18 @@ export class NoBidReceived extends Error {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
* the circuit breaker (so at boot time and once for each unique boot).
|
|
74
|
+
* Default circuit breaker parameters:
|
|
76
75
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
76
|
+
* SLOTS_PER_EPOCH <= FAULT_INSPECTION_WINDOW < 2 * SLOTS_PER_EPOCH (randomized at initialization)
|
|
77
|
+
* ALLOWED_FAULTS: FAULT_INSPECTION_WINDOW // 4
|
|
79
78
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
79
|
+
* e.g. on mainnet SLOTS_PER_EPOCH is 32, so FAULT_INSPECTION_WINDOW is between 32 and 63,
|
|
80
|
+
* and a window of 40 results in ALLOWED_FAULTS = 10.
|
|
81
|
+
*
|
|
82
|
+
* The randomized default window keeps the exact slots under inspection unpredictable per node,
|
|
83
|
+
* so a builder cannot tell when past faults age out of a given proposer's window and time
|
|
84
|
+
* withholding around trip or recovery points. Explicitly configured windows are clamped to at
|
|
85
|
+
* least SLOTS_PER_EPOCH, and configured allowedFaults is capped at the default ~25% budget.
|
|
83
86
|
*/
|
|
84
87
|
export function getFaultInspectionParams(opts: {faultInspectionWindow?: number; allowedFaults?: number}): {
|
|
85
88
|
faultInspectionWindow: number;
|
|
@@ -90,10 +93,7 @@ export function getFaultInspectionParams(opts: {faultInspectionWindow?: number;
|
|
|
90
93
|
SLOTS_PER_EPOCH
|
|
91
94
|
);
|
|
92
95
|
// allowedFaults should be < faultInspectionWindow, limiting them to faultInspectionWindow/4
|
|
93
|
-
const allowedFaults = Math.min(
|
|
94
|
-
opts.allowedFaults ?? Math.floor(faultInspectionWindow / 4),
|
|
95
|
-
Math.floor(faultInspectionWindow / 4)
|
|
96
|
-
);
|
|
96
|
+
const allowedFaults = Math.min(opts.allowedFaults ?? Infinity, Math.floor(faultInspectionWindow / 4));
|
|
97
97
|
return {faultInspectionWindow, allowedFaults};
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -143,17 +143,17 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
|
|
|
143
143
|
name: "beacon_builder_circuit_breaker_active",
|
|
144
144
|
help: "Whether the builder circuit breaker is active (1) causing builder bids to be ignored",
|
|
145
145
|
}),
|
|
146
|
-
|
|
147
|
-
name: "
|
|
148
|
-
help: "Count of
|
|
146
|
+
canonicalBlocks: register.gauge({
|
|
147
|
+
name: "beacon_builder_circuit_breaker_canonical_blocks",
|
|
148
|
+
help: "Count of canonical FULL and EMPTY blocks in the fault inspection window",
|
|
149
149
|
}),
|
|
150
|
-
|
|
151
|
-
name: "
|
|
152
|
-
help: "Count of blocks
|
|
150
|
+
fullBlocks: register.gauge({
|
|
151
|
+
name: "beacon_builder_circuit_breaker_full_blocks",
|
|
152
|
+
help: "Count of canonical FULL blocks in the fault inspection window",
|
|
153
153
|
}),
|
|
154
|
-
|
|
155
|
-
name: "
|
|
156
|
-
help: "Count of
|
|
154
|
+
emptyBlocks: register.gauge({
|
|
155
|
+
name: "beacon_builder_circuit_breaker_empty_blocks",
|
|
156
|
+
help: "Count of canonical EMPTY blocks in the fault inspection window",
|
|
157
157
|
}),
|
|
158
158
|
},
|
|
159
159
|
|
|
@@ -713,9 +713,32 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
713
713
|
const {serializedData} = gossipData;
|
|
714
714
|
|
|
715
715
|
const signedBlock = sszDeserialize(topic, serializedData);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
716
|
+
try {
|
|
717
|
+
const blockInput = await validateBeaconBlock(signedBlock, topic.boundary.fork, peerIdStr, seenTimestampSec);
|
|
718
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
719
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
720
|
+
} catch (e) {
|
|
721
|
+
// Spec: IGNORE the block, ie not to re-publish to peers
|
|
722
|
+
// but we should still import an equivocating (REPEAT_PROPOSAL) block into fork choice because we don't
|
|
723
|
+
// know if this block (or the 1st known block with same slot) will become canonical yet
|
|
724
|
+
if (
|
|
725
|
+
e instanceof BlockGossipError &&
|
|
726
|
+
e.type.code === BlockErrorCode.REPEAT_PROPOSAL &&
|
|
727
|
+
// this is make sure the block's proposer signature was verified, it should be true anyway
|
|
728
|
+
chain.seenBlockProposers.hasBlockRoot(signedBlock.message.slot, e.type.proposerIndex, e.type.root)
|
|
729
|
+
) {
|
|
730
|
+
// blockInput was optimistically seeded in validateBeaconBlock and retained on IGNORE
|
|
731
|
+
const blockInput = chain.seenBlockInputCache.get(e.type.root);
|
|
732
|
+
if (blockInput) {
|
|
733
|
+
chain.serializedCache.set(signedBlock, serializedData);
|
|
734
|
+
// this is technically not a valid gossip block but gossip validation is a cheap subset of checks
|
|
735
|
+
// this runs the full state transition, so importing an equivocating-but-valid block here is safe.
|
|
736
|
+
handleValidBeaconBlock(blockInput, peerIdStr, seenTimestampSec);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
// rethrow so gossipValidatorFn maps IGNORE -> TopicValidatorResult.Ignore (message not forwarded)
|
|
740
|
+
throw e;
|
|
741
|
+
}
|
|
719
742
|
},
|
|
720
743
|
|
|
721
744
|
[GossipType.blob_sidecar]: async ({
|
package/src/sync/unknownBlock.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {ForkSeq} from "@lodestar/params";
|
|
3
|
+
import {ForkSeq, isForkPostGloas} from "@lodestar/params";
|
|
4
4
|
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
5
5
|
import {computeTimeAtSlot} from "@lodestar/state-transition";
|
|
6
6
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
@@ -764,9 +764,10 @@ export class BlockInputSync {
|
|
|
764
764
|
// this prevents unbundling attack
|
|
765
765
|
// see https://lighthouse-blog.sigmaprime.io/mev-unbundling-rpc.html
|
|
766
766
|
const {slot: blockSlot, proposerIndex} = pendingBlock.blockInput.getBlock().message;
|
|
767
|
+
const blockRootHex = pendingBlock.blockInput.blockRootHex;
|
|
767
768
|
const logCtx = {
|
|
768
769
|
slot: blockSlot,
|
|
769
|
-
root:
|
|
770
|
+
root: blockRootHex,
|
|
770
771
|
delaySec: this.chain.clock.secFromSlot(blockSlot),
|
|
771
772
|
};
|
|
772
773
|
this.logger.verbose("Processing downloaded block", logCtx);
|
|
@@ -780,7 +781,7 @@ export class BlockInputSync {
|
|
|
780
781
|
// eligible for proposer boost to prevent unbundling attack
|
|
781
782
|
this.logger.verbose("Avoid proposer boost for this block of known proposer", {
|
|
782
783
|
slot: blockSlot,
|
|
783
|
-
root:
|
|
784
|
+
root: blockRootHex,
|
|
784
785
|
proposerIndex,
|
|
785
786
|
});
|
|
786
787
|
await sleep(proposerBoostWindowMs);
|
|
@@ -807,12 +808,24 @@ export class BlockInputSync {
|
|
|
807
808
|
if (!res.err) {
|
|
808
809
|
this.logger.verbose("Processed block from unknown sync", logCtx);
|
|
809
810
|
// no need to update status to "processed", delete anyway
|
|
810
|
-
this.pendingBlocks.delete(
|
|
811
|
+
this.pendingBlocks.delete(blockRootHex);
|
|
812
|
+
|
|
813
|
+
if (isForkPostGloas(fork)) {
|
|
814
|
+
const payloadInput = this.chain.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
815
|
+
if (!payloadInput) {
|
|
816
|
+
this.logger.warn("PayloadEnvelopeInput not seeded during unknown sync processReadyBlock()", logCtx);
|
|
817
|
+
} else {
|
|
818
|
+
// Similar to the gossip path: immediately attempt fetch of data columns from the execution engine.
|
|
819
|
+
// The bid already carries the kzg commitments, so there is no reason to wait for the payload to arrive.
|
|
820
|
+
this.chain.getBlobsTracker.triggerGetBlobs(payloadInput);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
811
824
|
// Re-enter the scheduler so descendants blocked on either parent blocks or parent payloads
|
|
812
825
|
// are advanced through the same dependency checks as every other pending item.
|
|
813
826
|
this.triggerUnknownBlockSearch();
|
|
814
827
|
} else {
|
|
815
|
-
const errorData = {slot: pendingBlock.blockInput.slot, root:
|
|
828
|
+
const errorData = {slot: pendingBlock.blockInput.slot, root: blockRootHex};
|
|
816
829
|
if (res.err instanceof BlockError) {
|
|
817
830
|
switch (res.err.type.code) {
|
|
818
831
|
// This cases are already handled with `{ignoreIfKnown: true}`
|