You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
13 KiB
468 lines
13 KiB
3 years ago
|
const { provider } = waffle;
|
||
|
const { expect } = require('chai');
|
||
|
const testUtils = require('../utils');
|
||
|
const { domainsToTestConfigs } = require('./generateTestChainConfigs');
|
||
|
const { formatCall, sendFromSigner } = require('./crossChainTestUtils');
|
||
|
const {
|
||
|
deployMultipleChains,
|
||
|
getHome,
|
||
|
getGovernanceRouter,
|
||
|
getUpdaterManager,
|
||
|
} = require('./deployCrossChainTest');
|
||
|
|
||
|
async function expectNotInRecovery(
|
||
|
updaterManager,
|
||
|
recoveryManager,
|
||
|
randomSigner,
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
home,
|
||
|
) {
|
||
|
expect(await governanceRouter.inRecovery()).to.be.false;
|
||
|
|
||
|
// Format optics call message
|
||
|
const call = await formatCall(updaterManager, 'setUpdater', [
|
||
|
randomSigner.address,
|
||
|
]);
|
||
|
|
||
|
// Expect that Governor *CAN* Call Local & Call Remote
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'callLocal', [[call]]),
|
||
|
)
|
||
|
.to.emit(home, 'NewUpdater')
|
||
|
.withArgs(randomSigner.address);
|
||
|
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'callRemote', [2000, [call]]),
|
||
|
).to.emit(home, 'Dispatch');
|
||
|
|
||
|
// set xApp Connection Manager
|
||
|
const xAppConnectionManager = await governanceRouter.xAppConnectionManager();
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'setXAppConnectionManager', [
|
||
|
randomSigner.address,
|
||
|
]),
|
||
|
).to.not.be.reverted;
|
||
|
// reset xApp Connection Manager to actual contract
|
||
|
await sendFromSigner(governor, governanceRouter, 'setXAppConnectionManager', [
|
||
|
xAppConnectionManager,
|
||
|
]);
|
||
|
|
||
|
// set Router Locally
|
||
|
const otherDomain = 2000;
|
||
|
const previousRouter = await governanceRouter.routers(otherDomain);
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'setRouterLocal', [
|
||
|
2000,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
]),
|
||
|
)
|
||
|
.to.emit(governanceRouter, 'SetRouter')
|
||
|
.withArgs(
|
||
|
otherDomain,
|
||
|
previousRouter,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
);
|
||
|
|
||
|
// Expect that Recovery Manager CANNOT Call Local OR Call Remote
|
||
|
// cannot dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'callLocal', [[call]]),
|
||
|
).to.be.revertedWith('! called by governor');
|
||
|
|
||
|
// cannot dispatch call to remote governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'callRemote', [
|
||
|
2000,
|
||
|
[call],
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by governor');
|
||
|
|
||
|
// cannot set xAppConnectionManager
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'setXAppConnectionManager',
|
||
|
[randomSigner.address],
|
||
|
),
|
||
|
).to.be.revertedWith('! called by governor');
|
||
|
|
||
|
// cannot set Router
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'setRouterLocal', [
|
||
|
2000,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by governor');
|
||
|
}
|
||
|
|
||
|
async function expectOnlyRecoveryManagerCanTransferRole(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
) {
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'transferRecoveryManager', [
|
||
|
randomSigner.address,
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(randomSigner, governanceRouter, 'transferRecoveryManager', [
|
||
|
randomSigner.address,
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'transferRecoveryManager',
|
||
|
[randomSigner.address],
|
||
|
),
|
||
|
)
|
||
|
.to.emit(governanceRouter, 'TransferRecoveryManager')
|
||
|
.withArgs(recoveryManager.address, randomSigner.address);
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(randomSigner, governanceRouter, 'transferRecoveryManager', [
|
||
|
recoveryManager.address,
|
||
|
]),
|
||
|
)
|
||
|
.to.emit(governanceRouter, 'TransferRecoveryManager')
|
||
|
.withArgs(randomSigner.address, recoveryManager.address);
|
||
|
}
|
||
|
|
||
|
async function expectOnlyRecoveryManagerCanExitRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
) {
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'exitRecovery', []),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(randomSigner, governanceRouter, 'exitRecovery', []),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'exitRecovery', []),
|
||
|
)
|
||
|
.to.emit(governanceRouter, 'ExitRecovery')
|
||
|
.withArgs(recoveryManager.address);
|
||
|
}
|
||
|
|
||
|
async function expectOnlyRecoveryManagerCanInitiateRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
) {
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'initiateRecoveryTimelock', []),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
randomSigner,
|
||
|
governanceRouter,
|
||
|
'initiateRecoveryTimelock',
|
||
|
[],
|
||
|
),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
expect(await governanceRouter.recoveryActiveAt()).to.equal(0);
|
||
|
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'initiateRecoveryTimelock',
|
||
|
[],
|
||
|
),
|
||
|
).to.emit(governanceRouter, 'InitiateRecovery');
|
||
|
|
||
|
expect(await governanceRouter.recoveryActiveAt()).to.not.equal(0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Deploy the full Optics suite on two chains
|
||
|
*/
|
||
|
describe('RecoveryManager', async () => {
|
||
|
const domains = [1000, 2000];
|
||
|
const domain = 1000;
|
||
|
const walletProvider = new testUtils.WalletProvider(provider);
|
||
|
const [
|
||
|
governor,
|
||
|
recoveryManager,
|
||
|
randomSigner,
|
||
|
] = walletProvider.getWalletsPersistent(5);
|
||
|
|
||
|
let governanceRouter, home, updaterManager, chainDetails;
|
||
|
|
||
|
before(async () => {
|
||
|
// generate TestChainConfigs for the given domains
|
||
|
const configs = await domainsToTestConfigs(
|
||
|
domains,
|
||
|
recoveryManager.address,
|
||
|
);
|
||
|
|
||
|
// deploy the entire Optics suite on each chain
|
||
|
chainDetails = await deployMultipleChains(configs);
|
||
|
|
||
|
// get the governance router
|
||
|
governanceRouter = getGovernanceRouter(chainDetails, domain);
|
||
|
// transfer governorship to the governor signer
|
||
|
await governanceRouter.transferGovernor(domain, governor.address);
|
||
|
|
||
|
home = getHome(chainDetails, domain);
|
||
|
|
||
|
updaterManager = getUpdaterManager(chainDetails, domain);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Initiated: Timelock has not been set', async () => {
|
||
|
expect(await governanceRouter.recoveryActiveAt()).to.equal(0);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Initiated: Cannot Exit Recovery yet', async () => {
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'exitRecovery', []),
|
||
|
).to.be.revertedWith('recovery not initiated');
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Initiated: Not in Recovery (Governor CAN Call Local & Remote; Recovery Manager CANNOT Call either)', async () => {
|
||
|
await expectNotInRecovery(
|
||
|
updaterManager,
|
||
|
recoveryManager,
|
||
|
randomSigner,
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
home,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Initiated: ONLY RecoveryManager can transfer RecoveryManager role', async () => {
|
||
|
await expectOnlyRecoveryManagerCanTransferRole(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Initiated: ONLY RecoveryManager can Initiate Recovery', async () => {
|
||
|
await expectOnlyRecoveryManagerCanInitiateRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Active: CANNOT Initiate Recovery Twice', async () => {
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'initiateRecoveryTimelock',
|
||
|
[],
|
||
|
),
|
||
|
).to.be.revertedWith('recovery already initiated');
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Active: Not in Recovery (Governor CAN Call Local & Remote; Recovery Manager CANNOT Call either)', async () => {
|
||
|
await expectNotInRecovery(
|
||
|
updaterManager,
|
||
|
recoveryManager,
|
||
|
randomSigner,
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
home,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Active: ONLY RecoveryManager can transfer RecoveryManager role', async () => {
|
||
|
await expectOnlyRecoveryManagerCanTransferRole(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Active: ONLY RecoveryManager can Exit Recovery', async () => {
|
||
|
await expectOnlyRecoveryManagerCanExitRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Before Recovery Active: ONLY RecoveryManager can Initiate Recovery (CAN initiate a second time)', async () => {
|
||
|
await expectOnlyRecoveryManagerCanInitiateRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: inRecovery becomes true when timelock expires', async () => {
|
||
|
// increase timestamp on-chain
|
||
|
const timelock = await governanceRouter.recoveryTimelock();
|
||
|
await testUtils.increaseTimestampBy(provider, timelock.toNumber());
|
||
|
expect(await governanceRouter.inRecovery()).to.be.true;
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: RecoveryManager CAN call local', async () => {
|
||
|
// Format optics call message
|
||
|
const call = await formatCall(updaterManager, 'setUpdater', [
|
||
|
randomSigner.address,
|
||
|
]);
|
||
|
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'callLocal', [[call]]),
|
||
|
)
|
||
|
.to.emit(home, 'NewUpdater')
|
||
|
.withArgs(randomSigner.address);
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: RecoveryManager CANNOT call remote', async () => {
|
||
|
// Format optics call message
|
||
|
const call = await formatCall(updaterManager, 'setUpdater', [
|
||
|
randomSigner.address,
|
||
|
]);
|
||
|
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'callRemote', [
|
||
|
2000,
|
||
|
[call],
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by governor');
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: RecoveryManager CAN set xAppConnectionManager', async () => {
|
||
|
// set xApp Connection Manager
|
||
|
const xAppConnectionManager = await governanceRouter.xAppConnectionManager();
|
||
|
await expect(
|
||
|
sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'setXAppConnectionManager',
|
||
|
[randomSigner.address],
|
||
|
),
|
||
|
).to.not.be.reverted;
|
||
|
// reset xApp Connection Manager to actual contract
|
||
|
await sendFromSigner(
|
||
|
recoveryManager,
|
||
|
governanceRouter,
|
||
|
'setXAppConnectionManager',
|
||
|
[xAppConnectionManager],
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: RecoveryManager CAN set Router locally', async () => {
|
||
|
const otherDomain = 2000;
|
||
|
const previousRouter = await governanceRouter.routers(otherDomain);
|
||
|
await expect(
|
||
|
sendFromSigner(recoveryManager, governanceRouter, 'setRouterLocal', [
|
||
|
2000,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
]),
|
||
|
)
|
||
|
.to.emit(governanceRouter, 'SetRouter')
|
||
|
.withArgs(
|
||
|
otherDomain,
|
||
|
previousRouter,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: Governor CANNOT call local OR remote', async () => {
|
||
|
// Format optics call message
|
||
|
const call = await formatCall(updaterManager, 'setUpdater', [
|
||
|
randomSigner.address,
|
||
|
]);
|
||
|
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'callLocal', [[call]]),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
|
||
|
// dispatch call on local governorRouter
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'callRemote', [2000, [call]]),
|
||
|
).to.be.revertedWith('in recovery');
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: Governor CANNOT set xAppConnectionManager', async () => {
|
||
|
// cannot set xAppConnectionManager
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'setXAppConnectionManager', [
|
||
|
randomSigner.address,
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: Governor CANNOT set Router locally', async () => {
|
||
|
// cannot set Router
|
||
|
await expect(
|
||
|
sendFromSigner(governor, governanceRouter, 'setRouterLocal', [
|
||
|
2000,
|
||
|
optics.ethersAddressToBytes32(randomSigner.address),
|
||
|
]),
|
||
|
).to.be.revertedWith('! called by recovery manager');
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: ONLY RecoveryManager can transfer RecoveryManager role', async () => {
|
||
|
await expectOnlyRecoveryManagerCanTransferRole(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Recovery Active: ONLY RecoveryManager can Exit Recovery', async () => {
|
||
|
await expectOnlyRecoveryManagerCanExitRecovery(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Exited Recovery: Timelock is deleted', async () => {
|
||
|
expect(await governanceRouter.recoveryActiveAt()).to.equal(0);
|
||
|
});
|
||
|
|
||
|
it('Exited Recovery: Not in Recovery (Governor CAN Call Local & Remote; Recovery Manager CANNOT Call either)', async () => {
|
||
|
await expectNotInRecovery(
|
||
|
updaterManager,
|
||
|
recoveryManager,
|
||
|
randomSigner,
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
home,
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it('Exited Recovery: ONLY RecoveryManager can transfer RecoveryManager role', async () => {
|
||
|
await expectOnlyRecoveryManagerCanTransferRole(
|
||
|
governor,
|
||
|
governanceRouter,
|
||
|
randomSigner,
|
||
|
recoveryManager,
|
||
|
);
|
||
|
});
|
||
|
});
|