@lodestar/state-transition 1.41.0-dev.702f7932c2 → 1.41.0-dev.95cf2edc4c
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/block/isValidIndexedAttestation.d.ts +3 -3
- package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
- package/lib/block/isValidIndexedAttestation.js +4 -4
- package/lib/block/isValidIndexedAttestation.js.map +1 -1
- package/lib/block/isValidIndexedPayloadAttestation.js +1 -1
- package/lib/block/isValidIndexedPayloadAttestation.js.map +1 -1
- package/lib/block/processAttestationPhase0.js +1 -1
- package/lib/block/processAttestationPhase0.js.map +1 -1
- package/lib/block/processAttestationsAltair.js +1 -1
- package/lib/block/processAttestationsAltair.js.map +1 -1
- package/lib/block/processAttesterSlashing.d.ts +2 -2
- package/lib/block/processAttesterSlashing.d.ts.map +1 -1
- package/lib/block/processAttesterSlashing.js +3 -3
- package/lib/block/processAttesterSlashing.js.map +1 -1
- package/lib/block/processDepositRequest.d.ts +1 -1
- package/lib/block/processDepositRequest.d.ts.map +1 -1
- package/lib/block/processDepositRequest.js +6 -5
- package/lib/block/processDepositRequest.js.map +1 -1
- package/lib/block/processExecutionPayloadBid.d.ts.map +1 -1
- package/lib/block/processExecutionPayloadBid.js +5 -0
- package/lib/block/processExecutionPayloadBid.js.map +1 -1
- package/lib/block/processExecutionPayloadEnvelope.js +5 -10
- package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
- package/lib/block/processProposerSlashing.d.ts +2 -2
- package/lib/block/processProposerSlashing.d.ts.map +1 -1
- package/lib/block/processProposerSlashing.js +3 -3
- package/lib/block/processProposerSlashing.js.map +1 -1
- package/lib/block/processRandao.js +1 -1
- package/lib/block/processRandao.js.map +1 -1
- package/lib/block/processSyncCommittee.js +1 -1
- package/lib/block/processSyncCommittee.js.map +1 -1
- package/lib/block/processVoluntaryExit.js +1 -1
- package/lib/block/processVoluntaryExit.js.map +1 -1
- package/lib/block/processWithdrawalRequest.js +2 -2
- package/lib/block/processWithdrawalRequest.js.map +1 -1
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +9 -1
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/cache/epochCache.d.ts +9 -18
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +28 -41
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/cache/pubkeyCache.d.ts +21 -6
- package/lib/cache/pubkeyCache.d.ts.map +1 -1
- package/lib/cache/pubkeyCache.js +39 -14
- package/lib/cache/pubkeyCache.js.map +1 -1
- package/lib/cache/stateCache.d.ts +1 -1
- package/lib/cache/stateCache.d.ts.map +1 -1
- package/lib/cache/stateCache.js +3 -7
- package/lib/cache/stateCache.js.map +1 -1
- package/lib/cache/syncCommitteeCache.d.ts +3 -2
- package/lib/cache/syncCommitteeCache.d.ts.map +1 -1
- package/lib/cache/syncCommitteeCache.js +4 -4
- package/lib/cache/syncCommitteeCache.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/lightClient/proofs.d.ts +10 -0
- package/lib/lightClient/proofs.d.ts.map +1 -0
- package/lib/lightClient/proofs.js +63 -0
- package/lib/lightClient/proofs.js.map +1 -0
- package/lib/lightClient/types.d.ts +34 -0
- package/lib/lightClient/types.d.ts.map +1 -0
- package/lib/lightClient/types.js +2 -0
- package/lib/lightClient/types.js.map +1 -0
- package/lib/rewards/attestationsRewards.d.ts +2 -2
- package/lib/rewards/attestationsRewards.d.ts.map +1 -1
- package/lib/rewards/attestationsRewards.js +4 -4
- package/lib/rewards/attestationsRewards.js.map +1 -1
- package/lib/rewards/syncCommitteeRewards.d.ts +2 -2
- package/lib/rewards/syncCommitteeRewards.d.ts.map +1 -1
- package/lib/rewards/syncCommitteeRewards.js +5 -2
- package/lib/rewards/syncCommitteeRewards.js.map +1 -1
- package/lib/signatureSets/indexedPayloadAttestation.d.ts +3 -4
- package/lib/signatureSets/indexedPayloadAttestation.d.ts.map +1 -1
- package/lib/signatureSets/indexedPayloadAttestation.js +4 -4
- package/lib/signatureSets/indexedPayloadAttestation.js.map +1 -1
- package/lib/signatureSets/proposer.d.ts +2 -2
- package/lib/signatureSets/proposer.d.ts.map +1 -1
- package/lib/signatureSets/proposer.js +2 -2
- package/lib/signatureSets/proposer.js.map +1 -1
- package/lib/signatureSets/randao.d.ts +2 -2
- package/lib/signatureSets/randao.d.ts.map +1 -1
- package/lib/signatureSets/randao.js +2 -2
- package/lib/signatureSets/randao.js.map +1 -1
- package/lib/signatureSets/voluntaryExits.d.ts +2 -2
- package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
- package/lib/signatureSets/voluntaryExits.js +2 -2
- package/lib/signatureSets/voluntaryExits.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +50 -0
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateTransition.d.ts +2 -1
- package/lib/stateTransition.d.ts.map +1 -1
- package/lib/stateTransition.js +2 -1
- package/lib/stateTransition.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +144 -0
- package/lib/stateView/beaconStateView.d.ts.map +1 -0
- package/lib/stateView/beaconStateView.js +496 -0
- package/lib/stateView/beaconStateView.js.map +1 -0
- package/lib/stateView/index.d.ts +3 -0
- package/lib/stateView/index.d.ts.map +1 -0
- package/lib/stateView/index.js +3 -0
- package/lib/stateView/index.js.map +1 -0
- package/lib/stateView/interface.d.ts +118 -0
- package/lib/stateView/interface.d.ts.map +1 -0
- package/lib/stateView/interface.js +2 -0
- package/lib/stateView/interface.js.map +1 -0
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +10 -3
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/seed.d.ts +20 -4
- package/lib/util/seed.d.ts.map +1 -1
- package/lib/util/seed.js +80 -8
- package/lib/util/seed.js.map +1 -1
- package/lib/util/signatureSets.d.ts +7 -7
- package/lib/util/signatureSets.d.ts.map +1 -1
- package/lib/util/signatureSets.js +18 -12
- package/lib/util/signatureSets.js.map +1 -1
- package/lib/util/weakSubjectivity.js +1 -1
- package/lib/util/weakSubjectivity.js.map +1 -1
- package/package.json +7 -7
- package/src/block/isValidIndexedAttestation.ts +5 -5
- package/src/block/isValidIndexedPayloadAttestation.ts +2 -2
- package/src/block/processAttestationPhase0.ts +1 -1
- package/src/block/processAttestationsAltair.ts +1 -1
- package/src/block/processAttesterSlashing.ts +4 -4
- package/src/block/processDepositRequest.ts +8 -5
- package/src/block/processExecutionPayloadBid.ts +8 -0
- package/src/block/processExecutionPayloadEnvelope.ts +5 -16
- package/src/block/processProposerSlashing.ts +4 -4
- package/src/block/processRandao.ts +1 -1
- package/src/block/processSyncCommittee.ts +1 -1
- package/src/block/processVoluntaryExit.ts +1 -1
- package/src/block/processWithdrawalRequest.ts +2 -2
- package/src/block/processWithdrawals.ts +10 -1
- package/src/cache/epochCache.ts +41 -55
- package/src/cache/pubkeyCache.ts +62 -21
- package/src/cache/stateCache.ts +4 -8
- package/src/cache/syncCommitteeCache.ts +4 -5
- package/src/index.ts +3 -1
- package/src/lightClient/proofs.ts +83 -0
- package/src/lightClient/types.ts +33 -0
- package/src/rewards/attestationsRewards.ts +5 -5
- package/src/rewards/syncCommitteeRewards.ts +6 -5
- package/src/signatureSets/indexedPayloadAttestation.ts +4 -6
- package/src/signatureSets/proposer.ts +3 -3
- package/src/signatureSets/randao.ts +3 -7
- package/src/signatureSets/voluntaryExits.ts +3 -3
- package/src/slot/upgradeStateToGloas.ts +74 -0
- package/src/stateTransition.ts +2 -1
- package/src/stateView/beaconStateView.ts +744 -0
- package/src/stateView/index.ts +2 -0
- package/src/stateView/interface.ts +196 -0
- package/src/util/gloas.ts +11 -3
- package/src/util/seed.ts +106 -18
- package/src/util/signatureSets.ts +23 -17
- package/src/util/weakSubjectivity.ts +1 -1
|
@@ -14,7 +14,8 @@ export function applyDepositForBuilder(
|
|
|
14
14
|
pubkey: BLSPubkey,
|
|
15
15
|
withdrawalCredentials: Bytes32,
|
|
16
16
|
amount: UintNum64,
|
|
17
|
-
signature: Bytes32
|
|
17
|
+
signature: Bytes32,
|
|
18
|
+
slot: UintNum64
|
|
18
19
|
): void {
|
|
19
20
|
const builderIndex = findBuilderIndexByPubkey(state, pubkey);
|
|
20
21
|
|
|
@@ -25,7 +26,7 @@ export function applyDepositForBuilder(
|
|
|
25
26
|
} else {
|
|
26
27
|
// New builder - verify signature and add to registry
|
|
27
28
|
if (isValidDepositSignature(state.config, pubkey, withdrawalCredentials, amount, signature)) {
|
|
28
|
-
addBuilderToRegistry(state, pubkey, withdrawalCredentials, amount);
|
|
29
|
+
addBuilderToRegistry(state, pubkey, withdrawalCredentials, amount, slot);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -38,9 +39,11 @@ function addBuilderToRegistry(
|
|
|
38
39
|
state: CachedBeaconStateGloas,
|
|
39
40
|
pubkey: BLSPubkey,
|
|
40
41
|
withdrawalCredentials: Bytes32,
|
|
41
|
-
amount: UintNum64
|
|
42
|
+
amount: UintNum64,
|
|
43
|
+
slot: UintNum64
|
|
42
44
|
): void {
|
|
43
45
|
const currentEpoch = computeEpochAtSlot(state.slot);
|
|
46
|
+
const depositEpoch = computeEpochAtSlot(slot);
|
|
44
47
|
|
|
45
48
|
// Try to find a reusable slot from an exited builder with zero balance
|
|
46
49
|
let builderIndex = state.builders.length;
|
|
@@ -58,7 +61,7 @@ function addBuilderToRegistry(
|
|
|
58
61
|
version: withdrawalCredentials[0],
|
|
59
62
|
executionAddress: withdrawalCredentials.subarray(12),
|
|
60
63
|
balance: amount,
|
|
61
|
-
depositEpoch:
|
|
64
|
+
depositEpoch: depositEpoch,
|
|
62
65
|
withdrawableEpoch: FAR_FUTURE_EPOCH,
|
|
63
66
|
});
|
|
64
67
|
|
|
@@ -93,7 +96,7 @@ export function processDepositRequest(
|
|
|
93
96
|
// Route to builder if it's an existing builder OR has builder prefix and is not a validator
|
|
94
97
|
if (isBuilder || (isBuilderPrefix && !isValidator)) {
|
|
95
98
|
// Apply builder deposits immediately
|
|
96
|
-
applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature);
|
|
99
|
+
applyDepositForBuilder(stateGloas, pubkey, withdrawalCredentials, amount, signature, state.slot);
|
|
97
100
|
return;
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -63,6 +63,14 @@ export function processExecutionPayloadBid(state: CachedBeaconStateGloas, block:
|
|
|
63
63
|
throw Error(`Prev randao ${toHex(bid.prevRandao)} of bid does not match state's randao mix ${toHex(stateRandao)}`);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// Verify commitments are under limit
|
|
67
|
+
const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(state.epochCtx.epoch);
|
|
68
|
+
if (bid.blobKzgCommitments.length > maxBlobsPerBlock) {
|
|
69
|
+
throw Error(
|
|
70
|
+
`Kzg commitments exceed limit commitments.length=${bid.blobKzgCommitments.length} limit=${maxBlobsPerBlock}`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
66
74
|
if (amount > 0) {
|
|
67
75
|
const pendingPaymentView = ssz.gloas.BuilderPendingPayment.toViewDU({
|
|
68
76
|
weight: 0,
|
|
@@ -96,13 +96,6 @@ function validateExecutionPayloadEnvelope(
|
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
const envelopeKzgRoot = ssz.deneb.BlobKzgCommitments.hashTreeRoot(envelope.blobKzgCommitments);
|
|
100
|
-
if (!byteArrayEquals(committedBid.blobKzgCommitmentsRoot, envelopeKzgRoot)) {
|
|
101
|
-
throw new Error(
|
|
102
|
-
`Kzg commitment root mismatch between envelope and committed bid envelope=${toRootHex(envelopeKzgRoot)} committedBid=${toRootHex(committedBid.blobKzgCommitmentsRoot)}`
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
99
|
if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
|
|
107
100
|
throw new Error(
|
|
108
101
|
`Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
@@ -146,14 +139,6 @@ function validateExecutionPayloadEnvelope(
|
|
|
146
139
|
);
|
|
147
140
|
}
|
|
148
141
|
|
|
149
|
-
// Verify commitments are under limit
|
|
150
|
-
const maxBlobsPerBlock = state.config.getMaxBlobsPerBlock(state.epochCtx.epoch);
|
|
151
|
-
if (envelope.blobKzgCommitments.length > maxBlobsPerBlock) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`Kzg commitments exceed limit commitment.length=${envelope.blobKzgCommitments.length} limit=${maxBlobsPerBlock}`
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
142
|
// Skipped: Verify the execution payload is valid
|
|
158
143
|
}
|
|
159
144
|
|
|
@@ -171,7 +156,11 @@ function verifyExecutionPayloadEnvelopeSignature(
|
|
|
171
156
|
|
|
172
157
|
if (builderIndex === BUILDER_INDEX_SELF_BUILD) {
|
|
173
158
|
const validatorIndex = state.latestBlockHeader.proposerIndex;
|
|
174
|
-
|
|
159
|
+
const proposerPubkey = state.epochCtx.pubkeyCache.get(validatorIndex);
|
|
160
|
+
if (!proposerPubkey) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
publicKey = proposerPubkey;
|
|
175
164
|
} else {
|
|
176
165
|
publicKey = PublicKey.fromBytes(state.builders.getReadonly(builderIndex).pubkey);
|
|
177
166
|
}
|
|
@@ -2,7 +2,7 @@ import {BeaconConfig} from "@lodestar/config";
|
|
|
2
2
|
import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
3
3
|
import {Slot, phase0, ssz} from "@lodestar/types";
|
|
4
4
|
import {Validator} from "@lodestar/types/phase0";
|
|
5
|
-
import {
|
|
5
|
+
import {PubkeyCache} from "../cache/pubkeyCache.js";
|
|
6
6
|
import {getProposerSlashingSignatureSets} from "../signatureSets/index.js";
|
|
7
7
|
import {CachedBeaconStateAllForks, CachedBeaconStateGloas} from "../types.js";
|
|
8
8
|
import {computeEpochAtSlot, isSlashableValidator} from "../util/index.js";
|
|
@@ -24,7 +24,7 @@ export function processProposerSlashing(
|
|
|
24
24
|
const proposer = state.validators.getReadonly(proposerSlashing.signedHeader1.message.proposerIndex);
|
|
25
25
|
assertValidProposerSlashing(
|
|
26
26
|
state.config,
|
|
27
|
-
state.epochCtx.
|
|
27
|
+
state.epochCtx.pubkeyCache,
|
|
28
28
|
state.slot,
|
|
29
29
|
proposerSlashing,
|
|
30
30
|
proposer,
|
|
@@ -57,7 +57,7 @@ export function processProposerSlashing(
|
|
|
57
57
|
|
|
58
58
|
export function assertValidProposerSlashing(
|
|
59
59
|
config: BeaconConfig,
|
|
60
|
-
|
|
60
|
+
pubkeyCache: PubkeyCache,
|
|
61
61
|
stateSlot: Slot,
|
|
62
62
|
proposerSlashing: phase0.ProposerSlashing,
|
|
63
63
|
proposer: Validator,
|
|
@@ -94,7 +94,7 @@ export function assertValidProposerSlashing(
|
|
|
94
94
|
if (verifySignatures) {
|
|
95
95
|
const signatureSets = getProposerSlashingSignatureSets(config, stateSlot, proposerSlashing);
|
|
96
96
|
for (let i = 0; i < signatureSets.length; i++) {
|
|
97
|
-
if (!verifySignatureSet(signatureSets[i],
|
|
97
|
+
if (!verifySignatureSet(signatureSets[i], pubkeyCache)) {
|
|
98
98
|
throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -17,7 +17,7 @@ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlo
|
|
|
17
17
|
const randaoReveal = block.body.randaoReveal;
|
|
18
18
|
|
|
19
19
|
// verify RANDAO reveal
|
|
20
|
-
if (verifySignature && !verifyRandaoSignature(config, epochCtx.
|
|
20
|
+
if (verifySignature && !verifyRandaoSignature(config, epochCtx.pubkeyCache, block)) {
|
|
21
21
|
throw new Error("RANDAO reveal is an invalid signature");
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -32,7 +32,7 @@ export function processSyncAggregate(
|
|
|
32
32
|
participantIndices
|
|
33
33
|
);
|
|
34
34
|
// When there's no participation we consider the signature valid and just ignore it
|
|
35
|
-
if (signatureSet !== null && !verifySignatureSet(signatureSet, state.epochCtx.
|
|
35
|
+
if (signatureSet !== null && !verifySignatureSet(signatureSet, state.epochCtx.pubkeyCache)) {
|
|
36
36
|
throw Error("Sync committee signature invalid");
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -131,7 +131,7 @@ export function getVoluntaryExitValidity(
|
|
|
131
131
|
|
|
132
132
|
if (
|
|
133
133
|
verifySignature &&
|
|
134
|
-
!verifyVoluntaryExitSignature(state.config, epochCtx.
|
|
134
|
+
!verifyVoluntaryExitSignature(state.config, epochCtx.pubkeyCache, state.slot, signedVoluntaryExit)
|
|
135
135
|
) {
|
|
136
136
|
return VoluntaryExitValidity.invalidSignature;
|
|
137
137
|
}
|
|
@@ -21,7 +21,7 @@ export function processWithdrawalRequest(
|
|
|
21
21
|
const amount = Number(withdrawalRequest.amount);
|
|
22
22
|
const {pendingPartialWithdrawals, validators, epochCtx} = state;
|
|
23
23
|
// no need to use unfinalized pubkey cache from 6110 as validator won't be active anyway
|
|
24
|
-
const {
|
|
24
|
+
const {pubkeyCache, config} = epochCtx;
|
|
25
25
|
const isFullExitRequest = amount === FULL_EXIT_REQUEST_AMOUNT;
|
|
26
26
|
|
|
27
27
|
// If partial withdrawal queue is full, only full exits are processed
|
|
@@ -31,7 +31,7 @@ export function processWithdrawalRequest(
|
|
|
31
31
|
|
|
32
32
|
// bail out if validator is not in beacon state
|
|
33
33
|
// note that we don't need to check for 6110 unfinalized vals as they won't be eligible for withdraw/exit anyway
|
|
34
|
-
const validatorIndex =
|
|
34
|
+
const validatorIndex = pubkeyCache.getIndex(withdrawalRequest.validatorPubkey);
|
|
35
35
|
if (validatorIndex === null) {
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
@@ -249,6 +249,10 @@ function getPendingPartialWithdrawals(
|
|
|
249
249
|
numPriorWithdrawal + MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP,
|
|
250
250
|
MAX_WITHDRAWALS_PER_PAYLOAD - 1
|
|
251
251
|
);
|
|
252
|
+
// There must be at least one space reserved for validator sweep withdrawals
|
|
253
|
+
if (numPriorWithdrawal > partialWithdrawalBound) {
|
|
254
|
+
throw Error(`Prior withdrawals exceed limit: ${numPriorWithdrawal} > ${partialWithdrawalBound}`);
|
|
255
|
+
}
|
|
252
256
|
|
|
253
257
|
// MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP = 8, PENDING_PARTIAL_WITHDRAWALS_LIMIT: 134217728 so we should only call getAllReadonly() if it makes sense
|
|
254
258
|
// pendingPartialWithdrawals comes from EIP-7002 smart contract where it takes fee so it's more likely than not validator is in correct condition to withdraw
|
|
@@ -309,6 +313,11 @@ function getValidatorsSweepWithdrawals(
|
|
|
309
313
|
numPriorWithdrawal: number,
|
|
310
314
|
validatorBalanceAfterWithdrawals: Map<ValidatorIndex, number>
|
|
311
315
|
): {sweepWithdrawals: capella.Withdrawal[]; processedCount: number} {
|
|
316
|
+
// There must be at least one space reserved for validator sweep withdrawals
|
|
317
|
+
if (numPriorWithdrawal >= MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
318
|
+
throw Error(`Prior withdrawals exceed limit: ${numPriorWithdrawal} >= ${MAX_WITHDRAWALS_PER_PAYLOAD}`);
|
|
319
|
+
}
|
|
320
|
+
|
|
312
321
|
const sweepWithdrawals: capella.Withdrawal[] = [];
|
|
313
322
|
const epoch = state.epochCtx.epoch;
|
|
314
323
|
const {validators, balances, nextWithdrawalValidatorIndex} = state;
|
|
@@ -319,7 +328,7 @@ function getValidatorsSweepWithdrawals(
|
|
|
319
328
|
// Just run a bounded loop max iterating over all withdrawals
|
|
320
329
|
// however breaks out once we have MAX_WITHDRAWALS_PER_PAYLOAD
|
|
321
330
|
for (let n = 0; n < validatorsLimit; n++) {
|
|
322
|
-
if (sweepWithdrawals.length + numPriorWithdrawal
|
|
331
|
+
if (sweepWithdrawals.length + numPriorWithdrawal >= MAX_WITHDRAWALS_PER_PAYLOAD) {
|
|
323
332
|
break;
|
|
324
333
|
}
|
|
325
334
|
|
package/src/cache/epochCache.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
-
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
|
3
2
|
import {BeaconConfig, ChainConfig, createBeaconConfig} from "@lodestar/config";
|
|
4
3
|
import {
|
|
5
4
|
ATTESTATION_SUBNET_COUNT,
|
|
@@ -36,6 +35,7 @@ import {
|
|
|
36
35
|
import {
|
|
37
36
|
computeActivationExitEpoch,
|
|
38
37
|
computeEpochAtSlot,
|
|
38
|
+
computePayloadTimelinessCommitteesForEpoch,
|
|
39
39
|
computeProposers,
|
|
40
40
|
computeSyncPeriodAtEpoch,
|
|
41
41
|
getActivationChurnLimit,
|
|
@@ -43,7 +43,6 @@ import {
|
|
|
43
43
|
getSeed,
|
|
44
44
|
isActiveValidator,
|
|
45
45
|
isAggregatorFromCommitteeLength,
|
|
46
|
-
naiveGetPayloadTimlinessCommitteeIndices,
|
|
47
46
|
} from "../util/index.js";
|
|
48
47
|
import {
|
|
49
48
|
AttesterDuty,
|
|
@@ -56,7 +55,7 @@ import {computeBaseRewardPerIncrement, computeSyncParticipantReward} from "../ut
|
|
|
56
55
|
import {sumTargetUnslashedBalanceIncrements} from "../util/targetUnslashedBalance.js";
|
|
57
56
|
import {EffectiveBalanceIncrements, getEffectiveBalanceIncrementsWithLen} from "./effectiveBalanceIncrements.js";
|
|
58
57
|
import {EpochTransitionCache} from "./epochTransitionCache.js";
|
|
59
|
-
import {
|
|
58
|
+
import {PubkeyCache, createPubkeyCache, syncPubkeys} from "./pubkeyCache.js";
|
|
60
59
|
import {CachedBeaconStateAllForks, CachedBeaconStateFulu} from "./stateCache.js";
|
|
61
60
|
import {
|
|
62
61
|
SyncCommitteeCache,
|
|
@@ -64,15 +63,14 @@ import {
|
|
|
64
63
|
computeSyncCommitteeCache,
|
|
65
64
|
getSyncCommitteeCache,
|
|
66
65
|
} from "./syncCommitteeCache.js";
|
|
67
|
-
import {BeaconStateAllForks, BeaconStateAltair,
|
|
66
|
+
import {BeaconStateAllForks, BeaconStateAltair, ShufflingGetter} from "./types.js";
|
|
68
67
|
|
|
69
68
|
/** `= PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT)` */
|
|
70
69
|
export const PROPOSER_WEIGHT_FACTOR = PROPOSER_WEIGHT / (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT);
|
|
71
70
|
|
|
72
71
|
export type EpochCacheImmutableData = {
|
|
73
72
|
config: BeaconConfig;
|
|
74
|
-
|
|
75
|
-
index2pubkey: Index2PubkeyCache;
|
|
73
|
+
pubkeyCache: PubkeyCache;
|
|
76
74
|
};
|
|
77
75
|
|
|
78
76
|
export type EpochCacheOpts = {
|
|
@@ -111,15 +109,9 @@ export class EpochCache {
|
|
|
111
109
|
/**
|
|
112
110
|
* Unique globally shared pubkey registry. There should only exist one for the entire application.
|
|
113
111
|
*
|
|
114
|
-
*
|
|
112
|
+
* Couples both index→pubkey and pubkey→index lookups, keeping them in sync atomically.
|
|
115
113
|
*/
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Unique globally shared pubkey registry. There should only exist one for the entire application.
|
|
119
|
-
*
|
|
120
|
-
* $VALIDATOR_COUNT x BLST deserialized pubkey (Jacobian coordinates)
|
|
121
|
-
*/
|
|
122
|
-
index2pubkey: Index2PubkeyCache;
|
|
114
|
+
pubkeyCache: PubkeyCache;
|
|
123
115
|
/**
|
|
124
116
|
* Indexes of the block proposers for the current epoch.
|
|
125
117
|
* For pre-fulu, this is computed and cached from the current shuffling.
|
|
@@ -235,8 +227,8 @@ export class EpochCache {
|
|
|
235
227
|
nextSyncCommitteeIndexed: SyncCommitteeCache;
|
|
236
228
|
|
|
237
229
|
// TODO GLOAS: See if we need to cached PTC for prev/next epoch
|
|
238
|
-
// PTC for current epoch
|
|
239
|
-
|
|
230
|
+
// PTC for current epoch, computed eagerly at epoch transition
|
|
231
|
+
payloadTimelinessCommittees: Uint32Array[];
|
|
240
232
|
|
|
241
233
|
// TODO: Helper stats
|
|
242
234
|
syncPeriod: SyncPeriod;
|
|
@@ -249,8 +241,7 @@ export class EpochCache {
|
|
|
249
241
|
|
|
250
242
|
constructor(data: {
|
|
251
243
|
config: BeaconConfig;
|
|
252
|
-
|
|
253
|
-
index2pubkey: Index2PubkeyCache;
|
|
244
|
+
pubkeyCache: PubkeyCache;
|
|
254
245
|
proposers: number[];
|
|
255
246
|
proposersPrevEpoch: number[] | null;
|
|
256
247
|
proposersNextEpoch: ProposersDeferred;
|
|
@@ -275,13 +266,12 @@ export class EpochCache {
|
|
|
275
266
|
previousTargetUnslashedBalanceIncrements: number;
|
|
276
267
|
currentSyncCommitteeIndexed: SyncCommitteeCache;
|
|
277
268
|
nextSyncCommitteeIndexed: SyncCommitteeCache;
|
|
278
|
-
|
|
269
|
+
payloadTimelinessCommittees: Uint32Array[];
|
|
279
270
|
epoch: Epoch;
|
|
280
271
|
syncPeriod: SyncPeriod;
|
|
281
272
|
}) {
|
|
282
273
|
this.config = data.config;
|
|
283
|
-
this.
|
|
284
|
-
this.index2pubkey = data.index2pubkey;
|
|
274
|
+
this.pubkeyCache = data.pubkeyCache;
|
|
285
275
|
this.proposers = data.proposers;
|
|
286
276
|
this.proposersPrevEpoch = data.proposersPrevEpoch;
|
|
287
277
|
this.proposersNextEpoch = data.proposersNextEpoch;
|
|
@@ -306,7 +296,7 @@ export class EpochCache {
|
|
|
306
296
|
this.previousTargetUnslashedBalanceIncrements = data.previousTargetUnslashedBalanceIncrements;
|
|
307
297
|
this.currentSyncCommitteeIndexed = data.currentSyncCommitteeIndexed;
|
|
308
298
|
this.nextSyncCommitteeIndexed = data.nextSyncCommitteeIndexed;
|
|
309
|
-
this.
|
|
299
|
+
this.payloadTimelinessCommittees = data.payloadTimelinessCommittees;
|
|
310
300
|
this.epoch = data.epoch;
|
|
311
301
|
this.syncPeriod = data.syncPeriod;
|
|
312
302
|
}
|
|
@@ -319,7 +309,7 @@ export class EpochCache {
|
|
|
319
309
|
*/
|
|
320
310
|
static createFromState(
|
|
321
311
|
state: BeaconStateAllForks,
|
|
322
|
-
{config,
|
|
312
|
+
{config, pubkeyCache}: EpochCacheImmutableData,
|
|
323
313
|
opts?: EpochCacheOpts
|
|
324
314
|
): EpochCache {
|
|
325
315
|
const currentEpoch = computeEpochAtSlot(state.slot);
|
|
@@ -335,9 +325,9 @@ export class EpochCache {
|
|
|
335
325
|
const validatorCount = validators.length;
|
|
336
326
|
|
|
337
327
|
// syncPubkeys here to ensure EpochCacheImmutableData is popualted before computing the rest of caches
|
|
338
|
-
// - computeSyncCommitteeCache() needs a fully populated
|
|
328
|
+
// - computeSyncCommitteeCache() needs a fully populated pubkeyCache
|
|
339
329
|
if (!opts?.skipSyncPubkeys) {
|
|
340
|
-
syncPubkeys(
|
|
330
|
+
syncPubkeys(pubkeyCache, validators);
|
|
341
331
|
}
|
|
342
332
|
|
|
343
333
|
const effectiveBalanceIncrements = getEffectiveBalanceIncrementsWithLen(validatorCount);
|
|
@@ -450,21 +440,21 @@ export class EpochCache {
|
|
|
450
440
|
// Allow to skip populating sync committee for initializeBeaconStateFromEth1()
|
|
451
441
|
if (afterAltairFork && !opts?.skipSyncCommitteeCache) {
|
|
452
442
|
const altairState = state as BeaconStateAltair;
|
|
453
|
-
currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee,
|
|
454
|
-
nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee,
|
|
443
|
+
currentSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.currentSyncCommittee, pubkeyCache);
|
|
444
|
+
nextSyncCommitteeIndexed = computeSyncCommitteeCache(altairState.nextSyncCommittee, pubkeyCache);
|
|
455
445
|
} else {
|
|
456
446
|
currentSyncCommitteeIndexed = new SyncCommitteeCacheEmpty();
|
|
457
447
|
nextSyncCommitteeIndexed = new SyncCommitteeCacheEmpty();
|
|
458
448
|
}
|
|
459
449
|
|
|
460
|
-
// Compute PTC for
|
|
461
|
-
let
|
|
450
|
+
// Compute PTC eagerly for all slots in the epoch
|
|
451
|
+
let payloadTimelinessCommittees: Uint32Array[] = [];
|
|
462
452
|
if (currentEpoch >= config.GLOAS_FORK_EPOCH) {
|
|
463
|
-
|
|
464
|
-
state
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
453
|
+
payloadTimelinessCommittees = computePayloadTimelinessCommitteesForEpoch(
|
|
454
|
+
state,
|
|
455
|
+
currentEpoch,
|
|
456
|
+
currentShuffling.committees,
|
|
457
|
+
effectiveBalanceIncrements
|
|
468
458
|
);
|
|
469
459
|
}
|
|
470
460
|
|
|
@@ -514,8 +504,7 @@ export class EpochCache {
|
|
|
514
504
|
|
|
515
505
|
return new EpochCache({
|
|
516
506
|
config,
|
|
517
|
-
|
|
518
|
-
index2pubkey,
|
|
507
|
+
pubkeyCache,
|
|
519
508
|
proposers,
|
|
520
509
|
// On first epoch, set to null to prevent unnecessary work since this is only used for metrics
|
|
521
510
|
proposersPrevEpoch: null,
|
|
@@ -541,7 +530,7 @@ export class EpochCache {
|
|
|
541
530
|
currentTargetUnslashedBalanceIncrements,
|
|
542
531
|
currentSyncCommitteeIndexed,
|
|
543
532
|
nextSyncCommitteeIndexed,
|
|
544
|
-
|
|
533
|
+
payloadTimelinessCommittees,
|
|
545
534
|
epoch: currentEpoch,
|
|
546
535
|
syncPeriod: computeSyncPeriodAtEpoch(currentEpoch),
|
|
547
536
|
});
|
|
@@ -557,8 +546,7 @@ export class EpochCache {
|
|
|
557
546
|
return new EpochCache({
|
|
558
547
|
config: this.config,
|
|
559
548
|
// Common append-only structures shared with all states, no need to clone
|
|
560
|
-
|
|
561
|
-
index2pubkey: this.index2pubkey,
|
|
549
|
+
pubkeyCache: this.pubkeyCache,
|
|
562
550
|
// Immutable data
|
|
563
551
|
proposers: this.proposers,
|
|
564
552
|
proposersPrevEpoch: this.proposersPrevEpoch,
|
|
@@ -587,7 +575,7 @@ export class EpochCache {
|
|
|
587
575
|
currentTargetUnslashedBalanceIncrements: this.currentTargetUnslashedBalanceIncrements,
|
|
588
576
|
currentSyncCommitteeIndexed: this.currentSyncCommitteeIndexed,
|
|
589
577
|
nextSyncCommitteeIndexed: this.nextSyncCommitteeIndexed,
|
|
590
|
-
|
|
578
|
+
payloadTimelinessCommittees: this.payloadTimelinessCommittees,
|
|
591
579
|
epoch: this.epoch,
|
|
592
580
|
syncPeriod: this.syncPeriod,
|
|
593
581
|
});
|
|
@@ -698,11 +686,11 @@ export class EpochCache {
|
|
|
698
686
|
|
|
699
687
|
this.proposersPrevEpoch = this.proposers;
|
|
700
688
|
if (upcomingEpoch >= this.config.GLOAS_FORK_EPOCH) {
|
|
701
|
-
this.
|
|
702
|
-
state
|
|
703
|
-
|
|
704
|
-
this.
|
|
705
|
-
|
|
689
|
+
this.payloadTimelinessCommittees = computePayloadTimelinessCommitteesForEpoch(
|
|
690
|
+
state,
|
|
691
|
+
upcomingEpoch,
|
|
692
|
+
this.currentShuffling.committees,
|
|
693
|
+
this.effectiveBalanceIncrements
|
|
706
694
|
);
|
|
707
695
|
}
|
|
708
696
|
if (upcomingEpoch >= this.config.FULU_FORK_EPOCH) {
|
|
@@ -882,16 +870,15 @@ export class EpochCache {
|
|
|
882
870
|
* Return pubkey given the validator index.
|
|
883
871
|
*/
|
|
884
872
|
getPubkey(index: ValidatorIndex): PublicKey | undefined {
|
|
885
|
-
return this.
|
|
873
|
+
return this.pubkeyCache.get(index);
|
|
886
874
|
}
|
|
887
875
|
|
|
888
876
|
getValidatorIndex(pubkey: Uint8Array): ValidatorIndex | null {
|
|
889
|
-
return this.
|
|
877
|
+
return this.pubkeyCache.getIndex(pubkey);
|
|
890
878
|
}
|
|
891
879
|
|
|
892
880
|
addPubkey(index: ValidatorIndex, pubkey: Uint8Array): void {
|
|
893
|
-
this.
|
|
894
|
-
this.index2pubkey[index] = PublicKey.fromBytes(pubkey); // Optimize for aggregation
|
|
881
|
+
this.pubkeyCache.set(index, pubkey);
|
|
895
882
|
}
|
|
896
883
|
|
|
897
884
|
getShufflingAtSlot(slot: Slot): EpochShuffling {
|
|
@@ -1022,18 +1009,18 @@ export class EpochCache {
|
|
|
1022
1009
|
return this.epoch >= this.config.ELECTRA_FORK_EPOCH;
|
|
1023
1010
|
}
|
|
1024
1011
|
|
|
1025
|
-
getPayloadTimelinessCommittee(slot: Slot):
|
|
1012
|
+
getPayloadTimelinessCommittee(slot: Slot): Uint32Array {
|
|
1026
1013
|
const epoch = computeEpochAtSlot(slot);
|
|
1027
1014
|
|
|
1028
1015
|
if (epoch < this.config.GLOAS_FORK_EPOCH) {
|
|
1029
1016
|
throw new Error("Payload Timeliness Committee is not available before gloas fork");
|
|
1030
1017
|
}
|
|
1031
1018
|
|
|
1032
|
-
if (epoch
|
|
1033
|
-
|
|
1019
|
+
if (epoch !== this.epoch) {
|
|
1020
|
+
throw new Error(`Payload Timeliness Committee is not available for slot=${slot}`);
|
|
1034
1021
|
}
|
|
1035
1022
|
|
|
1036
|
-
|
|
1023
|
+
return this.payloadTimelinessCommittees[slot % SLOTS_PER_EPOCH];
|
|
1037
1024
|
}
|
|
1038
1025
|
|
|
1039
1026
|
getIndexedPayloadAttestation(
|
|
@@ -1099,7 +1086,6 @@ export function createEmptyEpochCacheImmutableData(
|
|
|
1099
1086
|
return {
|
|
1100
1087
|
config: createBeaconConfig(chainConfig, state.genesisValidatorsRoot),
|
|
1101
1088
|
// This is a test state, there's no need to have a global shared cache of keys
|
|
1102
|
-
|
|
1103
|
-
index2pubkey: [],
|
|
1089
|
+
pubkeyCache: createPubkeyCache(),
|
|
1104
1090
|
};
|
|
1105
1091
|
}
|
package/src/cache/pubkeyCache.ts
CHANGED
|
@@ -1,33 +1,74 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
2
|
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
|
3
|
-
import {phase0} from "@lodestar/types";
|
|
3
|
+
import {ValidatorIndex, phase0} from "@lodestar/types";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Unified pubkey cache coupling index→pubkey and pubkey→index lookups.
|
|
7
|
+
* Both directions are kept in sync atomically via `set()`.
|
|
8
|
+
*/
|
|
9
|
+
export interface PubkeyCache {
|
|
10
|
+
/** Get deserialized PublicKey by validator index */
|
|
11
|
+
get(index: ValidatorIndex): PublicKey | undefined;
|
|
12
|
+
/** Get deserialized PublicKey by validator index or throw if not found */
|
|
13
|
+
getOrThrow(index: ValidatorIndex): PublicKey;
|
|
14
|
+
/** Get validator index by pubkey bytes */
|
|
15
|
+
getIndex(pubkey: Uint8Array): ValidatorIndex | null;
|
|
16
|
+
/** Set both directions atomically. Takes raw pubkey bytes — deserialization is handled internally. */
|
|
17
|
+
set(index: ValidatorIndex, pubkey: Uint8Array): void;
|
|
18
|
+
/** Number of entries */
|
|
19
|
+
readonly size: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class StandardPubkeyCache implements PubkeyCache {
|
|
23
|
+
private readonly pubkey2index: PubkeyIndexMap;
|
|
24
|
+
private readonly index2pubkey: (PublicKey | undefined)[];
|
|
25
|
+
|
|
26
|
+
constructor(pubkey2index?: PubkeyIndexMap, index2pubkey?: (PublicKey | undefined)[]) {
|
|
27
|
+
this.pubkey2index = pubkey2index ?? new PubkeyIndexMap();
|
|
28
|
+
this.index2pubkey = index2pubkey ?? [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get size(): number {
|
|
32
|
+
return this.pubkey2index.size;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get(index: ValidatorIndex): PublicKey | undefined {
|
|
36
|
+
return this.index2pubkey[index];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getOrThrow(index: ValidatorIndex): PublicKey {
|
|
40
|
+
const pubkey = this.get(index);
|
|
41
|
+
if (!pubkey) throw Error(`Missing pubkey for validator index ${index}`);
|
|
42
|
+
return pubkey;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getIndex(pubkey: Uint8Array): ValidatorIndex | null {
|
|
46
|
+
return this.pubkey2index.get(pubkey);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set(index: ValidatorIndex, pubkey: Uint8Array): void {
|
|
50
|
+
this.pubkey2index.set(pubkey, index);
|
|
51
|
+
// Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed.
|
|
52
|
+
// Afterwards any public key in the state is considered validated.
|
|
53
|
+
// > Do not do any validation here
|
|
54
|
+
this.index2pubkey[index] = PublicKey.fromBytes(pubkey); // Optimize for aggregation
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createPubkeyCache(): PubkeyCache {
|
|
59
|
+
return new StandardPubkeyCache();
|
|
60
|
+
}
|
|
6
61
|
|
|
7
62
|
/**
|
|
8
63
|
* Checks the pubkey indices against a state and adds missing pubkeys
|
|
9
64
|
*
|
|
10
|
-
* Mutates `
|
|
65
|
+
* Mutates `pubkeyCache`
|
|
11
66
|
*
|
|
12
|
-
* If pubkey
|
|
67
|
+
* If pubkey cache is empty: SLOW CODE - 🐢
|
|
13
68
|
*/
|
|
14
|
-
export function syncPubkeys(
|
|
15
|
-
validators: phase0.Validator[],
|
|
16
|
-
pubkey2index: PubkeyIndexMap,
|
|
17
|
-
index2pubkey: Index2PubkeyCache
|
|
18
|
-
): void {
|
|
19
|
-
if (pubkey2index.size !== index2pubkey.length) {
|
|
20
|
-
throw new Error(`Pubkey indices have fallen out of sync: ${pubkey2index.size} != ${index2pubkey.length}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
69
|
+
export function syncPubkeys(pubkeyCache: PubkeyCache, validators: phase0.Validator[]): void {
|
|
23
70
|
const newCount = validators.length;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const pubkey = validators[i].pubkey;
|
|
27
|
-
pubkey2index.set(pubkey, i);
|
|
28
|
-
// Pubkeys must be checked for group + inf. This must be done only once when the validator deposit is processed.
|
|
29
|
-
// Afterwards any public key is the state consider validated.
|
|
30
|
-
// > Do not do any validation here
|
|
31
|
-
index2pubkey[i] = PublicKey.fromBytes(pubkey); // Optimize for aggregation
|
|
71
|
+
for (let i = pubkeyCache.size; i < newCount; i++) {
|
|
72
|
+
pubkeyCache.set(i, validators[i].pubkey);
|
|
32
73
|
}
|
|
33
74
|
}
|
package/src/cache/stateCache.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {PublicKey} from "@chainsafe/blst";
|
|
2
1
|
import {BeaconConfig} from "@lodestar/config";
|
|
3
2
|
import {loadState} from "../util/loadState/loadState.js";
|
|
4
3
|
import {EpochCache, EpochCacheImmutableData, EpochCacheOpts} from "./epochCache.js";
|
|
@@ -168,7 +167,7 @@ export function createCachedBeaconState<T extends BeaconStateAllForks>(
|
|
|
168
167
|
* Check loadState() api for more details
|
|
169
168
|
* // TODO: rename to loadUnfinalizedCachedBeaconState() due to ELECTRA
|
|
170
169
|
*/
|
|
171
|
-
export function loadCachedBeaconState<T extends
|
|
170
|
+
export function loadCachedBeaconState<T extends CachedBeaconStateAllForks>(
|
|
172
171
|
cachedSeedState: T,
|
|
173
172
|
stateBytes: Uint8Array,
|
|
174
173
|
opts?: EpochCacheOpts,
|
|
@@ -180,22 +179,19 @@ export function loadCachedBeaconState<T extends BeaconStateAllForks & BeaconStat
|
|
|
180
179
|
stateBytes,
|
|
181
180
|
seedValidatorsBytes
|
|
182
181
|
);
|
|
183
|
-
const {
|
|
182
|
+
const {pubkeyCache} = cachedSeedState.epochCtx;
|
|
184
183
|
// Get the validators sub tree once for all the loop
|
|
185
184
|
const validators = migratedState.validators;
|
|
186
185
|
for (const validatorIndex of modifiedValidators) {
|
|
187
186
|
const validator = validators.getReadonly(validatorIndex);
|
|
188
|
-
|
|
189
|
-
pubkey2index.set(pubkey, validatorIndex);
|
|
190
|
-
index2pubkey[validatorIndex] = PublicKey.fromBytes(pubkey);
|
|
187
|
+
pubkeyCache.set(validatorIndex, validator.pubkey);
|
|
191
188
|
}
|
|
192
189
|
|
|
193
190
|
return createCachedBeaconState(
|
|
194
191
|
migratedState,
|
|
195
192
|
{
|
|
196
193
|
config: cachedSeedState.config,
|
|
197
|
-
|
|
198
|
-
index2pubkey,
|
|
194
|
+
pubkeyCache,
|
|
199
195
|
},
|
|
200
196
|
{...(opts ?? {}), ...{skipSyncPubkeys: true}}
|
|
201
197
|
) as T;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
|
2
1
|
import {CompositeViewDU} from "@chainsafe/ssz";
|
|
3
2
|
import {ValidatorIndex, ssz} from "@lodestar/types";
|
|
4
3
|
import {toPubkeyHex} from "@lodestar/utils";
|
|
@@ -39,9 +38,9 @@ export function getSyncCommitteeCache(validatorIndices: Uint32Array): SyncCommit
|
|
|
39
38
|
|
|
40
39
|
export function computeSyncCommitteeCache(
|
|
41
40
|
syncCommittee: CompositeViewDU<typeof ssz.altair.SyncCommittee>,
|
|
42
|
-
|
|
41
|
+
pubkeyCache: {getIndex(pubkey: Uint8Array): number | null}
|
|
43
42
|
): SyncCommitteeCache {
|
|
44
|
-
const validatorIndices = computeSyncCommitteeValidatorIndices(syncCommittee,
|
|
43
|
+
const validatorIndices = computeSyncCommitteeValidatorIndices(syncCommittee, pubkeyCache);
|
|
45
44
|
const validatorIndexMap = computeValidatorSyncCommitteeIndexMap(validatorIndices);
|
|
46
45
|
return {
|
|
47
46
|
validatorIndices,
|
|
@@ -79,12 +78,12 @@ export function computeValidatorSyncCommitteeIndexMap(
|
|
|
79
78
|
*/
|
|
80
79
|
function computeSyncCommitteeValidatorIndices(
|
|
81
80
|
syncCommittee: CompositeViewDU<typeof ssz.altair.SyncCommittee>,
|
|
82
|
-
|
|
81
|
+
pubkeyCache: {getIndex(pubkey: Uint8Array): number | null}
|
|
83
82
|
): Uint32Array {
|
|
84
83
|
const pubkeys = syncCommittee.pubkeys.getAllReadonly();
|
|
85
84
|
const validatorIndices = new Uint32Array(pubkeys.length);
|
|
86
85
|
for (const [i, pubkey] of pubkeys.entries()) {
|
|
87
|
-
const validatorIndex =
|
|
86
|
+
const validatorIndex = pubkeyCache.getIndex(pubkey);
|
|
88
87
|
if (validatorIndex === null) {
|
|
89
88
|
throw Error(`SyncCommittee pubkey is unknown ${toPubkeyHex(pubkey)}`);
|
|
90
89
|
}
|