-
-
Notifications
You must be signed in to change notification settings - Fork 343
/
Copy pathinitBeaconState.ts
205 lines (192 loc) Β· 8.65 KB
/
initBeaconState.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import {ssz} from "@lodestar/types";
import {createBeaconConfig, BeaconConfig, ChainForkConfig} from "@lodestar/config";
import {Logger} from "@lodestar/utils";
import {
isWithinWeakSubjectivityPeriod,
ensureWithinWeakSubjectivityPeriod,
BeaconStateAllForks,
} from "@lodestar/state-transition";
import {
IBeaconDb,
IBeaconNodeOptions,
initStateFromAnchorState,
initStateFromEth1,
getStateTypeFromBytes,
} from "@lodestar/beacon-node";
import {Checkpoint} from "@lodestar/types/phase0";
import {downloadOrLoadFile} from "../../util/index.js";
import {defaultNetwork, GlobalArgs} from "../../options/globalOptions.js";
import {
fetchWeakSubjectivityState,
getCheckpointFromArg,
getGenesisFileUrl,
getCheckpointFromState,
} from "../../networks/index.js";
import {BeaconArgs} from "./options.js";
async function initAndVerifyWeakSubjectivityState(
config: BeaconConfig,
db: IBeaconDb,
logger: Logger,
store: BeaconStateAllForks,
wsState: BeaconStateAllForks,
wsCheckpoint: Checkpoint
): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint: Checkpoint}> {
// Check if the store's state and wsState are compatible
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"
);
}
// Pick the state which is ahead as an anchor to initialize the beacon chain
let anchorState = wsState;
let anchorCheckpoint = wsCheckpoint;
let isCheckpointState = true;
if (store.slot > wsState.slot) {
anchorState = store;
anchorCheckpoint = getCheckpointFromState(store);
isCheckpointState = false;
logger.verbose(
"Db state is ahead of the provided checkpoint state, using the db state to initialize the beacon chain"
);
}
// Instead of warning user of wss check failure, we throw because user explicity wants to use
// the checkpoint sync
ensureWithinWeakSubjectivityPeriod(config, anchorState, anchorCheckpoint);
anchorState = await initStateFromAnchorState(config, db, logger, anchorState, {
isWithinWeakSubjectivityPeriod: true,
isCheckpointState,
});
// Return the latest anchorState but still return original wsCheckpoint to validate in backfill
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: IBeaconNodeOptions,
args: BeaconArgs & GlobalArgs,
chainForkConfig: ChainForkConfig,
db: IBeaconDb,
logger: Logger,
signal: AbortSignal
): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
if (args.forceCheckpointSync && !(args.checkpointState || args.checkpointSyncUrl)) {
throw new Error("Forced checkpoint sync without specifying a checkpointState or checkpointSyncUrl");
}
// fetch the latest state stored in the db which will be used in all cases, if it exists, either
// i) used directly as the anchor state
// ii) used during verification of a weak subjectivity state,
const lastDbState = await db.stateArchive.lastValue();
if (lastDbState) {
const config = createBeaconConfig(chainForkConfig, lastDbState.genesisValidatorsRoot);
const wssCheck = isWithinWeakSubjectivityPeriod(config, lastDbState, getCheckpointFromState(lastDbState));
// Explicitly force syncing from checkpoint state
if (args.forceCheckpointSync) {
// Forcing to sync from checkpoint is only recommended if node is taking too long to sync from last db state.
// It is important to remind the user to remove this flag again unless it is absolutely necessary.
if (wssCheck) {
logger.warn("Forced syncing from checkpoint even though db state is within weak subjectivity period");
logger.warn("Please consider removing --forceCheckpointSync flag unless absolutely necessary");
}
} else {
// All cases when we want to directly use lastDbState as the anchor state:
// - if no checkpoint sync args provided, or
// - the lastDbState is within weak subjectivity period:
if ((!args.checkpointState && !args.checkpointSyncUrl) || wssCheck) {
const anchorState = await initStateFromAnchorState(config, db, logger, lastDbState, {
isWithinWeakSubjectivityPeriod: wssCheck,
isCheckpointState: false,
});
return {anchorState};
}
}
}
// See if we can sync state using checkpoint sync args or else start from genesis
if (args.checkpointState) {
return readWSState(
lastDbState,
{checkpointState: args.checkpointState, wssCheckpoint: args.wssCheckpoint},
chainForkConfig,
db,
logger
);
} else if (args.checkpointSyncUrl) {
return fetchWSStateFromBeaconApi(
lastDbState,
{checkpointSyncUrl: args.checkpointSyncUrl, wssCheckpoint: args.wssCheckpoint},
chainForkConfig,
db,
logger
);
} else {
const genesisStateFile = args.genesisStateFile || getGenesisFileUrl(args.network || defaultNetwork);
if (genesisStateFile && !args.forceGenesis) {
const stateBytes = await downloadOrLoadFile(genesisStateFile);
let anchorState = getStateTypeFromBytes(chainForkConfig, stateBytes).deserializeToViewDU(stateBytes);
const config = createBeaconConfig(chainForkConfig, anchorState.genesisValidatorsRoot);
const wssCheck = isWithinWeakSubjectivityPeriod(config, anchorState, getCheckpointFromState(anchorState));
anchorState = await initStateFromAnchorState(config, db, logger, anchorState, {
isWithinWeakSubjectivityPeriod: wssCheck,
isCheckpointState: true,
});
return {anchorState};
} else {
// Only place we will not bother checking isWithinWeakSubjectivityPeriod as forceGenesis passed by user
const anchorState = await initStateFromEth1({config: chainForkConfig, db, logger, opts: options.eth1, signal});
return {anchorState};
}
}
}
async function readWSState(
lastDbState: BeaconStateAllForks | null,
wssOpts: {checkpointState: string; wssCheckpoint?: string},
chainForkConfig: ChainForkConfig,
db: IBeaconDb,
logger: Logger
): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
// weak subjectivity sync from a provided state file:
// if a weak subjectivity checkpoint has been provided, it is used for additional verification
// otherwise, the state itself is used for verification (not bad, because the trusted state has been explicitly provided)
const {checkpointState, wssCheckpoint} = wssOpts;
const stateBytes = await downloadOrLoadFile(checkpointState);
const wsState = getStateTypeFromBytes(chainForkConfig, stateBytes).deserializeToViewDU(stateBytes);
const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot);
const store = lastDbState ?? wsState;
const checkpoint = wssCheckpoint ? getCheckpointFromArg(wssCheckpoint) : getCheckpointFromState(wsState);
return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, checkpoint);
}
async function fetchWSStateFromBeaconApi(
lastDbState: BeaconStateAllForks | null,
wssOpts: {checkpointSyncUrl: string; wssCheckpoint?: string},
chainForkConfig: ChainForkConfig,
db: IBeaconDb,
logger: Logger
): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
// weak subjectivity sync from a state that needs to be fetched:
// if a weak subjectivity checkpoint has been provided, it is used to inform which state to download and used for additional verification
// otherwise, the 'finalized' state is downloaded and the state itself is used for verification (all trust delegated to the remote beacon node)
try {
// Validate the weakSubjectivityServerUrl and only log the origin to mask the
// username password credentials
const checkpointSyncUrl = new URL(wssOpts.checkpointSyncUrl);
logger.info("Fetching checkpoint state", {
checkpointSyncUrl: checkpointSyncUrl.origin,
});
} catch (e) {
logger.error("Invalid", {checkpointSyncUrl: wssOpts.checkpointSyncUrl}, e as Error);
throw e;
}
const {wsState, wsCheckpoint} = await fetchWeakSubjectivityState(chainForkConfig, logger, wssOpts);
const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot);
const store = lastDbState ?? wsState;
return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsState, wsCheckpoint);
}