Skip to content

Commit 23c29ce

Browse files
ypatil12eigenmikem
andauthored
feat: cleaner generator updates (#1537)
**Motivation:** We want to solve two problems: 1. Currently, `updateGenerator` will not work in all cases because when we call `updateOperatorTable`, the `bn254CertificateVerifier.isRootValidByTimestamp` must be true. This *only* works if the `latestReferenceTimestamp` for the new generator is the same as what the previous generator was initialized to 2. Have a clearer state machine for special casing the `generator` **Modifications:** 1. Get rid of `setGenerator` and only have `updateGenerator` 2. Remove notion of a configurable `referenceTimestamp` from `Generator`. The `Generator` *always* has a reference timestamp of `GENERATOR_REFERENCE_TIMESTAMP`. Now, the reference timestamp is not set in initialization or in `updateGenerator` 3. Require that `updateGenerator` can only be called for a *new* operatorSet. 4. Require the `updateOperatorTable` cannot update the table for the `Generator` 5. Prevent the `GENERATOR_GLOBAL_TABLE_ROOT` from being disabled 6. Hard-code the `operatorSetConfig` for the `generator` 7. Update deploy scripts to remove the `referenceTimestamp` and `operatorSetConfig` Also, update documentation & zeus script for `CrossChainRegistry.tableUpdateCadence` **Result:** Clearer state machine --------- Co-authored-by: Michael <[email protected]>
1 parent 5df3efc commit 23c29ce

18 files changed

+519
-275
lines changed

docs/multichain/destination/OperatorTableUpdater.md

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ The `OperatorTableUpdater` is responsible for updating the `GlobalTableRoot` and
1919
The contract supports both BN254 and ECDSA operator tables and routes updates to the appropriate certificate verifier based on the curve type.
2020

2121
## Parameterization
22-
Upon initialization, the `generator` is updated. The `generator` is represented in storage as an operatorSet. The `generator` should be considered a ghost-operatorSet` since it does not exist in the core protocol, does not have stake backing it, and is not transported to other chains via the multichain protocol. It can only be updated upon initialization or by a [privileged role](#updategenerator). This entity is the same across all destination chains.
22+
Upon initialization, the `generator` is updated. The `generator` is represented in storage as an operatorSet. The `generator` should be considered a ghost-operatorSet` since it does not exist in the core protocol, does not have stake backing it, and is not transported to other chains via the multichain protocol. It can only be set upon initialization and a new generator can be set by [privileged role](#updategenerator). This entity is the same across all destination chains.
2323

2424
The following values are set upon initialization:
2525

2626
* `generator` is an EigenLabs-run entity that signs off on `globalTableRoots`. The operatorSet is of size 1.
2727
* `globalRootConfirmationThreshold`: 10000. The threshold in basis points required for global root confirmation. Since the operatorSet is of size 1 a single signature is needed.
28-
* `referenceTimestamp`: A past timestamp at which the `generator` is set. We hardcode this value to 1 upon initialization. This value is also the `latestReferenceTimestamp`. Once roots are updated, the `latestReferenceTimestamp` will increase. *Note: the reference timestamp for the `generator`, given by `operatorTableUpdater.getGeneratorReferenceTimestamp` will remain 1 unless [`updateGenerator`](#updategenerator) is called*.
2928
* `generatorInfo`: The key material needed to verify certificates of the `generator`
3029
* `operatorSetConfig`: A configuration for the `generator`
31-
* `maxStalenessPeriod`: 0. Set to zero to confirm `globalTableRoots` without updating the `generator` operatorSet. See [`CertificateVerifier`](./CertificateVerifier.md#overview) for specifics`OperatorTableUpdater`. It is the same across all destination chains, even for destination chains that are supported after the initial deployment.
32-
* `owner`: Unused parameter for `Generator`
30+
* `maxStalenessPeriod`: 0 (`GENERATOR_MAX_STALENESS_PERIOD`). Set to zero to allow confirmation of generator certificates regardless of `referenceTimestamp`. See [`CertificateVerifier`](./CertificateVerifier.md#overview) for speccifics
31+
* `owner`: Unused parameter for `Generator`. Set to the address of the `OperatorTableUpdater`
32+
* The `latestReferenceTimestamp` for the `Generator` is 1 (`GENERATOR_REFERENCE_TIMESTAMP`)
33+
* The `globalTableRoot` for the `Generator` is `GENERATOR_GLOBAL_TABLE_ROOT`
3334

3435
Operator tables are updated daily on testnet and weekly on mainnet.
3536
---
@@ -113,6 +114,7 @@ Updates an operator table by verifying its inclusion in a confirmed global table
113114
*Requirements*:
114115
* The contract MUST NOT be paused for operator table updates
115116
* The `globalTableRoot` MUST be valid (not disabled)
117+
* The `operatorSet` MUST NOT be the `generator` (generator updates are handled separately)
116118
* The `referenceTimestamp` MUST be greater than the latest timestamp for the operator set
117119
* The merkle proof MUST verify the operator table's inclusion in the global root
118120
* The `globalTableRoot` at `referenceTimestamp` MUST match the provided root
@@ -124,28 +126,40 @@ Updates an operator table by verifying its inclusion in a confirmed global table
124126

125127
The `owner` can configure the `generator` and confirmation parameters.
126128

127-
### `setGenerator`
129+
### `updateGenerator`
128130

129131
```solidity
130132
/**
131-
* @notice Set the operatorSet which certifies against global roots
132-
* @param operatorSet the operatorSet which certifies against global roots
133-
* @dev The `operatorSet` is used to verify the certificate of the global table root
133+
* @notice Updates the `Generator` to a new operatorSet
134+
* @param generator The operatorSet which certifies against global roots
135+
* @param GeneratorInfo The operatorSetInfo for the generator
136+
* @param GeneratorConfig The operatorSetConfig for the generator
137+
* @dev We have a separate function for updating this operatorSet since it's not transported and updated
138+
* in the same way as the other operatorSets
134139
* @dev Only callable by the owner of the contract
140+
* @dev Uses GENERATOR_GLOBAL_TABLE_ROOT constant to break circular dependency for certificate verification
141+
* @dev We ensure that there are no collisions with other reference timestamps because we expect the generator to have an initial reference timestamp of 0
142+
* @dev The `_latestReferenceTimestamp` is not updated since this root is ONLY used for the `Generator`
135143
*/
136-
function setGenerator(
137-
OperatorSet calldata operatorSet
144+
function updateGenerator(
145+
OperatorSet calldata generator,
146+
BN254OperatorSetInfo calldata GeneratorInfo,
147+
OperatorSetConfig calldata GeneratorConfig
138148
) external;
139149
```
140150

141-
Updates the operator set responsible for confirming global table roots.
151+
Updates the operator set responsible for confirming global table roots. This function can only be called for operatorSets that have an uninitialized `latestReferenceTimestamp` in the `BN254CertificateVerifier`, ensuring that only *new* operatorSets can be the generator. Once set, the `latestReferenceTimestamp` for the generator is always 1 (`GENERATOR_REFERENCE_TIMESTAMP`).
142152

143153
*Effects*:
144154
* Updates `_generator` to the new `operatorSet`
155+
* Calls [`bn254CertificateVerifier.updateOperatorTable`](./CertificateVerifier.md#updateoperatortable-1)
145156
* Emits a `GeneratorUpdated` event
157+
* Sets the `GENERATOR_GLOBAL_TABLE_ROOT` to be valid
146158

147159
*Requirements*:
148160
* Caller MUST be the `owner`
161+
* The `latestReferenceTimestamp` for the `generator` MUST be zero
162+
* Meet all requirements in [`bn254CertificateVerifier.updateOperatorTable`](../destination/CertificateVerifier.md#updateoperatortable-1)
149163

150164
### `setGlobalRootConfirmationThreshold`
151165

@@ -192,31 +206,4 @@ Disables a global table root, preventing further operator table updates against
192206
*Requirements*:
193207
* Caller MUST be the `pauser`
194208
* The `globalTableRoot` MUST exist and be currently valid
195-
196-
### `updateGenerator`
197-
198-
```solidity
199-
/**
200-
* @notice Updates the operator table for the generator
201-
* @param referenceTimestamp The reference timestamp of the operator table update
202-
* @param generatorInfo The operatorSetInfo for the generator
203-
* @param generatorConfig The operatorSetConfig for the generator
204-
* @dev We have a separate function for updating this operatorSet since it's not transported and updated
205-
* in the same way as the other operatorSets
206-
* @dev Only callable by the owner of the contract
207-
*/
208-
function updateGenerator(
209-
uint32 referenceTimestamp,
210-
BN254OperatorSetInfo calldata generatorInfo,
211-
OperatorSetConfig calldata generatorConfig
212-
) external;
213-
```
214-
215-
Updates the operator table for the `generator` itself. This operatorSet is a ["shadow-operatorSet"](#parameterization), so it must be updated manually
216-
217-
*Effects*:
218-
* Calls `bn254CertificateVerifier.updateOperatorTable` for the `generator`
219-
220-
*Requirements*:
221-
* Caller MUST be the `owner`
222-
* Meet all requirements in [`bn254CertificateVerifier.updateOperatorTable`](../destination/CertificateVerifier.md#updateoperatortable-1)
209+
* The `globalTableRoot` MUST NOT be the `GENERATOR_GLOBAL_TABLE_ROOT`

docs/multichain/source/CrossChainRegistry.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,10 @@ Removes chain IDs from the global whitelist, preventing them from being used as
303303

304304
```solidity
305305
/**
306-
* @notice Sets the global table update cadence
307-
* @param tableUpdateCadence The table update cadence to set
306+
* @notice Sets the table update cadence in seconds
307+
* @param tableUpdateCadence the table update cadence
308308
* @dev msg.sender must be the owner of the CrossChainRegistry
309+
* @dev The table update cadence cannot be 0
309310
*/
310311
function setTableUpdateCadence(
311312
uint32 tableUpdateCadence

script/deploy/multichain/deploy_globalRootConfirmerSet.s.sol

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,14 @@ contract DeployGlobalRootConfirmerSet is Script, Test {
6363
);
6464
operatorSetInfo.operatorInfoTreeRoot = operatorInfoLeaves.merkleizeKeccak();
6565

66-
/**
67-
*
68-
* Create the `operatorSetConfig` struct
69-
*
70-
*/
71-
ICrossChainRegistry.OperatorSetConfig memory operatorSetConfig;
72-
operatorSetConfig.owner = operator.key.addr;
73-
operatorSetConfig.maxStalenessPeriod = 0;
74-
7566
/**
7667
*
7768
* OUTPUT - OPERATOR SET INFO (TOML FORMAT)
7869
*
7970
*/
8071

8172
// Write operator set info to TOML file
82-
_writeOperatorSetToml(network, operatorSetInfo, operatorSetConfig);
73+
_writeOperatorSetToml(network, operatorSetInfo);
8374

8475
/**
8576
*
@@ -129,29 +120,20 @@ contract DeployGlobalRootConfirmerSet is Script, Test {
129120

130121
function _writeOperatorSetToml(
131122
string memory network,
132-
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory operatorSetInfo,
133-
ICrossChainRegistry.OperatorSetConfig memory operatorSetConfig
123+
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory operatorSetInfo
134124
) internal {
135125
// Build JSON object using serializeJson
136126
string memory json_obj = "toml_output";
137127

138128
// Top level fields
139129
vm.serializeUint(json_obj, "globalRootConfirmationThreshold", 10_000);
140-
vm.serializeUint(json_obj, "referenceTimestamp", block.timestamp);
141130

142131
// globalRootConfirmerSet object
143132
string memory confirmerSet_obj = "globalRootConfirmerSet";
144133
vm.serializeString(confirmerSet_obj, "avs", AVS.toHexString());
145134
string memory confirmerSetOutput = vm.serializeUint(confirmerSet_obj, "id", 0);
146135
vm.serializeString(json_obj, "globalRootConfirmerSet", confirmerSetOutput);
147136

148-
// globalRootConfirmerSetConfig object
149-
string memory confirmerSetConfig_obj = "globalRootConfirmerSetConfig";
150-
vm.serializeUint(confirmerSetConfig_obj, "maxStalenessPeriod", operatorSetConfig.maxStalenessPeriod);
151-
string memory confirmerSetConfigOutput =
152-
vm.serializeAddress(confirmerSetConfig_obj, "owner", operatorSetConfig.owner);
153-
vm.serializeString(json_obj, "globalRootConfirmerSetConfig", confirmerSetConfigOutput);
154-
155137
// globalRootConfirmerSetInfo object
156138
string memory confirmerSetInfo_obj = "globalRootConfirmerSetInfo";
157139
vm.serializeUint(confirmerSetInfo_obj, "numOperators", operatorSetInfo.numOperators);

script/releases/Env.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ library Env {
177177
return _envU256("CROSS_CHAIN_REGISTRY_INIT_PAUSE_STATUS");
178178
}
179179

180+
function TABLE_UPDATE_CADENCE() internal view returns (uint32) {
181+
return _envU32("TABLE_UPDATE_CADENCE");
182+
}
183+
180184
function isSourceChain() internal view returns (bool) {
181185
return _envBool("SOURCE_CHAIN");
182186
}

script/releases/v1.7.0-multichain/1-deploySourceChain.s.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ contract DeploySourceChain is EOADeployer {
6868
CrossChainRegistry.initialize,
6969
(
7070
Env.opsMultisig(), // initialOwner
71-
1 days, // initialMinimumStalenessPeriod
71+
Env.TABLE_UPDATE_CADENCE(),
7272
Env.CROSS_CHAIN_REGISTRY_PAUSE_STATUS()
7373
)
7474
)
@@ -125,6 +125,9 @@ contract DeploySourceChain is EOADeployer {
125125
CrossChainRegistry crossChainRegistry = Env.proxy.crossChainRegistry();
126126
assertTrue(crossChainRegistry.owner() == Env.opsMultisig(), "ccr.owner invalid");
127127
assertTrue(crossChainRegistry.paused() == Env.CROSS_CHAIN_REGISTRY_PAUSE_STATUS(), "ccr.paused invalid");
128+
assertEq(
129+
crossChainRegistry.getTableUpdateCadence(), Env.TABLE_UPDATE_CADENCE(), "ccr.tableUpdateCadence invalid"
130+
);
128131

129132
// Validate ReleaseManager
130133
ReleaseManager releaseManager = Env.proxy.releaseManager();

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,14 @@ contract DeployDestinationChainImpls is EOADeployer, DeployDestinationChainProxi
129129
OperatorTableUpdater operatorTableUpdater = Env.impl.operatorTableUpdater();
130130
OperatorSet memory dummyOperatorSet = OperatorSet({avs: address(0), id: 0});
131131
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory dummyBN254Info;
132-
ICrossChainRegistryTypes.OperatorSetConfig memory dummyConfig;
133132

134133
vm.expectRevert(errInit);
135134
operatorTableUpdater.initialize(
136135
address(0), // owner
137136
0, // initial paused status
138137
dummyOperatorSet, // globalRootConfirmerSet
139138
0, // globalRootConfirmationThreshold
140-
0, // referenceTimestamp
141-
dummyBN254Info, // globalRootConfirmerSetInfo
142-
dummyConfig // globalRootConfirmerSetConfig
139+
dummyBN254Info // globalRootConfirmerSetInfo
143140
);
144141

145142
// ECDSACertificateVerifier and BN254CertificateVerifier don't have initialize functions

script/releases/v1.7.0-multichain/4-instantiateDestinationChainProxies.s.sol

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
5454
0, // initial paused status
5555
initParams.globalRootConfirmerSet,
5656
initParams.globalRootConfirmationThreshold,
57-
initParams.referenceTimestamp,
58-
initParams.globalRootConfirmerSetInfo,
59-
initParams.globalRootConfirmerSetConfig
57+
initParams.globalRootConfirmerSetInfo
6058
)
6159
)
6260
);
@@ -102,7 +100,45 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
102100
OperatorTableUpdater operatorTableUpdater = Env.proxy.operatorTableUpdater();
103101
assertTrue(operatorTableUpdater.owner() == Env.opsMultisig(), "operatorTableUpdater.owner invalid");
104102
assertTrue(operatorTableUpdater.paused() == 0, "operatorTableUpdater.paused invalid");
105-
// TODO: add checks on global root confirmer set
103+
104+
// Checks on the generator
105+
OperatorTableUpdaterInitParams memory initParams = _getTableUpdaterInitParams();
106+
assertEq(
107+
operatorTableUpdater.getGenerator().key(),
108+
initParams.globalRootConfirmerSet.key(),
109+
"operatorTableUpdater.generator invalid"
110+
);
111+
assertEq(
112+
operatorTableUpdater.getGeneratorReferenceTimestamp(),
113+
operatorTableUpdater.GENERATOR_REFERENCE_TIMESTAMP(),
114+
"operatorTableUpdater.generatorReferenceTimestamp invalid"
115+
);
116+
assertEq(
117+
operatorTableUpdater.getGeneratorReferenceTimestamp(),
118+
1,
119+
"operatorTableUpdater.generatorReferenceTimestamp invalid"
120+
);
121+
assertEq(
122+
operatorTableUpdater.getGlobalTableRootByTimestamp(1),
123+
operatorTableUpdater.GENERATOR_GLOBAL_TABLE_ROOT(),
124+
"operatorTableUpdater.generatorGlobalTableRoot invalid"
125+
);
126+
assertEq(
127+
operatorTableUpdater.getLatestReferenceTimestamp(),
128+
0,
129+
"operatorTableUpdater.latestReferenceTimestamp invalid"
130+
);
131+
assertTrue(
132+
operatorTableUpdater.isRootValid(operatorTableUpdater.GENERATOR_GLOBAL_TABLE_ROOT()),
133+
"operatorTableUpdater.generatorGlobalTableRoot invalid"
134+
);
135+
assertTrue(
136+
operatorTableUpdater.isRootValidByTimestamp(operatorTableUpdater.GENERATOR_REFERENCE_TIMESTAMP()),
137+
"operatorTableUpdater.generatorGlobalTableRoot invalid"
138+
);
139+
ICrossChainRegistryTypes.OperatorSetConfig memory generatorConfig = operatorTableUpdater.getGeneratorConfig();
140+
assertEq(generatorConfig.maxStalenessPeriod, 0, "generatorConfig.maxStalenessPeriod invalid");
141+
assertEq(generatorConfig.owner, address(operatorTableUpdater), "generatorConfig.owner invalid");
106142

107143
// Validate ECDSACertificateVerifier
108144
ECDSACertificateVerifier ecdsaCertificateVerifier = Env.proxy.ecdsaCertificateVerifier();
@@ -111,6 +147,11 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
111147
// Validate BN254CertificateVerifier
112148
BN254CertificateVerifier bn254CertificateVerifier = Env.proxy.bn254CertificateVerifier();
113149
assertTrue(address(bn254CertificateVerifier) != address(0), "bn254CertificateVerifier not deployed");
150+
assertEq(
151+
bn254CertificateVerifier.latestReferenceTimestamp(initParams.globalRootConfirmerSet),
152+
1,
153+
"bn254CertificateVerifier.latestReferenceTimestamp invalid"
154+
);
114155
}
115156

116157
/// @dev Ensure each deployed TUP/beacon is owned by the proxyAdmin/executorMultisig
@@ -165,17 +206,14 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
165206
OperatorTableUpdater operatorTableUpdater = Env.proxy.operatorTableUpdater();
166207
OperatorSet memory dummyOperatorSet = OperatorSet({avs: address(0), id: 0});
167208
IOperatorTableCalculatorTypes.BN254OperatorSetInfo memory dummyBN254Info;
168-
ICrossChainRegistryTypes.OperatorSetConfig memory dummyConfig;
169209

170210
vm.expectRevert(errInit);
171211
operatorTableUpdater.initialize(
172212
address(0), // owner
173213
0, // initial paused status
174214
dummyOperatorSet, // globalRootConfirmerSet
175215
0, // globalRootConfirmationThreshold
176-
0, // referenceTimestamp
177-
dummyBN254Info, // globalRootConfirmerSetInfo
178-
dummyConfig // globalRootConfirmerSetConfig
216+
dummyBN254Info // globalRootConfirmerSetInfo
179217
);
180218

181219
// ECDSACertificateVerifier and BN254CertificateVerifier don't have initialize functions
@@ -217,20 +255,11 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
217255
// Parse globalRootConfirmationThreshold
218256
initParams.globalRootConfirmationThreshold = uint16(toml.readUint(".globalRootConfirmationThreshold"));
219257

220-
// Parse referenceTimestamp
221-
initParams.referenceTimestamp = uint32(toml.readUint(".referenceTimestamp"));
222-
223258
// Parse globalRootConfirmerSet
224259
address avs = toml.readAddress(".globalRootConfirmerSet.avs");
225260
uint32 id = uint32(toml.readUint(".globalRootConfirmerSet.id"));
226261
initParams.globalRootConfirmerSet = OperatorSet({avs: avs, id: id});
227262

228-
// Parse globalRootConfirmerSetConfig
229-
address owner = toml.readAddress(".globalRootConfirmerSetConfig.owner");
230-
uint32 maxStalenessPeriod = uint32(toml.readUint(".globalRootConfirmerSetConfig.maxStalenessPeriod"));
231-
initParams.globalRootConfirmerSetConfig =
232-
ICrossChainRegistryTypes.OperatorSetConfig({owner: owner, maxStalenessPeriod: maxStalenessPeriod});
233-
234263
// Parse globalRootConfirmerSetInfo
235264
initParams.globalRootConfirmerSetInfo.numOperators =
236265
uint256(toml.readUint(".globalRootConfirmerSetInfo.numOperators"));
@@ -248,8 +277,6 @@ contract InstantiateDestinationChainProxies is DeployDestinationChainImpls {
248277
struct OperatorTableUpdaterInitParams {
249278
uint16 globalRootConfirmationThreshold;
250279
OperatorSet globalRootConfirmerSet;
251-
ICrossChainRegistryTypes.OperatorSetConfig globalRootConfirmerSetConfig;
252280
IOperatorTableCalculatorTypes.BN254OperatorSetInfo globalRootConfirmerSetInfo;
253-
uint32 referenceTimestamp;
254281
}
255282
}

script/releases/v1.7.0-multichain/configs/mainnet.toml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,10 @@
1-
# Global root confirmation threshold in basis points (e.g., 10000 = 100%)
2-
globalRootConfirmationThreshold = 10000
3-
4-
# Reference timestamp
5-
referenceTimestamp = 1700000000
61

2+
globalRootConfirmationThreshold = 10000
73
# Global root confirmer set configuration
84
[globalRootConfirmerSet]
95
avs = "0x0000000000000000000000000000000000000042"
106
id = 0
117

12-
# Global root confirmer set config
13-
[globalRootConfirmerSetConfig]
14-
maxStalenessPeriod = 0
15-
owner = "0x0000000000000000000000000000000000000042"
16-
178
# Global root confirmer set info
189
[globalRootConfirmerSetInfo]
1910
numOperators = 1

0 commit comments

Comments
 (0)