@lodestar/fork-choice 1.36.0-dev.c7f3e8d129 → 1.36.0-dev.d4044b6621
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/forkChoice.d.ts +11 -9
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +54 -43
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +2 -6
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/interface.js.map +1 -1
- package/lib/metrics.d.ts +10 -0
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +35 -0
- package/lib/metrics.js.map +1 -1
- package/lib/protoArray/computeDeltas.d.ts +10 -2
- package/lib/protoArray/computeDeltas.d.ts.map +1 -1
- package/lib/protoArray/computeDeltas.js +44 -12
- package/lib/protoArray/computeDeltas.js.map +1 -1
- package/lib/protoArray/interface.d.ts +8 -7
- package/lib/protoArray/interface.d.ts.map +1 -1
- package/lib/protoArray/interface.js +6 -0
- package/lib/protoArray/interface.js.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/forkChoice.ts +73 -45
- package/src/forkChoice/interface.ts +2 -16
- package/src/metrics.ts +35 -0
- package/src/protoArray/computeDeltas.ts +65 -15
- package/src/protoArray/interface.ts +9 -8
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import {EffectiveBalanceIncrements} from "@lodestar/state-transition";
|
|
2
2
|
import {ValidatorIndex} from "@lodestar/types";
|
|
3
3
|
import {ProtoArrayError, ProtoArrayErrorCode} from "./errors.js";
|
|
4
|
-
import {
|
|
4
|
+
import {NULL_VOTE_INDEX, VoteIndex} from "./interface.js";
|
|
5
5
|
|
|
6
6
|
// reuse arrays to avoid memory reallocation and gc
|
|
7
7
|
const deltas = new Array<number>();
|
|
8
8
|
|
|
9
|
+
export type DeltasResult = {
|
|
10
|
+
deltas: number[];
|
|
11
|
+
equivocatingValidators: number;
|
|
12
|
+
// inactive validators before beacon node started
|
|
13
|
+
oldInactiveValidators: number;
|
|
14
|
+
// new inactive validators after beacon node started
|
|
15
|
+
newInactiveValidators: number;
|
|
16
|
+
// below is for active validators
|
|
17
|
+
unchangedVoteValidators: number;
|
|
18
|
+
newVoteValidators: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
9
21
|
/**
|
|
10
22
|
* Returns a list of `deltas`, where there is one delta for each of the indices in `indices`
|
|
11
23
|
*
|
|
@@ -17,31 +29,49 @@ const deltas = new Array<number>();
|
|
|
17
29
|
*/
|
|
18
30
|
export function computeDeltas(
|
|
19
31
|
numProtoNodes: number,
|
|
20
|
-
|
|
32
|
+
voteCurrentIndices: VoteIndex[],
|
|
33
|
+
voteNextIndices: VoteIndex[],
|
|
21
34
|
oldBalances: EffectiveBalanceIncrements,
|
|
22
35
|
newBalances: EffectiveBalanceIncrements,
|
|
23
36
|
equivocatingIndices: Set<ValidatorIndex>
|
|
24
|
-
):
|
|
37
|
+
): DeltasResult {
|
|
38
|
+
if (voteCurrentIndices.length !== voteNextIndices.length) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`voteCurrentIndices and voteNextIndices must have the same length: ${voteCurrentIndices.length} !== ${voteNextIndices.length}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (numProtoNodes >= NULL_VOTE_INDEX) {
|
|
45
|
+
// this never happen in practice, but we check to be safe
|
|
46
|
+
throw new Error(`numProtoNodes must be less than NULL_VOTE_INDEX: ${numProtoNodes} >= ${NULL_VOTE_INDEX}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
25
49
|
deltas.length = numProtoNodes;
|
|
26
50
|
deltas.fill(0);
|
|
27
51
|
|
|
28
52
|
// avoid creating new variables in the loop to potentially reduce GC pressure
|
|
29
53
|
let oldBalance: number, newBalance: number;
|
|
30
|
-
let currentIndex:
|
|
54
|
+
let currentIndex: VoteIndex, nextIndex: VoteIndex;
|
|
31
55
|
// sort equivocating indices to avoid Set.has() in the loop
|
|
32
56
|
const equivocatingArray = Array.from(equivocatingIndices).sort((a, b) => a - b);
|
|
33
57
|
let equivocatingIndex = 0;
|
|
34
58
|
let equivocatingValidatorIndex = equivocatingArray[equivocatingIndex];
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
const equivocatingValidators = equivocatingIndices.size;
|
|
61
|
+
let oldInactiveValidators = 0;
|
|
62
|
+
let newInactiveValidators = 0;
|
|
63
|
+
let unchangedVoteValidators = 0;
|
|
64
|
+
let newVoteValidators = 0;
|
|
65
|
+
|
|
66
|
+
for (let vIndex = 0; vIndex < voteNextIndices.length; vIndex++) {
|
|
67
|
+
currentIndex = voteCurrentIndices[vIndex];
|
|
68
|
+
nextIndex = voteNextIndices[vIndex];
|
|
38
69
|
// There is no need to create a score change if the validator has never voted or both of their
|
|
39
70
|
// votes are for the zero hash (genesis block)
|
|
40
|
-
if (
|
|
71
|
+
if (currentIndex === NULL_VOTE_INDEX && nextIndex === NULL_VOTE_INDEX) {
|
|
72
|
+
oldInactiveValidators++;
|
|
41
73
|
continue;
|
|
42
74
|
}
|
|
43
|
-
currentIndex = vote.currentIndex;
|
|
44
|
-
nextIndex = vote.nextIndex;
|
|
45
75
|
|
|
46
76
|
// IF the validator was not included in the _old_ balances (i.e. it did not exist yet)
|
|
47
77
|
// then say its balance was 0
|
|
@@ -56,7 +86,7 @@ export function computeDeltas(
|
|
|
56
86
|
|
|
57
87
|
if (vIndex === equivocatingValidatorIndex) {
|
|
58
88
|
// this function could be called multiple times but we only want to process slashing validator for 1 time
|
|
59
|
-
if (currentIndex !==
|
|
89
|
+
if (currentIndex !== NULL_VOTE_INDEX) {
|
|
60
90
|
if (currentIndex >= numProtoNodes) {
|
|
61
91
|
throw new ProtoArrayError({
|
|
62
92
|
code: ProtoArrayErrorCode.INVALID_NODE_DELTA,
|
|
@@ -65,16 +95,21 @@ export function computeDeltas(
|
|
|
65
95
|
}
|
|
66
96
|
deltas[currentIndex] -= oldBalance;
|
|
67
97
|
}
|
|
68
|
-
|
|
98
|
+
voteCurrentIndices[vIndex] = NULL_VOTE_INDEX;
|
|
69
99
|
equivocatingIndex++;
|
|
70
100
|
equivocatingValidatorIndex = equivocatingArray[equivocatingIndex];
|
|
71
101
|
continue;
|
|
72
102
|
}
|
|
73
103
|
|
|
104
|
+
if (oldBalance === 0 && newBalance === 0) {
|
|
105
|
+
newInactiveValidators++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
74
109
|
if (currentIndex !== nextIndex || oldBalance !== newBalance) {
|
|
75
110
|
// We ignore the vote if it is not known in `indices .
|
|
76
111
|
// We assume that it is outside of our tree (ie: pre-finalization) and therefore not interesting
|
|
77
|
-
if (currentIndex !==
|
|
112
|
+
if (currentIndex !== NULL_VOTE_INDEX) {
|
|
78
113
|
if (currentIndex >= numProtoNodes) {
|
|
79
114
|
throw new ProtoArrayError({
|
|
80
115
|
code: ProtoArrayErrorCode.INVALID_NODE_DELTA,
|
|
@@ -86,7 +121,7 @@ export function computeDeltas(
|
|
|
86
121
|
|
|
87
122
|
// We ignore the vote if it is not known in `indices .
|
|
88
123
|
// We assume that it is outside of our tree (ie: pre-finalization) and therefore not interesting
|
|
89
|
-
if (nextIndex !==
|
|
124
|
+
if (nextIndex !== NULL_VOTE_INDEX) {
|
|
90
125
|
if (nextIndex >= numProtoNodes) {
|
|
91
126
|
throw new ProtoArrayError({
|
|
92
127
|
code: ProtoArrayErrorCode.INVALID_NODE_DELTA,
|
|
@@ -95,9 +130,24 @@ export function computeDeltas(
|
|
|
95
130
|
}
|
|
96
131
|
deltas[nextIndex] += newBalance;
|
|
97
132
|
}
|
|
133
|
+
voteCurrentIndices[vIndex] = nextIndex;
|
|
134
|
+
newVoteValidators++;
|
|
135
|
+
} else {
|
|
136
|
+
unchangedVoteValidators++;
|
|
98
137
|
}
|
|
99
|
-
|
|
138
|
+
} // end validator loop
|
|
139
|
+
|
|
140
|
+
if (deltas.length !== numProtoNodes) {
|
|
141
|
+
// deltas array could be growed in the loop, especially if we mistakenly set the [NULL_VOTE_INDEX] to it , just to be safe
|
|
142
|
+
throw new Error(`deltas length mismatch: expected ${numProtoNodes}, got ${deltas.length}`);
|
|
100
143
|
}
|
|
101
144
|
|
|
102
|
-
return
|
|
145
|
+
return {
|
|
146
|
+
deltas,
|
|
147
|
+
equivocatingValidators,
|
|
148
|
+
oldInactiveValidators,
|
|
149
|
+
newInactiveValidators,
|
|
150
|
+
unchangedVoteValidators,
|
|
151
|
+
newVoteValidators,
|
|
152
|
+
};
|
|
103
153
|
}
|
|
@@ -6,15 +6,16 @@ import {Epoch, RootHex, Slot, UintNum64} from "@lodestar/types";
|
|
|
6
6
|
export const HEX_ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* The null vote index indicates that a validator votes to a non-existent block. This usually happens when
|
|
10
|
+
* we prune the proto array and the validator's latest message is in the pruned part.
|
|
11
|
+
* The number of proto nodes will never exceed this value because it represents (0xffffffff / 365 / 24 / 60 / 5), ie > 1634 years of non-finalized network.
|
|
11
12
|
*/
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
export const NULL_VOTE_INDEX = 0xffffffff;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A vote index is a non-negative integer from 0 to NULL_VOTE_INDEX inclusive, and it will never be undefined.
|
|
17
|
+
*/
|
|
18
|
+
export type VoteIndex = number;
|
|
18
19
|
|
|
19
20
|
export enum ExecutionStatus {
|
|
20
21
|
Valid = "Valid",
|