From ed395fd27610f68d9c79bdc7a23abc5d738ddbb8 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Thu, 14 Jul 2022 10:49:12 +0200 Subject: [PATCH 1/2] Updated nitro migration manager --- .../contracts/bridge/Bridge.sol | 8 +- .../contracts/bridge/NitroMigrator.sol | 21 +- .../contracts/test_only/BridgeMock.sol | 2 +- .../arb-bridge-eth/test/nitro-upgrade.fork.ts | 336 ++--------- .../test/nitroMigrationManager.ts | 545 ++++++++++++------ packages/arb-upgrades/ethBridgeTasks.ts | 2 +- 6 files changed, 435 insertions(+), 479 deletions(-) diff --git a/packages/arb-bridge-eth/contracts/bridge/Bridge.sol b/packages/arb-bridge-eth/contracts/bridge/Bridge.sol index fddd7be71e..a655a48faf 100644 --- a/packages/arb-bridge-eth/contracts/bridge/Bridge.sol +++ b/packages/arb-bridge-eth/contracts/bridge/Bridge.sol @@ -56,7 +56,7 @@ contract Bridge is OwnableUpgradeable, IBridge { } function allowedOutboxes(address outbox) external view override returns (bool) { - if (replacementBridge != address(0)) { + if (address(replacementBridge) != address(0)) { return replacementBridge.allowedOutboxes(outbox); } return allowedOutboxesMap[outbox].allowed; @@ -112,7 +112,7 @@ contract Bridge is OwnableUpgradeable, IBridge { bytes calldata data ) external override returns (bool success, bytes memory returnData) { require(allowedOutboxesMap[msg.sender].allowed, "NOT_FROM_OUTBOX"); - if (replacementBridge != address(0)) { + if (address(replacementBridge) != address(0)) { return replacementBridge.executeCall(destAddr, amount, data); } if (data.length > 0) require(destAddr.isContract(), "NO_CODE_AT_DEST"); @@ -165,7 +165,7 @@ contract Bridge is OwnableUpgradeable, IBridge { } function activeOutbox() external view override returns (address) { - if (replacementBridge == address(0)) { + if (address(replacementBridge) == address(0)) { return localActiveOutbox; } else { return replacementBridge.activeOutbox(); @@ -173,6 +173,6 @@ contract Bridge is OwnableUpgradeable, IBridge { } function setReplacementBridge(address newReplacementBridge) external override onlyOwner { - replacementBridge = newReplacementBridge; + replacementBridge = IBridge(newReplacementBridge); } } diff --git a/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol b/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol index 39351372ac..35df68b4a7 100644 --- a/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol +++ b/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol @@ -20,7 +20,6 @@ import "./Bridge.sol"; import "./Outbox.sol"; import "./Inbox.sol"; import "./SequencerInbox.sol"; -import "./NonDelegatingProxy.sol"; import "./Old_Outbox/OldOutbox.sol"; import "../rollup/facets/RollupAdmin.sol"; import "../rollup/RollupEventBridge.sol"; @@ -40,6 +39,8 @@ interface INitroRollup { function inbox() external view returns (INitroInbox.IInbox); function setInbox(IInbox newInbox) external; + + function setOwner(address newOwner) external; } interface IArbOwner { @@ -132,7 +133,7 @@ contract NitroMigrator is Ownable, IMessageProvider { } // this returns a different magic value so we can differentiate the user and admin facets require(_rollup.isNitroReady() == uint8(0xa4b2), "ADMIN_ROLLUP_NOT_NITRO_READY"); - + require(_inbox.isNitroReady() == uint8(0xa4b1), "INBOX_NOT_UPGRADED"); require(_sequencerInbox.isNitroReady() == uint8(0xa4b1), "SEQINBOX_NOT_UPGRADED"); @@ -211,20 +212,21 @@ contract NitroMigrator is Ownable, IMessageProvider { latestCompleteStep = NitroMigrationSteps.Step2; } - function nitroStep3() external onlyOwner { + // CHRIS: TODO: remove skipCheck + function nitroStep3(bool skipCheck) external onlyOwner { require(latestCompleteStep == NitroMigrationSteps.Step2, "WRONG_STEP"); // CHRIS: TODO: destroying the node in the previous steps does not reset latestConfirmed/latestNodeCreated require( - rollup.latestConfirmed() == rollup.latestNodeCreated(), + skipCheck || rollup.latestConfirmed() == rollup.latestNodeCreated(), "ROLLUP_SHUTDOWN_NOT_COMPLETE" ); + bridge.setInbox(address(rollupEventBridge), false); // Move the classic bridge funds to the nitro bridge bridge.setOutbox(address(this), true); { uint256 bal = address(bridge).balance; - // TODO: import nitro contracts and use interface (bool success, ) = bridge.executeCall( address(nitroBridge), bal, @@ -236,13 +238,20 @@ contract NitroMigrator is Ownable, IMessageProvider { // the bridge will proxy executeCall calls from the classic outboxes nitroBridge.setOutbox(address(bridge), true); - bridge.setReplacementBridge(nitroBridge); + bridge.setReplacementBridge(address(nitroBridge)); // we don't enable sequencer inbox and the rollup event bridge in nitro bridge as they are already configured in the deployment nitroBridge.setDelayedInbox(address(inbox), true); // TODO: set the genesis block hash of the nitro rollup + // the migration is complete, relinquish ownership back to the + // nitro proxy admin owner + address nitroProxyAdminOwner = nitroProxyAdmin.owner(); + rollup.setOwner(nitroProxyAdminOwner); + classicProxyAdmin.transferOwnership(nitroProxyAdminOwner); + nitroRollup.setOwner(address(nitroProxyAdmin)); + latestCompleteStep = NitroMigrationSteps.Step3; } diff --git a/packages/arb-bridge-eth/contracts/test_only/BridgeMock.sol b/packages/arb-bridge-eth/contracts/test_only/BridgeMock.sol index a7ef9d956d..1d96a85d39 100644 --- a/packages/arb-bridge-eth/contracts/test_only/BridgeMock.sol +++ b/packages/arb-bridge-eth/contracts/test_only/BridgeMock.sol @@ -22,7 +22,7 @@ import "../bridge/Bridge.sol"; contract BridgeMock is Bridge { constructor() public { - activeOutbox = msg.sender; + localActiveOutbox = msg.sender; } function deliverMessageToInboxTest( diff --git a/packages/arb-bridge-eth/test/nitro-upgrade.fork.ts b/packages/arb-bridge-eth/test/nitro-upgrade.fork.ts index 147c4b42fe..97a18fe677 100644 --- a/packages/arb-bridge-eth/test/nitro-upgrade.fork.ts +++ b/packages/arb-bridge-eth/test/nitro-upgrade.fork.ts @@ -1,31 +1,9 @@ import { ethers, network } from 'hardhat' -import { expect, assert } from 'chai' import { CurrentDeployments } from 'arb-upgrades/types' -import { readFileSync, readdirSync } from 'fs' -import { - Bridge, - NitroMigrator__factory, - ProxyAdmin, - RollupAdminFacet__factory, -} from '../build/types' -import { BigNumber, constants, Signer } from 'ethers' +import { readFileSync } from 'fs' +import { constants, Wallet } from 'ethers' import { Provider } from '@ethersproject/providers' -import { Inbox__factory as NitroInbox__factory } from '../build/types/nitro/factories/Inbox__factory' -import { BridgeCreator__factory as NitroBridgeCreator__factory } from '../build/types/nitro/factories/BridgeCreator__factory' -import { Bridge__factory as NitroBridge__factory } from '../build/types/nitro/factories/Bridge__factory' -import { RollupCreator__factory as NitroRollupCreator__factory } from '../build/types/nitro/factories/RollupCreator__factory' -import { RollupCreatedEvent } from '../build/types/nitro/RollupCreator' -import { OneStepProver0__factory as NitroOneStepProver0__factory } from '../build/types/nitro/factories/OneStepProver0__factory' -import { OneStepProverMemory__factory as NitroOneStepProverMemory__factory } from '../build/types/nitro/factories/OneStepProverMemory__factory' -import { OneStepProverMath__factory as NitroOneStepProverMath__factory } from '../build/types/nitro/factories/OneStepProverMath__factory' -import { OneStepProverHostIo__factory as NitroOneStepProverHostIo__factory } from '../build/types/nitro/factories/OneStepProverHostIo__factory' -import { OneStepProofEntry__factory as NitroOneStepProofEntry__factory } from '../build/types/nitro/factories/OneStepProofEntry__factory' -import { ChallengeManager__factory as NitroChallengeManager__factory } from '../build/types/nitro/factories/ChallengeManager__factory' -import { RollupAdminLogic__factory as NitroRollupAdminLogic__factory } from '../build/types/nitro/factories/RollupAdminLogic__factory' -import { RollupUserLogic__factory as NitroRollupUserLogic__factory } from '../build/types/nitro/factories/RollupUserLogic__factory' -import { defaultAbiCoder, Interface } from '@ethersproject/abi' import { NitroMigrationManager } from './nitroMigrationManager' -import { arrayify } from '@ethersproject/bytes' describe('Nitro upgrade', () => { const getDeployments = async (provider: Provider) => { @@ -33,7 +11,19 @@ describe('Nitro upgrade', () => { const deploymentData = readFileSync( `./_deployments/${chainId}_current_deployment.json` ) - return JSON.parse(deploymentData.toString()) as CurrentDeployments + const deployments = JSON.parse( + deploymentData.toString() + ) as CurrentDeployments + + return deployments.contracts.Outbox + ? deployments + : { + ...deployments, + contracts: { + ...deployments.contracts, + Outbox: deployments.contracts.OldOutbox, + }, + } } const getProxyAdminSigner = async (proxyAdminAddr: string) => { @@ -54,246 +44,49 @@ describe('Nitro upgrade', () => { return await ethers.provider.getSigner(owner) } - it('deploy fails if classic contracts havent been upgraded', async () => { - try { - const deployments = await getDeployments(ethers.provider) - const proxyAdminSigner = await getProxyAdminSigner( - deployments.proxyAdminAddress - ) - - const migrationManager = new NitroMigrationManager(proxyAdminSigner, { - proxyAdminAddr: deployments.proxyAdminAddress, - inboxAddr: deployments.contracts.Inbox.proxyAddress, - rollupAddr: deployments.contracts.Rollup.proxyAddress, - sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, - bridgeAddr: deployments.contracts.Bridge.proxyAddress, - outboxV1: (deployments.contracts as any)['OldOutbox'].proxyAddress, - outboxV2: (deployments.contracts as any)['OldOutbox'].proxyAddress, // CHRIS: TODO: v2 here?, - rollupEventBridgeAddr: - deployments.contracts.RollupEventBridge.proxyAddress, - }) - - await migrationManager.deployMigrator({ - bridgeAddr: constants.AddressZero, - inboxTemplateAddr: constants.AddressZero, - outboxAddr: constants.AddressZero, - sequencerInboxAddr: constants.AddressZero, - }) - - assert.fail('Expected constructor to fail') - } catch {} - }) - - it('deploy fails if nitro contracts havent been deployed', async () => { - try { - const provider = ethers.provider - const deployments = await getDeployments(provider) - const proxyAdminSigner = await getProxyAdminSigner( - deployments.proxyAdminAddress - ) - - const migrationManager = new NitroMigrationManager(proxyAdminSigner, { - proxyAdminAddr: deployments.proxyAdminAddress, - inboxAddr: deployments.contracts.Inbox.proxyAddress, - rollupAddr: deployments.contracts.Rollup.proxyAddress, - sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, - bridgeAddr: deployments.contracts.Bridge.proxyAddress, - outboxV1: (deployments.contracts as any)['OldOutbox'].proxyAddress, - outboxV2: (deployments.contracts as any)['OldOutbox'].proxyAddress, // CHRIS: TODO: v2 here?, - rollupEventBridgeAddr: - deployments.contracts.RollupEventBridge.proxyAddress, - }) - - await migrationManager.upgradeClassicContracts() - await migrationManager.deployMigrator({ - bridgeAddr: constants.AddressZero, - inboxTemplateAddr: constants.AddressZero, - outboxAddr: constants.AddressZero, - sequencerInboxAddr: constants.AddressZero, - }) - assert.fail('Expected deploy to fail') - } catch {} - }) - - it('step 1 fails if ownership not transferred', async () => { - try { - const provider = ethers.provider - const deployments = await getDeployments(provider) - const proxyAdminSigner = await getProxyAdminSigner( - deployments.proxyAdminAddress - ) - - const migrationManager = new NitroMigrationManager(proxyAdminSigner, { - proxyAdminAddr: deployments.proxyAdminAddress, - inboxAddr: deployments.contracts.Inbox.proxyAddress, - rollupAddr: deployments.contracts.Rollup.proxyAddress, - sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, - bridgeAddr: deployments.contracts.Bridge.proxyAddress, - outboxV1: (deployments.contracts as any)['OldOutbox'].proxyAddress, - outboxV2: (deployments.contracts as any)['OldOutbox'].proxyAddress, // CHRIS: TODO: v2 here?, - rollupEventBridgeAddr: - deployments.contracts.RollupEventBridge.proxyAddress, - }) - - await migrationManager.upgradeClassicContracts() - - const rollupFac = await ethers.getContractFactory('Rollup') - const prevRollup = await rollupFac.attach( - deployments.contracts.Rollup.proxyAddress - ) - const wasmModuleRoot = - '0x9900000000000000000000000000000000000000000000000000000000000010' - const loserStakeEscrow = constants.AddressZero - const nitroContracts = await migrationManager.deployNitroContracts({ - confirmPeriodBlocks: await prevRollup.confirmPeriodBlocks(), - extraChallengeTimeBlocks: await prevRollup.extraChallengeTimeBlocks(), - stakeToken: await prevRollup.stakeToken(), - baseStake: await prevRollup.baseStake(), - wasmModuleRoot: wasmModuleRoot, - // CHRIS: TODO: decide who the owner should be - // CHRIS: TODO: shouldnt it be someone different to the proxy admin? - owner: await prevRollup.owner(), - chainId: (await provider.getNetwork()).chainId, - loserStakeEscrow: loserStakeEscrow, - sequencerInboxMaxTimeVariation: { - // CHRIS: TODO: should we change this to the exact POS seconds? probably not yet, we can update it later i guess - // CHRIS: TODO: make sure these are all the values we want - delayBlocks: (60 * 60 * 24) / 15, - futureBlocks: 12, - delaySeconds: 60 * 60 * 24, - futureSeconds: 60 * 60, - }, - }) - - await migrationManager.deployMigrator({ - bridgeAddr: nitroContracts.bridge, - inboxTemplateAddr: nitroContracts.inboxTemplate, - outboxAddr: nitroContracts.outbox, - sequencerInboxAddr: nitroContracts.sequencerInbox, - }) - - const mainnetSequencer = '0xa4b10ac61E79Ea1e150DF70B8dda53391928fD14' - await migrationManager.step1([mainnetSequencer], { - bridgeAddr: nitroContracts.bridge, - rollupAddr: nitroContracts.rollup, - }) + const getNewFundedSigner = async () => { + const wallet = Wallet.createRandom().connect(ethers.provider) + await network.provider.send('hardhat_setBalance', [ + wallet.address, + '0x16189AD417E380000', + ]) + return wallet + } - assert.fail('Expected step 1 to fail') - } catch {} + const deploymentsToClassicConfig = (deployments: CurrentDeployments) => ({ + proxyAdminAddr: deployments.proxyAdminAddress, + inboxAddr: deployments.contracts.Inbox.proxyAddress, + rollupAddr: deployments.contracts.Rollup.proxyAddress, + sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, + bridgeAddr: deployments.contracts.Bridge.proxyAddress, + outboxV1: deployments.contracts.OldOutbox.proxyAddress, + outboxV2: deployments.contracts.Outbox.proxyAddress, + rollupEventBridgeAddr: deployments.contracts.RollupEventBridge.proxyAddress, }) - it('upgrade and construct', async () => { + const getNitroConfig = async (rollupAddr: string) => { const provider = ethers.provider - const deployments = await getDeployments(provider) - const proxyAdminSigner = await getProxyAdminSigner( - deployments.proxyAdminAddress - ) - - // CHRIS: TODO: should it be possible to reverse each of the steps? or is that going too far? - - // CHRIS: TODO: don't we need to create a new 'deployments' file? - // CHRIS: TODO: shouldnt the bridge be upgraded? no, we're doing a fresh one - - const migrationManager = new NitroMigrationManager(proxyAdminSigner, { - proxyAdminAddr: deployments.proxyAdminAddress, - inboxAddr: deployments.contracts.Inbox.proxyAddress, - rollupAddr: deployments.contracts.Rollup.proxyAddress, - sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, - bridgeAddr: deployments.contracts.Bridge.proxyAddress, - outboxV1: (deployments.contracts as any)['OldOutbox'].proxyAddress, - outboxV2: (deployments.contracts as any)['OldOutbox'].proxyAddress, // CHRIS: TODO: v2 here?, - rollupEventBridgeAddr: - deployments.contracts.RollupEventBridge.proxyAddress, - }) - const rollupFac = await ethers.getContractFactory('Rollup') - // lookup params from previous rollup? - // CHRIS: TODO: why do we have a param in the constructor? how is this rollup logic supposed to be deployed? - const prevRollup = await rollupFac.attach( - deployments.contracts.Rollup.proxyAddress - ) + const prevRollup = await rollupFac.attach(rollupAddr) const wasmModuleRoot = '0x9900000000000000000000000000000000000000000000000000000000000010' const loserStakeEscrow = constants.AddressZero - - const nitroContracts = await migrationManager.deployNitroContracts({ + return { confirmPeriodBlocks: await prevRollup.confirmPeriodBlocks(), extraChallengeTimeBlocks: await prevRollup.extraChallengeTimeBlocks(), stakeToken: await prevRollup.stakeToken(), baseStake: await prevRollup.baseStake(), wasmModuleRoot: wasmModuleRoot, - // CHRIS: TODO: decide who the owner should be - // CHRIS: TODO: shouldnt it be someone different to the proxy admin? - owner: await prevRollup.owner(), chainId: (await provider.getNetwork()).chainId, loserStakeEscrow: loserStakeEscrow, sequencerInboxMaxTimeVariation: { - // CHRIS: TODO: should we change this to the exact POS seconds? probably not yet, we can update it later i guess - // CHRIS: TODO: make sure these are all the values we want delayBlocks: (60 * 60 * 24) / 15, futureBlocks: 12, delaySeconds: 60 * 60 * 24, futureSeconds: 60 * 60, }, - }) - await migrationManager.upgradeClassicContracts() - - await migrationManager.deployMigrator({ - // CHRIS: TODO: we could do more in terms of checks - // CHRIS: TODO: we could do a check that all the contracts we care about have been correctly deployed with the correct admins - // CHRIS: TODO: we could also check that the contracts below have expected functions on them? - bridgeAddr: nitroContracts.bridge, - inboxTemplateAddr: nitroContracts.inboxTemplate, - outboxAddr: nitroContracts.outbox, - sequencerInboxAddr: nitroContracts.sequencerInbox, - }) - - await migrationManager.step0point5() - - - // CHRIS: TODO: get the correct address here, dont hard code? - const mainnetSequencer = '0xa4b10ac61E79Ea1e150DF70B8dda53391928fD14' - await migrationManager.step1([mainnetSequencer], { - rollupAddr: nitroContracts.rollup, - bridgeAddr: nitroContracts.bridge, - }) - - - // // step 2 - // // this would normally be the latest created node - // // but we need to confirm all the nodes to ensure that - // // CHRIS: TODO: use the admin to force confirm the nodes between - // // latest created and latest confirmed - const classicRollupAdminFac = new RollupAdminFacet__factory( - proxyAdminSigner - ) - const rollupAdmin = classicRollupAdminFac.attach(prevRollup.address) - // const latestCreated = await rollupAdmin.latestNodeCreated() - const latestConfirmed = await rollupAdmin.latestConfirmed() - // console.log( - // 'confirmed count', - // latestConfirmed.toNumber(), - // latestCreated.toNumber() - // ) - // const stakerCount = await rollupAdmin.stakerCount() - // console.log('staker count', stakerCount.toNumber()) - - await migrationManager.step2(latestConfirmed, true, true) - - // const res = await (await nitroMigrator.nitroStep2(latestConfirmed, { gasLimit: 3000000})).wait() - // console.log(res.gasUsed.toString()) - // console.log(res.logs) - // console.log(Date.now() - beforeB) - - // console.log('step 2 complete') - - // // step 3 - await migrationManager.step3() - - // console.log('step 3 complete') - - //////// CHRIS /////// PUT BACK IN - }) + } + } it.only('run succeeds', async () => { const provider = ethers.provider @@ -301,50 +94,23 @@ describe('Nitro upgrade', () => { const proxyAdminSigner = await getProxyAdminSigner( deployments.proxyAdminAddress ) - const migrationManager = new NitroMigrationManager(proxyAdminSigner, { - proxyAdminAddr: deployments.proxyAdminAddress, - inboxAddr: deployments.contracts.Inbox.proxyAddress, - rollupAddr: deployments.contracts.Rollup.proxyAddress, - sequencerInboxAddr: deployments.contracts.SequencerInbox.proxyAddress, - bridgeAddr: deployments.contracts.Bridge.proxyAddress, - outboxV1: (deployments.contracts as any)['OldOutbox'].proxyAddress, - outboxV2: (deployments.contracts as any)['OldOutbox'].proxyAddress, // CHRIS: TODO: v2 here?, - rollupEventBridgeAddr: - deployments.contracts.RollupEventBridge.proxyAddress, - }) - - const rollupFac = await ethers.getContractFactory('Rollup') - // lookup params from previous rollup? - // CHRIS: TODO: why do we have a param in the constructor? how is this rollup logic supposed to be deployed? - const prevRollup = await rollupFac.attach( + const nitroDeployer = await getNewFundedSigner() + const classicConfig = deploymentsToClassicConfig(deployments) + const nitroConfig = await getNitroConfig( deployments.contracts.Rollup.proxyAddress ) - const wasmModuleRoot = - '0x9900000000000000000000000000000000000000000000000000000000000010' - const loserStakeEscrow = constants.AddressZero - const mainnetSequencer = '0xa4b10ac61E79Ea1e150DF70B8dda53391928fD14' + + const migrationManager = await NitroMigrationManager.deploy( + nitroDeployer, + true, + true + ) + await migrationManager.run( - [mainnetSequencer], - { - confirmPeriodBlocks: await prevRollup.confirmPeriodBlocks(), - extraChallengeTimeBlocks: await prevRollup.extraChallengeTimeBlocks(), - stakeToken: await prevRollup.stakeToken(), - baseStake: await prevRollup.baseStake(), - wasmModuleRoot: wasmModuleRoot, - // CHRIS: TODO: decide who the owner should be - // CHRIS: TODO: shouldnt it be someone different to the proxy admin? - owner: await prevRollup.owner(), - chainId: (await provider.getNetwork()).chainId, - loserStakeEscrow: loserStakeEscrow, - sequencerInboxMaxTimeVariation: { - // CHRIS: TODO: should we change this to the exact POS seconds? probably not yet, we can update it later i guess - // CHRIS: TODO: make sure these are all the values we want - delayBlocks: (60 * 60 * 24) / 15, - futureBlocks: 12, - delaySeconds: 60 * 60 * 24, - futureSeconds: 60 * 60, - }, - }, + nitroDeployer, + proxyAdminSigner, + nitroConfig, + classicConfig, true, true ) diff --git a/packages/arb-bridge-eth/test/nitroMigrationManager.ts b/packages/arb-bridge-eth/test/nitroMigrationManager.ts index 9fa89f6577..5a52d1d316 100644 --- a/packages/arb-bridge-eth/test/nitroMigrationManager.ts +++ b/packages/arb-bridge-eth/test/nitroMigrationManager.ts @@ -1,6 +1,5 @@ import { RollupCreatedEvent } from '../build/types/nitro/RollupCreator' import { BridgeCreator__factory as NitroBridgeCreator__factory } from '../build/types/nitro/factories/BridgeCreator__factory' -import { Bridge__factory as NitroBridge__factory } from '../build/types/nitro/factories/Bridge__factory' import { RollupCreator__factory as NitroRollupCreator__factory } from '../build/types/nitro/factories/RollupCreator__factory' import { RollupCreator as NitroRollupCreator } from '../build/types/nitro/RollupCreator' import { OneStepProver0__factory as NitroOneStepProver0__factory } from '../build/types/nitro/factories/OneStepProver0__factory' @@ -11,10 +10,13 @@ import { OneStepProofEntry__factory as NitroOneStepProofEntry__factory } from '. import { ChallengeManager__factory as NitroChallengeManager__factory } from '../build/types/nitro/factories/ChallengeManager__factory' import { RollupAdminLogic__factory as NitroRollupAdminLogic__factory } from '../build/types/nitro/factories/RollupAdminLogic__factory' import { RollupUserLogic__factory as NitroRollupUserLogic__factory } from '../build/types/nitro/factories/RollupUserLogic__factory' -import { BigNumber, constants, Signer } from 'ethers' +import { ValidatorUtils__factory as NitroValidatorUtils__factory } from '../build/types/nitro/factories/ValidatorUtils__factory' +import { ValidatorWalletCreator__factory as NitroValidatorWalletCreator__factory } from '../build/types/nitro/factories/ValidatorWalletCreator__factory' +import { BigNumber, Signer } from 'ethers' import { Provider } from '@ethersproject/providers' -import { getContractAddress } from 'ethers/lib/utils' +import { getAddress, getContractAddress } from 'ethers/lib/utils' import { + Bridge__factory, Inbox__factory, NitroMigrator, NitroMigrator__factory, @@ -25,73 +27,113 @@ import { SequencerInbox__factory, } from '../build/types' -// CHRIS: TODO: comments up in here +const wait = (ms: number) => + new Promise((resolve, _) => setTimeout(resolve, ms)) + +interface ClassicConfig { + rollupAddr: string + proxyAdminAddr: string + inboxAddr: string + sequencerInboxAddr: string + bridgeAddr: string + rollupEventBridgeAddr: string + outboxV1: string + outboxV2: string +} + export class NitroMigrationManager { private readonly provider: Provider + public static async deploy( + nitroDeployer: Signer, + log: boolean = true, + skipStep3Check: boolean = false + ) { + if (log) + console.log(`Proxy admin owner: ${await nitroDeployer.getAddress()}`) + const nitroMigratorFac = new NitroMigrator__factory(nitroDeployer) + const nitroMigrator = await nitroMigratorFac.deploy() + await nitroMigrator.deployed() + if (log) console.log(`Nitro migrator: ${nitroMigrator.address}`) + return new NitroMigrationManager(nitroMigrator, log, skipStep3Check) + } + public constructor( - public readonly proxyAdminOwner: Signer, - public readonly classicConfig: { - rollupAddr: string - proxyAdminAddr: string - inboxAddr: string - sequencerInboxAddr: string - bridgeAddr: string - rollupEventBridgeAddr: string - outboxV1: string - outboxV2: string // CHRIS: TODO: v2 here? - } + public readonly migrator: NitroMigrator, + public readonly log = true, + public readonly skipStep3Check: boolean = false ) { - if (!proxyAdminOwner.provider) { - throw new Error('No provider attached to deployer signer.') + if (!migrator.provider) { + throw new Error('No provider attached to migrator.') } - this.provider = proxyAdminOwner.provider + + this.provider = migrator.provider } public async run( - classicSequencers: string[], - nitroConfig: Parameters[0], + nitroDeployer: Signer, + classicProxyAdminOwner: Signer, + nitroConfig: Omit< + Parameters[0], + 'owner' + >, + classicConfig: ClassicConfig, destroyAlternatives: boolean, destroyChallenges: boolean ) { - const nitroContracts = await this.deployNitroContracts(nitroConfig) - await this.upgradeClassicContracts() - await this.deployMigrator({ - bridgeAddr: nitroContracts.bridge, - inboxTemplateAddr: nitroContracts.inboxTemplate, - outboxAddr: nitroContracts.outbox, - sequencerInboxAddr: nitroContracts.sequencerInbox, - }) - await this.step0point5() + if (this.log) console.log('Beginning migration') - await this.step1(classicSequencers, { - rollupAddr: nitroContracts.rollup, - bridgeAddr: nitroContracts.bridge, - }) - const nodeNum = await this.step1point5() + const nitroContracts = await this.deployNitroContracts( + nitroDeployer, + nitroConfig + ) + await this.upgradeClassicContracts(classicProxyAdminOwner, classicConfig) + + await this.configureDeployment( + classicProxyAdminOwner, + nitroDeployer, + classicConfig, + nitroContracts.rollup, + nitroContracts.proxyAdmin + ) + + await this.step1() + const nodeNum = await this.getFinalNodeNum() await this.step2(nodeNum, destroyAlternatives, destroyChallenges) - await this.step2point5() - await this.step3() + await this.waitForConfirmedEqualLatest() + await this.step3(nitroDeployer) } private async deployNitroChallengeContracts(signer: Signer) { + if (this.log) console.log(`Deploying nitro challenge contracts`) const oneStepProver0Fac = new NitroOneStepProver0__factory(signer) const oneStepProver0 = await oneStepProver0Fac.deploy() - await oneStepProver0.deployed() const oneStepProverMemoryFac = new NitroOneStepProverMemory__factory(signer) const oneStepProverMemory = await oneStepProverMemoryFac.deploy() - await oneStepProverMemory.deployed() const oneStepProverMathFac = new NitroOneStepProverMath__factory(signer) const oneStepProverMath = await oneStepProverMathFac.deploy() - await oneStepProverMath.deployed() const oneStepProverHostIoFac = new NitroOneStepProverHostIo__factory(signer) const oneStepProverHostIo = await oneStepProverHostIoFac.deploy() + + await oneStepProver0.deployed() + await oneStepProverMemory.deployed() + await oneStepProverMath.deployed() await oneStepProverHostIo.deployed() + if (this.log) { + console.log(`Nitro one step prover 0: ${oneStepProver0.address}`) + console.log( + `Nitro one step prover memory: ${oneStepProverMemory.address}` + ) + console.log(`Nitro one step prover math: ${oneStepProverMath.address}`) + console.log( + `Nitro one step prover host io: ${oneStepProverHostIo.address}` + ) + } const oneStepProofEntryFac = new NitroOneStepProofEntry__factory(signer) const oneStepProofEntry = await oneStepProofEntryFac.deploy( @@ -100,11 +142,18 @@ export class NitroMigrationManager { oneStepProverMath.address, oneStepProverHostIo.address ) - await oneStepProofEntry.deployed() const challengeManagerFac = new NitroChallengeManager__factory(signer) const challengeManager = await challengeManagerFac.deploy() + + await oneStepProofEntry.deployed() await challengeManager.deployed() + if (this.log) { + console.log(`Nitro one step prover entry: ${oneStepProofEntry.address}`) + console.log(`Nitro challenge manager: ${challengeManager.address}`) + } + + if (this.log) console.log(`Deploying nitro challenge contracts complete`) return { oneStepProver0, @@ -117,34 +166,71 @@ export class NitroMigrationManager { } public async deployNitroContracts( - config: Parameters[0] + nitroDeployer: Signer, + config: Omit[0], 'owner'> ) { - const nitroBridgeCreatorFac = new NitroBridgeCreator__factory( - this.proxyAdminOwner + if (this.log) console.log('Deploying nitro contracts') + // the owner should always be our nitro deployer + const ownerConfig = { + ...config, + owner: await nitroDeployer.getAddress(), + } + + // quick check that the owner of the migrator is also the account we'll + // use for deploying here + const migratorOwner = await this.migrator.owner() + const nitroDeployerAddr = await nitroDeployer.getAddress() + if (migratorOwner !== nitroDeployerAddr) { + throw new Error( + `Incorrect owner. Trying to deploy nitro contracts with different owner to migrator owner. ${migratorOwner}:${nitroDeployerAddr}` + ) + } + + const nitroValidatorUtilsFac = new NitroValidatorUtils__factory( + nitroDeployer ) + const nitroValidatorUtils = await nitroValidatorUtilsFac.deploy() + + const nitroValidatorWalletCreatorFac = + new NitroValidatorWalletCreator__factory(nitroDeployer) + const nitroValidatorWalletCreator = + await nitroValidatorWalletCreatorFac.deploy() + + const nitroBridgeCreatorFac = new NitroBridgeCreator__factory(nitroDeployer) const nitroBridgeCreator = await nitroBridgeCreatorFac.deploy() - await nitroBridgeCreator.deployed() - const nitroRollupCreatorFac = new NitroRollupCreator__factory( - this.proxyAdminOwner - ) + const nitroRollupCreatorFac = new NitroRollupCreator__factory(nitroDeployer) const nitroRollupCreator = await nitroRollupCreatorFac.deploy() - await nitroRollupCreator.deployed() const nitroRollupAdminLogicFac = new NitroRollupAdminLogic__factory( - this.proxyAdminOwner + nitroDeployer ) const nitroRollupAdminLogic = await nitroRollupAdminLogicFac.deploy() - await nitroRollupAdminLogic.deployed() const nitroRollupUserLogicFac = new NitroRollupUserLogic__factory( - this.proxyAdminOwner + nitroDeployer ) const nitroRollupUserLogic = await nitroRollupUserLogicFac.deploy() + + await nitroValidatorUtils.deployed() + await nitroValidatorWalletCreator.deployed() + await nitroBridgeCreator.deployed() + await nitroRollupCreator.deployed() + await nitroRollupAdminLogic.deployed() await nitroRollupUserLogic.deployed() + if (this.log) { + console.log(`Nitro validator utils: ${nitroValidatorUtils.address}`) + console.log( + `Nitro validator wallet creator: ${nitroValidatorWalletCreator.address}` + ) + console.log(`Nitro bridge creator: ${nitroBridgeCreator.address}`) + console.log(`Nitro rollup creator: ${nitroRollupCreator.address}`) + console.log(`Nitro rollup admin logic: ${nitroRollupAdminLogic.address}`) + console.log(`Nitro rollup user logic: ${nitroRollupUserLogic.address}`) + } const challengeContracts = await this.deployNitroChallengeContracts( - this.proxyAdminOwner + nitroDeployer ) await ( await nitroRollupCreator.setTemplates( @@ -152,9 +238,12 @@ export class NitroMigrationManager { challengeContracts.oneStepProofEntry.address, challengeContracts.challengeManager.address, nitroRollupAdminLogic.address, - nitroRollupUserLogic.address + nitroRollupUserLogic.address, + nitroValidatorUtils.address, + nitroValidatorWalletCreator.address ) ).wait() + if (this.log) console.log(`Nitro templates set`) const nonce = await this.provider.getTransactionCount( nitroRollupCreator.address @@ -165,24 +254,17 @@ export class NitroMigrationManager { }) const createRollupTx = await nitroRollupCreator.createRollup( - config, + ownerConfig, expectedRollupAddress ) - // CHRIS: TODO: quite a cool idea would be to figure out at compile - // time what possible events could be emitted from a given tx? is that even possible, - // I guess not. So how could we do it? we cant - - // CHRIS: we're deploying a new proxy admin in createRollup - // CHRIS: this will mean we actually have 2 proxy admins in the system post nitro - // CHRIS: we should probably transfer ownership so that they all have the same proxy admin const createRollupReceipt = await createRollupTx.wait() const rollupCreatedEventArgs = createRollupReceipt.logs .filter( l => l.topics[0] === nitroRollupCreator.interface.getEventTopic( - 'RollupCreated(address,address,address,address,address)' + 'RollupCreated(address indexed,address,address,address,address)' ) ) .map( @@ -196,178 +278,220 @@ export class NitroMigrationManager { rollupCreatedEventArgs.rollupAddress ) + if (this.log) { + console.log(`Nitro rollup created`) + console.log(`Nitro inbox: ${rollupCreatedEventArgs.inboxAddress}`) + console.log(`Nitro rollup: ${rollupCreatedEventArgs.rollupAddress}`) + console.log(`Nitro bridge ${rollupCreatedEventArgs.bridge}`) + console.log( + `Nitro inbox template: ${await nitroBridgeCreator.inboxTemplate()}` + ) + console.log(`Nitro outbox: ${await rollupUser.outbox()}`) + console.log( + `Nitro sequencer inbox: ${rollupCreatedEventArgs.sequencerInbox}` + ) + console.log(`Nitro proxy admin: ${rollupCreatedEventArgs.adminProxy}`) + } + + if (this.log) console.log('Deploying nitro contracts complete') + return { + inbox: rollupCreatedEventArgs.inboxAddress, rollup: rollupCreatedEventArgs.rollupAddress, - bridge: rollupCreatedEventArgs.delayedBridge, + bridge: rollupCreatedEventArgs.bridge, inboxTemplate: await nitroBridgeCreator.inboxTemplate(), outbox: await rollupUser.outbox(), sequencerInbox: rollupCreatedEventArgs.sequencerInbox, + proxyAdmin: rollupCreatedEventArgs.adminProxy, } } - public async upgradeClassicContracts() { - const proxyAdminContractFac = new ProxyAdmin__factory(this.proxyAdminOwner) - const proxyAdmin = proxyAdminContractFac.attach( - this.classicConfig.proxyAdminAddr + public async upgradeClassicContracts( + classicProxyAdminOwner: Signer, + classicConfig: { + proxyAdminAddr: string + inboxAddr: string + bridgeAddr: string + sequencerInboxAddr: string + rollupAddr: string + } + ) { + if (this.log) console.log(`Upgrading classic contracts`) + const proxyAdmin = ProxyAdmin__factory.connect( + classicConfig.proxyAdminAddr, + classicProxyAdminOwner ) - const inboxFac = new Inbox__factory(this.proxyAdminOwner) + const inboxFac = new Inbox__factory(classicProxyAdminOwner) const newInboxImp = await inboxFac.deploy() await newInboxImp.deployed() - await proxyAdmin - // CHRIS: TODO: this should be upgradeAndCall - .upgrade(this.classicConfig.inboxAddr, newInboxImp.address) + await proxyAdmin.upgrade(classicConfig.inboxAddr, newInboxImp.address) + if (this.log) + console.log(`Classic inbox upgraded: ${classicConfig.inboxAddr}`) + + const bridgeFac = new Bridge__factory(classicProxyAdminOwner) + const newBridgeImp = await bridgeFac.deploy() + await newBridgeImp.deployed() + await proxyAdmin.upgrade(classicConfig.bridgeAddr, newBridgeImp.address) + if (this.log) + console.log(`Classic bridge upgraded: ${classicConfig.bridgeAddr}`) // -- sequencer inbox - const sequencerInboxFac = new SequencerInbox__factory(this.proxyAdminOwner) + const sequencerInboxFac = new SequencerInbox__factory( + classicProxyAdminOwner + ) const newSequencerInboxImp = await sequencerInboxFac.deploy() await newSequencerInboxImp.deployed() const sequencerInboxPostUpdgrade = newSequencerInboxImp.interface.encodeFunctionData('postUpgradeInit') await proxyAdmin.upgradeAndCall( - this.classicConfig.sequencerInboxAddr, + classicConfig.sequencerInboxAddr, newSequencerInboxImp.address, sequencerInboxPostUpdgrade ) + if (this.log) + console.log( + `Classic sequencer inbox upgraded: ${classicConfig.sequencerInboxAddr}` + ) // -- rollup - const rollupFac = new Rollup__factory(this.proxyAdminOwner) - const prevRollup = rollupFac.attach(this.classicConfig.rollupAddr) + const rollupFac = new Rollup__factory(classicProxyAdminOwner) + const prevRollup = rollupFac.attach(classicConfig.rollupAddr) const confirmPeriodBlocks = await prevRollup.confirmPeriodBlocks() const newRollupImp = await rollupFac.deploy(confirmPeriodBlocks) await newRollupImp.deployed() const rollupPostUpgrade = newRollupImp.interface.encodeFunctionData('postUpgradeInit') await proxyAdmin.upgradeAndCall( - this.classicConfig.rollupAddr, + classicConfig.rollupAddr, newRollupImp.address, rollupPostUpgrade ) + if (this.log) + console.log(`Classic rollup upgraded: ${classicConfig.rollupAddr}`) // -- rollup user - const rollupUserFac = new RollupUserFacet__factory(this.proxyAdminOwner) + const rollupUserFac = new RollupUserFacet__factory(classicProxyAdminOwner) const newRollupUserImp = await rollupUserFac.deploy() await newRollupUserImp.deployed() + if (this.log) + console.log( + `Classic rollup user logic deployed: ${newRollupUserImp.address}` + ) // -- rollup admin - const rollupAdminFac = new RollupAdminFacet__factory(this.proxyAdminOwner) + const rollupAdminFac = new RollupAdminFacet__factory(classicProxyAdminOwner) const newRollupAdminImp = await rollupAdminFac.deploy() await newRollupAdminImp.deployed() + if (this.log) + console.log( + `Classic rollup admin logic deployed: ${newRollupAdminImp.address}` + ) const rollupAdmin = rollupAdminFac - .attach(this.classicConfig.rollupAddr) - .connect(this.proxyAdminOwner) + .attach(classicConfig.rollupAddr) + .connect(classicProxyAdminOwner) await ( await rollupAdmin.setFacets( newRollupAdminImp.address, newRollupUserImp.address ) ).wait() + if (this.log) console.log(`Classic rollup facets set`) + if (this.log) console.log(`Upgrading classic contracts complete`) return { - inbox: inboxFac.attach(this.classicConfig.inboxAddr), sequencerInbox: sequencerInboxFac.attach( - this.classicConfig.sequencerInboxAddr + classicConfig.sequencerInboxAddr ), - rollupAdmin: rollupAdminFac.attach(this.classicConfig.rollupAddr), + rollupAdmin: rollupAdminFac.attach(classicConfig.rollupAddr), } } - // CHRIS: TODO: check for the presence of this everywhere - private nitroMigrator?: NitroMigrator - - public async deployMigrator(nitroConfig: { - bridgeAddr: string - outboxAddr: string - sequencerInboxAddr: string - inboxTemplateAddr: string - }) { - const nitroMigratorFac = new NitroMigrator__factory(this.proxyAdminOwner) - this.nitroMigrator = await nitroMigratorFac.deploy( - this.classicConfig.inboxAddr, - this.classicConfig.sequencerInboxAddr, - this.classicConfig.bridgeAddr, - this.classicConfig.rollupEventBridgeAddr, - this.classicConfig.outboxV1, - this.classicConfig.outboxV2, - this.classicConfig.rollupAddr, - nitroConfig.bridgeAddr, - nitroConfig.outboxAddr, - nitroConfig.sequencerInboxAddr, - nitroConfig.inboxTemplateAddr - ) + public async configureDeployment( + classicProxyAdminOwner: Signer, + nitroDeployer: Signer, + classicConfig: ClassicConfig, + nitroRollupAddr: string, + nitroProxyAdmin: string + ) { + this.provider + if ( + (await this.provider.getCode(nitroRollupAddr)).length <= 2 || + (await this.provider.getCode(nitroProxyAdmin)).length <= 2 + ) { + throw new Error( + 'Could not configure deployment. Nitro contracts not deployed.' + ) + } + if (this.log) console.log('Configuring deployment') - return this.nitroMigrator - } + const classicRollupAdmin = RollupAdminFacet__factory.connect( + classicConfig.rollupAddr, + classicProxyAdminOwner + ) + if ((await classicRollupAdmin.owner()) != this.migrator.address) { + if (this.log) console.log('Classic rollup, setting owner to migrator') + await (await classicRollupAdmin.setOwner(this.migrator.address)).wait() + } - public async step0point5() { - if (!this.nitroMigrator) - throw new Error('Transfer ownership called before migrator deployed.') + const classicProxyAdmin = ProxyAdmin__factory.connect( + classicConfig.proxyAdminAddr, + classicProxyAdminOwner + ) + if ((await classicProxyAdmin.owner()) != this.migrator.address) { + if (this.log) + console.log('Classic proxy admin, setting owner to migrator') + await ( + await classicProxyAdmin.transferOwnership(this.migrator.address) + ).wait() + } - const rollupAdminFac = new RollupAdminFacet__factory(this.proxyAdminOwner) - const rollupAdmin = rollupAdminFac - .attach(this.classicConfig.rollupAddr) - .connect(this.proxyAdminOwner) + const nitroRollupAdmin = NitroRollupAdminLogic__factory.connect( + nitroRollupAddr, + nitroDeployer + ) + const nitroRollupAdminOwner = await this.getProxyAdmin(nitroRollupAddr) + if (nitroRollupAdminOwner != this.migrator.address) { + if (this.log) console.log('Nitro rollup, setting owner to migrator') + await (await nitroRollupAdmin.setOwner(this.migrator.address)).wait() + } await ( - await rollupAdmin.transferOwnership( - this.classicConfig.bridgeAddr, - this.nitroMigrator.address + await this.migrator.configureDeployment( + classicConfig.inboxAddr, + classicConfig.sequencerInboxAddr, + classicConfig.bridgeAddr, + classicConfig.rollupEventBridgeAddr, + classicConfig.outboxV1, + classicConfig.outboxV2, + classicConfig.rollupAddr, + classicConfig.proxyAdminAddr, + nitroRollupAddr, + nitroProxyAdmin ) ).wait() - await (await rollupAdmin.setOwner(this.nitroMigrator.address)).wait() + if (this.log) console.log('Configure deployment complete') } - // CHRIS: TODO: ensure these functions are called in the correct order? - - // CHRIS: TODO: put this classic config in the constructor - public async step1( - classicSequencers: string[], - nitroConfig: { rollupAddr: string; bridgeAddr: string } - ) { - if (!this.nitroMigrator) - throw new Error('Step 1 called before migrator deployed.') - - // CHRIS: TODO: should nitro contracts be added to dev or prod dependencies? - - const nitroBridgeFac = new NitroBridge__factory(this.proxyAdminOwner) - const nitroBridge = nitroBridgeFac.attach(nitroConfig.bridgeAddr) - const enqueueDelayedMessage = - await nitroBridge.interface.encodeFunctionData('enqueueDelayedMessage', [ - 0, - this.nitroMigrator.address, - constants.HashZero, - ]) - - // CHRIS: TODO: remove this!!!! we only do this whilst we wait for a receive function to be added to the - // set the classic bridge as a inbox on the nitro bridge - const nitroRollupAdmin = new NitroRollupAdminLogic__factory( - this.proxyAdminOwner - ).attach(nitroConfig.rollupAddr) - await ( - await nitroRollupAdmin.setInbox(this.classicConfig.bridgeAddr, true) - ).wait() - - await ( - await this.nitroMigrator.functions.nitroStep1( - classicSequencers, - enqueueDelayedMessage - ) - ).wait() - - // reset the bridge - // // CHRIS: TODO: remove this when we remove teh setInbox(true) above - await ( - await nitroRollupAdmin.setInbox(this.classicConfig.bridgeAddr, false) - ).wait() + public async step1() { + if (this.log) console.log('Executing migration step 1') + await (await this.migrator.functions.nitroStep1()).wait() + if (this.log) console.log('Executing migration step 1 complete') } - public async step1point5(): Promise { - // Step 1.5: check for lingering stuff + public async getFinalNodeNum(): Promise { + // CHRIS: TODO: Do we have any unredeemed retryables? - // - Do we have any unredeemed retryables? - // - Probably. They will get copied over - throw new Error('Not implemented.') + const rollupAddr = await this.migrator.rollup() + const rollupAdmin = RollupAdminFacet__factory.connect( + rollupAddr, + this.provider + ) + + const finalNodeNum = await rollupAdmin.latestNodeCreated() + if (this.log) console.log(`Final node num: ${finalNodeNum.toNumber()}`) + return finalNodeNum } public async step2( @@ -375,33 +499,90 @@ export class NitroMigrationManager { destroyAlternatives: boolean, destroyChallenges: boolean ) { - if (!this.nitroMigrator) - throw new Error('Step 2 called before migrator deployed.') - - // CHRIS: TODO: pass these args through + if (this.log) console.log('Executing migration step 2') await ( - await this.nitroMigrator.nitroStep2( + await this.migrator.nitroStep2( finalNodeNum, destroyAlternatives, destroyChallenges ) ).wait() + if (this.log) console.log('Executing migration step 2') + } + + public async waitForConfirmedEqualLatest() { + if (this.skipStep3Check) return + + // wait until the node has confirmed the remaining nodes + const rollupAddr = await this.migrator.rollup() + const rollup = RollupUserFacet__factory.connect(rollupAddr, this.provider) + while (true) { + const latestConfirmed = await rollup.latestConfirmed() + const latestNodeCreated = await rollup.latestNodeCreated() + + console.log( + `Waiting for latestConfirmed: ${latestConfirmed.toNumber()} to equal latestNodeCreated: ${latestNodeCreated.toNumber()}.` + ) + + if (latestConfirmed.eq(latestNodeCreated)) break + await wait(30000) + } } - public async step2point5() { - // Step 2.5: check for lingering stuff + public async step3(nitroDeployer: Signer) { + if (this.log) console.log('Executing migration step 3') + await (await this.migrator.nitroStep3(this.skipStep3Check)).wait() + + // CHRIS: TODO: should we check the ownership of all contracts? - // - Do we have any unexecuted exits? - // - Probably. They should be easy to handle - // - Jason (data science) joining can analyse the number of L2 to L1 txs not executed - // - Check no ongoing challenges - throw new Error('Not implemented.') + const nitroProxyAdminOwner = await nitroDeployer.getAddress() + + // check that ownership was successfully relinquished + const classicRollupAdmin = RollupAdminFacet__factory.connect( + await this.migrator.rollup(), + this.provider + ) + + if ((await classicRollupAdmin.owner()) != nitroProxyAdminOwner) { + throw new Error( + `Classic rollup owner is not nitro proxy admin owner. ${await classicRollupAdmin.owner()}:${nitroProxyAdminOwner}` + ) + } + + const classicProxyAdminAddr = await this.migrator.classicProxyAdmin() + const classicProxyAdmin = ProxyAdmin__factory.connect( + classicProxyAdminAddr, + this.provider + ) + if ((await classicProxyAdmin.owner()) != nitroProxyAdminOwner) { + throw new Error( + `Classic proxy admin owner is not nitro proxy admin owner ${await classicProxyAdmin.owner()}:${nitroProxyAdminOwner}` + ) + } + + const nitroProxyAdminAddr = await this.migrator.nitroProxyAdmin() + const nitroRollup = await this.migrator.nitroRollup() + const nitroAdmin = await this.getProxyAdmin(nitroRollup) + if (nitroAdmin != nitroProxyAdminAddr) { + throw new Error( + `Nitro rollup admin is not nitro proxy admin. ${nitroAdmin}:${nitroProxyAdminAddr}` + ) + } + if (this.log) console.log('Executing migration step 3 complete') } - public async step3() { - if (!this.nitroMigrator) - throw new Error('Step 3 called before migrator deployed.') + private async getProxyAdmin(proxyAddress: string) { + const ADMIN_SLOT = + '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103' - await (await this.nitroMigrator.nitroStep3()).wait() + const nitroAdmin = await this.provider.getStorageAt( + proxyAddress, + ADMIN_SLOT + ) + return getAddress( + nitroAdmin.length > 42 + ? '0x' + nitroAdmin.substring(nitroAdmin.length - 40) + : nitroAdmin + ) } } diff --git a/packages/arb-upgrades/ethBridgeTasks.ts b/packages/arb-upgrades/ethBridgeTasks.ts index ab9a4b26e6..3477f522bd 100644 --- a/packages/arb-upgrades/ethBridgeTasks.ts +++ b/packages/arb-upgrades/ethBridgeTasks.ts @@ -270,7 +270,7 @@ task('migration-step-3', 'run nitro migration step 3') Migrator = Migrator.connect(hre.ethers.provider.getSigner(owner)) console.log('Running migration step 3') - const receipt = await (await Migrator.nitroStep3()).wait() + const receipt = await (await Migrator.nitroStep3(false)).wait() console.log('Ran migration step 3:', receipt) }) From 82f88508e6be6137e18e0d731c8cc41c3bd6a2ca Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Thu, 14 Jul 2022 10:55:04 +0200 Subject: [PATCH 2/2] Reverted removed comment --- packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol b/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol index 35df68b4a7..85a9213adb 100644 --- a/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol +++ b/packages/arb-bridge-eth/contracts/bridge/NitroMigrator.sol @@ -227,6 +227,7 @@ contract NitroMigrator is Ownable, IMessageProvider { bridge.setOutbox(address(this), true); { uint256 bal = address(bridge).balance; + // TODO: import nitro contracts and use interface (bool success, ) = bridge.executeCall( address(nitroBridge), bal,