import { AbortSignal } from "@chainsafe/abort-controller" ; import { ssz } from "@chainsafe/lodestar-types" ; import { TreeBacked } from "@chainsafe/ssz" ; import { createIBeaconConfig , IBeaconConfig , IChainForkConfig } from "@chainsafe/lodestar-config" ; import { fromHex , ILogger } from "@chainsafe/lodestar-utils" ; import { computeEpochAtSlot , allForks } from "@chainsafe/lodestar-beacon-state-transition" ; import { IBeaconDb , IBeaconNodeOptions , initStateFromAnchorState , initStateFromEth1 } from "@chainsafe/lodestar" ; import { getStateTypeFromBytes } from "@chainsafe/lodestar/lib/util/multifork" ; import { downloadOrLoadFile } from "../../util" ; import { IBeaconArgs } from "./options" ; import { defaultNetwork , IGlobalArgs } from "../../options/globalOptions" ; import { fetchWeakSubjectivityState , getGenesisFileUrl } from "../../networks" ; import { Checkpoint } from "@chainsafe/lodestar-types/phase0" ; import { SLOTS_PER_EPOCH } from "@chainsafe/lodestar-params" ; const checkpointRegex = new RegExp ( "^(?:0x)?([0-9a-f]{64}):([0-9]+)$" ) ; function getCheckpointFromArg ( checkpointStr ) { const match = checkpointRegex . exec ( checkpointStr . toLowerCase ( ) ) ; if ( ! match ) { throw new Error ( `Could not parse checkpoint string: ${ checkpointStr }` ) ; } return { root : fromHex ( match [ 1 ] ) , epoch : parseInt ( match [ 2 ] ) } ; } function getCheckpointFromState ( config , state ) { return { epoch : computeEpochAtSlot ( state . latestBlockHeader . slot ) , root : allForks . getLatestBlockRoot ( config , state ) , } ; } async function initAndVerifyWeakSubjectivityState ( config , db , logger , store , wsState , wsCheckpoint ) { if ( store . genesisTime !== wsState . genesisTime || ! ssz . Root . equals ( store . genesisValidatorsRoot , wsState . genesisValidatorsRoot ) ) { throw new Error ( "Db state and checkpoint state are not compatible, either clear the db or verify your checkpoint source" ) ; } let anchorState = wsState ; let anchorCheckpoint = wsCheckpoint ; if ( store . slot > wsState . slot ) { anchorState = store ; anchorCheckpoint = getCheckpointFromState ( config , store ) ; logger . verbose ( "Db state is ahead of the provided checkpoint state, using the db state to initialize the beacon chain" ) ; } if ( ! allForks . isWithinWeakSubjectivityPeriod ( config , anchorState , anchorCheckpoint ) ) { throw new Error ( "Fetched weak subjectivity checkpoint not within weak subjectivity period." ) ; } anchorState = await initStateFromAnchorState ( config , db , logger , anchorState ) ; return { anchorState , wsCheckpoint } ; } /**
 * Initialize a beacon state, picking the strategy based on the `IBeaconArgs`
 *
 * State is initialized in one of three ways:
 * 1. restore from weak subjectivity state (possibly downloaded from a remote beacon node)
 * 2. restore from db
 * 3. restore from genesis state (possibly downloaded via URL)
 * 4. create genesis state from eth1
 */ export async function initBeaconState ( options , args , chainForkConfig , db , logger , signal ) { const lastDbState = await db . stateArchive . lastValue ( ) ; if ( args . weakSubjectivityStateFile ) { const stateBytes = await downloadOrLoadFile ( args . weakSubjectivityStateFile ) ; const wsState = getStateTypeFromBytes ( chainForkConfig , stateBytes ) . createTreeBackedFromBytes ( stateBytes ) ; const config = createIBeaconConfig ( chainForkConfig , wsState . genesisValidatorsRoot ) ; const store = lastDbState ?? wsState ; const checkpoint = args . weakSubjectivityCheckpoint ? getCheckpointFromArg ( args . weakSubjectivityCheckpoint ) : getCheckpointFromState ( config , wsState ) ; return initAndVerifyWeakSubjectivityState ( config , db , logger , store , wsState , checkpoint ) ; } else if ( args . weakSubjectivitySyncLatest ) { const remoteBeaconUrl = args . weakSubjectivityServerUrl ; if ( ! remoteBeaconUrl ) { throw Error ( `Must set arg --weakSubjectivityServerUrl for network ${ args . network }` ) ; } let stateId = "finalized" ; let checkpoint ; if ( args . weakSubjectivityCheckpoint ) { checkpoint = getCheckpointFromArg ( args . weakSubjectivityCheckpoint ) ; stateId = ( checkpoint . epoch * SLOTS_PER_EPOCH ) . toString ( ) ; } const url = `${ remoteBeaconUrl }/eth/v1/debug/beacon/states/${ stateId }` ; logger . info ( "Fetching weak subjectivity state from " + url ) ; const wsState = await fetchWeakSubjectivityState ( chainForkConfig , url ) ; const config = createIBeaconConfig ( chainForkConfig , wsState . genesisValidatorsRoot ) ; const store = lastDbState ?? wsState ; return initAndVerifyWeakSubjectivityState ( config , db , logger , store , wsState , checkpoint || getCheckpointFromState ( config , wsState ) ) ; } else if ( lastDbState ) { const config = createIBeaconConfig ( chainForkConfig , lastDbState . genesisValidatorsRoot ) ; const anchorState = await initStateFromAnchorState ( config , db , logger , lastDbState ) ; return { anchorState } ; } else { const genesisStateFile = args . genesisStateFile || getGenesisFileUrl ( args . network || defaultNetwork ) ; if ( genesisStateFile && ! args . forceGenesis ) { const stateBytes = await downloadOrLoadFile ( genesisStateFile ) ; let anchorState = getStateTypeFromBytes ( chainForkConfig , stateBytes ) . createTreeBackedFromBytes ( stateBytes ) ; const config = createIBeaconConfig ( chainForkConfig , anchorState . genesisValidatorsRoot ) ; anchorState = await initStateFromAnchorState ( config , db , logger , anchorState ) ; return { anchorState } ; } else { const anchorState = await initStateFromEth1 ( { config : chainForkConfig , db , logger , opts : options . eth1 , signal } ) ; return { anchorState } ; } } }