mirror of
https://github.com/ChainSafe/lodestar.git
synced 2026-05-02 03:01:08 -04:00
chore: use config from beacon chain (#8703)
**Motivation** - as a preparation for lodestar-z integration, we should not access config from any cached BeaconState **Description** - use chain.config instead part of #8652 --------- Co-authored-by: Tuyen Nguyen <twoeths@users.noreply.github.com>
This commit is contained in:
@@ -139,6 +139,7 @@ export async function verifyBlocksInEpoch(
|
||||
// All signatures at once
|
||||
opts.skipVerifyBlockSignatures !== true
|
||||
? verifyBlocksSignatures(
|
||||
this.config,
|
||||
this.index2pubkey,
|
||||
this.bls,
|
||||
this.logger,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {CachedBeaconStateAllForks, Index2PubkeyCache, getBlockSignatureSets} from "@lodestar/state-transition";
|
||||
import {IndexedAttestation, SignedBeaconBlock} from "@lodestar/types";
|
||||
import {Logger} from "@lodestar/utils";
|
||||
@@ -15,6 +16,7 @@ import {ImportBlockOpts} from "./types.js";
|
||||
* Since all data is known in advance all signatures are verified at once in parallel.
|
||||
*/
|
||||
export async function verifyBlocksSignatures(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
bls: IBlsVerifier,
|
||||
logger: Logger,
|
||||
@@ -39,7 +41,7 @@ export async function verifyBlocksSignatures(
|
||||
: //
|
||||
// Verify signatures per block to track which block is invalid
|
||||
bls.verifySignatureSets(
|
||||
getBlockSignatureSets(index2pubkey, preState0, block, indexedAttestationsByBlock[i], {
|
||||
getBlockSignatureSets(config, index2pubkey, preState0, block, indexedAttestationsByBlock[i], {
|
||||
skipProposerSignature: opts.validProposerSignature,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -142,7 +142,7 @@ export class BeaconChain implements IBeaconChain {
|
||||
readonly aggregatedAttestationPool: AggregatedAttestationPool;
|
||||
readonly syncCommitteeMessagePool: SyncCommitteeMessagePool;
|
||||
readonly syncContributionAndProofPool;
|
||||
readonly opPool = new OpPool();
|
||||
readonly opPool: OpPool;
|
||||
|
||||
// Gossip seen cache
|
||||
readonly seenAttesters = new SeenAttesters();
|
||||
@@ -260,6 +260,7 @@ export class BeaconChain implements IBeaconChain {
|
||||
this.aggregatedAttestationPool = new AggregatedAttestationPool(this.config, metrics);
|
||||
this.syncCommitteeMessagePool = new SyncCommitteeMessagePool(config, clock, this.opts?.preaggregateSlotDistance);
|
||||
this.syncContributionAndProofPool = new SyncContributionAndProofPool(config, clock, metrics, logger);
|
||||
this.opPool = new OpPool(config);
|
||||
|
||||
this.seenAggregatedAttestations = new SeenAggregatedAttestations(metrics);
|
||||
this.seenContributionAndProof = new SeenContributionAndProof(metrics);
|
||||
@@ -334,6 +335,7 @@ export class BeaconChain implements IBeaconChain {
|
||||
this.cpStateDatastore = fileDataStore ? new FileCPStateDatastore(dataDir) : new DbCPStateDatastore(this.db);
|
||||
checkpointStateCache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
metrics,
|
||||
logger,
|
||||
clock,
|
||||
@@ -1306,7 +1308,7 @@ export class BeaconChain implements IBeaconChain {
|
||||
|
||||
const postState = this.regen.getStateSync(toRootHex(block.stateRoot)) ?? undefined;
|
||||
|
||||
return computeBlockRewards(block, preState.clone(), postState?.clone());
|
||||
return computeBlockRewards(this.config, block, preState.clone(), postState?.clone());
|
||||
}
|
||||
|
||||
async getAttestationsRewards(
|
||||
@@ -1330,7 +1332,7 @@ export class BeaconChain implements IBeaconChain {
|
||||
throw Error(`State is not in cache for slot ${slot}`);
|
||||
}
|
||||
|
||||
const rewards = await computeAttestationsRewards(this.pubkey2index, cachedState, validatorIds);
|
||||
const rewards = await computeAttestationsRewards(this.config, this.pubkey2index, cachedState, validatorIds);
|
||||
|
||||
return {rewards, executionOptimistic, finalized};
|
||||
}
|
||||
@@ -1347,6 +1349,6 @@ export class BeaconChain implements IBeaconChain {
|
||||
|
||||
preState = processSlots(preState, block.slot); // Dial preState's slot to block.slot
|
||||
|
||||
return computeSyncCommitteeRewards(this.index2pubkey, block, preState.clone(), validatorIds);
|
||||
return computeSyncCommitteeRewards(this.config, this.index2pubkey, block, preState.clone(), validatorIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Signature, aggregateSignatures} from "@chainsafe/blst";
|
||||
import {BitArray} from "@chainsafe/ssz";
|
||||
import {ChainForkConfig} from "@lodestar/config";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {IForkChoice} from "@lodestar/fork-choice";
|
||||
import {
|
||||
ForkName,
|
||||
@@ -162,7 +162,7 @@ export class AggregatedAttestationPool {
|
||||
private lowestPermissibleSlot = 0;
|
||||
|
||||
constructor(
|
||||
private readonly config: ChainForkConfig,
|
||||
private readonly config: BeaconConfig,
|
||||
private readonly metrics: Metrics | null = null
|
||||
) {
|
||||
metrics?.opPool.aggregatedAttestationPool.attDataPerSlot.addCollect(() => this.onScrapeMetrics(metrics));
|
||||
@@ -249,7 +249,7 @@ export class AggregatedAttestationPool {
|
||||
const stateEpoch = state.epochCtx.epoch;
|
||||
const statePrevEpoch = stateEpoch - 1;
|
||||
|
||||
const notSeenValidatorsFn = getNotSeenValidatorsFn(state);
|
||||
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
|
||||
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
|
||||
|
||||
const attestationsByScore: AttestationWithScore[] = [];
|
||||
@@ -362,7 +362,7 @@ export class AggregatedAttestationPool {
|
||||
const statePrevEpoch = stateEpoch - 1;
|
||||
const rootCache = new RootCache(state);
|
||||
|
||||
const notSeenValidatorsFn = getNotSeenValidatorsFn(state);
|
||||
const notSeenValidatorsFn = getNotSeenValidatorsFn(this.config, state);
|
||||
const validateAttestationDataFn = getValidateAttestationDataFn(forkChoice, state);
|
||||
|
||||
const slots = Array.from(this.attestationGroupByIndexByDataHexBySlot.keys()).sort((a, b) => b - a);
|
||||
@@ -656,7 +656,7 @@ export class MatchingDataAttestationGroup {
|
||||
private readonly attestations: AttestationWithIndex[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly config: ChainForkConfig,
|
||||
private readonly config: BeaconConfig,
|
||||
readonly committee: Uint32Array,
|
||||
readonly data: phase0.AttestationData
|
||||
) {}
|
||||
@@ -864,9 +864,9 @@ export function aggregateConsolidation({byCommittee, attData}: AttestationsConso
|
||||
* Pre-compute participation from a CachedBeaconStateAllForks, for use to check if an attestation's committee
|
||||
* has already attested or not.
|
||||
*/
|
||||
export function getNotSeenValidatorsFn(state: CachedBeaconStateAllForks): GetNotSeenValidatorsFn {
|
||||
export function getNotSeenValidatorsFn(config: BeaconConfig, state: CachedBeaconStateAllForks): GetNotSeenValidatorsFn {
|
||||
const stateSlot = state.slot;
|
||||
if (state.config.getForkName(stateSlot) === ForkName.phase0) {
|
||||
if (config.getForkName(stateSlot) === ForkName.phase0) {
|
||||
// Get attestations to be included in a phase0 block.
|
||||
// As we are close to altair, this is not really important, it's mainly for e2e.
|
||||
// The performance is not great due to the different BeaconState data structure to altair.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {Id, Repository} from "@lodestar/db";
|
||||
import {
|
||||
BLS_WITHDRAWAL_PREFIX,
|
||||
@@ -51,6 +52,8 @@ export class OpPool {
|
||||
/** Map of validator index -> SignedBLSToExecutionChange */
|
||||
private readonly blsToExecutionChanges = new Map<ValidatorIndex, SignedBLSToExecutionChangeVersioned>();
|
||||
|
||||
constructor(private readonly config: BeaconConfig) {}
|
||||
|
||||
// Getters for metrics
|
||||
|
||||
get attesterSlashingsSize(): number {
|
||||
@@ -191,9 +194,8 @@ export class OpPool {
|
||||
phase0.SignedVoluntaryExit[],
|
||||
capella.SignedBLSToExecutionChange[],
|
||||
] {
|
||||
const {config} = state;
|
||||
const stateEpoch = computeEpochAtSlot(state.slot);
|
||||
const stateFork = config.getForkSeq(state.slot);
|
||||
const stateFork = this.config.getForkSeq(state.slot);
|
||||
const toBeSlashedIndices = new Set<ValidatorIndex>();
|
||||
const proposerSlashings: phase0.ProposerSlashing[] = [];
|
||||
|
||||
@@ -265,7 +267,7 @@ export class OpPool {
|
||||
// a future fork.
|
||||
isVoluntaryExitSignatureIncludable(
|
||||
stateFork,
|
||||
config.getForkSeq(computeStartSlotAtEpoch(voluntaryExit.message.epoch))
|
||||
this.config.getForkSeq(computeStartSlotAtEpoch(voluntaryExit.message.epoch))
|
||||
)
|
||||
) {
|
||||
voluntaryExits.push(voluntaryExit);
|
||||
@@ -368,14 +370,13 @@ export class OpPool {
|
||||
* Prune if validator has already exited at or before the finalized checkpoint of the head.
|
||||
*/
|
||||
private pruneVoluntaryExits(headState: CachedBeaconStateAllForks): void {
|
||||
const {config} = headState;
|
||||
const headStateFork = config.getForkSeq(headState.slot);
|
||||
const headStateFork = this.config.getForkSeq(headState.slot);
|
||||
const finalizedEpoch = headState.finalizedCheckpoint.epoch;
|
||||
|
||||
for (const [key, voluntaryExit] of this.voluntaryExits.entries()) {
|
||||
// VoluntaryExit messages signed in the previous fork become invalid and can never be included in any future
|
||||
// block, so just drop as the head state advances into the next fork.
|
||||
if (config.getForkSeq(computeStartSlotAtEpoch(voluntaryExit.message.epoch)) < headStateFork) {
|
||||
if (this.config.getForkSeq(computeStartSlotAtEpoch(voluntaryExit.message.epoch)) < headStateFork) {
|
||||
this.voluntaryExits.delete(key);
|
||||
}
|
||||
|
||||
@@ -392,9 +393,8 @@ export class OpPool {
|
||||
* to opPool once gossipsub seen cache TTL passes.
|
||||
*/
|
||||
private pruneBlsToExecutionChanges(headBlock: SignedBeaconBlock, headState: CachedBeaconStateAllForks): void {
|
||||
const {config} = headState;
|
||||
const recentBlsToExecutionChanges =
|
||||
config.getForkSeq(headBlock.message.slot) >= ForkSeq.capella
|
||||
this.config.getForkSeq(headBlock.message.slot) >= ForkSeq.capella
|
||||
? (headBlock as capella.SignedBeaconBlock).message.body.blsToExecutionChanges
|
||||
: [];
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ export async function produceBlockBody<T extends BlockType>(
|
||||
// even though shouldOverrideBuilder is relevant for the engine response, for simplicity of typing
|
||||
// we just return it undefined for the builder which anyway doesn't get consumed downstream
|
||||
let shouldOverrideBuilder: boolean | undefined;
|
||||
const fork = currentState.config.getForkName(blockSlot);
|
||||
const fork = this.config.getForkName(blockSlot);
|
||||
const produceResult = {
|
||||
type: blockType,
|
||||
fork,
|
||||
@@ -644,7 +644,7 @@ export async function produceCommonBlockBody<T extends BlockType>(
|
||||
? this.metrics?.executionBlockProductionTimeSteps
|
||||
: this.metrics?.builderBlockProductionTimeSteps;
|
||||
|
||||
const fork = currentState.config.getForkName(slot);
|
||||
const fork = this.config.getForkName(slot);
|
||||
|
||||
// TODO:
|
||||
// Iterate through the naive aggregation pool and ensure all the attestations from there
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
||||
import {routes} from "@lodestar/api";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {
|
||||
EFFECTIVE_BALANCE_INCREMENT,
|
||||
ForkName,
|
||||
@@ -38,11 +39,12 @@ const defaultAttestationsReward = {head: 0, target: 0, source: 0, inclusionDelay
|
||||
const defaultAttestationsPenalty = {target: 0, source: 0};
|
||||
|
||||
export async function computeAttestationsRewards(
|
||||
config: BeaconConfig,
|
||||
pubkey2index: PubkeyIndexMap,
|
||||
state: CachedBeaconStateAllForks,
|
||||
validatorIds?: (ValidatorIndex | string)[]
|
||||
): Promise<AttestationsRewards> {
|
||||
const fork = state.config.getForkName(state.slot);
|
||||
const fork = config.getForkName(state.slot);
|
||||
if (fork === ForkName.phase0) {
|
||||
throw Error("Unsupported fork. Attestations rewards calculation is not available in phase0");
|
||||
}
|
||||
@@ -50,8 +52,13 @@ export async function computeAttestationsRewards(
|
||||
const stateAltair = state as CachedBeaconStateAltair;
|
||||
const transitionCache = beforeProcessEpoch(stateAltair);
|
||||
|
||||
const [idealRewards, penalties] = computeIdealAttestationsRewardsAndPenaltiesAltair(stateAltair, transitionCache);
|
||||
const [idealRewards, penalties] = computeIdealAttestationsRewardsAndPenaltiesAltair(
|
||||
config,
|
||||
stateAltair,
|
||||
transitionCache
|
||||
);
|
||||
const totalRewards = computeTotalAttestationsRewardsAltair(
|
||||
config,
|
||||
pubkey2index,
|
||||
stateAltair,
|
||||
transitionCache,
|
||||
@@ -64,12 +71,13 @@ export async function computeAttestationsRewards(
|
||||
}
|
||||
|
||||
function computeIdealAttestationsRewardsAndPenaltiesAltair(
|
||||
config: BeaconConfig,
|
||||
state: CachedBeaconStateAllForks,
|
||||
transitionCache: EpochTransitionCache
|
||||
): [IdealAttestationsReward[], AttestationsPenalty[]] {
|
||||
const baseRewardPerIncrement = transitionCache.baseRewardPerIncrement;
|
||||
const activeBalanceByIncrement = transitionCache.totalActiveStakeByIncrement;
|
||||
const fork = state.config.getForkName(state.slot);
|
||||
const fork = config.getForkName(state.slot);
|
||||
const maxEffectiveBalance = isForkPostElectra(fork) ? MAX_EFFECTIVE_BALANCE_ELECTRA : MAX_EFFECTIVE_BALANCE;
|
||||
const maxEffectiveBalanceByIncrement = Math.floor(maxEffectiveBalance / EFFECTIVE_BALANCE_INCREMENT);
|
||||
|
||||
@@ -139,6 +147,7 @@ function computeIdealAttestationsRewardsAndPenaltiesAltair(
|
||||
|
||||
// Same calculation as `getRewardsAndPenaltiesAltair` but returns the breakdown of rewards instead of aggregated
|
||||
function computeTotalAttestationsRewardsAltair(
|
||||
config: BeaconConfig,
|
||||
pubkey2index: PubkeyIndexMap,
|
||||
state: CachedBeaconStateAltair,
|
||||
transitionCache: EpochTransitionCache,
|
||||
@@ -148,7 +157,7 @@ function computeTotalAttestationsRewardsAltair(
|
||||
): TotalAttestationsReward[] {
|
||||
const rewards = [];
|
||||
const {flags} = transitionCache;
|
||||
const {epochCtx, config} = state;
|
||||
const {epochCtx} = state;
|
||||
const validatorIndices = validatorIds
|
||||
.map((id) => (typeof id === "number" ? id : pubkey2index.get(fromHex(id))))
|
||||
.filter((index) => index !== undefined); // Validator indices to include in the result
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {routes} from "@lodestar/api";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {
|
||||
ForkName,
|
||||
WHISTLEBLOWER_REWARD_QUOTIENT,
|
||||
@@ -26,11 +27,12 @@ type SubRewardValue = number; // All reward values should be integer
|
||||
* 3) Reporting slashable behaviours from proposer and attester
|
||||
*/
|
||||
export async function computeBlockRewards(
|
||||
config: BeaconConfig,
|
||||
block: BeaconBlock,
|
||||
preState: CachedBeaconStateAllForks,
|
||||
postState?: CachedBeaconStateAllForks
|
||||
): Promise<BlockRewards> {
|
||||
const fork = preState.config.getForkName(block.slot);
|
||||
const fork = config.getForkName(block.slot);
|
||||
const {attestations: cachedAttestationsReward = 0, syncAggregate: cachedSyncAggregateReward = 0} =
|
||||
postState?.proposerRewards ?? {};
|
||||
let blockAttestationReward = cachedAttestationsReward;
|
||||
@@ -40,7 +42,7 @@ export async function computeBlockRewards(
|
||||
blockAttestationReward =
|
||||
fork === ForkName.phase0
|
||||
? computeBlockAttestationRewardPhase0(block as phase0.BeaconBlock, preState as CachedBeaconStatePhase0)
|
||||
: computeBlockAttestationRewardAltair(block as altair.BeaconBlock, preState as CachedBeaconStateAltair);
|
||||
: computeBlockAttestationRewardAltair(config, block as altair.BeaconBlock, preState as CachedBeaconStateAltair);
|
||||
}
|
||||
|
||||
if (syncAggregateReward === 0) {
|
||||
@@ -78,10 +80,11 @@ function computeBlockAttestationRewardPhase0(
|
||||
* Reuses `processAttestationsAltair()`. Has dependency on RewardCache
|
||||
*/
|
||||
function computeBlockAttestationRewardAltair(
|
||||
config: BeaconConfig,
|
||||
block: altair.BeaconBlock,
|
||||
preState: CachedBeaconStateAltair
|
||||
): SubRewardValue {
|
||||
const fork = preState.config.getForkSeq(block.slot);
|
||||
const fork = config.getForkSeq(block.slot);
|
||||
const {attestations} = block.body;
|
||||
|
||||
processAttestationsAltair(fork, preState, attestations, false);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {routes} from "@lodestar/api";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {ForkName, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
||||
import {CachedBeaconStateAllForks, CachedBeaconStateAltair, Index2PubkeyCache} from "@lodestar/state-transition";
|
||||
import {BeaconBlock, ValidatorIndex, altair} from "@lodestar/types";
|
||||
@@ -7,12 +8,13 @@ export type SyncCommitteeRewards = routes.beacon.SyncCommitteeRewards;
|
||||
type BalanceRecord = {val: number}; // Use val for convenient way to increment/decrement balance
|
||||
|
||||
export async function computeSyncCommitteeRewards(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
block: BeaconBlock,
|
||||
preState: CachedBeaconStateAllForks,
|
||||
validatorIds: (ValidatorIndex | string)[] = []
|
||||
): Promise<SyncCommitteeRewards> {
|
||||
const fork = preState.config.getForkName(block.slot);
|
||||
const fork = config.getForkName(block.slot);
|
||||
if (fork === ForkName.phase0) {
|
||||
throw Error("Cannot get sync rewards as phase0 block does not have sync committee");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {routes} from "@lodestar/api";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
computeStartSlotAtEpoch,
|
||||
@@ -24,6 +25,7 @@ export type PersistentCheckpointStateCacheOpts = {
|
||||
};
|
||||
|
||||
type PersistentCheckpointStateCacheModules = {
|
||||
config: BeaconConfig;
|
||||
metrics?: Metrics | null;
|
||||
logger: Logger;
|
||||
clock?: IClock | null;
|
||||
@@ -107,6 +109,7 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
||||
private readonly cache: MapTracker<CacheKey, CacheItem>;
|
||||
/** Epoch -> Set<blockRoot> */
|
||||
private readonly epochIndex = new MapDef<Epoch, Set<RootHex>>(() => new Set<string>());
|
||||
private readonly config: BeaconConfig;
|
||||
private readonly metrics: Metrics | null | undefined;
|
||||
private readonly logger: Logger;
|
||||
private readonly clock: IClock | null | undefined;
|
||||
@@ -120,10 +123,20 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
||||
private readonly bufferPool?: BufferPool | null;
|
||||
|
||||
constructor(
|
||||
{metrics, logger, clock, signal, datastore, blockStateCache, bufferPool}: PersistentCheckpointStateCacheModules,
|
||||
{
|
||||
config,
|
||||
metrics,
|
||||
logger,
|
||||
clock,
|
||||
signal,
|
||||
datastore,
|
||||
blockStateCache,
|
||||
bufferPool,
|
||||
}: PersistentCheckpointStateCacheModules,
|
||||
opts: PersistentCheckpointStateCacheOpts
|
||||
) {
|
||||
this.cache = new MapTracker(metrics?.cpStateCache);
|
||||
this.config = config;
|
||||
if (metrics) {
|
||||
this.metrics = metrics;
|
||||
metrics.cpStateCache.size.addCollect(() => {
|
||||
@@ -484,7 +497,7 @@ export class PersistentCheckpointStateCache implements CheckpointStateCache {
|
||||
}
|
||||
|
||||
const blockSlot = state.slot;
|
||||
const processCPStatesTimeMs = state.config.getSlotComponentDurationMs(PROCESS_CHECKPOINT_STATES_BPS);
|
||||
const processCPStatesTimeMs = this.config.getSlotComponentDurationMs(PROCESS_CHECKPOINT_STATES_BPS);
|
||||
// we always have clock in production, fallback value is only for test
|
||||
const msFromSlot = this.clock?.msFromSlot(blockSlot) ?? processCPStatesTimeMs;
|
||||
const msToProcessCPStates = processCPStatesTimeMs - msFromSlot;
|
||||
|
||||
@@ -51,7 +51,7 @@ export async function validateAttesterSlashing(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSets = getAttesterSlashingSignatureSets(chain.index2pubkey, state, attesterSlashing);
|
||||
const signatureSets = getAttesterSlashingSignatureSets(chain.config, chain.index2pubkey, state, attesterSlashing);
|
||||
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new AttesterSlashingError(GossipAction.REJECT, {
|
||||
code: AttesterSlashingErrorCode.INVALID,
|
||||
|
||||
@@ -153,7 +153,7 @@ export async function validateGossipBlock(
|
||||
|
||||
// [REJECT] The proposer signature, signed_beacon_block.signature, is valid with respect to the proposer_index pubkey.
|
||||
if (!chain.seenBlockInputCache.isVerifiedProposerSignature(blockSlot, blockRoot, signedBlock.signature)) {
|
||||
const signatureSet = getBlockProposerSignatureSet(chain.index2pubkey, blockState, signedBlock);
|
||||
const signatureSet = getBlockProposerSignatureSet(chain.config, chain.index2pubkey, blockState, signedBlock);
|
||||
// Don't batch so verification is not delayed
|
||||
if (!(await chain.bls.verifySignatureSets([signatureSet], {verifyOnMainThread: true}))) {
|
||||
throw new BlockGossipError(GossipAction.REJECT, {
|
||||
|
||||
@@ -41,7 +41,7 @@ async function validateBlsToExecutionChange(
|
||||
// NOTE: No need to advance head state since the signature's fork is handled with `broadcastedOnFork`,
|
||||
// and chanes relevant to `isValidBlsToExecutionChange()` happen only on processBlock(), not processEpoch()
|
||||
const state = chain.getHeadState();
|
||||
const {config} = state;
|
||||
const {config} = chain;
|
||||
|
||||
// [REJECT] All of the conditions within process_bls_to_execution_change pass validation.
|
||||
// verifySignature = false, verified in batch below
|
||||
|
||||
@@ -44,7 +44,7 @@ async function validateProposerSlashing(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSets = getProposerSlashingSignatureSets(chain.index2pubkey, state, proposerSlashing);
|
||||
const signatureSets = getProposerSlashingSignatureSets(chain.config, chain.index2pubkey, state, proposerSlashing);
|
||||
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new ProposerSlashingError(GossipAction.REJECT, {
|
||||
code: ProposerSlashingErrorCode.INVALID,
|
||||
|
||||
@@ -14,7 +14,7 @@ export function getAggregateAndProofSigningRoot(
|
||||
epoch: Epoch,
|
||||
aggregateAndProof: SignedAggregateAndProof
|
||||
): Uint8Array {
|
||||
// previously, we call `const aggregatorDomain = state.config.getDomain(state.slot, DOMAIN_AGGREGATE_AND_PROOF, slot);`
|
||||
// previously, we call `const aggregatorDomain = config.getDomain(state.slot, DOMAIN_AGGREGATE_AND_PROOF, slot);`
|
||||
// at fork boundary, it's required to dial to target epoch https://github.com/ChainSafe/lodestar/blob/v1.11.3/packages/beacon-node/src/chain/validation/attestation.ts#L573
|
||||
// instead of that, just use the fork of slot in the attestation data
|
||||
const slot = computeStartSlotAtEpoch(epoch);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_CONTRIBUTION_AND_PROOF} from "@lodestar/params";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
@@ -9,11 +10,12 @@ import {
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
|
||||
export function getContributionAndProofSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedContributionAndProof: altair.SignedContributionAndProof
|
||||
): ISignatureSet {
|
||||
const domain = state.config.getDomain(
|
||||
const domain = config.getDomain(
|
||||
state.slot,
|
||||
DOMAIN_CONTRIBUTION_AND_PROOF,
|
||||
signedContributionAndProof.message.contribution.slot
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_SYNC_COMMITTEE} from "@lodestar/params";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
@@ -9,11 +10,12 @@ import {
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
|
||||
export function getSyncCommitteeSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
syncCommittee: altair.SyncCommitteeMessage
|
||||
): ISignatureSet {
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, syncCommittee.slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, syncCommittee.slot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import {PublicKey} from "@chainsafe/blst";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_SYNC_COMMITTEE} from "@lodestar/params";
|
||||
import {CachedBeaconStateAltair, ISignatureSet, SignatureSetType, computeSigningRoot} from "@lodestar/state-transition";
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
|
||||
export function getSyncCommitteeContributionSignatureSet(
|
||||
config: BeaconConfig,
|
||||
state: CachedBeaconStateAltair,
|
||||
contribution: altair.SyncCommitteeContribution,
|
||||
pubkeys: PublicKey[]
|
||||
): ISignatureSet {
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, contribution.slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, contribution.slot);
|
||||
return {
|
||||
type: SignatureSetType.aggregate,
|
||||
pubkeys,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF} from "@lodestar/params";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
@@ -9,11 +10,11 @@ import {
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
|
||||
export function getSyncCommitteeSelectionProofSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
contributionAndProof: altair.ContributionAndProof
|
||||
): ISignatureSet {
|
||||
const {config} = state;
|
||||
const slot = contributionAndProof.contribution.slot;
|
||||
const domain = config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, slot);
|
||||
const signingData: altair.SyncAggregatorSelectionData = {
|
||||
|
||||
@@ -89,7 +89,7 @@ async function validateSyncCommitteeSigOnly(
|
||||
syncCommittee: altair.SyncCommitteeMessage,
|
||||
prioritizeBls = false
|
||||
): Promise<void> {
|
||||
const signatureSet = getSyncCommitteeSignatureSet(chain.index2pubkey, headState, syncCommittee);
|
||||
const signatureSet = getSyncCommitteeSignatureSet(chain.config, chain.index2pubkey, headState, syncCommittee);
|
||||
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new SyncCommitteeError(GossipAction.REJECT, {
|
||||
code: SyncCommitteeErrorCode.INVALID_SIGNATURE,
|
||||
|
||||
@@ -78,14 +78,19 @@ export async function validateSyncCommitteeGossipContributionAndProof(
|
||||
const signatureSets = [
|
||||
// [REJECT] The contribution_and_proof.selection_proof is a valid signature of the SyncAggregatorSelectionData
|
||||
// derived from the contribution by the validator with index contribution_and_proof.aggregator_index.
|
||||
getSyncCommitteeSelectionProofSignatureSet(index2pubkey, headState, contributionAndProof),
|
||||
getSyncCommitteeSelectionProofSignatureSet(chain.config, index2pubkey, headState, contributionAndProof),
|
||||
|
||||
// [REJECT] The aggregator signature, signed_contribution_and_proof.signature, is valid.
|
||||
getContributionAndProofSignatureSet(index2pubkey, headState, signedContributionAndProof),
|
||||
getContributionAndProofSignatureSet(chain.config, index2pubkey, headState, signedContributionAndProof),
|
||||
|
||||
// [REJECT] The aggregate signature is valid for the message beacon_block_root and aggregate pubkey derived from
|
||||
// the participation info in aggregation_bits for the subcommittee specified by the contribution.subcommittee_index.
|
||||
getSyncCommitteeContributionSignatureSet(headState as CachedBeaconStateAltair, contribution, participantPubkeys),
|
||||
getSyncCommitteeContributionSignatureSet(
|
||||
chain.config,
|
||||
headState as CachedBeaconStateAltair,
|
||||
contribution,
|
||||
participantPubkeys
|
||||
),
|
||||
];
|
||||
|
||||
if (!(await chain.bls.verifySignatureSets(signatureSets, {batchable: true}))) {
|
||||
|
||||
@@ -59,7 +59,7 @@ async function validateVoluntaryExit(
|
||||
});
|
||||
}
|
||||
|
||||
const signatureSet = getVoluntaryExitSignatureSet(chain.index2pubkey, state, voluntaryExit);
|
||||
const signatureSet = getVoluntaryExitSignatureSet(chain.config, chain.index2pubkey, state, voluntaryExit);
|
||||
if (!(await chain.bls.verifySignatureSets([signatureSet], {batchable: true, priority: prioritizeBls}))) {
|
||||
throw new VoluntaryExitError(GossipAction.REJECT, {
|
||||
code: VoluntaryExitErrorCode.INVALID_SIGNATURE,
|
||||
|
||||
@@ -750,9 +750,13 @@ export class BackfillSync extends (EventEmitter as {new (): BackfillSyncEmitter}
|
||||
|
||||
// GENESIS_SLOT doesn't has valid signature
|
||||
if (anchorBlock.message.slot === GENESIS_SLOT) return;
|
||||
await verifyBlockProposerSignature(this.chain.index2pubkey, this.chain.bls, this.chain.getHeadState(), [
|
||||
anchorBlock,
|
||||
]);
|
||||
await verifyBlockProposerSignature(
|
||||
this.chain.config,
|
||||
this.chain.index2pubkey,
|
||||
this.chain.bls,
|
||||
this.chain.getHeadState(),
|
||||
[anchorBlock]
|
||||
);
|
||||
|
||||
// We can write to the disk if this is ahead of prevFinalizedCheckpointBlock otherwise
|
||||
// we will need to go make checks on the top of sync loop before writing as it might
|
||||
@@ -818,6 +822,7 @@ export class BackfillSync extends (EventEmitter as {new (): BackfillSyncEmitter}
|
||||
// If any of the block's proposer signature fail, we can't trust this peer at all
|
||||
if (verifiedBlocks.length > 0) {
|
||||
await verifyBlockProposerSignature(
|
||||
this.chain.config,
|
||||
this.chain.index2pubkey,
|
||||
this.chain.bls,
|
||||
this.chain.getHeadState(),
|
||||
|
||||
@@ -46,6 +46,7 @@ export function verifyBlockSequence(
|
||||
}
|
||||
|
||||
export async function verifyBlockProposerSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
bls: IBlsVerifier,
|
||||
state: CachedBeaconStateAllForks,
|
||||
@@ -54,7 +55,8 @@ export async function verifyBlockProposerSignature(
|
||||
if (blocks.length === 1 && blocks[0].message.slot === GENESIS_SLOT) return;
|
||||
const signatures = blocks.reduce((sigs: ISignatureSet[], block) => {
|
||||
// genesis block doesn't have valid signature
|
||||
if (block.message.slot !== GENESIS_SLOT) sigs.push(getBlockProposerSignatureSet(index2pubkey, state, block));
|
||||
if (block.message.slot !== GENESIS_SLOT)
|
||||
sigs.push(getBlockProposerSignatureSet(config, index2pubkey, state, block));
|
||||
return sigs;
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Mock, Mocked, vi} from "vitest";
|
||||
import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
|
||||
import {ChainForkConfig} from "@lodestar/config";
|
||||
import {BeaconConfig, ChainForkConfig} from "@lodestar/config";
|
||||
import {config as defaultConfig} from "@lodestar/config/default";
|
||||
import {EpochDifference, ForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
||||
import {Logger} from "@lodestar/utils";
|
||||
@@ -133,8 +133,8 @@ vi.mock("../../src/chain/chain.js", async (importActual) => {
|
||||
getClientVersion: vi.fn(),
|
||||
},
|
||||
executionBuilder: {},
|
||||
opPool: new OpPool(),
|
||||
aggregatedAttestationPool: new AggregatedAttestationPool(config),
|
||||
opPool: new OpPool(config as BeaconConfig),
|
||||
aggregatedAttestationPool: new AggregatedAttestationPool(config as BeaconConfig),
|
||||
syncContributionAndProofPool: new SyncContributionAndProofPool(config, clock),
|
||||
// @ts-expect-error
|
||||
beaconProposerCache: new BeaconProposerCache(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {beforeAll, bench, describe} from "@chainsafe/benchmark";
|
||||
import {BitArray, toHexString} from "@chainsafe/ssz";
|
||||
import {createChainForkConfig, defaultChainConfig} from "@lodestar/config";
|
||||
import {createBeaconConfig, defaultChainConfig} from "@lodestar/config";
|
||||
import {ExecutionStatus, ForkChoice, IForkChoiceStore, ProtoArray} from "@lodestar/fork-choice";
|
||||
import {HISTORICAL_ROOTS_LIMIT, SLOTS_PER_EPOCH} from "@lodestar/params";
|
||||
import {
|
||||
@@ -232,7 +232,7 @@ function getAggregatedAttestationPool(
|
||||
numMissedVotes: number,
|
||||
numBadVotes: number
|
||||
): AggregatedAttestationPool {
|
||||
const config = createChainForkConfig(defaultChainConfig);
|
||||
const config = createBeaconConfig(defaultChainConfig, Buffer.alloc(32, 0xaa));
|
||||
|
||||
const pool = new AggregatedAttestationPool(config);
|
||||
for (let epochSlot = 0; epochSlot < SLOTS_PER_EPOCH; epochSlot++) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {beforeAll, bench, describe} from "@chainsafe/benchmark";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {
|
||||
ForkName,
|
||||
MAX_ATTESTER_SLASHINGS,
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
|
||||
describe("opPool", () => {
|
||||
let originalState: CachedBeaconStateAltair;
|
||||
const config = createBeaconConfig(chainConfigDef, Buffer.alloc(32, 0xaa));
|
||||
|
||||
beforeAll(
|
||||
() => {
|
||||
@@ -31,7 +34,7 @@ describe("opPool", () => {
|
||||
bench({
|
||||
id: "getSlashingsAndExits - default max",
|
||||
beforeEach: () => {
|
||||
const pool = new OpPool();
|
||||
const pool = new OpPool(config);
|
||||
fillAttesterSlashing(pool, originalState, MAX_ATTESTER_SLASHINGS);
|
||||
fillProposerSlashing(pool, originalState, MAX_PROPOSER_SLASHINGS);
|
||||
fillVoluntaryExits(pool, originalState, MAX_VOLUNTARY_EXITS);
|
||||
@@ -48,7 +51,7 @@ describe("opPool", () => {
|
||||
bench({
|
||||
id: "getSlashingsAndExits - 2k",
|
||||
beforeEach: () => {
|
||||
const pool = new OpPool();
|
||||
const pool = new OpPool(config);
|
||||
const maxItemsInPool = 2_000;
|
||||
|
||||
fillAttesterSlashing(pool, originalState, maxItemsInPool);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import {SecretKey, Signature, aggregateSignatures, fastAggregateVerify} from "@chainsafe/blst";
|
||||
import {BitArray, fromHexString, toHexString} from "@chainsafe/ssz";
|
||||
import {createChainForkConfig, defaultChainConfig} from "@lodestar/config";
|
||||
import {createBeaconConfig, createChainForkConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDefault} from "@lodestar/config/default";
|
||||
import {
|
||||
ACTIVE_PRESET,
|
||||
FAR_FUTURE_EPOCH,
|
||||
@@ -48,9 +49,6 @@ describe("AggregatedAttestationPool - Altair", () => {
|
||||
|
||||
let pool: AggregatedAttestationPool;
|
||||
const fork = ForkName.altair;
|
||||
const config = createChainForkConfig({
|
||||
...defaultChainConfig,
|
||||
});
|
||||
const altairForkEpoch = 2020;
|
||||
const currentEpoch = altairForkEpoch + 10;
|
||||
const currentSlot = SLOTS_PER_EPOCH * currentEpoch;
|
||||
@@ -93,6 +91,10 @@ describe("AggregatedAttestationPool - Altair", () => {
|
||||
let altairState: CachedBeaconStateAllForks;
|
||||
|
||||
let forkchoiceStub: MockedForkChoice;
|
||||
const config = createBeaconConfig(
|
||||
createChainForkConfig({...chainConfigDefault, ALTAIR_FORK_EPOCH: altairForkEpoch}),
|
||||
originalState.genesisValidatorsRoot
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
pool = new AggregatedAttestationPool(config);
|
||||
@@ -107,7 +109,7 @@ describe("AggregatedAttestationPool - Altair", () => {
|
||||
it("getNotSeenValidatorsFn", () => {
|
||||
// previousEpochParticipation and currentEpochParticipation is created inside generateCachedState
|
||||
// 0 and 1 are fully participated
|
||||
const notSeenValidatorFn = getNotSeenValidatorsFn(altairState);
|
||||
const notSeenValidatorFn = getNotSeenValidatorsFn(config, altairState);
|
||||
// seen attesting indices are 0, 1 => not seen are 2, 3
|
||||
expect(notSeenValidatorFn(currentEpoch, currentSlot - 1, committeeIndex)).toEqual(new Set([2, 3]));
|
||||
// attestations in current slot are always included (since altairState.slot = currentSlot + 1)
|
||||
@@ -169,14 +171,15 @@ describe("AggregatedAttestationPool - get packed attestations - Electra", () =>
|
||||
let pool: AggregatedAttestationPool;
|
||||
const fork = ForkName.electra;
|
||||
const electraForkEpoch = 2020;
|
||||
const config = createChainForkConfig({
|
||||
...defaultChainConfig,
|
||||
const chainConfig = createChainForkConfig({
|
||||
...chainConfigDefault,
|
||||
ALTAIR_FORK_EPOCH: 0,
|
||||
BELLATRIX_FORK_EPOCH: 0,
|
||||
CAPELLA_FORK_EPOCH: 0,
|
||||
DENEB_FORK_EPOCH: 0,
|
||||
ELECTRA_FORK_EPOCH: electraForkEpoch,
|
||||
});
|
||||
const config = createBeaconConfig(chainConfig, Buffer.alloc(32, 0xaa));
|
||||
const currentEpoch = electraForkEpoch + 10;
|
||||
const currentSlot = SLOTS_PER_EPOCH * currentEpoch;
|
||||
|
||||
@@ -393,9 +396,7 @@ describe("AggregatedAttestationPool - get packed attestations - Electra", () =>
|
||||
});
|
||||
|
||||
describe("MatchingDataAttestationGroup.add()", () => {
|
||||
const config = createChainForkConfig({
|
||||
...defaultChainConfig,
|
||||
});
|
||||
const config = createBeaconConfig(chainConfigDefault, Buffer.alloc(32, 0xaa));
|
||||
|
||||
const testCases: {id: string; attestationsToAdd: {bits: number[]; res: InsertOutcome; isKept: boolean}[]}[] = [
|
||||
{
|
||||
@@ -465,9 +466,7 @@ describe("MatchingDataAttestationGroup.add()", () => {
|
||||
});
|
||||
|
||||
describe("MatchingDataAttestationGroup.getAttestationsForBlock", () => {
|
||||
const config = createChainForkConfig({
|
||||
...defaultChainConfig,
|
||||
});
|
||||
const config = createBeaconConfig(chainConfigDefault, Buffer.alloc(32, 0xaa));
|
||||
|
||||
const maxAttestations = 2;
|
||||
const testCases: {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {beforeAll, beforeEach, describe, expect, it} from "vitest";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {ACTIVE_PRESET, PresetName, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
||||
import {CachedBeaconStateAllForks, computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
||||
import {RootHex, phase0} from "@lodestar/types";
|
||||
@@ -27,6 +29,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
let fileApisBuffer: Map<string, Uint8Array>;
|
||||
let states: Record<"cp0a" | "cp0b" | "cp1" | "cp2", CachedBeaconStateAllForks>;
|
||||
let stateBytes: Record<"cp0a" | "cp0b" | "cp1" | "cp2", Uint8Array>;
|
||||
const config = createBeaconConfig(chainConfigDef, Buffer.alloc(32, 0xaa));
|
||||
|
||||
beforeAll(() => {
|
||||
root0a = Buffer.alloc(32);
|
||||
@@ -91,6 +94,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
@@ -165,6 +169,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
@@ -241,6 +246,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
@@ -546,6 +552,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
@@ -817,6 +824,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
@@ -907,6 +915,7 @@ describe("PersistentCheckpointStateCache", () => {
|
||||
const datastore = getTestDatastore(fileApisBuffer);
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("api/validator - produceBlockV3", () => {
|
||||
const config = createBeaconConfig(chainConfig, genesisValidatorsRoot);
|
||||
|
||||
beforeEach(() => {
|
||||
modules = getApiTestModules();
|
||||
modules = getApiTestModules({config});
|
||||
api = getValidatorApi(defaultApiOptions, {...modules, config});
|
||||
state = generateCachedBellatrixState();
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {beforeEach, describe, expect, it} from "vitest";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
||||
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
||||
import {RegenCaller} from "../../../../src/chain/regen/interface.js";
|
||||
@@ -10,6 +12,7 @@ import {testLogger} from "../../../utils/logger.js";
|
||||
import {generateCachedState} from "../../../utils/state.js";
|
||||
|
||||
describe("regen", () => {
|
||||
const config = createBeaconConfig(chainConfigDef, Buffer.alloc(32, 0xaa));
|
||||
//
|
||||
// epoch: 19 20 21 22 23
|
||||
// |-----------|-----------|-----------|-----------|
|
||||
@@ -74,6 +77,7 @@ describe("regen", () => {
|
||||
beforeEach(() => {
|
||||
cache = new PersistentCheckpointStateCache(
|
||||
{
|
||||
config,
|
||||
datastore,
|
||||
logger: testLogger(),
|
||||
blockStateCache: new FIFOBlockStateCache({}, {}),
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {describe, expect, it, vi} from "vitest";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
import {computeBlockRewards} from "../../../../src/chain/rewards/blockRewards.js";
|
||||
|
||||
describe("chain / rewards / blockRewards", () => {
|
||||
const config = createBeaconConfig({...chainConfigDef, ALTAIR_FORK_EPOCH: 0}, Buffer.alloc(32, 0xaa));
|
||||
const testCases: {id: string; timeout?: number; opts: BlockAltairOpts}[] = [
|
||||
{
|
||||
id: "Normal case",
|
||||
@@ -92,7 +95,11 @@ describe("chain / rewards / blockRewards", () => {
|
||||
// Populate tree root caches of the state
|
||||
state.hashTreeRoot();
|
||||
cachedStateAltairPopulateCaches(state);
|
||||
const calculatedBlockReward = await computeBlockRewards(block.message, state as CachedBeaconStateAllForks);
|
||||
const calculatedBlockReward = await computeBlockRewards(
|
||||
config,
|
||||
block.message,
|
||||
state as CachedBeaconStateAllForks
|
||||
);
|
||||
const {proposerIndex, total, attestations, syncAggregate, proposerSlashings, attesterSlashings} =
|
||||
calculatedBlockReward;
|
||||
|
||||
@@ -153,6 +160,7 @@ describe("chain / rewards / blockRewards", () => {
|
||||
postState.proposerRewards = {attestations: 1000, syncAggregate: 1001, slashing: 1002};
|
||||
|
||||
const calculatedBlockReward = await computeBlockRewards(
|
||||
config,
|
||||
block.message,
|
||||
preState as CachedBeaconStateAllForks,
|
||||
postState
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {afterEach, beforeEach, describe, it, vi} from "vitest";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {phase0, ssz} from "@lodestar/types";
|
||||
import {AttesterSlashingErrorCode} from "../../../../src/chain/errors/attesterSlashingError.js";
|
||||
import {validateGossipAttesterSlashing} from "../../../../src/chain/validation/attesterSlashing.js";
|
||||
@@ -9,9 +11,10 @@ import {generateCachedState} from "../../../utils/state.js";
|
||||
describe("GossipMessageValidator", () => {
|
||||
let chainStub: MockedBeaconChain;
|
||||
let opPool: MockedBeaconChain["opPool"];
|
||||
const config = createBeaconConfig(chainConfigDef, Buffer.alloc(32, 0xaa));
|
||||
|
||||
beforeEach(() => {
|
||||
chainStub = getMockedBeaconChain();
|
||||
chainStub = getMockedBeaconChain({config});
|
||||
opPool = chainStub.opPool;
|
||||
|
||||
const state = generateCachedState();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Mock, Mocked, beforeEach, describe, it, vi} from "vitest";
|
||||
import {createChainForkConfig} from "@lodestar/config";
|
||||
import {config} from "@lodestar/config/default";
|
||||
import {createBeaconConfig, createChainForkConfig} from "@lodestar/config";
|
||||
import {config as configDef} from "@lodestar/config/default";
|
||||
import {ProtoBlock} from "@lodestar/fork-choice";
|
||||
import {ForkName, ForkPostDeneb, ForkPreFulu} from "@lodestar/params";
|
||||
import {SignedBeaconBlock, ssz} from "@lodestar/types";
|
||||
@@ -26,15 +26,16 @@ describe("gossip block validation", () => {
|
||||
const signature = EMPTY_SIGNATURE;
|
||||
const maxSkipSlots = 10;
|
||||
const denebConfig = createChainForkConfig({
|
||||
...config,
|
||||
...configDef,
|
||||
ALTAIR_FORK_EPOCH: 0,
|
||||
BELLATRIX_FORK_EPOCH: 0,
|
||||
CAPELLA_FORK_EPOCH: 0,
|
||||
DENEB_FORK_EPOCH: 0,
|
||||
});
|
||||
const config = createBeaconConfig(configDef, Buffer.alloc(32, 0xaa));
|
||||
|
||||
beforeEach(() => {
|
||||
chain = getMockedBeaconChain();
|
||||
chain = getMockedBeaconChain({config});
|
||||
vi.spyOn(chain.clock, "currentSlotWithGossipDisparity", "get").mockReturnValue(clockSlot);
|
||||
forkChoice = chain.forkChoice;
|
||||
forkChoice.getBlockHex.mockReturnValue(null);
|
||||
|
||||
@@ -83,7 +83,7 @@ describe("validate bls to execution change", () => {
|
||||
const signedBlsToExecChange = {message: blsToExecutionChange, signature: wsk.sign(signingRoot).toBytes()};
|
||||
|
||||
beforeEach(() => {
|
||||
chainStub = getMockedBeaconChain();
|
||||
chainStub = getMockedBeaconChain({config});
|
||||
opPool = chainStub.opPool;
|
||||
vi.spyOn(chainStub, "getHeadState").mockReturnValue(state);
|
||||
vi.spyOn(chainStub, "getHeadStateAtCurrentEpoch");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {afterEach, beforeEach, describe, it, vi} from "vitest";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {chainConfig as chainConfigDef} from "@lodestar/config/default";
|
||||
import {phase0, ssz} from "@lodestar/types";
|
||||
import {ProposerSlashingErrorCode} from "../../../../src/chain/errors/proposerSlashingError.js";
|
||||
import {validateGossipProposerSlashing} from "../../../../src/chain/validation/proposerSlashing.js";
|
||||
@@ -9,9 +11,10 @@ import {generateCachedState} from "../../../utils/state.js";
|
||||
describe("validate proposer slashing", () => {
|
||||
let chainStub: MockedBeaconChain;
|
||||
let opPool: MockedBeaconChain["opPool"];
|
||||
const config = createBeaconConfig(chainConfigDef, Buffer.alloc(32, 0xaa));
|
||||
|
||||
beforeEach(() => {
|
||||
chainStub = getMockedBeaconChain();
|
||||
chainStub = getMockedBeaconChain({config});
|
||||
opPool = chainStub.opPool;
|
||||
|
||||
const state = generateCachedState();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Mock, afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import {toHexString} from "@chainsafe/ssz";
|
||||
import {createChainForkConfig, defaultChainConfig} from "@lodestar/config";
|
||||
import {createBeaconConfig, createChainForkConfig, defaultChainConfig} from "@lodestar/config";
|
||||
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
||||
import {Epoch, Slot, altair} from "@lodestar/types";
|
||||
import {SyncCommitteeErrorCode} from "../../../../src/chain/errors/syncCommitteeError.js";
|
||||
@@ -20,7 +20,10 @@ describe("Sync Committee Signature validation", () => {
|
||||
let altairForkEpochBk: Epoch;
|
||||
const altairForkEpoch = 2020;
|
||||
const currentSlot = SLOTS_PER_EPOCH * (altairForkEpoch + 1);
|
||||
const config = createChainForkConfig(Object.assign({}, defaultChainConfig, {ALTAIR_FORK_EPOCH: altairForkEpoch}));
|
||||
const chainConfig = createChainForkConfig(
|
||||
Object.assign({}, defaultChainConfig, {ALTAIR_FORK_EPOCH: altairForkEpoch})
|
||||
);
|
||||
const config = createBeaconConfig(chainConfig, Buffer.alloc(32, 0xaa));
|
||||
// all validators have same pubkey
|
||||
const validatorIndexInSyncCommittee = 15;
|
||||
|
||||
@@ -34,7 +37,7 @@ describe("Sync Committee Signature validation", () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
chain = getMockedBeaconChain();
|
||||
chain = getMockedBeaconChain({config});
|
||||
(
|
||||
chain as {
|
||||
seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, it, vi} from "vitest";
|
||||
import {SecretKey} from "@chainsafe/blst";
|
||||
import {createBeaconConfig} from "@lodestar/config";
|
||||
import {config} from "@lodestar/config/default";
|
||||
import {BeaconConfig, createBeaconConfig, createChainForkConfig} from "@lodestar/config";
|
||||
import {chainConfig} from "@lodestar/config/default";
|
||||
import {DOMAIN_VOLUNTARY_EXIT, FAR_FUTURE_EPOCH, SLOTS_PER_EPOCH} from "@lodestar/params";
|
||||
import {
|
||||
CachedBeaconStateAllForks,
|
||||
@@ -22,6 +22,7 @@ describe("validate voluntary exit", () => {
|
||||
let state: CachedBeaconStateAllForks;
|
||||
let signedVoluntaryExit: phase0.SignedVoluntaryExit;
|
||||
let opPool: MockedBeaconChain["opPool"];
|
||||
let config: BeaconConfig;
|
||||
|
||||
beforeAll(() => {
|
||||
const sk = SecretKey.fromKeygen(Buffer.alloc(32));
|
||||
@@ -29,7 +30,7 @@ describe("validate voluntary exit", () => {
|
||||
const stateEmpty = ssz.phase0.BeaconState.defaultValue();
|
||||
|
||||
// Validator has to be active for long enough
|
||||
stateEmpty.slot = config.SHARD_COMMITTEE_PERIOD * SLOTS_PER_EPOCH;
|
||||
stateEmpty.slot = chainConfig.SHARD_COMMITTEE_PERIOD * SLOTS_PER_EPOCH;
|
||||
|
||||
// Add a validator that's active since genesis and ready to exit
|
||||
const validator = ssz.phase0.Validator.toViewDU({
|
||||
@@ -55,13 +56,14 @@ describe("validate voluntary exit", () => {
|
||||
);
|
||||
const signingRoot = computeSigningRoot(ssz.phase0.VoluntaryExit, voluntaryExit, domain);
|
||||
signedVoluntaryExit = {message: voluntaryExit, signature: sk.sign(signingRoot).toBytes()};
|
||||
const _state = generateState(stateEmpty, config);
|
||||
const _state = generateState(stateEmpty, createChainForkConfig(chainConfig));
|
||||
config = createBeaconConfig(chainConfig, _state.genesisValidatorsRoot);
|
||||
|
||||
state = createCachedBeaconStateTest(_state, createBeaconConfig(config, _state.genesisValidatorsRoot));
|
||||
state = createCachedBeaconStateTest(_state, config);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
chainStub = getMockedBeaconChain();
|
||||
chainStub = getMockedBeaconChain({config});
|
||||
opPool = chainStub.opPool;
|
||||
vi.spyOn(chainStub, "getHeadStateAtCurrentEpoch").mockResolvedValue(state);
|
||||
vi.spyOn(opPool, "hasSeenBlsToExecutionChange");
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {ForkSeq, MAX_COMMITTEES_PER_SLOT, MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params";
|
||||
import {IndexedAttestation, IndexedAttestationBigint} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -9,6 +10,7 @@ import {verifySignatureSet} from "../util/index.js";
|
||||
* Check if `indexedAttestation` has sorted and unique indices and a valid aggregate signature.
|
||||
*/
|
||||
export function isValidIndexedAttestation(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
indexedAttestation: IndexedAttestation,
|
||||
@@ -19,12 +21,13 @@ export function isValidIndexedAttestation(
|
||||
}
|
||||
|
||||
if (verifySignature) {
|
||||
return verifySignatureSet(getIndexedAttestationSignatureSet(index2pubkey, state, indexedAttestation));
|
||||
return verifySignatureSet(getIndexedAttestationSignatureSet(config, index2pubkey, state, indexedAttestation));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isValidIndexedAttestationBigint(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
indexedAttestation: IndexedAttestationBigint,
|
||||
@@ -35,7 +38,7 @@ export function isValidIndexedAttestationBigint(
|
||||
}
|
||||
|
||||
if (verifySignature) {
|
||||
return verifySignatureSet(getIndexedAttestationBigintSignatureSet(index2pubkey, state, indexedAttestation));
|
||||
return verifySignatureSet(getIndexedAttestationBigintSignatureSet(config, index2pubkey, state, indexedAttestation));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export function processAttestationPhase0(
|
||||
|
||||
if (
|
||||
!isValidIndexedAttestation(
|
||||
state.config,
|
||||
epochCtx.index2pubkey,
|
||||
state,
|
||||
epochCtx.getIndexedAttestation(ForkSeq.phase0, attestation),
|
||||
|
||||
@@ -64,7 +64,13 @@ export function processAttestationsAltair(
|
||||
// TODO: Why should we verify an indexed attestation that we just created? If it's just for the signature
|
||||
// we can verify only that and nothing else.
|
||||
if (verifySignature) {
|
||||
const sigSet = getAttestationWithIndicesSignatureSet(epochCtx.index2pubkey, state, attestation, attestingIndices);
|
||||
const sigSet = getAttestationWithIndicesSignatureSet(
|
||||
state.config,
|
||||
epochCtx.index2pubkey,
|
||||
state,
|
||||
attestation,
|
||||
attestingIndices
|
||||
);
|
||||
if (!verifySignatureSet(sigSet)) {
|
||||
throw new Error("Attestation signature is not valid");
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ export function assertValidAttesterSlashing(
|
||||
// be higher than the clock and the slashing would still be valid. Same applies to attestation data index, which
|
||||
// can be any arbitrary value. Must use bigint variants to hash correctly to all possible values
|
||||
for (const [i, attestation] of [attestation1, attestation2].entries()) {
|
||||
if (!isValidIndexedAttestationBigint(index2pubkey, state, attestation, verifySignatures)) {
|
||||
if (!isValidIndexedAttestationBigint(state.config, index2pubkey, state, attestation, verifySignatures)) {
|
||||
throw new Error(`AttesterSlashing attestation${i} is invalid`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,12 @@ export function assertValidProposerSlashing(
|
||||
|
||||
// verify signatures
|
||||
if (verifySignatures) {
|
||||
const signatureSets = getProposerSlashingSignatureSets(state.epochCtx.index2pubkey, state, proposerSlashing);
|
||||
const signatureSets = getProposerSlashingSignatureSets(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
proposerSlashing
|
||||
);
|
||||
for (let i = 0; i < signatureSets.length; i++) {
|
||||
if (!verifySignatureSet(signatureSets[i])) {
|
||||
throw new Error(`ProposerSlashing header${i + 1} signature invalid`);
|
||||
|
||||
@@ -17,7 +17,7 @@ export function processRandao(state: CachedBeaconStateAllForks, block: BeaconBlo
|
||||
const randaoReveal = block.body.randaoReveal;
|
||||
|
||||
// verify RANDAO reveal
|
||||
if (verifySignature && !verifyRandaoSignature(epochCtx.index2pubkey, state, block)) {
|
||||
if (verifySignature && !verifyRandaoSignature(state.config, epochCtx.index2pubkey, state, block)) {
|
||||
throw new Error("RANDAO reveal is an invalid signature");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {byteArrayEquals} from "@chainsafe/ssz";
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_SYNC_COMMITTEE, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
||||
import {altair, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -24,7 +25,13 @@ export function processSyncAggregate(
|
||||
if (verifySignatures) {
|
||||
// This is to conform to the spec - we want the signature to be verified
|
||||
const participantIndices = block.body.syncAggregate.syncCommitteeBits.intersectValues(committeeIndices);
|
||||
const signatureSet = getSyncCommitteeSignatureSet(state.epochCtx.index2pubkey, state, block, participantIndices);
|
||||
const signatureSet = getSyncCommitteeSignatureSet(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
block,
|
||||
participantIndices
|
||||
);
|
||||
// When there's no participation we consider the signature valid and just ignore i
|
||||
if (signatureSet !== null && !verifySignatureSet(signatureSet)) {
|
||||
throw Error("Sync committee signature invalid");
|
||||
@@ -64,6 +71,7 @@ export function processSyncAggregate(
|
||||
}
|
||||
|
||||
export function getSyncCommitteeSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
block: altair.BeaconBlock,
|
||||
@@ -107,7 +115,7 @@ export function getSyncCommitteeSignatureSet(
|
||||
throw Error("Empty sync committee signature is not infinity");
|
||||
}
|
||||
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, previousSlot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_SYNC_COMMITTEE, previousSlot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.aggregate,
|
||||
|
||||
@@ -74,7 +74,10 @@ export function getVoluntaryExitValidity(
|
||||
return VoluntaryExitValidity.pendingWithdrawals;
|
||||
}
|
||||
|
||||
if (verifySignature && !verifyVoluntaryExitSignature(epochCtx.index2pubkey, state, signedVoluntaryExit)) {
|
||||
if (
|
||||
verifySignature &&
|
||||
!verifyVoluntaryExitSignature(state.config, epochCtx.index2pubkey, state, signedVoluntaryExit)
|
||||
) {
|
||||
return VoluntaryExitValidity.invalidSignature;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "./types.js";
|
||||
|
||||
export type BeaconStateCache = {
|
||||
/** @deprecated should not access config outside of state-transition package */
|
||||
config: BeaconConfig;
|
||||
epochCtx: EpochCache;
|
||||
/** Count of clones created from this BeaconStateCache instance. readonly to prevent accidental usage downstream */
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
|
||||
import {AttesterSlashing, IndexedAttestationBigint, SignedBeaconBlock, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -6,33 +7,36 @@ import {ISignatureSet, SignatureSetType, computeSigningRoot, computeStartSlotAtE
|
||||
|
||||
/** Get signature sets from all AttesterSlashing objects in a block */
|
||||
export function getAttesterSlashingsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
return signedBlock.message.body.attesterSlashings.flatMap((attesterSlashing) =>
|
||||
getAttesterSlashingSignatureSets(index2pubkey, state, attesterSlashing)
|
||||
getAttesterSlashingSignatureSets(config, index2pubkey, state, attesterSlashing)
|
||||
);
|
||||
}
|
||||
|
||||
/** Get signature sets from a single AttesterSlashing object */
|
||||
export function getAttesterSlashingSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
attesterSlashing: AttesterSlashing
|
||||
): ISignatureSet[] {
|
||||
return [attesterSlashing.attestation1, attesterSlashing.attestation2].map((attestation) =>
|
||||
getIndexedAttestationBigintSignatureSet(index2pubkey, state, attestation)
|
||||
getIndexedAttestationBigintSignatureSet(config, index2pubkey, state, attestation)
|
||||
);
|
||||
}
|
||||
|
||||
export function getIndexedAttestationBigintSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
indexedAttestation: IndexedAttestationBigint
|
||||
): ISignatureSet {
|
||||
const slot = computeStartSlotAtEpoch(Number(indexedAttestation.data.target.epoch as bigint));
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.aggregate,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {ForkSeq} from "@lodestar/params";
|
||||
import {IndexedAttestation, SignedBeaconBlock, altair, capella} from "@lodestar/types";
|
||||
import {getSyncCommitteeSignatureSet} from "../block/processSyncCommittee.js";
|
||||
@@ -26,6 +27,7 @@ export * from "./voluntaryExits.js";
|
||||
* Deposits are not included because they can legally have invalid signatures.
|
||||
*/
|
||||
export function getBlockSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock,
|
||||
@@ -39,20 +41,21 @@ export function getBlockSignatureSets(
|
||||
const fork = state.config.getForkSeq(signedBlock.message.slot);
|
||||
|
||||
const signatureSets = [
|
||||
getRandaoRevealSignatureSet(index2pubkey, state, signedBlock.message),
|
||||
...getProposerSlashingsSignatureSets(index2pubkey, state, signedBlock),
|
||||
...getAttesterSlashingsSignatureSets(index2pubkey, state, signedBlock),
|
||||
...getAttestationsSignatureSets(index2pubkey, state, signedBlock, indexedAttestations),
|
||||
...getVoluntaryExitsSignatureSets(index2pubkey, state, signedBlock),
|
||||
getRandaoRevealSignatureSet(config, index2pubkey, state, signedBlock.message),
|
||||
...getProposerSlashingsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
...getAttesterSlashingsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
...getAttestationsSignatureSets(config, index2pubkey, state, signedBlock, indexedAttestations),
|
||||
...getVoluntaryExitsSignatureSets(config, index2pubkey, state, signedBlock),
|
||||
];
|
||||
|
||||
if (!opts?.skipProposerSignature) {
|
||||
signatureSets.push(getBlockProposerSignatureSet(index2pubkey, state, signedBlock));
|
||||
signatureSets.push(getBlockProposerSignatureSet(config, index2pubkey, state, signedBlock));
|
||||
}
|
||||
|
||||
// Only after altair fork, validate tSyncCommitteeSignature
|
||||
if (fork >= ForkSeq.altair) {
|
||||
const syncCommitteeSignatureSet = getSyncCommitteeSignatureSet(
|
||||
config,
|
||||
index2pubkey,
|
||||
state as CachedBeaconStateAltair,
|
||||
(signedBlock as altair.SignedBeaconBlock).message
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_ATTESTER} from "@lodestar/params";
|
||||
import {IndexedAttestation, SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -10,16 +11,18 @@ import {
|
||||
} from "../util/index.js";
|
||||
|
||||
export function getAttestationDataSigningRoot(
|
||||
config: BeaconConfig,
|
||||
state: CachedBeaconStateAllForks,
|
||||
data: phase0.AttestationData
|
||||
): Uint8Array {
|
||||
const slot = computeStartSlotAtEpoch(data.target.epoch);
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_ATTESTER, slot);
|
||||
|
||||
return computeSigningRoot(ssz.phase0.AttestationData, data, domain);
|
||||
}
|
||||
|
||||
export function getAttestationWithIndicesSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
attestation: Pick<phase0.Attestation, "data" | "signature">,
|
||||
@@ -27,17 +30,19 @@ export function getAttestationWithIndicesSignatureSet(
|
||||
): ISignatureSet {
|
||||
return createAggregateSignatureSetFromComponents(
|
||||
attestingIndices.map((i) => index2pubkey[i]),
|
||||
getAttestationDataSigningRoot(state, attestation.data),
|
||||
getAttestationDataSigningRoot(config, state, attestation.data),
|
||||
attestation.signature
|
||||
);
|
||||
}
|
||||
|
||||
export function getIndexedAttestationSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
indexedAttestation: IndexedAttestation
|
||||
): ISignatureSet {
|
||||
return getAttestationWithIndicesSignatureSet(
|
||||
config,
|
||||
index2pubkey,
|
||||
state,
|
||||
indexedAttestation,
|
||||
@@ -46,6 +51,7 @@ export function getIndexedAttestationSignatureSet(
|
||||
}
|
||||
|
||||
export function getAttestationsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock,
|
||||
@@ -57,6 +63,6 @@ export function getAttestationsSignatureSets(
|
||||
);
|
||||
}
|
||||
return indexedAttestations.map((indexedAttestation) =>
|
||||
getIndexedAttestationSignatureSet(index2pubkey, state, indexedAttestation)
|
||||
getIndexedAttestationSignatureSet(config, index2pubkey, state, indexedAttestation)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
|
||||
import {SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, isBlindedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -6,20 +7,21 @@ import {computeSigningRoot} from "../util/index.js";
|
||||
import {ISignatureSet, SignatureSetType, verifySignatureSet} from "../util/signatureSets.js";
|
||||
|
||||
export function verifyProposerSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock
|
||||
): boolean {
|
||||
const signatureSet = getBlockProposerSignatureSet(index2pubkey, state, signedBlock);
|
||||
const signatureSet = getBlockProposerSignatureSet(config, index2pubkey, state, signedBlock);
|
||||
return verifySignatureSet(signatureSet);
|
||||
}
|
||||
|
||||
export function getBlockProposerSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock
|
||||
): ISignatureSet {
|
||||
const {config} = state;
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, signedBlock.message.slot);
|
||||
|
||||
const blockType = isBlindedBeaconBlock(signedBlock.message)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_BEACON_PROPOSER} from "@lodestar/params";
|
||||
import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -8,6 +9,7 @@ import {ISignatureSet, SignatureSetType, computeSigningRoot} from "../util/index
|
||||
* Extract signatures to allow validating all block signatures at once
|
||||
*/
|
||||
export function getProposerSlashingSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
proposerSlashing: phase0.ProposerSlashing
|
||||
@@ -17,11 +19,7 @@ export function getProposerSlashingSignatureSets(
|
||||
// In state transition, ProposerSlashing headers are only partially validated. Their slot could be higher than the
|
||||
// clock and the slashing would still be valid. Must use bigint variants to hash correctly to all possible values
|
||||
return [proposerSlashing.signedHeader1, proposerSlashing.signedHeader2].map((signedHeader): ISignatureSet => {
|
||||
const domain = state.config.getDomain(
|
||||
state.slot,
|
||||
DOMAIN_BEACON_PROPOSER,
|
||||
Number(signedHeader.message.slot as bigint)
|
||||
);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_BEACON_PROPOSER, Number(signedHeader.message.slot as bigint));
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
@@ -33,11 +31,12 @@ export function getProposerSlashingSignatureSets(
|
||||
}
|
||||
|
||||
export function getProposerSlashingsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
return signedBlock.message.body.proposerSlashings.flatMap((proposerSlashing) =>
|
||||
getProposerSlashingSignatureSets(index2pubkey, state, proposerSlashing)
|
||||
getProposerSlashingSignatureSets(config, index2pubkey, state, proposerSlashing)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {DOMAIN_RANDAO} from "@lodestar/params";
|
||||
import {BeaconBlock, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
@@ -11,24 +12,26 @@ import {
|
||||
} from "../util/index.js";
|
||||
|
||||
export function verifyRandaoSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
block: BeaconBlock
|
||||
): boolean {
|
||||
return verifySignatureSet(getRandaoRevealSignatureSet(index2pubkey, state, block));
|
||||
return verifySignatureSet(getRandaoRevealSignatureSet(config, index2pubkey, state, block));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract signatures to allow validating all block signatures at once
|
||||
*/
|
||||
export function getRandaoRevealSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
block: BeaconBlock
|
||||
): ISignatureSet {
|
||||
// should not get epoch from epochCtx
|
||||
const epoch = computeEpochAtSlot(block.slot);
|
||||
const domain = state.config.getDomain(state.slot, DOMAIN_RANDAO, block.slot);
|
||||
const domain = config.getDomain(state.slot, DOMAIN_RANDAO, block.slot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {BeaconConfig} from "@lodestar/config";
|
||||
import {SignedBeaconBlock, phase0, ssz} from "@lodestar/types";
|
||||
import {Index2PubkeyCache} from "../cache/pubkeyCache.js";
|
||||
import {CachedBeaconStateAllForks} from "../types.js";
|
||||
@@ -10,23 +11,25 @@ import {
|
||||
} from "../util/index.js";
|
||||
|
||||
export function verifyVoluntaryExitSignature(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedVoluntaryExit: phase0.SignedVoluntaryExit
|
||||
): boolean {
|
||||
return verifySignatureSet(getVoluntaryExitSignatureSet(index2pubkey, state, signedVoluntaryExit));
|
||||
return verifySignatureSet(getVoluntaryExitSignatureSet(config, index2pubkey, state, signedVoluntaryExit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract signatures to allow validating all block signatures at once
|
||||
*/
|
||||
export function getVoluntaryExitSignatureSet(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedVoluntaryExit: phase0.SignedVoluntaryExit
|
||||
): ISignatureSet {
|
||||
const slot = computeStartSlotAtEpoch(signedVoluntaryExit.message.epoch);
|
||||
const domain = state.config.getDomainForVoluntaryExit(state.slot, slot);
|
||||
const domain = config.getDomainForVoluntaryExit(state.slot, slot);
|
||||
|
||||
return {
|
||||
type: SignatureSetType.single,
|
||||
@@ -37,11 +40,12 @@ export function getVoluntaryExitSignatureSet(
|
||||
}
|
||||
|
||||
export function getVoluntaryExitsSignatureSets(
|
||||
config: BeaconConfig,
|
||||
index2pubkey: Index2PubkeyCache,
|
||||
state: CachedBeaconStateAllForks,
|
||||
signedBlock: SignedBeaconBlock
|
||||
): ISignatureSet[] {
|
||||
return signedBlock.message.body.voluntaryExits.map((voluntaryExit) =>
|
||||
getVoluntaryExitSignatureSet(index2pubkey, state, voluntaryExit)
|
||||
getVoluntaryExitSignatureSet(config, index2pubkey, state, voluntaryExit)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,10 @@ export function stateTransition(
|
||||
postState = processSlotsWithTransientCache(postState, blockSlot, options, {metrics, validatorMonitor});
|
||||
|
||||
// Verify proposer signature only
|
||||
if (verifyProposer && !verifyProposerSignature(postState.epochCtx.index2pubkey, postState, signedBlock)) {
|
||||
if (
|
||||
verifyProposer &&
|
||||
!verifyProposerSignature(postState.config, postState.epochCtx.index2pubkey, postState, signedBlock)
|
||||
) {
|
||||
throw new Error("Invalid block signature");
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ describe("validate indexed attestation", () => {
|
||||
data: attestationData,
|
||||
signature: EMPTY_SIGNATURE,
|
||||
};
|
||||
expect(isValidIndexedAttestation(state.epochCtx.index2pubkey, state, indexedAttestation, false)).toBe(
|
||||
expect(isValidIndexedAttestation(state.config, state.epochCtx.index2pubkey, state, indexedAttestation, false)).toBe(
|
||||
expectedValue
|
||||
);
|
||||
});
|
||||
|
||||
@@ -70,7 +70,13 @@ describe("signatureSets", () => {
|
||||
state.epochCtx.getIndexedAttestation(fork, attestation)
|
||||
);
|
||||
|
||||
const signatureSets = getBlockSignatureSets(state.epochCtx.index2pubkey, state, signedBlock, indexedAttestations);
|
||||
const signatureSets = getBlockSignatureSets(
|
||||
state.config,
|
||||
state.epochCtx.index2pubkey,
|
||||
state,
|
||||
signedBlock,
|
||||
indexedAttestations
|
||||
);
|
||||
expect(signatureSets.length).toBe(
|
||||
// block signature
|
||||
1 +
|
||||
|
||||
Reference in New Issue
Block a user