@lodestar/beacon-node
Version:
A Typescript implementation of the beacon chain
83 lines (71 loc) • 3.55 kB
text/typescript
import {routes} from "@lodestar/api";
import {ATTESTATION_SUBNET_COUNT} from "@lodestar/params";
import {IBeaconStateView, computeSlotsSinceEpochStart} from "@lodestar/state-transition";
import {BLSPubkey, CommitteeIndex, ProducedBlockSource, Slot, SubnetID, ValidatorIndex} from "@lodestar/types";
import {MAX_BUILDER_BOOST_FACTOR} from "@lodestar/validator";
import {BlockSelectionResult, BuilderBlockSelectionReason, EngineBlockSelectionReason} from "./index.js";
export function computeSubnetForCommitteesAtSlot(
slot: Slot,
committeesAtSlot: number,
committeeIndex: CommitteeIndex
): SubnetID {
const slotsSinceEpochStart = computeSlotsSinceEpochStart(slot);
const committeesSinceEpochStart = committeesAtSlot * slotsSinceEpochStart;
return (committeesSinceEpochStart + committeeIndex) % ATTESTATION_SUBNET_COUNT;
}
/**
* Precompute all pubkeys for given `validatorIndices`. Ensures that all `validatorIndices` are known
* before doing other expensive logic.
*
* Uses special BranchNodeStruct state.validators data structure to optimize getting pubkeys.
* Type-unsafe: assumes state.validators[i] is of BranchNodeStruct type.
* Note: This is the fastest way of getting compressed pubkeys.
* See benchmark -> packages/beacon-node/test/perf/api/impl/validator/attester.test.ts
*/
export function getPubkeysForIndices(state: IBeaconStateView, indexes: ValidatorIndex[]): BLSPubkey[] {
const validatorsLen = state.validatorCount; // Get once, it's expensive
const pubkeys: BLSPubkey[] = [];
for (let i = 0, len = indexes.length; i < len; i++) {
const index = indexes[i];
if (index >= validatorsLen) {
throw Error(`validatorIndex ${index} too high. Current validator count ${validatorsLen}`);
}
// NOTE: This could be optimized further by traversing the tree optimally with .getNodes()
const validator = state.getValidator(index);
pubkeys.push(validator.pubkey);
}
return pubkeys;
}
export function selectBlockProductionSource({
builderSelection,
engineExecutionPayloadValue,
builderExecutionPayloadValue,
builderBoostFactor,
}: {
builderSelection: routes.validator.BuilderSelection;
engineExecutionPayloadValue: bigint;
builderExecutionPayloadValue: bigint;
builderBoostFactor: bigint;
}): BlockSelectionResult {
switch (builderSelection) {
case routes.validator.BuilderSelection.ExecutionAlways:
case routes.validator.BuilderSelection.ExecutionOnly:
return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.EnginePreferred};
case routes.validator.BuilderSelection.Default:
case routes.validator.BuilderSelection.MaxProfit: {
if (builderBoostFactor === BigInt(0)) {
return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.EnginePreferred};
}
if (builderBoostFactor === MAX_BUILDER_BOOST_FACTOR) {
return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BuilderPreferred};
}
if (engineExecutionPayloadValue >= (builderExecutionPayloadValue * builderBoostFactor) / BigInt(100)) {
return {source: ProducedBlockSource.engine, reason: EngineBlockSelectionReason.BlockValue};
}
return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BlockValue};
}
case routes.validator.BuilderSelection.BuilderAlways:
case routes.validator.BuilderSelection.BuilderOnly:
return {source: ProducedBlockSource.builder, reason: BuilderBlockSelectionReason.BuilderPreferred};
}
}