Skip to content

Commit a5940fa

Browse files
eddorttamtamchik
authored andcommitted
feat: auto initial epoch detection for oracles
1 parent 47edc26 commit a5940fa

6 files changed

Lines changed: 178 additions & 21 deletions

File tree

packages/cl-client/src/index.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ export const BeaconConfigSchema = z.object({
4444

4545
export type BeaconConfigResponse = z.infer<typeof BeaconConfigSchema>;
4646

47+
export const BeaconFinalizedCheckpointSchema = z.object({
48+
data: z.object({
49+
finalized: z.object({
50+
epoch: z.string(),
51+
}),
52+
}),
53+
});
54+
55+
export const BeaconHeadCheckpointSchema = z.object({
56+
data: z.object({
57+
current_justified: z.object({
58+
epoch: z.string(),
59+
}),
60+
}),
61+
});
62+
4763
export class BeaconClient {
4864
private baseUrl: string;
4965

@@ -54,39 +70,57 @@ export class BeaconClient {
5470
public async getBlock(blockId: string): Promise<BeaconBlockResponse> {
5571
return this.fetchAndValidate(
5672
`${this.baseUrl}/eth/v1/beacon/blocks/${blockId}`,
57-
BeaconBlockSchema
73+
BeaconBlockSchema,
5874
);
5975
}
6076

6177
public async getConfig(): Promise<BeaconConfigResponse> {
6278
return this.fetchAndValidate(
6379
`${this.baseUrl}/eth/v1/config/spec`,
64-
BeaconConfigSchema
80+
BeaconConfigSchema,
81+
);
82+
}
83+
84+
public async getFinalizedEpoch(): Promise<number> {
85+
const response = await this.fetchAndValidate(
86+
`${this.baseUrl}/eth/v1/beacon/states/finalized/finality_checkpoints`,
87+
BeaconFinalizedCheckpointSchema,
6588
);
89+
return Number.parseInt(response.data.finalized.epoch, 10);
6690
}
6791

6892
public async getGenesis(): Promise<BeaconGenesisResponse> {
6993
return this.fetchAndValidate(
7094
`${this.baseUrl}/eth/v1/beacon/genesis`,
71-
BeaconGenesisSchema
95+
BeaconGenesisSchema,
96+
);
97+
}
98+
99+
public async getHeadEpoch(): Promise<number> {
100+
const response = await this.fetchAndValidate(
101+
`${this.baseUrl}/eth/v1/beacon/states/head/finality_checkpoints`,
102+
BeaconHeadCheckpointSchema,
72103
);
104+
return Number.parseInt(response.data.current_justified.epoch, 10);
73105
}
74106

75-
public async getValidators(stateId: string): Promise<BeaconValidatorsResponse> {
107+
public async getValidators(
108+
stateId: string,
109+
): Promise<BeaconValidatorsResponse> {
76110
return this.fetchAndValidate(
77111
`${this.baseUrl}/eth/v1/beacon/states/${stateId}/validators`,
78-
BeaconValidatorsSchema
112+
BeaconValidatorsSchema,
79113
);
80114
}
81115

82116
private async fetchAndValidate<T>(
83117
url: string,
84-
schema: z.Schema<T>
118+
schema: z.Schema<T>,
85119
): Promise<T> {
86120
const response = await fetch(url);
87121
if (!response.ok) {
88122
throw new Error(
89-
`Error fetching ${url}: ${response.status} ${response.statusText}`
123+
`Error fetching ${url}: ${response.status} ${response.statusText}`,
90124
);
91125
}
92126

packages/services/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,10 @@ const oracle = new DevNetServiceConfig({
115115
},
116116
workspace: "workspaces/oracle-v5",
117117
name: "oracle" as const,
118-
constants: {},
118+
constants: {
119+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME: 8,
120+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME: 8
121+
},
119122
labels: {},
120123
});
121124

src/commands/csm/activate.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,34 @@ export const ActivateCSM = command.cli({
1414
description:
1515
"Activates the csm by deploying smart contracts and configuring the environment based on the current network state.",
1616
params: {},
17-
async handler({ dre, dre: { logger } }) {
18-
const { lidoCLI } = dre.services;
17+
async handler({ dre, dre: { logger, network } }) {
18+
const { lidoCLI, oracle } = dre.services;
1919

2020
const { elPublic } = await dre.state.getChain();
2121
const csmState = await dre.state.getCSM();
22+
const clClient = await network.getCLClient();
2223

2324
await dre.network.waitEL();
2425

26+
const {
27+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
28+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
29+
} = oracle.config.constants;
30+
31+
const epochPerFrame = Math.max(
32+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
33+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
34+
);
35+
36+
const currentEpoch = await clClient.getHeadEpoch();
37+
const initialEpoch = epochPerFrame + currentEpoch + 2;
38+
2539
const env: CSMActivateENV = {
2640
CS_ACCOUNTING_ADDRESS: csmState.accounting,
2741
CS_MODULE_ADDRESS: csmState.module,
2842
CS_ORACLE_HASH_CONSENSUS_ADDRESS: csmState.hashConsensus,
2943
// TODO: calculate it
30-
CS_ORACLE_INITIAL_EPOCH: "60",
44+
CS_ORACLE_INITIAL_EPOCH: initialEpoch.toString(),
3145
EL_API_PROVIDER: elPublic,
3246
};
3347

src/commands/lido-core/activate.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ export const ActivateLidoProtocol = command.cli({
77
async handler({ dre }) {
88
const {
99
state,
10-
services: { lidoCLI },
10+
services: { lidoCLI, oracle },
11+
network,
1112
} = dre;
1213

1314
const { elPublic } = await state.getChain();
1415
const { deployer, oracles, councils } = await state.getNamedWallet();
16+
const clClient = await network.getCLClient();
1517

1618
await dre.network.waitEL();
1719

@@ -23,12 +25,25 @@ export const ActivateLidoProtocol = command.cli({
2325
EL_API_PROVIDER: elPublic,
2426
};
2527

28+
const {
29+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
30+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
31+
} = oracle.config.constants;
32+
33+
const epochPerFrame = Math.max(
34+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
35+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
36+
);
37+
38+
const currentEpoch = await clClient.getHeadEpoch();
39+
const initialEpoch = epochPerFrame + currentEpoch + 2;
40+
2641
const activateCoreSh = lidoCLI.sh({ env: deployEnv });
27-
// TODO: calc oracles-initial-epoch (time voting + 1 epoch for core and +50 for CSM)
28-
await activateCoreSh`./run.sh devnet setup
42+
43+
await activateCoreSh`./run.sh devnet setup
2944
--oracles-members ${oracles.map(({ publicKey }) => publicKey).join(",")}
3045
--oracles-quorum ${oracles.length - 1}
31-
--oracles-initial-epoch 60
46+
--oracles-initial-epoch ${initialEpoch}
3247
--dsm-guardians ${councils.map(({ publicKey }) => publicKey).join(",")}
3348
--dsm-quorum ${councils.length}
3449
--roles-beneficiary ${deployer.publicKey}`;

src/commands/lido-core/prepare-repository.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,49 @@ export const PrepareLidoCore = command.cli({
66
vesting: Params.string({
77
description: "Vesting LDO amount",
88
default: "820000000000000000000000",
9-
required: false
9+
required: false,
1010
}),
1111
voteDuration: Params.integer({
1212
description: "Voting duration",
1313
default: 60,
14-
required: false
14+
required: false,
1515
}),
1616
objectionPhaseDuration: Params.integer({
1717
description: "Objection phase duration",
1818
default: 5,
19-
required: false
19+
required: false,
2020
}),
2121
},
2222
async handler({
2323
dre,
2424
params: { voteDuration, objectionPhaseDuration, vesting },
2525
}) {
2626
const { state, services } = dre;
27-
const { lidoCore } = services;
27+
const { lidoCore, oracle } = services;
2828
const { constants } = lidoCore.config;
2929
const { deployer, secondDeployer } = await state.getNamedWallet();
30-
30+
3131
const filePath = constants.NETWORK_STATE_DEFAULTS_FILE;
3232
const networkStateDefaults = await lidoCore.readJson(filePath);
3333

34-
const { vestingParams, daoInitialSettings } = networkStateDefaults;
34+
const {
35+
vestingParams,
36+
daoInitialSettings,
37+
oracleDaemonConfig,
38+
hashConsensusForAccountingOracle,
39+
hashConsensusForValidatorsExitBusOracle,
40+
} = networkStateDefaults;
3541
assert(vestingParams?.holders, "Missing vestingParams.holders");
3642
assert(daoInitialSettings?.voting, "Missing daoInitialSettings.voting");
43+
assert(oracleDaemonConfig, "Missing oracleDaemonConfig");
44+
assert(
45+
hashConsensusForAccountingOracle,
46+
"Missing hashConsensusForAccountingOracle",
47+
);
48+
assert(
49+
hashConsensusForValidatorsExitBusOracle,
50+
"Missing hashConsensusForValidatorsExitBusOracle",
51+
);
3752

3853
Object.assign(vestingParams.holders, {
3954
[deployer.publicKey]: vesting,
@@ -45,6 +60,39 @@ export const PrepareLidoCore = command.cli({
4560
objectionPhaseDuration,
4661
});
4762

63+
Object.assign(oracleDaemonConfig, {
64+
deployParameters: {
65+
NORMALIZED_CL_REWARD_PER_EPOCH: 64,
66+
NORMALIZED_CL_REWARD_MISTAKE_RATE_BP: 1000,
67+
REBASE_CHECK_NEAREST_EPOCH_DISTANCE: 1,
68+
REBASE_CHECK_DISTANT_EPOCH_DISTANCE: 2,
69+
VALIDATOR_DELAYED_TIMEOUT_IN_SLOTS: 7200,
70+
VALIDATOR_DELINQUENT_TIMEOUT_IN_SLOTS: 28_800,
71+
NODE_OPERATOR_NETWORK_PENETRATION_THRESHOLD_BP: 100,
72+
PREDICTION_DURATION_IN_SLOTS: 50_400,
73+
FINALIZATION_MAX_NEGATIVE_REBASE_EPOCH_SHIFT: 1350,
74+
},
75+
});
76+
77+
const {
78+
HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
79+
HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
80+
} = oracle.config.constants;
81+
82+
Object.assign(hashConsensusForAccountingOracle, {
83+
deployParameters: {
84+
fastLaneLengthSlots: 10,
85+
epochsPerFrame: HASH_CONSENSUS_AO_EPOCHS_PER_FRAME,
86+
},
87+
});
88+
89+
Object.assign(hashConsensusForValidatorsExitBusOracle, {
90+
deployParameters: {
91+
fastLaneLengthSlots: 10,
92+
epochsPerFrame: HASH_CONSENSUS_VEBO_EPOCHS_PER_FRAME,
93+
},
94+
});
95+
4896
await lidoCore.writeJson(filePath, networkStateDefaults, true);
4997
},
5098
});
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Params, command } from "@devnet/command";
2+
3+
import { BlockscoutUp } from "../blockscout/up.js";
4+
import { KurtosisUp } from "../chain/up.js";
5+
import { DeployCSMContracts } from "../csm/deploy.js";
6+
import { GitCheckout } from "../git/checkout.js";
7+
import { DeployLidoContracts } from "../lido-core/deploy.js";
8+
9+
10+
export const PectraDevNetUp = command.cli({
11+
description: "Pectra cotracts only with protocol smart contracts.",
12+
params: {
13+
verify: Params.boolean({
14+
description: "Enables verification of smart contracts during deployment.",
15+
}),
16+
dsm: Params.boolean({
17+
description: "Use full DSM setup.",
18+
default: false,
19+
}),
20+
},
21+
async handler({ params, dre, dre: { logger } }) {
22+
await dre.runCommand(GitCheckout, {
23+
service: "lidoCore",
24+
ref: "develop",
25+
});
26+
27+
await dre.runCommand(KurtosisUp, { preset: "pectra-devnet6" });
28+
logger.log("✅ Network initialized.");
29+
30+
await dre.runCommand(BlockscoutUp, {});
31+
logger.log("✅ BlockScout launched for transaction visualization.");
32+
33+
const deployArgs = { verify: params.verify };
34+
35+
logger.log("🚀 Deploying Lido Core contracts...");
36+
await dre.runCommand(DeployLidoContracts, deployArgs);
37+
logger.log("✅ Lido contracts deployed.");
38+
39+
logger.log("🚀 Deploying CSM contracts...");
40+
await dre.runCommand(DeployCSMContracts, deployArgs);
41+
logger.log("✅ CSM contracts deployed.");
42+
},
43+
});

0 commit comments

Comments
 (0)