@lodestar/fork-choice 1.44.0-dev.f715896231 → 1.44.0-dev.fb3e80a516
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/forkChoice/fastConfirmation/data.d.ts +4 -0
- package/lib/forkChoice/fastConfirmation/data.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/data.js +31 -0
- package/lib/forkChoice/fastConfirmation/data.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.d.ts +17 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.js +129 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/index.d.ts +4 -0
- package/lib/forkChoice/fastConfirmation/index.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/index.js +4 -0
- package/lib/forkChoice/fastConfirmation/index.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/metrics.d.ts +21 -0
- package/lib/forkChoice/fastConfirmation/metrics.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/metrics.js +42 -0
- package/lib/forkChoice/fastConfirmation/metrics.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/rules.d.ts +9 -0
- package/lib/forkChoice/fastConfirmation/rules.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/rules.js +91 -0
- package/lib/forkChoice/fastConfirmation/rules.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/types.d.ts +101 -0
- package/lib/forkChoice/fastConfirmation/types.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/types.js +12 -0
- package/lib/forkChoice/fastConfirmation/types.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/utils.d.ts +47 -0
- package/lib/forkChoice/fastConfirmation/utils.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/utils.js +681 -0
- package/lib/forkChoice/fastConfirmation/utils.js.map +1 -0
- package/lib/forkChoice/forkChoice.d.ts +20 -2
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +115 -9
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +16 -2
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/safeBlocks.d.ts +2 -6
- package/lib/forkChoice/safeBlocks.d.ts.map +1 -1
- package/lib/forkChoice/safeBlocks.js +15 -7
- package/lib/forkChoice/safeBlocks.js.map +1 -1
- package/lib/forkChoice/store.d.ts +33 -2
- package/lib/forkChoice/store.d.ts.map +1 -1
- package/lib/forkChoice/store.js +37 -1
- package/lib/forkChoice/store.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/metrics.d.ts +12 -1
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +2 -0
- package/lib/metrics.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +28 -6
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +76 -20
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/fastConfirmation/data.ts +43 -0
- package/src/forkChoice/fastConfirmation/fastConfirmationRule.ts +159 -0
- package/src/forkChoice/fastConfirmation/index.ts +3 -0
- package/src/forkChoice/fastConfirmation/metrics.ts +44 -0
- package/src/forkChoice/fastConfirmation/rules.ts +124 -0
- package/src/forkChoice/fastConfirmation/types.ts +111 -0
- package/src/forkChoice/fastConfirmation/utils.ts +968 -0
- package/src/forkChoice/forkChoice.ts +143 -9
- package/src/forkChoice/interface.ts +17 -1
- package/src/forkChoice/safeBlocks.ts +15 -7
- package/src/forkChoice/store.ts +45 -1
- package/src/index.ts +11 -0
- package/src/metrics.ts +3 -1
- package/src/protoArray/protoArray.ts +88 -19
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {MetricsRegisterExtra} from "@lodestar/utils";
|
|
2
|
+
|
|
3
|
+
export type FastConfirmationMetrics = ReturnType<typeof getFastConfirmationMetrics>;
|
|
4
|
+
|
|
5
|
+
export enum FastConfirmationSteps {
|
|
6
|
+
updateHead = "updateHead",
|
|
7
|
+
buildCache = "buildCache",
|
|
8
|
+
buildSnapshot = "buildSnapshot",
|
|
9
|
+
runRules = "runRules",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getFastConfirmationMetrics(register: MetricsRegisterExtra) {
|
|
13
|
+
return {
|
|
14
|
+
fastConfirmation: {
|
|
15
|
+
totalDuration: register.histogram({
|
|
16
|
+
name: "lodestar_fast_confirmation_duration_seconds",
|
|
17
|
+
help: "Time to run Fast Confirmation Rule algorithm",
|
|
18
|
+
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2],
|
|
19
|
+
}),
|
|
20
|
+
stepsDuration: register.histogram<{step: FastConfirmationSteps}>({
|
|
21
|
+
name: "lodestar_fast_confirmation_steps_duration_seconds",
|
|
22
|
+
help: "Time to run Fast Confirmation Steps",
|
|
23
|
+
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 2],
|
|
24
|
+
labelNames: ["step"],
|
|
25
|
+
}),
|
|
26
|
+
confirmedEpoch: register.gauge({
|
|
27
|
+
name: "lodestar_fast_confirmation_confirmed_epoch",
|
|
28
|
+
help: "Current confirmed epoch from fast confirmation",
|
|
29
|
+
}),
|
|
30
|
+
confirmedSlot: register.gauge({
|
|
31
|
+
name: "lodestar_fast_confirmation_confirmed_slot",
|
|
32
|
+
help: "Current confirmed slot from fast confirmation",
|
|
33
|
+
}),
|
|
34
|
+
votesTracked: register.gauge({
|
|
35
|
+
name: "lodestar_fast_confirmation_votes_tracked",
|
|
36
|
+
help: "Number of checkpoint votes tracked by fast confirmation",
|
|
37
|
+
}),
|
|
38
|
+
resets: register.gauge({
|
|
39
|
+
name: "lodestar_fast_confirmation_resets_total",
|
|
40
|
+
help: "Count of fast confirmation resets due to reorgs",
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {computeEpochAtSlot, isStartSlotOfEpoch} from "@lodestar/state-transition";
|
|
2
|
+
import {Logger} from "@lodestar/utils";
|
|
3
|
+
import {equalCheckpointWithHex} from "../store.ts";
|
|
4
|
+
import {
|
|
5
|
+
FastConfirmationCache,
|
|
6
|
+
FastConfirmationContext,
|
|
7
|
+
FastConfirmationDecision,
|
|
8
|
+
FastConfirmationDecisionReason,
|
|
9
|
+
FastConfirmationRule,
|
|
10
|
+
FastConfirmationSnapshot,
|
|
11
|
+
IFastConfirmationStore,
|
|
12
|
+
} from "./types.ts";
|
|
13
|
+
import {findLatestConfirmedDescendant, getBlock, isAncestor, isConfirmedChainSafe} from "./utils.ts";
|
|
14
|
+
|
|
15
|
+
export const resetIfConfirmedUnavailable: FastConfirmationRule = (snapshot, ctx, _store, cache, decision) => {
|
|
16
|
+
const confirmedBlock = getBlock(ctx, cache, decision.confirmedRoot);
|
|
17
|
+
if (!confirmedBlock) {
|
|
18
|
+
return {
|
|
19
|
+
confirmedRoot: snapshot.finalizedRoot,
|
|
20
|
+
didReset: true,
|
|
21
|
+
reason: FastConfirmationDecisionReason.ConfirmedNotFound,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return decision;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const resetIfBehindOrNotAncestorOrUnsafe: FastConfirmationRule = (
|
|
28
|
+
snapshot,
|
|
29
|
+
ctx,
|
|
30
|
+
store,
|
|
31
|
+
cache,
|
|
32
|
+
decision,
|
|
33
|
+
logger
|
|
34
|
+
) => {
|
|
35
|
+
const confirmedBlock = getBlock(ctx, cache, decision.confirmedRoot);
|
|
36
|
+
if (!confirmedBlock) return decision;
|
|
37
|
+
const confirmedEpoch = computeEpochAtSlot(confirmedBlock.slot);
|
|
38
|
+
|
|
39
|
+
const confirmedEpochBehindHead = confirmedEpoch + 1 < snapshot.currentEpoch;
|
|
40
|
+
const notAncestorOfHead = !isAncestor(ctx, cache, snapshot.headRoot, decision.confirmedRoot);
|
|
41
|
+
const allChildrenNotConfirmed =
|
|
42
|
+
isStartSlotOfEpoch(snapshot.currentSlot) &&
|
|
43
|
+
!isConfirmedChainSafe(ctx, store, cache, decision.confirmedRoot, logger);
|
|
44
|
+
|
|
45
|
+
if (confirmedEpochBehindHead || notAncestorOfHead || allChildrenNotConfirmed) {
|
|
46
|
+
const didReset = decision.didReset || decision.confirmedRoot !== snapshot.finalizedRoot;
|
|
47
|
+
const reason = confirmedEpochBehindHead
|
|
48
|
+
? FastConfirmationDecisionReason.ResetBehind
|
|
49
|
+
: notAncestorOfHead
|
|
50
|
+
? FastConfirmationDecisionReason.ResetNotAncestor
|
|
51
|
+
: FastConfirmationDecisionReason.ResetChainUnsafe;
|
|
52
|
+
return {confirmedRoot: snapshot.finalizedRoot, didReset, reason};
|
|
53
|
+
}
|
|
54
|
+
return decision;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const advanceIfObservedJustified: FastConfirmationRule = (snapshot, ctx, store, cache, decision) => {
|
|
58
|
+
if (!isStartSlotOfEpoch(snapshot.currentSlot)) return decision;
|
|
59
|
+
if (store.currentEpochObservedJustifiedCheckpoint.epoch + 1 !== snapshot.currentEpoch) return decision;
|
|
60
|
+
if (!snapshot.headUnrealized) return decision;
|
|
61
|
+
if (!equalCheckpointWithHex(store.currentEpochObservedJustifiedCheckpoint, snapshot.headUnrealized)) return decision;
|
|
62
|
+
const observedBlock = getBlock(ctx, cache, store.currentEpochObservedJustifiedCheckpoint.rootHex);
|
|
63
|
+
if (!observedBlock || computeEpochAtSlot(observedBlock.slot) + 1 < snapshot.currentEpoch) return decision;
|
|
64
|
+
|
|
65
|
+
const confirmedSlot = getBlock(ctx, cache, decision.confirmedRoot)?.slot ?? null;
|
|
66
|
+
const observedSlot = observedBlock.slot;
|
|
67
|
+
if (confirmedSlot !== null && observedSlot !== null && confirmedSlot < observedSlot) {
|
|
68
|
+
return {
|
|
69
|
+
...decision,
|
|
70
|
+
confirmedRoot: store.currentEpochObservedJustifiedCheckpoint.rootHex,
|
|
71
|
+
reason: FastConfirmationDecisionReason.ObservedJustified,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return decision;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const advanceToLatestConfirmedDescendant: FastConfirmationRule = (
|
|
78
|
+
snapshot,
|
|
79
|
+
ctx,
|
|
80
|
+
store,
|
|
81
|
+
cache,
|
|
82
|
+
decision,
|
|
83
|
+
logger
|
|
84
|
+
) => {
|
|
85
|
+
const confirmedBlock = getBlock(ctx, cache, decision.confirmedRoot);
|
|
86
|
+
const confirmedEpoch = confirmedBlock ? computeEpochAtSlot(confirmedBlock.slot) : null;
|
|
87
|
+
if (confirmedEpoch !== null && confirmedEpoch + 1 >= snapshot.currentEpoch) {
|
|
88
|
+
const newConfirmed = findLatestConfirmedDescendant(snapshot, ctx, store, cache, decision.confirmedRoot, logger);
|
|
89
|
+
return {
|
|
90
|
+
...decision,
|
|
91
|
+
confirmedRoot: newConfirmed,
|
|
92
|
+
reason: FastConfirmationDecisionReason.ConfirmedDescendant,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return decision;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const FAST_CONFIRMATION_RULES: FastConfirmationRule[] = [
|
|
99
|
+
resetIfConfirmedUnavailable,
|
|
100
|
+
resetIfBehindOrNotAncestorOrUnsafe,
|
|
101
|
+
advanceIfObservedJustified,
|
|
102
|
+
advanceToLatestConfirmedDescendant,
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
// Spec mapping: this rule runner implements the `get_latest_confirmed` decision flow
|
|
106
|
+
// over Lodestar's snapshot/store/cache abstractions.
|
|
107
|
+
export function runFastConfirmationRules(
|
|
108
|
+
snapshot: FastConfirmationSnapshot,
|
|
109
|
+
ctx: FastConfirmationContext,
|
|
110
|
+
store: IFastConfirmationStore,
|
|
111
|
+
cache: FastConfirmationCache,
|
|
112
|
+
logger?: Logger
|
|
113
|
+
): FastConfirmationDecision {
|
|
114
|
+
let decision: FastConfirmationDecision = {
|
|
115
|
+
confirmedRoot: snapshot.confirmedRoot,
|
|
116
|
+
didReset: false,
|
|
117
|
+
reason: FastConfirmationDecisionReason.Unchanged,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const rule of FAST_CONFIRMATION_RULES) {
|
|
121
|
+
decision = rule(snapshot, ctx, store, cache, decision, logger);
|
|
122
|
+
}
|
|
123
|
+
return decision;
|
|
124
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
|
|
2
|
+
import {Epoch, RootHex, Slot, ValidatorIndex} from "@lodestar/types";
|
|
3
|
+
import {Logger} from "@lodestar/utils";
|
|
4
|
+
import {ProtoBlock} from "../../protoArray/interface.ts";
|
|
5
|
+
import {CheckpointWithHex} from "../store.ts";
|
|
6
|
+
|
|
7
|
+
export type FastConfirmationBalanceSource = {
|
|
8
|
+
state: IBeaconStateView | null;
|
|
9
|
+
balances: EffectiveBalanceIncrements;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ForkChoiceStateGetter = (
|
|
13
|
+
opts: {stateRoot: RootHex; checkpoint?: never} | {stateRoot?: never; checkpoint: CheckpointWithHex}
|
|
14
|
+
) => IBeaconStateView | null;
|
|
15
|
+
|
|
16
|
+
type IFastConfirmationSpecStore = {
|
|
17
|
+
confirmedRoot: RootHex;
|
|
18
|
+
previousEpochObservedJustifiedCheckpoint: CheckpointWithHex;
|
|
19
|
+
currentEpochObservedJustifiedCheckpoint: CheckpointWithHex;
|
|
20
|
+
previousEpochGreatestUnrealizedCheckpoint: CheckpointWithHex;
|
|
21
|
+
previousSlotHead: RootHex;
|
|
22
|
+
currentSlotHead: RootHex;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type IFastConfirmationAuxStore = {
|
|
26
|
+
previousEpochObservedJustifiedBalances: EffectiveBalanceIncrements;
|
|
27
|
+
currentEpochObservedJustifiedBalances: EffectiveBalanceIncrements;
|
|
28
|
+
previousEpochGreatestUnrealizedBalances: EffectiveBalanceIncrements;
|
|
29
|
+
stateGetter: ForkChoiceStateGetter;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type IFastConfirmationStore = IFastConfirmationSpecStore & IFastConfirmationAuxStore;
|
|
33
|
+
|
|
34
|
+
export type FastConfirmationResult = {
|
|
35
|
+
confirmedRoot: RootHex;
|
|
36
|
+
didReset?: boolean;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type FastConfirmationSnapshot = {
|
|
40
|
+
currentSlot: Slot;
|
|
41
|
+
currentEpoch: Epoch;
|
|
42
|
+
headRoot: RootHex;
|
|
43
|
+
confirmedRoot: RootHex;
|
|
44
|
+
confirmedEpoch: Epoch | null;
|
|
45
|
+
confirmedSlot: Slot | null;
|
|
46
|
+
observedJustified: CheckpointWithHex;
|
|
47
|
+
headUnrealized: CheckpointWithHex | null;
|
|
48
|
+
finalizedRoot: RootHex;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export enum FastConfirmationDecisionReason {
|
|
52
|
+
Unchanged = "unchanged",
|
|
53
|
+
ConfirmedNotFound = "confirmed_not_found",
|
|
54
|
+
ResetBehind = "reset_behind",
|
|
55
|
+
ResetNotAncestor = "reset_not_ancestor",
|
|
56
|
+
ResetChainUnsafe = "reset_chain_unsafe",
|
|
57
|
+
ObservedJustified = "observed_justified",
|
|
58
|
+
ConfirmedDescendant = "confirmed_descendant",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type FastConfirmationDecision = {
|
|
62
|
+
confirmedRoot: RootHex;
|
|
63
|
+
didReset: boolean;
|
|
64
|
+
reason: FastConfirmationDecisionReason;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type FastConfirmationRule = (
|
|
68
|
+
snapshot: FastConfirmationSnapshot,
|
|
69
|
+
ctx: FastConfirmationContext,
|
|
70
|
+
store: IFastConfirmationStore,
|
|
71
|
+
cache: FastConfirmationCache,
|
|
72
|
+
decision: FastConfirmationDecision,
|
|
73
|
+
logger?: Logger
|
|
74
|
+
) => FastConfirmationDecision;
|
|
75
|
+
|
|
76
|
+
export type BalanceSourceKey = "current" | "previous";
|
|
77
|
+
|
|
78
|
+
// This cache is created once per slot
|
|
79
|
+
export type FastConfirmationCache = {
|
|
80
|
+
blockByRoot: Map<RootHex, ProtoBlock | null>;
|
|
81
|
+
ancestorRoots: Map<string, RootHex[] | null>;
|
|
82
|
+
committeeBySlot: Map<Slot, Set<ValidatorIndex>>;
|
|
83
|
+
isDescendantByRootPair: Map<string, boolean>;
|
|
84
|
+
/** voteRoot -> totalWeight, keyed by sourceKey */
|
|
85
|
+
voteWeightBySource: Map<BalanceSourceKey, Map<RootHex, number>>;
|
|
86
|
+
headState?: IBeaconStateView;
|
|
87
|
+
pulledUpHeadState?: IBeaconStateView;
|
|
88
|
+
checkpointStateByKey: Map<string, IBeaconStateView | null>;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type FastConfirmationContext = {
|
|
92
|
+
config: {
|
|
93
|
+
CONFIRMATION_BYZANTINE_THRESHOLD: number;
|
|
94
|
+
PROPOSER_SCORE_BOOST: number;
|
|
95
|
+
};
|
|
96
|
+
getCurrentSlot(): Slot;
|
|
97
|
+
getHead(): ProtoBlock;
|
|
98
|
+
getBlock(root: RootHex): ProtoBlock | null;
|
|
99
|
+
getAncestor(root: RootHex, slot: Slot): RootHex;
|
|
100
|
+
isDescendant(ancestor: RootHex, descendant: RootHex): boolean;
|
|
101
|
+
getLatestMessage(validatorIndex: ValidatorIndex): {root: RootHex; epoch: Epoch} | null;
|
|
102
|
+
getUnrealizedJustified(): {checkpoint: CheckpointWithHex; balances: EffectiveBalanceIncrements};
|
|
103
|
+
getFinalizedCheckpoint(): CheckpointWithHex;
|
|
104
|
+
getEquivocatingIndices(): Set<ValidatorIndex>;
|
|
105
|
+
getTrackedVotesCount(): number;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export interface IFastConfirmationRule {
|
|
109
|
+
getConfirmedRoot(): RootHex;
|
|
110
|
+
onSlotStartAfterPastAttestationsApplied(ctx: FastConfirmationContext): FastConfirmationResult;
|
|
111
|
+
}
|