Skip to content
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {EpochContext} from "../util";
import {BeaconBlock, BeaconState} from "@chainsafe/lodestar-types";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {BeaconBlock} from "@chainsafe/lodestar-types";

import {processBlockHeader} from "./processBlockHeader";
import {processRandao} from "./processRandao";
Expand All @@ -25,7 +25,7 @@ export {

export function processBlock(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
block: BeaconBlock,
verifySignatures = true
): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
import {readOnlyMap} from "@chainsafe/ssz";
import {BeaconState, ValidatorIndex} from "@chainsafe/lodestar-types";
import {ValidatorIndex} from "@chainsafe/lodestar-types";

import {FAR_FUTURE_EPOCH} from "../../constants";
import {computeActivationExitEpoch, getChurnLimit} from "../../util";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";

/**
* Initiate the exit of the validator with index ``index``.
*/
export function initiateValidatorExit(epochCtx: EpochContext, state: BeaconState, index: ValidatorIndex): void {
export function initiateValidatorExit(
epochCtx: EpochContext,
state: CachedValidatorsBeaconState,
index: ValidatorIndex
): void {
const config = epochCtx.config;
// return if validator already initiated exit
const validator = state.validators[index];
if (validator.exitEpoch !== FAR_FUTURE_EPOCH) {
if (state.validators[index].exitEpoch !== FAR_FUTURE_EPOCH) {
return;
}

const currentEpoch = epochCtx.currentShuffling.epoch;

// compute exit queue epoch
const validatorExitEpochs = readOnlyMap(state.validators, (v) => v.exitEpoch);
const validatorExitEpochs = state.flatValidators().readOnlyMap((v) => v.exitEpoch);
const exitEpochs = validatorExitEpochs.filter((exitEpoch) => exitEpoch !== FAR_FUTURE_EPOCH);
exitEpochs.push(computeActivationExitEpoch(config, currentEpoch));
let exitQueueEpoch = Math.max(...exitEpochs);
Expand All @@ -29,6 +31,8 @@ export function initiateValidatorExit(epochCtx: EpochContext, state: BeaconState
}

// set validator exit epoch and withdrawable epoch
validator.exitEpoch = exitQueueEpoch;
validator.withdrawableEpoch = validator.exitEpoch + config.params.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
state.setValidator(index, {
exitEpoch: exitQueueEpoch,
withdrawableEpoch: exitQueueEpoch + config.params.MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
});
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {AttesterSlashing, BeaconState, ValidatorIndex} from "@chainsafe/lodestar-types";
import {AttesterSlashing, ValidatorIndex} from "@chainsafe/lodestar-types";

import {isSlashableValidator, isSlashableAttestationData} from "../../util";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {slashValidator} from "./slashValidator";
import {isValidIndexedAttestation} from "./isValidIndexedAttestation";

export function processAttesterSlashing(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
attesterSlashing: AttesterSlashing,
verifySignatures = true
): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import bls from "@chainsafe/bls";
import {BeaconState, Deposit} from "@chainsafe/lodestar-types";
import {Deposit} from "@chainsafe/lodestar-types";
import {verifyMerkleBranch, bigIntMin} from "@chainsafe/lodestar-utils";

import {DEPOSIT_CONTRACT_TREE_DEPTH, DomainType, FAR_FUTURE_EPOCH} from "../../constants";
import {computeDomain, computeSigningRoot, increaseBalance} from "../../util";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";

export function processDeposit(epochCtx: EpochContext, state: BeaconState, deposit: Deposit): void {
export function processDeposit(epochCtx: EpochContext, state: CachedValidatorsBeaconState, deposit: Deposit): void {
const config = epochCtx.config;
const {EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE} = config.params;
// verify the merkle branch
Expand Down Expand Up @@ -43,7 +43,7 @@ export function processDeposit(epochCtx: EpochContext, state: BeaconState, depos
}

// add validator and balance entries
state.validators.push({
state.addValidator({
pubkey: pubkey,
withdrawalCredentials: deposit.data.withdrawalCredentials,
activationEligibilityEpoch: FAR_FUTURE_EPOCH,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,29 @@ import {
Attestation,
AttesterSlashing,
BeaconBlockBody,
BeaconState,
Deposit,
ProposerSlashing,
VoluntaryExit,
} from "@chainsafe/lodestar-types";

import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {processProposerSlashing} from "./processProposerSlashing";
import {processAttesterSlashing} from "./processAttesterSlashing";
import {processAttestation} from "./processAttestation";
import {processDeposit} from "./processDeposit";
import {processVoluntaryExit} from "./processVoluntaryExit";

type Operation = ProposerSlashing | AttesterSlashing | Attestation | Deposit | VoluntaryExit;
type OperationFunction = (epochCtx: EpochContext, state: BeaconState, op: Operation, verify: boolean) => void;
type OperationFunction = (
epochCtx: EpochContext,
state: CachedValidatorsBeaconState,
op: Operation,
verify: boolean
) => void;

export function processOperations(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
body: BeaconBlockBody,
verifySignatures = true
): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {BeaconState, ProposerSlashing} from "@chainsafe/lodestar-types";
import {DomainType} from "../../constants";
import {computeEpochAtSlot, computeSigningRoot, getDomain, isSlashableValidator} from "../../util";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {slashValidator} from "./slashValidator";
import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../signatureSets";

export function processProposerSlashing(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
proposerSlashing: ProposerSlashing,
verifySignatures = true
): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import {BeaconState, SignedVoluntaryExit} from "@chainsafe/lodestar-types";
import {DomainType, FAR_FUTURE_EPOCH} from "../../constants";
import {computeSigningRoot, getDomain, isActiveValidator} from "../../util";
import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../signatureSets";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {initiateValidatorExit} from "./initiateValidatorExit";

export function processVoluntaryExit(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
signedVoluntaryExit: SignedVoluntaryExit,
verifySignature = true
): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {BeaconState, ValidatorIndex} from "@chainsafe/lodestar-types";
import {ValidatorIndex} from "@chainsafe/lodestar-types";

import {decreaseBalance, increaseBalance} from "../../util";
import {EpochContext} from "../util";
import {EpochContext, CachedValidatorsBeaconState} from "../util";
import {initiateValidatorExit} from "./initiateValidatorExit";

export function slashValidator(
epochCtx: EpochContext,
state: BeaconState,
state: CachedValidatorsBeaconState,
slashedIndex: ValidatorIndex,
whistleblowerIndex?: ValidatorIndex
): void {
Expand All @@ -19,8 +19,10 @@ export function slashValidator(
const epoch = epochCtx.currentShuffling.epoch;
initiateValidatorExit(epochCtx, state, slashedIndex);
const validator = state.validators[slashedIndex];
validator.slashed = true;
validator.withdrawableEpoch = Math.max(validator.withdrawableEpoch, epoch + EPOCHS_PER_SLASHINGS_VECTOR);
state.setValidator(slashedIndex, {
slashed: true,
withdrawableEpoch: Math.max(validator.withdrawableEpoch, epoch + EPOCHS_PER_SLASHINGS_VECTOR),
});
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effectiveBalance;
decreaseBalance(state, slashedIndex, validator.effectiveBalance / BigInt(MIN_SLASHING_PENALTY_QUOTIENT));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {BeaconState} from "@chainsafe/lodestar-types";

import {prepareEpochProcessState} from "../util";
import {CachedValidatorsBeaconState, prepareEpochProcessState} from "../util";
import {StateTransitionEpochContext} from "../util/epochContext";
import {processJustificationAndFinalization} from "./processJustificationAndFinalization";
import {processRewardsAndPenalties} from "./processRewardsAndPenalties";
Expand All @@ -20,7 +18,7 @@ export {
getAttestationDeltas,
};

export function processEpoch(epochCtx: StateTransitionEpochContext, state: BeaconState): void {
export function processEpoch(epochCtx: StateTransitionEpochContext, state: CachedValidatorsBeaconState): void {
const process = prepareEpochProcessState(epochCtx, state);
epochCtx.epochProcess = process;
processJustificationAndFinalization(epochCtx, process, state);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {readOnlyMap, List} from "@chainsafe/ssz";
import {BeaconState, Eth1Data, PendingAttestation} from "@chainsafe/lodestar-types";
import {Eth1Data, PendingAttestation} from "@chainsafe/lodestar-types";
import {bigIntMin, intDiv} from "@chainsafe/lodestar-utils";

import {getRandaoMix} from "../../util";
import {EpochContext, IEpochProcess} from "../util";
import {EpochContext, IEpochProcess, CachedValidatorsBeaconState} from "../util";

export function processFinalUpdates(epochCtx: EpochContext, process: IEpochProcess, state: BeaconState): void {
export function processFinalUpdates(
epochCtx: EpochContext,
process: IEpochProcess,
state: CachedValidatorsBeaconState
): void {
const config = epochCtx.config;
const currentEpoch = process.currentEpoch;
const nextEpoch = currentEpoch + 1;
Expand Down Expand Up @@ -37,10 +41,9 @@ export function processFinalUpdates(epochCtx: EpochContext, process: IEpochProce
const balance = balances[i];
const effectiveBalance = status.validator.effectiveBalance;
if (balance + DOWNWARD_THRESHOLD < effectiveBalance || effectiveBalance + UPWARD_THRESHOLD < balance) {
state.validators[i].effectiveBalance = bigIntMin(
balance - (balance % EFFECTIVE_BALANCE_INCREMENT),
MAX_EFFECTIVE_BALANCE
);
state.setValidator(i, {
effectiveBalance: bigIntMin(balance - (balance % EFFECTIVE_BALANCE_INCREMENT), MAX_EFFECTIVE_BALANCE),
});
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import {BeaconState} from "@chainsafe/lodestar-types";

import {computeActivationExitEpoch} from "../../util";
import {EpochContext, IEpochProcess} from "../util";
import {EpochContext, IEpochProcess, CachedValidatorsBeaconState} from "../util";

export function processRegistryUpdates(epochCtx: EpochContext, process: IEpochProcess, state: BeaconState): void {
export function processRegistryUpdates(
epochCtx: EpochContext,
process: IEpochProcess,
state: CachedValidatorsBeaconState
): void {
const config = epochCtx.config;
let exitEnd = process.exitQueueEnd;
let endChurn = process.exitQueueEndChurn;
const {MIN_VALIDATOR_WITHDRAWABILITY_DELAY} = epochCtx.config.params;
// process ejections
for (const index of process.indicesToEject) {
const validator = state.validators[index];

// set validator exit epoch and withdrawable epoch
validator.exitEpoch = exitEnd;
validator.withdrawableEpoch = exitEnd + MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
state.setValidator(index, {
exitEpoch: exitEnd,
withdrawableEpoch: exitEnd + MIN_VALIDATOR_WITHDRAWABILITY_DELAY,
});

endChurn += 1;
if (endChurn >= process.churnLimit) {
Expand All @@ -25,7 +27,9 @@ export function processRegistryUpdates(epochCtx: EpochContext, process: IEpochPr

// set new activation eligibilities
for (const index of process.indicesToSetActivationEligibility) {
state.validators[index].activationEligibilityEpoch = epochCtx.currentShuffling.epoch + 1;
state.setValidator(index, {
activationEligibilityEpoch: epochCtx.currentShuffling.epoch + 1,
});
}

const finalityEpoch = state.finalizedCheckpoint.epoch;
Expand All @@ -35,6 +39,8 @@ export function processRegistryUpdates(epochCtx: EpochContext, process: IEpochPr
if (process.statuses[index].validator.activationEligibilityEpoch > finalityEpoch) {
break; // remaining validators all have an activationEligibilityEpoch that is higher anyway, break early
}
state.validators[index].activationEpoch = computeActivationExitEpoch(config, process.currentEpoch);
state.setValidator(index, {
activationEpoch: computeActivationExitEpoch(config, process.currentEpoch),
});
}
}
11 changes: 7 additions & 4 deletions packages/lodestar-beacon-state-transition/src/fast/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {BeaconState, SignedBeaconBlock} from "@chainsafe/lodestar-types";
import {SignedBeaconBlock} from "@chainsafe/lodestar-types";

import {verifyBlockSignature} from "./util";
import {CachedValidatorsBeaconState, verifyBlockSignature} from "./util";
import {IStateContext} from "./util";
import {StateTransitionEpochContext} from "./util/epochContext";
import {EpochContext} from "./util/epochContext";
Expand All @@ -22,7 +22,7 @@ export function fastStateTransition(
const types = epochCtx.config.types;

const block = signedBlock.message;
const postState = types.BeaconState.clone(state);
const postState = state.clone();
// process slots (including those with no blocks) since block
processSlots(epochCtx, postState, block.slot);

Expand All @@ -46,7 +46,10 @@ export function fastStateTransition(
/**
* Trim epochProcess in epochCtx, and insert the standard/exchange interface epochProcess to the final IStateContext
*/
export function toIStateContext(epochCtx: StateTransitionEpochContext, state: BeaconState): IStateContext {
export function toIStateContext(
epochCtx: StateTransitionEpochContext,
state: CachedValidatorsBeaconState
): IStateContext {
const epochProcess = epochCtx.epochProcess;
epochCtx.epochProcess = undefined;
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import {BeaconState, Slot} from "@chainsafe/lodestar-types";
import {Slot} from "@chainsafe/lodestar-types";

import {StateTransitionEpochContext} from "../util/epochContext";
import {processEpoch} from "../epoch";
import {processSlot} from "./processSlot";
import {CachedValidatorsBeaconState} from "../util/interface";

export {processSlot};

export function processSlots(epochCtx: StateTransitionEpochContext, state: BeaconState, slot: Slot): void {
export function processSlots(
epochCtx: StateTransitionEpochContext,
state: CachedValidatorsBeaconState,
slot: Slot
): void {
if (!(state.slot < slot)) {
throw new Error("State slot must transition to a future slot: " + `stateSlot=${state.slot} slot=${slot}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from "../../util";
import {computeEpochShuffling, IEpochShuffling} from "./epochShuffling";
import {IEpochProcess} from "./epochProcess";
import {CachedValidatorsBeaconState} from "./interface";

export class PubkeyIndexMap extends Map<ByteVector, ValidatorIndex> {
get(key: ByteVector): ValidatorIndex | undefined {
Expand Down Expand Up @@ -138,15 +139,13 @@ export class EpochContext {
* Called to re-use information, such as the shuffling of the next epoch, after transitioning into a
* new epoch.
*/
public rotateEpochs(state: BeaconState): void {
public rotateEpochs(state: CachedValidatorsBeaconState): void {
this.previousShuffling = this.currentShuffling;
this.currentShuffling = this.nextShuffling;
const nextEpoch = this.currentShuffling.epoch + 1;
const indicesBounded: [ValidatorIndex, Epoch, Epoch][] = readOnlyMap(state.validators, (v, i) => [
i,
v.activationEpoch,
v.exitEpoch,
]);
const indicesBounded = state
.flatValidators()
.readOnlyMap<[number, Epoch, Epoch]>((v, i) => [i, v.activationEpoch, v.exitEpoch]);
this.nextShuffling = computeEpochShuffling(this.config, state, indicesBounded, nextEpoch);
this._resetProposers(state);
}
Expand Down
Loading