@lodestar/state-transition 1.39.0-dev.268dcb02bf → 1.39.0-dev.3932675174

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.
@@ -22,13 +22,9 @@ export function processProposerLookahead(
22
22
  // Fill in the last epoch with new proposer indices
23
23
  const epoch = state.epochCtx.epoch + MIN_SEED_LOOKAHEAD + 1;
24
24
 
25
- const shuffling =
26
- state.epochCtx.shufflingCache?.getSync(epoch, cache.nextShufflingDecisionRoot, {
27
- state,
28
- activeIndices: cache.nextShufflingActiveIndices,
29
- }) ??
30
- // Only for testing. shufflingCache should always be available in prod
31
- computeEpochShuffling(state, cache.nextShufflingActiveIndices, epoch);
25
+ const shuffling = computeEpochShuffling(state, cache.nextShufflingActiveIndices, epoch);
26
+ // Save shuffling to cache so afterProcessEpoch can reuse it instead of recomputing
27
+ cache.nextShuffling = shuffling;
32
28
 
33
29
  const lastEpochProposerLookahead = computeProposerIndices(fork, state, shuffling, epoch);
34
30
 
@@ -4,8 +4,6 @@ import {BeaconBlock, ValidatorIndex, altair, rewards} from "@lodestar/types";
4
4
  import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
5
5
  import {CachedBeaconStateAllForks, CachedBeaconStateAltair} from "../cache/stateCache.js";
6
6
 
7
- type BalanceRecord = {val: number}; // Use val for convenient way to increment/decrement balance
8
-
9
7
  export async function computeSyncCommitteeRewards(
10
8
  config: BeaconConfig,
11
9
  index2pubkey: Index2PubkeyCache,
@@ -29,24 +27,23 @@ export async function computeSyncCommitteeRewards(
29
27
  const {syncParticipantReward} = preStateAltair.epochCtx;
30
28
  const {syncCommitteeBits} = altairBlock.body.syncAggregate;
31
29
 
32
- // Use balance of each committee as starting point such that we cap the penalty to avoid balance dropping below 0
33
- const balances: Map<ValidatorIndex, BalanceRecord> = new Map();
34
- for (const i of syncCommitteeValidatorIndices) {
35
- balances.set(i, {val: preStateAltair.balances.get(i)});
36
- }
30
+ // Track reward deltas per validator (can appear multiple times in sync committee)
31
+ const rewardDeltas: Map<ValidatorIndex, number> = new Map();
37
32
 
38
- for (const i of syncCommitteeValidatorIndices) {
39
- const balanceRecord = balances.get(i) as BalanceRecord;
33
+ // Iterate by position index to correctly access syncCommitteeBits
34
+ for (let i = 0; i < syncCommitteeValidatorIndices.length; i++) {
35
+ const validatorIndex = syncCommitteeValidatorIndices[i];
36
+ const currentDelta = rewardDeltas.get(validatorIndex) ?? 0;
40
37
  if (syncCommitteeBits.get(i)) {
41
38
  // Positive rewards for participants
42
- balanceRecord.val += syncParticipantReward;
39
+ rewardDeltas.set(validatorIndex, currentDelta + syncParticipantReward);
43
40
  } else {
44
41
  // Negative rewards for non participants
45
- balanceRecord.val = Math.max(0, balanceRecord.val - syncParticipantReward);
42
+ rewardDeltas.set(validatorIndex, currentDelta - syncParticipantReward);
46
43
  }
47
44
  }
48
45
 
49
- const rewards = Array.from(balances, ([validatorIndex, v]) => ({validatorIndex, reward: v.val}));
46
+ const rewards = Array.from(rewardDeltas, ([validatorIndex, reward]) => ({validatorIndex, reward}));
50
47
 
51
48
  if (validatorIds.length) {
52
49
  const filtersSet = new Set(validatorIds);
package/src/types.ts CHANGED
@@ -23,4 +23,5 @@ export type {
23
23
  BeaconStateFulu,
24
24
  BeaconStateGloas,
25
25
  BeaconStatePhase0,
26
+ ShufflingGetter,
26
27
  } from "./cache/types.js";
@@ -9,54 +9,13 @@ import {
9
9
  TARGET_COMMITTEE_SIZE,
10
10
  } from "@lodestar/params";
11
11
  import {Epoch, RootHex, ValidatorIndex, ssz} from "@lodestar/types";
12
- import {GaugeExtra, Logger, NoLabels, intDiv, toRootHex} from "@lodestar/utils";
12
+ import {intDiv, toRootHex} from "@lodestar/utils";
13
13
  import {BeaconStateAllForks} from "../types.js";
14
14
  import {getBlockRootAtSlot} from "./blockRoot.js";
15
15
  import {computeAnchorCheckpoint} from "./computeAnchorCheckpoint.js";
16
16
  import {computeStartSlotAtEpoch} from "./epoch.js";
17
17
  import {getSeed} from "./seed.js";
18
18
 
19
- export interface ShufflingBuildProps {
20
- state: BeaconStateAllForks;
21
- activeIndices: Uint32Array;
22
- }
23
-
24
- export interface PublicShufflingCacheMetrics {
25
- shufflingCache: {
26
- nextShufflingNotOnEpochCache: GaugeExtra<NoLabels>;
27
- };
28
- }
29
- export interface IShufflingCache {
30
- metrics: PublicShufflingCacheMetrics | null;
31
- logger: Logger | null;
32
- /**
33
- * Gets a cached shuffling via the epoch and decision root. If the state and
34
- * activeIndices are passed and a shuffling is not available it will be built
35
- * synchronously. If the state is not passed and the shuffling is not available
36
- * nothing will be returned.
37
- *
38
- * NOTE: If a shuffling is already queued and not calculated it will build and resolve
39
- * the promise but the already queued build will happen at some later time
40
- */
41
- getSync<T extends ShufflingBuildProps | undefined>(
42
- epoch: Epoch,
43
- decisionRoot: RootHex,
44
- buildProps?: T
45
- ): T extends ShufflingBuildProps ? EpochShuffling : EpochShuffling | null;
46
-
47
- /**
48
- * Gets a cached shuffling via the epoch and decision root. Returns a promise
49
- * for the shuffling if it hs not calculated yet. Returns null if a build has
50
- * not been queued nor a shuffling was calculated.
51
- */
52
- get(epoch: Epoch, decisionRoot: RootHex): Promise<EpochShuffling | null>;
53
-
54
- /**
55
- * Queue asynchronous build for an EpochShuffling
56
- */
57
- build(epoch: Epoch, decisionRoot: RootHex, state: BeaconStateAllForks, activeIndices: Uint32Array): void;
58
- }
59
-
60
19
  /**
61
20
  * Readonly interface for EpochShuffling.
62
21
  */
@@ -164,7 +123,7 @@ export async function computeEpochShufflingAsync(
164
123
  };
165
124
  }
166
125
 
167
- function calculateDecisionRoot(state: BeaconStateAllForks, epoch: Epoch): RootHex {
126
+ export function calculateDecisionRoot(state: BeaconStateAllForks, epoch: Epoch): RootHex {
168
127
  const pivotSlot = computeStartSlotAtEpoch(epoch - 1) - 1;
169
128
  return toRootHex(getBlockRootAtSlot(state, pivotSlot));
170
129
  }