From 1c5229cdab5dce07308452aa9cea1b0f53f7a4b9 Mon Sep 17 00:00:00 2001 From: kelemeno <34402761+kelemeno@users.noreply.github.com> Date: Fri, 31 May 2024 09:27:30 +0100 Subject: [PATCH] feat: update protocol upgrade tool for stm EVM-598 (#1834) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warning: bumps contracts for STM contract, do not merge until contracts are merged. ## What ❔ ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `zk spellcheck`. - [ ] Linkcheck has been run via `zk linkcheck`. --------- Co-authored-by: Stanislav Breadless --- .../protocol-upgrade/src/transaction.ts | 381 ++++++++++++------ 1 file changed, 253 insertions(+), 128 deletions(-) diff --git a/infrastructure/protocol-upgrade/src/transaction.ts b/infrastructure/protocol-upgrade/src/transaction.ts index ea9f0ae7611c..38f4ed1e91bd 100644 --- a/infrastructure/protocol-upgrade/src/transaction.ts +++ b/infrastructure/protocol-upgrade/src/transaction.ts @@ -1,10 +1,11 @@ import { BigNumberish } from '@ethersproject/bignumber'; -import { BytesLike, ethers } from 'ethers'; +import { Bytes, BytesLike, ethers } from 'ethers'; import { ForceDeployUpgraderFactory as ForceDeployUpgraderFactoryL2 } from 'l2-contracts/typechain'; import { DefaultUpgradeFactory as DefaultUpgradeFactoryL1, AdminFacetFactory, - GovernanceFactory + GovernanceFactory, + StateTransitionManagerFactory } from 'l1-contracts/typechain'; import { FacetCut } from 'l1-contracts/src.ts/diamondCut'; import { IZkSyncFactory } from '../pre-boojum/IZkSyncFactory'; @@ -28,7 +29,7 @@ import * as path from 'path'; const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, `etc/test_config/constant`); const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: 'utf-8' })); -export interface TransparentUpgrade { +export interface DiamondCutData { facetCuts: FacetCut[]; initAddress: string; initCalldata: string; @@ -170,78 +171,120 @@ export function prepareDefaultCalldataForL2upgrade(forcedDeployments: ForceDeplo return complexUpgraderCalldata; } -export function prepareProposeTransparentUpgradeCalldata( - initCalldata, - upgradeAddress: string, - facetCuts: FacetCut[], - diamondUpgradeProposalId: number -) { - let zkSyncFactory = IZkSyncFactory.connect(upgradeAddress, ethers.providers.getDefaultProvider()); - let transparentUpgrade: TransparentUpgrade = { - facetCuts, - initAddress: upgradeAddress, - initCalldata +interface GovernanceTx { + scheduleCalldata: string; + executeCalldata: string; + operation: any; +} + +function prepareGovernanceTxs(target: string, data: BytesLike): GovernanceTx { + const govCall = { + target: target, + value: 0, + data: data }; - let proposeTransparentUpgradeCalldata = zkSyncFactory.interface.encodeFunctionData('proposeTransparentUpgrade', [ - transparentUpgrade, - diamondUpgradeProposalId - ]); + const operation = { + calls: [govCall], + predecessor: ethers.constants.HashZero, + salt: ethers.constants.HashZero + }; + + const governance = new GovernanceFactory(); - let executeUpgradeCalldata = zkSyncFactory.interface.encodeFunctionData('executeUpgrade', [ - transparentUpgrade, - ethers.constants.HashZero + // Get transaction data of the `scheduleTransparent` + const scheduleCalldata = governance.interface.encodeFunctionData('scheduleTransparent', [ + operation, + 0 // delay ]); + + // Get transaction data of the `execute` + const executeCalldata = governance.interface.encodeFunctionData('execute', [operation]); + return { - transparentUpgrade, - proposeTransparentUpgradeCalldata, - executeUpgradeCalldata + scheduleCalldata, + executeCalldata, + operation }; } export function prepareTransparentUpgradeCalldataForNewGovernance( + oldProtocolVersion, + oldProtocolVersionDeadline, + newProtocolVersion, initCalldata, upgradeAddress: string, facetCuts: FacetCut[], - zksyncAddress: string + stmAddress: string, + zksyncAddress: string, + prepareDirectOperation?: boolean, + chainId?: string ) { - let transparentUpgrade: TransparentUpgrade = { + let diamondCut: DiamondCutData = { facetCuts, initAddress: upgradeAddress, initCalldata }; + // Prepare calldata for STM + let stm = new StateTransitionManagerFactory(); + const stmUpgradeCalldata = stm.interface.encodeFunctionData('setNewVersionUpgrade', [ + diamondCut, + oldProtocolVersion, + oldProtocolVersionDeadline, + newProtocolVersion + ]); + + const { scheduleCalldata: stmScheduleTransparentOperation, executeCalldata: stmExecuteOperation } = + prepareGovernanceTxs(stmAddress, stmUpgradeCalldata); // Prepare calldata for upgrading diamond proxy let adminFacet = new AdminFacetFactory(); - const diamondProxyUpgradeCalldata = adminFacet.interface.encodeFunctionData('executeUpgrade', [transparentUpgrade]); - - const call = { - target: zksyncAddress, - value: 0, - data: diamondProxyUpgradeCalldata - }; - const governanceOperation = { - calls: [call], - predecessor: ethers.constants.HashZero, - salt: ethers.constants.HashZero - }; - - const governance = new GovernanceFactory(); - // Get transaction data of the `scheduleTransparent` - const scheduleTransparentOperation = governance.interface.encodeFunctionData('scheduleTransparent', [ - governanceOperation, - 0 // delay + const diamondProxyUpgradeCalldata = adminFacet.interface.encodeFunctionData('upgradeChainFromVersion', [ + oldProtocolVersion, + diamondCut ]); - // Get transaction data of the `execute` - const executeOperation = governance.interface.encodeFunctionData('execute', [governanceOperation]); + const { + scheduleCalldata: scheduleTransparentOperation, + executeCalldata: executeOperation, + operation: governanceOperation + } = prepareGovernanceTxs(zksyncAddress, diamondProxyUpgradeCalldata); - return { + const legacyScheduleTransparentOperation = adminFacet.interface.encodeFunctionData('executeUpgrade', [diamondCut]); + const { scheduleCalldata: legacyScheduleOperation, executeCalldata: legacyExecuteOperation } = prepareGovernanceTxs( + zksyncAddress, + legacyScheduleTransparentOperation + ); + + let result: any = { + stmScheduleTransparentOperation, + stmExecuteOperation, scheduleTransparentOperation, executeOperation, + diamondCut, governanceOperation, - transparentUpgrade + legacyScheduleOperation, + legacyExecuteOperation }; + + if (prepareDirectOperation) { + if (!chainId) { + throw new Error('chainId is required for direct operation'); + } + + const stmDirecUpgradeCalldata = stm.interface.encodeFunctionData('executeUpgrade', [chainId, diamondCut]); + + const { scheduleCalldata: stmScheduleOperationDirect, executeCalldata: stmExecuteOperationDirect } = + prepareGovernanceTxs(stmAddress, stmDirecUpgradeCalldata); + + result = { + ...result, + stmScheduleOperationDirect, + stmExecuteOperationDirect + }; + } + + return result; } export function buildDefaultUpgradeTx( @@ -249,17 +292,21 @@ export function buildDefaultUpgradeTx( diamondUpgradeProposalId, upgradeAddress, l2UpgraderAddress, + oldProtocolVersion, + oldProtocolVersionDeadline, upgradeTimestamp, newAllowList, + stmAddress, zksyncAddress, - useNewGovernance, - postUpgradeCalldataFlag + postUpgradeCalldataFlag, + prepareDirectOperation?, + chainId? ) { const commonData = JSON.parse(fs.readFileSync(getCommonDataFileName(), { encoding: 'utf-8' })); - const protocolVersionSemVer: string = commonData.protocolVersion; - const packedProtocolVersion = packSemver(...unpackStringSemVer(protocolVersionSemVer)); + const newProtocolVersionSemVer: string = commonData.protocolVersion; + const packedNewProtocolVersion = packSemver(...unpackStringSemVer(newProtocolVersionSemVer)); console.log( - `Building default upgrade tx for ${environment} protocol version ${protocolVersionSemVer} upgradeTimestamp ${upgradeTimestamp} ` + `Building default upgrade tx for ${environment} protocol version ${newProtocolVersionSemVer} upgradeTimestamp ${upgradeTimestamp} ` ); let facetCuts = []; let facetCutsFileName = getFacetCutsFileName(environment); @@ -319,7 +366,7 @@ export function buildDefaultUpgradeTx( let proposeUpgradeTx = buildProposeUpgrade( ethers.BigNumber.from(upgradeTimestamp), - packedProtocolVersion, + packedNewProtocolVersion, '0x', postUpgradeCalldata, cryptoVerifierParams, @@ -332,28 +379,25 @@ export function buildDefaultUpgradeTx( let l1upgradeCalldata = prepareDefaultCalldataForL1upgrade(proposeUpgradeTx); - let upgradeData; - if (useNewGovernance) { - upgradeData = prepareTransparentUpgradeCalldataForNewGovernance( - l1upgradeCalldata, - upgradeAddress, - facetCuts, - zksyncAddress - ); - } else { - upgradeData = prepareProposeTransparentUpgradeCalldata( - l1upgradeCalldata, - upgradeAddress, - facetCuts, - diamondUpgradeProposalId - ); - } + let upgradeData = prepareTransparentUpgradeCalldataForNewGovernance( + oldProtocolVersion, + oldProtocolVersionDeadline, + packedNewProtocolVersion, + l1upgradeCalldata, + upgradeAddress, + facetCuts, + stmAddress, + zksyncAddress, + prepareDirectOperation, + chainId + ); + const transactions = { proposeUpgradeTx, l1upgradeCalldata, upgradeAddress, - protocolVersionSemVer, - packedProtocolVersion, + protocolVersionSemVer: newProtocolVersionSemVer, + packedProtocolVersion: packedNewProtocolVersion, diamondUpgradeProposalId, upgradeTimestamp, ...upgradeData @@ -402,50 +446,21 @@ export function getWallet(l1rpc, privateKey) { ).connect(provider); } -async function proposeUpgrade( +async function sendPreparedTx( privateKey: string, l1rpc: string, - zksyncAddress: string, environment: string, gasPrice: ethers.BigNumber, nonce: number, - newGovernanceAddress: string + governanceAddr: string, + transactionsJsonField: string, + logText: string ) { const transactions = JSON.parse(fs.readFileSync(getL2TransactionsFileName(environment)).toString()); - let to; - let calldata; - if (newGovernanceAddress != null) { - to = newGovernanceAddress; - calldata = transactions.scheduleTransparentOperation; - } else { - to = zksyncAddress ?? process.env.CONTRACTS_DIAMOND_PROXY_ADDR; - calldata = transactions.proposeTransparentUpgradeCalldata; - } - console.log(`Proposing upgrade for protocolVersion ${transactions.protocolVersion}`); - await sendTransaction(calldata, privateKey, l1rpc, to, environment, gasPrice, nonce); -} + const calldata = transactions[transactionsJsonField]; -async function executeUpgrade( - privateKey: string, - l1rpc: string, - zksyncAddress: string, - environment: string, - gasPrice: ethers.BigNumber, - nonce: number, - newGovernanceAddress: string -) { - const transactions = JSON.parse(fs.readFileSync(getL2TransactionsFileName(environment)).toString()); - let to; - let calldata; - if (newGovernanceAddress != null) { - to = newGovernanceAddress; - calldata = transactions.executeOperation; - } else { - to = zksyncAddress ?? process.env.CONTRACTS_DIAMOND_PROXY_ADDR; - calldata = transactions.executeUpgradeCalldata; - } - console.log(`Execute upgrade for protocolVersion ${transactions.protocolVersion}`); - await sendTransaction(calldata, privateKey, l1rpc, to, environment, gasPrice, nonce); + console.log(`${logText} for protocolVersion ${transactions.protocolVersion}`); + await sendTransaction(calldata, privateKey, l1rpc, governanceAddr, environment, gasPrice, nonce); } async function cancelUpgrade( @@ -536,8 +551,13 @@ command .option('--new-allow-list ') .option('--l2-upgrader-address ') .option('--diamond-upgrade-proposal-id ') + .option('--old-protocol-version ') + .option('--old-protocol-version-deadline ') .option('--l1rpc ') .option('--zksync-address ') + .option('--state-transition-manager-address ') + .option('--chain-id ') + .option('--prepare-direct-operation ') .option('--use-new-governance') .option('--post-upgrade-calldata') .action(async (options) => { @@ -556,11 +576,65 @@ command diamondUpgradeProposalId, options.upgradeAddress, options.l2UpgraderAddress, + options.oldProtocolVersion, + options.oldProtocolVersionDeadline, options.upgradeTimestamp, options.newAllowList, + options.stateTransitionManagerAddress, options.zksyncAddress, - options.useNewGovernance, - options.postUpgradeCalldata + options.postUpgradeCalldata, + options.prepareDirectOperation, + options.chainId + ); + }); + +command + .command('propose-upgrade-stm') + .option('--environment ') + .option('--private-key ') + .option('--gas-price ') + .option('--nonce ') + .option('--l1rpc ') + .option('--governance-addr ') + .action(async (options) => { + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); + } + + await sendPreparedTx( + options.privateKey, + options.l1rpc, + options.environment, + options.gasPrice, + options.nonce, + options.governanceAddr, + 'stmScheduleTransparentOperation', + 'Proposing upgrade for STM' + ); + }); + +command + .command('execute-upgrade-stm') + .option('--environment ') + .option('--private-key ') + .option('--gas-price ') + .option('--nonce ') + .option('--l1rpc ') + .option('--governance-addr ') + .action(async (options) => { + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); + } + + await sendPreparedTx( + options.privateKey, + options.l1rpc, + options.environment, + options.gasPrice, + options.nonce, + options.governanceAddr, + 'stmExecuteOperation', + 'Executing upgrade for STM' ); }); @@ -572,21 +646,21 @@ command .option('--gas-price ') .option('--nonce ') .option('--l1rpc ') - .option('--new-governance ') + .option('--governance-addr ') .action(async (options) => { - if (!options.newGovernance) { - // TODO(X): remove old governance functionality from the protocol upgrade tool - throw new Error('Old governance is not supported anymore'); + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); } - await proposeUpgrade( + await sendPreparedTx( options.privateKey, options.l1rpc, - options.zksyncAddress, options.environment, options.gasPrice, options.nonce, - options.newGovernance + options.governanceAddr, + 'scheduleTransparentOperation', + 'Proposing "upgradeChainFromVersion" upgrade' ); }); @@ -598,21 +672,73 @@ command .option('--gas-price ') .option('--nonce ') .option('--l1rpc ') - .option('--new-governance ') + .option('--governance-addr ') .action(async (options) => { - if (!options.newGovernance) { - // TODO(X): remove old governance functionality from the protocol upgrade tool - throw new Error('Old governance is not supported anymore'); + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); } - await executeUpgrade( + await sendPreparedTx( options.privateKey, options.l1rpc, - options.zksyncAddress, options.environment, options.gasPrice, options.nonce, - options.newGovernance + options.governanceAddr, + 'executeOperation', + 'Executing "upgradeChainFromVersion" upgrade' + ); + }); + +command + .command('propose-upgrade-direct') + .option('--environment ') + .option('--private-key ') + .option('--zksync-address ') + .option('--gas-price ') + .option('--nonce ') + .option('--l1rpc ') + .option('--governance-addr ') + .action(async (options) => { + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); + } + + await sendPreparedTx( + options.privateKey, + options.l1rpc, + options.environment, + options.gasPrice, + options.nonce, + options.governanceAddr, + 'stmScheduleOperationDirect', + 'Executing direct upgrade via STM' + ); + }); + +command + .command('execute-upgrade-direct') + .option('--environment ') + .option('--private-key ') + .option('--zksync-address ') + .option('--gas-price ') + .option('--nonce ') + .option('--l1rpc ') + .option('--governance-addr ') + .action(async (options) => { + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); + } + + await sendPreparedTx( + options.privateKey, + options.l1rpc, + options.environment, + options.gasPrice, + options.nonce, + options.governanceAddr, + 'stmExecuteOperationDirect', + 'Executing direct upgrade via STM' ); }); @@ -625,11 +751,10 @@ command .option('--nonce ') .option('--l1rpc ') .option('--execute') - .option('--new-governance ') + .option('--governance-addr ') .action(async (options) => { - if (!options.newGovernance) { - // TODO(X): remove old governance functionality from the protocol upgrade tool - throw new Error('Old governance is not supported anymore'); + if (!options.governanceAddr) { + throw new Error('Governance address must be provided'); } await cancelUpgrade(