Skip to content

Commit 59bce17

Browse files
committed
WIP review changes
1 parent 1f6b0c0 commit 59bce17

File tree

4 files changed

+115
-43
lines changed

4 files changed

+115
-43
lines changed

src/genesis-hardhat/create-genesis-hardhat.ts

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
getExpectedStorageTokenWrappedBridgeUpgradeable,
2424
updateExpectedStorageBridgeToken,
2525
getExpectedStorageAggOracleCommittee,
26+
getStorageTimelockAdminRoleMember,
2627
} from './utils';
2728
import { checkParams } from '../utils';
2829
import { logger } from '../logger';
@@ -41,6 +42,10 @@ const supportedBridgeContractsProxy = ['PolygonZkEVMBridgeV2 proxy', 'PolygonZkE
4142
* @returns The genesis file
4243
*/
4344
export async function createGenesisHardhat(_genesisBase: any, initializeParams: any, config: any) {
45+
let isDebug = false;
46+
if (config && config.debug) {
47+
isDebug = config.debug;
48+
}
4449
logger.info('createGenesisHardhat tool');
4550

4651
/// //////////////////////////
@@ -113,27 +118,27 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
113118
}
114119

115120
// TIMELOCK_OWNER
116-
// let timelockOwner;
117-
// // Set the default owner from the genesis base
118-
// if (config.timelock === undefined || config.timelock.owner === undefined) {
119-
// timelockOwner = genesisBaseAddresses.deployerAddress;
120-
// } else {
121-
// // Check if the owner is a valid address
122-
// if (!ethers.isAddress(config.timelock.owner)) {
123-
// throw new Error('timelock owner must be a valid address');
124-
// }
125-
// timelockOwner = config.timelock.owner;
126-
// }
121+
let timelockOwner;
122+
// Set the default owner from the genesis base
123+
if (config.timelock === undefined || config.timelock.owner === undefined) {
124+
timelockOwner = genesisBaseAddresses.deployerAddress;
125+
} else {
126+
// Check if the owner is a valid address
127+
if (!ethers.isAddress(config.timelock.owner)) {
128+
throw new Error('timelock owner must be a valid address');
129+
}
130+
timelockOwner = config.timelock.owner;
131+
}
127132

128133
/// ///////////////////////////////////
129134
/// DEPLOY SOVEREIGN CONTRACTS ///
130135
/// ///////////////////////////////////
131136
const genesisInfo = [];
132137

133138
// Load deployer
134-
await ethers.provider.send('hardhat_impersonateAccount', [genesisBaseAddresses.deployerAddress]);
135-
await ethers.provider.send('hardhat_setBalance', [genesisBaseAddresses.deployerAddress, '0xffffffffffffffff']); // 18 ethers aprox
136-
const deployer = await ethers.getSigner(genesisBaseAddresses.deployerAddress);
139+
await ethers.provider.send('hardhat_impersonateAccount', [timelockOwner]);
140+
await ethers.provider.send('hardhat_setBalance', [timelockOwner, '0xffffffffffffffff']); // 18 ethers aprox
141+
const deployer = await ethers.getSigner(timelockOwner);
137142

138143
// deploy BridgeL2SovereignChain
139144
const BridgeL2SovereignChainFactory = await ethers.getContractFactory(
@@ -290,6 +295,16 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
290295
await proxyAdminInstance.connect(deployer).transferOwnership(genesisBaseAddresses.timelockAddress as string)
291296
).wait();
292297

298+
// Set timelock role with timelockAddress from genesisBaseAddresses
299+
const txTimelockAdminRole = await timelockContract.connect(deployer).grantRole(
300+
ethers.id('TIMELOCK_ADMIN_ROLE'),
301+
genesisBaseAddresses.timelockAddress, // timelockAddress from genesisBaseAddresses
302+
);
303+
const txRevokeTimelockAdminRole = await timelockContract.connect(deployer).revokeRole(
304+
ethers.id('TIMELOCK_ADMIN_ROLE'),
305+
timelockContract.target, // Revoke the role from the deployer
306+
);
307+
293308
/// /////////////////////////////////
294309
/// SANITY CHECKS DEPLOYMENT ///
295310
/// /////////////////////////////////
@@ -308,6 +323,12 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
308323
expect(globalExitRootUpdater).to.equal(await gerManagerContract.globalExitRootUpdater());
309324
expect(globalExitRootRemover).to.equal(await gerManagerContract.globalExitRootRemover());
310325

326+
// Check AggOracleCommittee params
327+
if (initializeParams.useAggOracleCommittee === true) {
328+
expect(initializeParams.aggOracleOwner).to.equal(await aggOracleCommitteeContract.owner());
329+
expect(initializeParams.quorum).to.equal(await aggOracleCommitteeContract.quorum());
330+
}
331+
311332
/// //////////////////////////////
312333
/// SANITY CHECKS STORAGE ///
313334
/// //////////////////////////////
@@ -468,13 +489,18 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
468489
const timelockStorageWrites = await getTraceStorageWrites(txDeployTimelockHash);
469490
const depthTimelockStorageWrites = 1;
470491
storageModifications.PolygonZkEVMTimelock = timelockStorageWrites[depthTimelockStorageWrites];
471-
472-
// Output the storage modifications JSON
473-
logger.info('Writing storage modifications JSON to file...');
474-
await fs.writeFileSync(
475-
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/storageModifications.json'),
476-
JSON.stringify(storageModifications, null, 2),
492+
const timelockStorageAdmin = (await getTraceStorageWrites(txTimelockAdminRole.hash))[depthTimelockStorageWrites];
493+
const timelockStorageRevokeAdmin = (await getTraceStorageWrites(txRevokeTimelockAdminRole.hash))[depthTimelockStorageWrites];
494+
expect(timelockStorageAdmin[getStorageTimelockAdminRoleMember(genesisBaseAddresses.timelockAddress)]).to.equal(
495+
'0x0000000000000000000000000000000000000000000000000000000000000001',
496+
);
497+
expect(timelockStorageRevokeAdmin[getStorageTimelockAdminRoleMember(timelockContract.target)]).to.equal(
498+
'0x0000000000000000000000000000000000000000000000000000000000000000',
477499
);
500+
storageModifications.PolygonZkEVMTimelock[getStorageTimelockAdminRoleMember(timelockContract.target)] =
501+
'0x0000000000000000000000000000000000000000000000000000000000000000';
502+
storageModifications.PolygonZkEVMTimelock[getStorageTimelockAdminRoleMember(genesisBaseAddresses.timelockAddress)] =
503+
'0x0000000000000000000000000000000000000000000000000000000000000001';
478504

479505
/// /////////////////////////////////////////////////
480506
/// BUILD EXPECTED STORAGE MODIFICATIONS JSON ///
@@ -551,11 +577,8 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
551577
// PolygonZkEVMTimelock
552578
expectedStorageModifications.PolygonZkEVMTimelock = getExpectedStoragePolygonZkEVMTimelock(
553579
timelockMinDelay,
554-
initializeParams,
555-
);
556-
await fs.writeFileSync(
557-
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/expectedStorageModifications.json'),
558-
JSON.stringify(expectedStorageModifications, null, 2),
580+
genesisBaseAddresses.timelockAddress,
581+
timelockContract.target,
559582
);
560583

561584
/// //////////////////////////////
@@ -642,10 +665,23 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
642665
timelockContract.target,
643666
);
644667

645-
await fs.writeFileSync(
646-
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/actualStorage.json'),
647-
JSON.stringify(actualStorage, null, 2),
648-
);
668+
if (isDebug) {
669+
logger.info('**DEBUG**: Writing actual storage JSON to file...');
670+
await fs.writeFileSync(
671+
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/actualStorage.json'),
672+
JSON.stringify(actualStorage, null, 2),
673+
);
674+
logger.info('**DEBUG**: Writing expected storage modifications JSON to file...');
675+
await fs.writeFileSync(
676+
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/expectedStorageModifications.json'),
677+
JSON.stringify(expectedStorageModifications, null, 2),
678+
);
679+
logger.info('**DEBUG**: Writing storage modifications JSON to file...');
680+
await fs.writeFileSync(
681+
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/storageModifications.json'),
682+
JSON.stringify(storageModifications, null, 2),
683+
);
684+
}
649685

650686
let equal = deepEqual(storageModifications, expectedStorageModifications);
651687
if (!equal) {
@@ -660,6 +696,12 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
660696
logger.info('Actual storage matches expected storage');
661697
}
662698

699+
logger.info('Writing storage modifications JSON to file...');
700+
await fs.writeFileSync(
701+
path.join(__dirname, '../../tools/createSovereignGenesisHardhat/storageModifications.json'),
702+
JSON.stringify(storageModifications, null, 2),
703+
);
704+
663705
/// ///////////////////////////
664706
/// BUILD GENESIS FILE ///
665707
/// ///////////////////////////
@@ -675,12 +717,12 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
675717
const bridgeL2SovereignChainImplementation = newGenesis.genesis.find(function (obj) {
676718
return supportedBridgeContracts.includes(obj.contractName);
677719
});
678-
// Update the contract name and bytecode
720+
// Update the contract name, bytecode, storage and nonce
721+
// Address is not modified because it must match the L1 address
679722
bridgeL2SovereignChainImplementation.contractName = GENESIS_CONTRACT_NAMES.SOVEREIGN_BRIDGE_IMPLEMENTATION;
680723
bridgeL2SovereignChainImplementation.bytecode = await ethers.provider.getCode(
681724
await upgrades.erc1967.getImplementationAddress(sovereignChainBridgeContract.target),
682725
);
683-
// Update the storage and nonce
684726
bridgeL2SovereignChainImplementation.storage = storageModifications.BridgeL2SovereignChain_Implementation;
685727
bridgeL2SovereignChainImplementation.nonce = await ethers.provider.getTransactionCount(
686728
await upgrades.erc1967.getImplementationAddress(sovereignChainBridgeContract.target),
@@ -694,8 +736,10 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
694736
const bridgeL2SovereignChain = newGenesis.genesis.find(function (obj) {
695737
return supportedBridgeContractsProxy.includes(obj.contractName);
696738
});
697-
// Update the contract name, storage and nonce
739+
// Update the contract name, bytecode, storage and nonce
698740
bridgeL2SovereignChain.contractName = GENESIS_CONTRACT_NAMES.SOVEREIGN_BRIDGE_PROXY;
741+
bridgeL2SovereignChain.bytecode = await ethers.provider.getCode(sovereignChainBridgeContract.target);
742+
// The storage is the storage modified during deployment and initialization.
699743
bridgeL2SovereignChain.storage = {
700744
...storageModifications.BridgeL2SovereignChain,
701745
...storageModifications.BridgeL2SovereignChain_Initialization,
@@ -706,9 +750,11 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
706750
/// GER IMPLEMENTATION //////
707751
/// /////////////////////////
708752
logger.info('Updating GlobalExitRootManagerL2SovereignChain implementation in genesis file...');
753+
// Get genesis info for ger implementation
709754
const gerManagerL2SovereignChainImplementation = newGenesis.genesis.find(function (obj) {
710755
return supportedGERManagers.includes(obj.contractName);
711756
});
757+
// Update the contract name, bytecode, storage and nonce
712758
gerManagerL2SovereignChainImplementation.contractName = GENESIS_CONTRACT_NAMES.GER_L2_SOVEREIGN_IMPLEMENTATION;
713759
gerManagerL2SovereignChainImplementation.bytecode = await ethers.provider.getCode(
714760
await upgrades.erc1967.getImplementationAddress(gerManagerContract.target),
@@ -723,10 +769,14 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
723769
/// GER PROXY ///////////////
724770
/// /////////////////////////
725771
logger.info('Updating GlobalExitRootManagerL2SovereignChain proxy in genesis file...');
772+
// Get genesis info for ger proxy
726773
const gerManagerL2SovereignChain = newGenesis.genesis.find(function (obj) {
727774
return obj.contractName === GENESIS_CONTRACT_NAMES.GER_L2_PROXY;
728775
});
776+
// Update the contract name, bytecode, storage and nonce
729777
gerManagerL2SovereignChain.contractName = GENESIS_CONTRACT_NAMES.GER_L2_SOVEREIGN_PROXY;
778+
gerManagerL2SovereignChain.bytecode = await ethers.provider.getCode(gerManagerContract.target);
779+
// The storage is the storage modified during deployment and initialization.
730780
gerManagerL2SovereignChain.storage = {
731781
...storageModifications.GlobalExitRootManagerL2SovereignChain,
732782
...storageModifications.GlobalExitRootManagerL2SovereignChain_Initialization,
@@ -776,7 +826,7 @@ export async function createGenesisHardhat(_genesisBase: any, initializeParams:
776826
address: tokenWrappedAddress,
777827
bytecode: tokenWrappedDeployedBytecode,
778828
};
779-
tokenWrappedGenesis.storage = storageModifications.TokenWrappedBridgeUpgradeable;
829+
tokenWrappedGenesis.storage = storageModifications.TokenWrappedBridgeUpgradeable_Implementation;
780830
newGenesis.genesis.push(tokenWrappedGenesis);
781831
} else {
782832
// Check bytecode of the TokenWrapped contract is the same as the one in the genesis

src/genesis-hardhat/utils.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ethers, upgrades } from 'hardhat';
22
import { SUPPORTED_BRIDGE_CONTRACTS, SUPPORTED_BRIDGE_CONTRACTS_PROXY, GENESIS_CONTRACT_NAMES } from './constants';
33
import { STORAGE_GENESIS } from './storage';
44
import { getStorageWrites } from '../utils';
5+
import { logger } from '../logger';
56

67
/**
78
* Function to get the storage modifications of a tx from the txHash
@@ -323,25 +324,36 @@ export function getExpectedStorageGERManagerL2SovereignChain(initParams) {
323324
};
324325
}
325326

327+
/**
328+
* Get the storage position of the timelock admin role member
329+
* @param {String} timelockAddress - address of the timelock contract
330+
* @returns {String} - storage position of the timelock admin role member
331+
*/
332+
export function getStorageTimelockAdminRoleMember(timelockAddress) {
333+
const storagePosition = ethers.solidityPackedKeccak256(
334+
['uint256', 'uint256'],
335+
[ethers.id('TIMELOCK_ADMIN_ROLE'), 0],
336+
);
337+
return ethers.solidityPackedKeccak256(['uint256', 'uint256'], [timelockAddress, storagePosition]);
338+
}
339+
326340
/**
327341
* Get the expected storage of the timelock contract
328342
* @param {Number} minDelay - minimum delay for the timelock
343+
* @param {String} timelockContractAddress - address of the timelock contract
329344
* @returns {Object} - expected storage of the timelock contract
330345
*/
331-
export function getExpectedStoragePolygonZkEVMTimelock(minDelay, initParams) {
346+
export function getExpectedStoragePolygonZkEVMTimelock(minDelay, timelockContractAddressGenesis, timelockContractAddress) {
332347
const timelockAdminRole = ethers.keccak256(ethers.toUtf8Bytes('TIMELOCK_ADMIN_ROLE'));
333-
let storageTimelockAdminRoleMemberContract;
334-
if (initParams.useAggOracleCommittee) {
335-
storageTimelockAdminRoleMemberContract = STORAGE_GENESIS.TIMELOCK.TIMELOCK_ADMIN_ROLE_MEMBER_CONTRACT_AGG;
336-
} else {
337-
storageTimelockAdminRoleMemberContract = STORAGE_GENESIS.TIMELOCK.TIMELOCK_ADMIN_ROLE_MEMBER_CONTRACT;
338-
}
348+
const storageTimelockAdminRoleMemberGenesis = getStorageTimelockAdminRoleMember(timelockContractAddressGenesis);
349+
const storageTimelockAdminRoleMember = getStorageTimelockAdminRoleMember(timelockContractAddress);
339350
return {
340351
[STORAGE_GENESIS.TIMELOCK.TIMELOCK_ADMIN_ROLE]: timelockAdminRole,
341352
[STORAGE_GENESIS.TIMELOCK.PROPOSER_ROLE]: timelockAdminRole,
342353
[STORAGE_GENESIS.TIMELOCK.CANCELLER_ROLE]: timelockAdminRole,
343354
[STORAGE_GENESIS.TIMELOCK.EXECUTOR_ROLE]: timelockAdminRole,
344-
[storageTimelockAdminRoleMemberContract]: ethers.zeroPadValue('0x01', 32),
355+
[storageTimelockAdminRoleMemberGenesis]: ethers.zeroPadValue('0x01', 32),
356+
[storageTimelockAdminRoleMember]: ethers.zeroPadValue('0x00', 32),
345357
[STORAGE_GENESIS.TIMELOCK.TIMELOCK_ADMIN_ROLE_MEMBER]: ethers.zeroPadValue('0x01', 32),
346358
[STORAGE_GENESIS.TIMELOCK.PROPOSER_ROLE_MEMBER]: ethers.zeroPadValue('0x01', 32),
347359
[STORAGE_GENESIS.TIMELOCK.CANCELLER_ROLE_MEMBER]: ethers.zeroPadValue('0x01', 32),
@@ -453,14 +465,19 @@ export async function getActualStorage(modificationsStorage, address) {
453465
export function deepEqual(a, b) {
454466
if (a === b) return true;
455467
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
468+
logger.error(`Type error: a: ${a}, b: ${b}`);
456469
return false;
457470
}
458471
const keysA = Object.keys(a);
459472
const keysB = Object.keys(b);
460-
if (keysA.length !== keysB.length) return false;
473+
if (keysA.length !== keysB.length) {
474+
logger.error(`Length mismatch: a: ${keysA.length}, b: ${keysB.length}`);
475+
return false;
476+
}
461477
// eslint-disable-next-line no-restricted-syntax
462478
for (const key of keysA) {
463479
if (!keysB.includes(key) || !deepEqual(a[key], b[key])) {
480+
logger.error(`Key mismatch: ${key} in a: ${a[key]}, in b: ${b[key]}`);
464481
return false;
465482
}
466483
}

tools/createSovereignGenesisHardhat/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cp ./tools/createSovereignGenesisHardhat/genesis-base.json.example ./tools/creat
5555

5656
- Optional parameters
5757
- `format`: choose genesis output format. Supported ones: `geth`
58+
- `debug`: to print more info
5859

5960
- Run tool:
6061
```

tools/createSovereignGenesisHardhat/create-sovereign-genesis-hardhat.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ async function main() {
197197
aggOracleCommittee?: string[];
198198
quorum?: number;
199199
aggOracleOwner?: string;
200+
debug?: boolean;
200201
} = {
201202
rollupID: createGenesisSovereignParams.rollupID,
202203
gasTokenAddress,
@@ -225,7 +226,10 @@ async function main() {
225226
}
226227
logger.info('Update genesis-base to the SovereignContracts');
227228

228-
finalGenesis = await createGenesisHardhat(genesisBase, initializeParams, {});
229+
const config = {
230+
debug: typeof createGenesisSovereignParams.debug !== 'undefined' ? createGenesisSovereignParams.debug : false,
231+
};
232+
finalGenesis = await createGenesisHardhat(genesisBase, initializeParams, config);
229233

230234
// Add weth address to deployment output if gas token address is provided and sovereignWETHAddress is not provided
231235
let outWETHAddress;

0 commit comments

Comments
 (0)