The home for Hyperlane core contracts, sdk packages, and other infrastructure
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.
 
 
 
 
 
 
hyperlane-monorepo/solidity/abacus-core/test/cross-chain/governanceRouter.test.ts

392 lines
12 KiB

import { ethers, abacus } from 'hardhat';
import { expect } from 'chai';
import { formatCall, formatAbacusMessage } from './utils';
import { increaseTimestampBy, UpgradeTestHelpers } from '../utils';
import { Validator } from '../lib/core';
import { Address, Signer } from '../lib/types';
import { AbacusDeployment } from '../lib/AbacusDeployment';
import { GovernanceDeployment } from '../lib/GovernanceDeployment';
import {
MysteryMathV2__factory,
TestReplica,
TestReplica__factory,
TestRecipient__factory,
TestGovernanceRouter,
Replica,
Home,
} from '../../typechain';
const helpers = require('../../../../vectors/proof.json');
const governorDomain = 1000;
const nonGovernorDomain = 2000;
const thirdDomain = 3000;
const domains = [governorDomain, nonGovernorDomain, thirdDomain];
const processGas = 850000;
const reserveGas = 15000;
const nullRoot = '0x' + '00'.repeat(32);
/*
* Deploy the full Abacus suite on two chains
*/
describe('GovernanceRouter', async () => {
let abacusDeployment: AbacusDeployment;
let governanceDeployment: GovernanceDeployment;
let signer: Signer,
secondSigner: Signer,
thirdRouter: Signer,
firstGovernor: Address,
secondGovernor: Address,
governorRouter: TestGovernanceRouter,
nonGovernorRouter: TestGovernanceRouter,
governorHome: Home,
governorReplicaOnNonGovernorChain: TestReplica,
nonGovernorReplicaOnGovernorChain: TestReplica,
validator: Validator;
async function expectGovernor(
governanceRouter: TestGovernanceRouter,
expectedGovernorDomain: number,
expectedGovernor: Address,
) {
expect(await governanceRouter.governorDomain()).to.equal(
expectedGovernorDomain,
);
expect(await governanceRouter.governor()).to.equal(expectedGovernor);
}
before(async () => {
[thirdRouter, signer, secondSigner] = await ethers.getSigners();
validator = await Validator.fromSigner(signer, governorDomain);
});
beforeEach(async () => {
abacusDeployment = await abacus.deployment.fromDomains(domains, signer);
governanceDeployment = await GovernanceDeployment.fromAbacusDeployment(
abacusDeployment,
signer,
);
firstGovernor = await signer.getAddress();
secondGovernor = await secondSigner.getAddress();
governorRouter = governanceDeployment.router(governorDomain);
nonGovernorRouter = governanceDeployment.router(nonGovernorDomain);
governorReplicaOnNonGovernorChain = abacusDeployment.replica(
nonGovernorDomain,
governorDomain,
);
nonGovernorReplicaOnGovernorChain = abacusDeployment.replica(
governorDomain,
nonGovernorDomain,
);
governorHome = abacusDeployment.home(governorDomain);
});
// NB: must be first test for message proof
it('Sends cross-chain message to upgrade contract', async () => {
const upgradeUtils = new UpgradeTestHelpers();
// get upgradeBeaconController
const ubc = abacusDeployment.ubc(nonGovernorDomain);
// Transfer ownership of the UBC to governance.
await ubc.transferOwnership(nonGovernorRouter.address);
const mysteryMath = await upgradeUtils.deployMysteryMathUpgradeSetup(
signer,
ubc,
);
// expect results before upgrade
await upgradeUtils.expectMysteryMathV1(mysteryMath.proxy);
// Deploy Implementation 2
const factory2 = new MysteryMathV2__factory(signer);
const implementation2 = await factory2.deploy();
// Format abacus call message
const call = await formatCall(ubc, 'upgrade', [
mysteryMath.beacon.address,
implementation2.address,
]);
// dispatch call on local governorRouter
let tx = await governorRouter.callRemote(nonGovernorDomain, [call]);
await abacusDeployment.processMessagesFromDomain(governorDomain);
// test implementation was upgraded
await upgradeUtils.expectMysteryMathV2(mysteryMath.proxy);
});
it('Rejects message from unenrolled replica', async () => {
const replicaFactory = new TestReplica__factory(signer);
const unenrolledReplica = await replicaFactory.deploy(
nonGovernorDomain,
processGas,
reserveGas,
);
// The ValdiatorManager is unused in this test, but needs to be a contract.
await unenrolledReplica.initialize(
thirdDomain,
unenrolledReplica.address,
nullRoot,
0,
);
// Create TransferGovernor message
const transferGovernorMessage = abacus.governance.formatTransferGovernor(
thirdDomain,
abacus.ethersAddressToBytes32(secondGovernor),
);
const abacusMessage = await formatAbacusMessage(
unenrolledReplica,
governorRouter,
nonGovernorRouter,
transferGovernorMessage,
);
// Expect replica processing to fail when nonGovernorRouter reverts in handle
let success = await unenrolledReplica.callStatic.testProcess(abacusMessage);
expect(success).to.be.false;
});
it('Rejects message not from governor router', async () => {
// Create TransferGovernor message
const transferGovernorMessage = abacus.governance.formatTransferGovernor(
nonGovernorDomain,
abacus.ethersAddressToBytes32(nonGovernorRouter.address),
);
const abacusMessage = await formatAbacusMessage(
governorReplicaOnNonGovernorChain,
nonGovernorRouter,
governorRouter,
transferGovernorMessage,
);
// Set message status to MessageStatus.Proven
await nonGovernorReplicaOnGovernorChain.setMessageProven(abacusMessage);
// Expect replica processing to fail when nonGovernorRouter reverts in handle
let success =
await nonGovernorReplicaOnGovernorChain.callStatic.testProcess(
abacusMessage,
);
expect(success).to.be.false;
});
it('Accepts a valid transfer governor message', async () => {
// Enroll router for new domain (in real setting this would
// be executed with an Abacus message sent to the nonGovernorRouter)
await nonGovernorRouter.testSetRouter(
thirdDomain,
abacus.ethersAddressToBytes32(thirdRouter.address),
);
// Create TransferGovernor message
const transferGovernorMessage = abacus.governance.formatTransferGovernor(
thirdDomain,
abacus.ethersAddressToBytes32(thirdRouter.address),
);
const abacusMessage = await formatAbacusMessage(
governorReplicaOnNonGovernorChain,
governorRouter,
nonGovernorRouter,
transferGovernorMessage,
);
// Expect successful tx on static call
let success = await governorReplicaOnNonGovernorChain.callStatic.process(
abacusMessage,
);
expect(success).to.be.true;
await governorReplicaOnNonGovernorChain.process(abacusMessage);
await expectGovernor(
nonGovernorRouter,
thirdDomain,
ethers.constants.AddressZero,
);
});
it('Accepts valid set router message', async () => {
// Create address for router to enroll and domain for router
const [router] = await ethers.getSigners();
// Create SetRouter message
const setRouterMessage = abacus.governance.formatSetRouter(
thirdDomain,
abacus.ethersAddressToBytes32(router.address),
);
const abacusMessage = await formatAbacusMessage(
governorReplicaOnNonGovernorChain,
governorRouter,
nonGovernorRouter,
setRouterMessage,
);
// Expect successful tx
let success = await governorReplicaOnNonGovernorChain.callStatic.process(
abacusMessage,
);
expect(success).to.be.true;
// Expect new router to be registered for domain and for new domain to be
// in domains array
await governorReplicaOnNonGovernorChain.process(abacusMessage);
expect(await nonGovernorRouter.routers(thirdDomain)).to.equal(
abacus.ethersAddressToBytes32(router.address),
);
expect(await nonGovernorRouter.containsDomain(thirdDomain)).to.be.true;
});
it('Accepts valid call messages', async () => {
// const TestRecipient = await abacus.deployImplementation('TestRecipient');
const testRecipientFactory = new TestRecipient__factory(signer);
const testRecipient = await testRecipientFactory.deploy();
// Format abacus call message
const arg = 'String!';
const call = await formatCall(testRecipient, 'receiveString', [arg]);
// Create Call message to test recipient that calls receiveString
const callMessage = abacus.governance.formatCalls([call, call]);
const abacusMessage = await formatAbacusMessage(
governorReplicaOnNonGovernorChain,
governorRouter,
nonGovernorRouter,
callMessage,
);
// Expect successful tx
let success =
await governorReplicaOnNonGovernorChain.callStatic.testProcess(
abacusMessage,
);
expect(success).to.be.true;
});
it('Transfers governorship', async () => {
// Transfer governor on current governor chain
// Governor HAS NOT been transferred on original governor domain
await expectGovernor(governorRouter, governorDomain, firstGovernor);
// Governor HAS NOT been transferred on original non-governor domain
await expectGovernor(
nonGovernorRouter,
governorDomain,
ethers.constants.AddressZero,
);
// transfer governorship to nonGovernorRouter
await governorRouter.transferGovernor(nonGovernorDomain, secondGovernor);
// Governor HAS been transferred on original governor domain
await expectGovernor(
governorRouter,
nonGovernorDomain,
ethers.constants.AddressZero,
);
// Governor HAS NOT been transferred on original non-governor domain
await expectGovernor(
nonGovernorRouter,
governorDomain,
ethers.constants.AddressZero,
);
const transferGovernorMessage = abacus.governance.formatTransferGovernor(
nonGovernorDomain,
abacus.ethersAddressToBytes32(secondGovernor),
);
const abacusMessage = await formatAbacusMessage(
governorReplicaOnNonGovernorChain,
governorRouter,
nonGovernorRouter,
transferGovernorMessage,
);
// Process transfer governor message on Replica
await governorReplicaOnNonGovernorChain.process(abacusMessage);
// Governor HAS been transferred on original governor domain
await expectGovernor(
governorRouter,
nonGovernorDomain,
ethers.constants.AddressZero,
);
// Governor HAS been transferred on original non-governor domain
await expectGovernor(nonGovernorRouter, nonGovernorDomain, secondGovernor);
});
it('Upgrades using GovernanceRouter call', async () => {
const upgradeUtils = new UpgradeTestHelpers();
// get upgradeBeaconController
const ubc = abacusDeployment.ubc(governorDomain);
// Transfer ownership of the UBC to governance.
await ubc.transferOwnership(governorRouter.address);
const mysteryMath = await upgradeUtils.deployMysteryMathUpgradeSetup(
signer,
ubc,
);
// expect results before upgrade
await upgradeUtils.expectMysteryMathV1(mysteryMath.proxy);
// Deploy Implementation 2
const v2Factory = new MysteryMathV2__factory(signer);
const implementation = await v2Factory.deploy();
// Format abacus call message
const call = await formatCall(ubc, 'upgrade', [
mysteryMath.beacon.address,
implementation.address,
]);
// dispatch call on local governorRouter
await expect(governorRouter.callLocal([call])).to.emit(
ubc,
'BeaconUpgraded',
);
// test implementation was upgraded
await upgradeUtils.expectMysteryMathV2(mysteryMath.proxy);
});
it('Calls ValidatorManager to set the validator for a domain', async () => {
const [newValidator] = await ethers.getSigners();
const validatorManager = abacusDeployment.validatorManager(governorDomain);
await validatorManager.transferOwnership(governorRouter.address);
// check current Validator address on Home
let currentValidatorAddr = await validatorManager.validators(
governorDomain,
);
expect(currentValidatorAddr).to.equal(
await abacusDeployment.validator(governorDomain).signer.getAddress(),
);
// format abacus call message
const call = await formatCall(validatorManager, 'setValidator', [
governorDomain,
newValidator.address,
]);
await expect(governorRouter.callLocal([call])).to.emit(
validatorManager,
'NewValidator',
);
// check for new validator
currentValidatorAddr = await validatorManager.validators(governorDomain);
expect(currentValidatorAddr).to.equal(newValidator.address);
});
});