Skip to content

Commit 0fb74c2

Browse files
authored
fix: create generator script; initialization ordering (#1630)
**Motivation:** We want to create a generator script that takes in the bn254Key and outputs the generator config. The previous one generated the private key but now we're doing so via AWS. Also, during adding additional checks to the upgrade script, I noticed that the order of how we initialize the `generator` in the `OperatorTableUpdater` is wrong. We are setting the `generator` in storage *after* it is updated on the certificate verifier. Though this doesn't cause any bugs since `GENERATOR_STALENESS_PERIOD` is 0 (and the value is initialized to 0), it's good to fix this ordering for correctness. **Modifications:** - Fix the order of setting params in the initialize function. Add additional unit tests - Create a `create_generator_config.s.sol` file - Update checks on mainnet deploy scripts - `globalRootConfirmerSet` -> `generator` **Result:** Mainnet ready
1 parent 73644e2 commit 0fb74c2

File tree

16 files changed

+395
-110
lines changed

16 files changed

+395
-110
lines changed

.github/workflows/validate-deployment-scripts.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
fail-fast: true
2020
matrix:
21-
env: [preprod, testnet, mainnet, testnet-sepolia, testnet-hoodi, testnet-base-sepolia]
21+
env: [preprod, testnet, mainnet, testnet-sepolia, testnet-hoodi, testnet-base-sepolia, base]
2222

2323
steps:
2424
- uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911
@@ -91,6 +91,8 @@ jobs:
9191
RPC_URL="${{ secrets.RPC_BASE_SEPOLIA }}"
9292
elif [ "${{ matrix.env }}" = "mainnet" ]; then
9393
RPC_URL="${{ secrets.RPC_MAINNET }}"
94+
elif [ "${{ matrix.env }}" = "base" ]; then
95+
RPC_URL="${{ secrets.RPC_BASE }}"
9496
fi
9597
9698
# Run zeus test on each file with the specified environment and RPC URL

docs/multichain/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ An AVS is an entity that uses delegated or slashable security from operators to
152152
The `Generator` is an EigenLabs-operated entity that calculates and signs off on the `GlobalTableRoot` for all operatorSets that have requested to be transported to a `DestinationChain`. For the pilot program, there is no stake backing the `Generator` and it is not slashable.
153153

154154
*Offchain Flows:*
155-
* The generator **calculates** and **signs** the `GlobalTableRoot`. Within the contracts, this is referred to as the `globalRootConfirmerSet`.
155+
* The generator **calculates** and **signs** the `GlobalTableRoot`. Within the contracts, this is referred to as the `generator`.
156156

157157
#### Transporter
158158

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "@openzeppelin/contracts/utils/Strings.sol";
5+
import "src/contracts/interfaces/IOperatorTableCalculator.sol";
6+
import "src/contracts/interfaces/ICrossChainRegistry.sol";
7+
import "src/contracts/libraries/BN254.sol";
8+
import "src/contracts/libraries/Merkle.sol";
9+
import {Env} from "../../releases/Env.sol";
10+
import "forge-std/Script.sol";
11+
import "forge-std/Test.sol";
12+
13+
// zeus run --command 'forge script script/deploy/multichain/create_generator_config.s.sol --sig "run(string memory,uint256,uint256)" $NETWORK $X_COORD $Y_COORD' --env $NETWORK
14+
contract CreateGeneratorConfig is Script, Test {
15+
using Strings for *;
16+
using Merkle for bytes32[];
17+
using BN254 for BN254.G1Point;
18+
19+
function run(string memory network, uint256 xCoord, uint256 yCoord) public {
20+
// Network must be preprod, testnet-sepolia, or mainnet
21+
require(
22+
_strEq(network, "preprod") || _strEq(network, "testnet-sepolia") || _strEq(network, "mainnet"),
23+
"Invalid network"
24+
);
25+
26+
// Create G1Point from provided coordinates
27+
BN254.G1Point memory publicKeyG1 = BN254.G1Point({X: xCoord, Y: yCoord});
28+
29+
/**
30+
*
31+
* Create the `BN254OperatorSetInfo` struct
32+
*
33+
*/
34+
35+
// 1. Generate the `BN254OperatorSetInfo` struct
36+
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory operatorSetInfo;
37+
38+
// 2. Set the numOperators and totalWeights
39+
operatorSetInfo.numOperators = 1;
40+
uint256[] memory weights = new uint256[](1);
41+
weights[0] = 1;
42+
operatorSetInfo.totalWeights = weights;
43+
44+
// 3. Set the apk (aggregate public key)
45+
operatorSetInfo.aggregatePubkey = publicKeyG1;
46+
47+
// 4. Set the operatorInfoTreeRoot
48+
bytes32[] memory operatorInfoLeaves = new bytes32[](1);
49+
operatorInfoLeaves[0] = keccak256(
50+
abi.encode(IOperatorTableCalculatorTypes.BN254OperatorInfo({pubkey: publicKeyG1, weights: weights}))
51+
);
52+
operatorSetInfo.operatorInfoTreeRoot = operatorInfoLeaves.merkleizeKeccak();
53+
54+
/**
55+
*
56+
* OUTPUT - OPERATOR SET INFO (TOML FORMAT)
57+
*
58+
*/
59+
60+
// Write operator set info to TOML file
61+
_writeOperatorSetToml(network, operatorSetInfo);
62+
63+
// Log the generated config
64+
console.log("Generated operator set config for network:", network);
65+
console.log("Public key G1 - X:", xCoord);
66+
console.log("Public key G1 - Y:", yCoord);
67+
console.log(
68+
"Config written to:",
69+
string.concat("script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/configs/", network, ".toml")
70+
);
71+
}
72+
73+
function _writeOperatorSetToml(
74+
string memory network,
75+
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory operatorSetInfo
76+
) internal {
77+
// Build JSON object using serializeJson
78+
string memory json_obj = "toml_output";
79+
80+
// Top level fields
81+
vm.serializeUint(json_obj, "globalRootConfirmationThreshold", 10_000);
82+
83+
// generator object
84+
string memory generatorSet_obj = "generator";
85+
vm.serializeString(generatorSet_obj, "avs", _getGeneratorAddress().toHexString());
86+
string memory generatorOutput = vm.serializeUint(generatorSet_obj, "id", 0);
87+
vm.serializeString(json_obj, "generator", generatorOutput);
88+
89+
// generatorInfo object
90+
string memory generatorInfo_obj = "generatorInfo";
91+
vm.serializeUint(generatorInfo_obj, "numOperators", operatorSetInfo.numOperators);
92+
vm.serializeBytes32(generatorInfo_obj, "operatorInfoTreeRoot", operatorSetInfo.operatorInfoTreeRoot);
93+
vm.serializeUint(generatorInfo_obj, "totalWeights", operatorSetInfo.totalWeights);
94+
95+
// aggregatePubkey nested object
96+
string memory aggregatePubkey_obj = "aggregatePubkey";
97+
vm.serializeString(aggregatePubkey_obj, "X", operatorSetInfo.aggregatePubkey.X.toString());
98+
string memory aggregatePubkeyOutput =
99+
vm.serializeString(aggregatePubkey_obj, "Y", operatorSetInfo.aggregatePubkey.Y.toString());
100+
101+
string memory generatorInfoOutput =
102+
vm.serializeString(generatorInfo_obj, "aggregatePubkey", aggregatePubkeyOutput);
103+
string memory finalJson = vm.serializeString(json_obj, "generatorInfo", generatorInfoOutput);
104+
105+
// Write TOML file using writeToml
106+
// If we are on testnet-sepolia, write to testnet.toml
107+
if (_strEq(network, "testnet-sepolia")) {
108+
network = "testnet";
109+
}
110+
string memory outputPath =
111+
string.concat("script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/configs/", network, ".toml");
112+
113+
vm.writeToml(finalJson, outputPath);
114+
}
115+
116+
function _strEq(string memory a, string memory b) internal pure returns (bool) {
117+
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
118+
}
119+
120+
/// @dev Returns the ops multisig address for the given network
121+
function _getGeneratorAddress() internal view returns (address generatorAddress) {
122+
generatorAddress = Env.opsMultisig();
123+
}
124+
}

script/deploy/multichain/deploy_globalRootConfirmerSet.s.sol renamed to script/deploy/multichain/deploy_generator.s.sol.sol

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,27 @@ import "src/contracts/interfaces/IOperatorTableCalculator.sol";
88
import "src/contracts/interfaces/ICrossChainRegistry.sol";
99

1010
import "src/contracts/libraries/Merkle.sol";
11+
import {Env} from "../../releases/Env.sol";
1112

1213
import "forge-std/Script.sol";
1314
import "forge-std/Test.sol";
1415

15-
// forge script script/deploy/multichain/deploy_globalRootConfirmerSet.s.sol --sig "run(string memory)" $NETWORK
16-
contract DeployGlobalRootConfirmerSet is Script, Test {
16+
// zeus run --command 'forge script script/deploy/multichain/deploy_generator.s.sol --sig "run(string memory,string memory)" $NETWORK $SALT' --env $NETWORK
17+
contract DeployGenerator is Script, Test {
1718
using Strings for *;
1819
using Merkle for bytes32[];
1920
using BN254 for BN254.G1Point;
2021

21-
address internal constant AVS = 0xDA29BB71669f46F2a779b4b62f03644A84eE3479;
22-
2322
function run(string memory network, string memory salt) public {
2423
/**
2524
*
2625
* WALLET CREATION
2726
*
2827
*/
29-
require(_strEq(network, "preprod") || _strEq(network, "testnet"), "Invalid network");
28+
require(
29+
_strEq(network, "preprod") || _strEq(network, "testnet-sepolia") || _strEq(network, "mainnet"),
30+
"Invalid network"
31+
);
3032

3133
// 1. Create a BN254 Wallet using random salt
3234
Operator memory operator = OperatorWalletLib.createOperator(salt);
@@ -128,34 +130,44 @@ contract DeployGlobalRootConfirmerSet is Script, Test {
128130
// Top level fields
129131
vm.serializeUint(json_obj, "globalRootConfirmationThreshold", 10_000);
130132

131-
// globalRootConfirmerSet object
132-
string memory confirmerSet_obj = "globalRootConfirmerSet";
133-
vm.serializeString(confirmerSet_obj, "avs", AVS.toHexString());
134-
string memory confirmerSetOutput = vm.serializeUint(confirmerSet_obj, "id", 0);
135-
vm.serializeString(json_obj, "globalRootConfirmerSet", confirmerSetOutput);
133+
// generator object
134+
string memory generator_obj = "generator";
135+
vm.serializeString(generator_obj, "avs", _getGeneratorAddress().toHexString());
136+
string memory generatorOutput = vm.serializeUint(generator_obj, "id", 0);
137+
vm.serializeString(json_obj, "generator", generatorOutput);
136138

137-
// globalRootConfirmerSetInfo object
138-
string memory confirmerSetInfo_obj = "globalRootConfirmerSetInfo";
139-
vm.serializeUint(confirmerSetInfo_obj, "numOperators", operatorSetInfo.numOperators);
140-
vm.serializeBytes32(confirmerSetInfo_obj, "operatorInfoTreeRoot", operatorSetInfo.operatorInfoTreeRoot);
141-
vm.serializeUint(confirmerSetInfo_obj, "totalWeights", operatorSetInfo.totalWeights);
139+
// generatorInfo object
140+
string memory generatorInfo_obj = "generatorInfo";
141+
vm.serializeUint(generatorInfo_obj, "numOperators", operatorSetInfo.numOperators);
142+
vm.serializeBytes32(generatorInfo_obj, "operatorInfoTreeRoot", operatorSetInfo.operatorInfoTreeRoot);
143+
vm.serializeUint(generatorInfo_obj, "totalWeights", operatorSetInfo.totalWeights);
142144

143145
// aggregatePubkey nested object
144146
string memory aggregatePubkey_obj = "aggregatePubkey";
145147
vm.serializeString(aggregatePubkey_obj, "X", operatorSetInfo.aggregatePubkey.X.toString());
146148
string memory aggregatePubkeyOutput =
147149
vm.serializeString(aggregatePubkey_obj, "Y", operatorSetInfo.aggregatePubkey.Y.toString());
148150

149-
string memory confirmerSetInfoOutput =
150-
vm.serializeString(confirmerSetInfo_obj, "aggregatePubkey", aggregatePubkeyOutput);
151-
string memory finalJson = vm.serializeString(json_obj, "globalRootConfirmerSetInfo", confirmerSetInfoOutput);
151+
string memory generatorInfoOutput =
152+
vm.serializeString(generatorInfo_obj, "aggregatePubkey", aggregatePubkeyOutput);
153+
string memory finalJson = vm.serializeString(json_obj, "generatorInfo", generatorInfoOutput);
152154

153155
// Write TOML file using writeToml
154-
string memory outputPath = string.concat("script/releases/v1.7.0-multichain/configs/", network, ".toml");
156+
// If we are on testnet-sepolia, write to testnet.toml
157+
if (_strEq(network, "testnet-sepolia")) {
158+
network = "testnet";
159+
}
160+
string memory outputPath =
161+
string.concat("script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/configs/", network, ".toml");
155162
vm.writeToml(finalJson, outputPath);
156163
}
157164

158165
function _strEq(string memory a, string memory b) internal pure returns (bool) {
159166
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
160167
}
168+
169+
/// @dev Returns the ops multisig address for the given network
170+
function _getGeneratorAddress() internal view returns (address generatorAddress) {
171+
generatorAddress = Env.opsMultisig();
172+
}
161173
}

script/operations/update-generator/1-updateGenerator.s.sol

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ import "src/contracts/interfaces/IBaseCertificateVerifier.sol";
1414
import {stdToml} from "forge-std/StdToml.sol";
1515

1616
/**
17-
* Purpose: Update the generator on a TESTNET environment
17+
* Purpose: Update the generator on a PREPROD/TESTNET environment
1818
*/
1919
contract QueueTransferProxyAdmin is MultisigBuilder {
2020
using Env for *;
2121
using OperatorSetLib for OperatorSet;
2222
using stdToml for string;
2323

24-
string private constant TESTNET_CONFIG_PATH = "script/releases/v1.7.0-multichain/configs/testnet.toml";
24+
string private constant TESTNET_CONFIG_PATH =
25+
"script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/configs/preprod.toml";
2526

2627
function _runAsMultisig() internal virtual override prank(Env.opsMultisig()) {
2728
GeneratorParams memory generatorParams = _getGeneratorParams(TESTNET_CONFIG_PATH);
@@ -31,8 +32,9 @@ contract QueueTransferProxyAdmin is MultisigBuilder {
3132
function testScript() public virtual {
3233
// Require that the environment is a testnet environment supported by multichain
3334
require(
34-
Env._strEq(Env.env(), "testnet-sepolia") || Env._strEq(Env.env(), "testnet-base-sepolia"),
35-
"Environment must be a testnet environment"
35+
Env._strEq(Env.env(), "preprod") || Env._strEq(Env.env(), "testnet-sepolia")
36+
|| Env._strEq(Env.env(), "testnet-base-sepolia"),
37+
"Environment must be a preprod/testnet environment"
3638
);
3739

3840
// Update the generator
@@ -120,18 +122,17 @@ contract QueueTransferProxyAdmin is MultisigBuilder {
120122
string memory fullPath = string.concat(root, "/", path);
121123
string memory toml = vm.readFile(fullPath);
122124

123-
// Parse globalRootConfirmerSet
124-
address avs = toml.readAddress(".globalRootConfirmerSet.avs");
125-
uint32 id = uint32(toml.readUint(".globalRootConfirmerSet.id"));
125+
// Parse generator
126+
address avs = toml.readAddress(".generator.avs");
127+
uint32 id = uint32(toml.readUint(".generator.id"));
126128
generatorParams.generator = OperatorSet({avs: avs, id: id});
127129

128-
// Parse globalRootConfirmerSetInfo
129-
generatorParams.generatorInfo.numOperators = uint256(toml.readUint(".globalRootConfirmerSetInfo.numOperators"));
130-
generatorParams.generatorInfo.operatorInfoTreeRoot =
131-
toml.readBytes32(".globalRootConfirmerSetInfo.operatorInfoTreeRoot");
132-
generatorParams.generatorInfo.totalWeights = toml.readUintArray(".globalRootConfirmerSetInfo.totalWeights");
133-
uint256 apkX = toml.readUint(".globalRootConfirmerSetInfo.aggregatePubkey.X");
134-
uint256 apkY = toml.readUint(".globalRootConfirmerSetInfo.aggregatePubkey.Y");
130+
// Parse generatorInfo
131+
generatorParams.generatorInfo.numOperators = uint256(toml.readUint(".generatorInfo.numOperators"));
132+
generatorParams.generatorInfo.operatorInfoTreeRoot = toml.readBytes32(".generatorInfo.operatorInfoTreeRoot");
133+
generatorParams.generatorInfo.totalWeights = toml.readUintArray(".generatorInfo.totalWeights");
134+
uint256 apkX = toml.readUint(".generatorInfo.aggregatePubkey.X");
135+
uint256 apkY = toml.readUint(".generatorInfo.aggregatePubkey.Y");
135136
generatorParams.generatorInfo.aggregatePubkey = BN254.G1Point({X: apkX, Y: apkY});
136137

137138
return generatorParams;

script/operations/update-generator/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Generator Update
22

3-
The `generator`, signs off on roots. In order to generate this operatorSet, we use the following [script](../../deploy/multichain/deploy_globalRootConfirmerSet.s.sol) The script outputs two items:
3+
The `generator`, signs off on roots. In order to generate this operatorSet, we use the following [script](../../deploy/multichain/deploy_generator.s.sol) The script outputs two items:
44

55
1. `network.wallet.json`: Private keys and BLS sig info (should be kept secure)
66
2. `network.toml`: A toml file passed into initialization on `operatorTableUpdater`

script/releases/v1.7.0-v1.8.0-multichain-hourglass-combined/3-deployDestinationChainImpls.s.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ contract DeployDestinationChainImpls is EOADeployer, DeployDestinationChainProxi
173173
operatorTableUpdater.initialize(
174174
address(0), // owner
175175
0, // initial paused status
176-
dummyOperatorSet, // globalRootConfirmerSet
176+
dummyOperatorSet, // generator
177177
0, // globalRootConfirmationThreshold
178-
dummyBN254Info // globalRootConfirmerSetInfo
178+
dummyBN254Info // generatorInfo
179179
);
180180

181181
/// TaskMailbox

0 commit comments

Comments
 (0)