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.
302 lines
9.0 KiB
302 lines
9.0 KiB
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
|
|
import { expect } from 'chai';
|
|
import { ethers } from 'hardhat';
|
|
|
|
import { utils } from '@hyperlane-xyz/utils';
|
|
|
|
import {
|
|
BadRecipient1__factory,
|
|
BadRecipient2__factory,
|
|
BadRecipient3__factory,
|
|
BadRecipient5__factory,
|
|
BadRecipient6__factory,
|
|
TestMailbox,
|
|
TestMailbox__factory,
|
|
TestMultisigIsm,
|
|
TestMultisigIsm__factory,
|
|
TestRecipient,
|
|
TestRecipient__factory,
|
|
} from '../types';
|
|
|
|
import { inferMessageValues } from './lib/mailboxes';
|
|
|
|
const originDomain = 1000;
|
|
const destDomain = 2000;
|
|
const ONLY_OWNER_REVERT_MSG = 'Ownable: caller is not the owner';
|
|
|
|
describe('Mailbox', async () => {
|
|
let mailbox: TestMailbox,
|
|
module: TestMultisigIsm,
|
|
signer: SignerWithAddress,
|
|
nonOwner: SignerWithAddress;
|
|
|
|
beforeEach(async () => {
|
|
[signer, nonOwner] = await ethers.getSigners();
|
|
const moduleFactory = new TestMultisigIsm__factory(signer);
|
|
module = await moduleFactory.deploy();
|
|
const mailboxFactory = new TestMailbox__factory(signer);
|
|
mailbox = await mailboxFactory.deploy(originDomain);
|
|
await mailbox.initialize(signer.address, module.address);
|
|
});
|
|
|
|
describe('#initialize', () => {
|
|
it('Sets the owner', async () => {
|
|
const mailboxFactory = new TestMailbox__factory(signer);
|
|
mailbox = await mailboxFactory.deploy(originDomain);
|
|
const expectedOwner = nonOwner.address;
|
|
await mailbox.initialize(expectedOwner, module.address);
|
|
const owner = await mailbox.owner();
|
|
expect(owner).equals(expectedOwner);
|
|
});
|
|
|
|
it('Cannot be initialized twice', async () => {
|
|
await expect(
|
|
mailbox.initialize(signer.address, module.address),
|
|
).to.be.revertedWith('Initializable: contract is already initialized');
|
|
});
|
|
});
|
|
|
|
describe('#dispatch', () => {
|
|
let recipient: SignerWithAddress, message: string, id: string, body: string;
|
|
before(async () => {
|
|
[, recipient] = await ethers.getSigners();
|
|
({ message, id, body } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
destDomain,
|
|
recipient.address,
|
|
'message',
|
|
));
|
|
});
|
|
|
|
it('Does not dispatch too large messages', async () => {
|
|
const longMessage = `0x${Buffer.alloc(3000).toString('hex')}`;
|
|
await expect(
|
|
mailbox.dispatch(
|
|
destDomain,
|
|
utils.addressToBytes32(recipient.address),
|
|
longMessage,
|
|
),
|
|
).to.be.revertedWith('msg too long');
|
|
});
|
|
|
|
it('Dispatches a message', async () => {
|
|
// Send message with signer address as msg.sender
|
|
const recipientBytes = utils.addressToBytes32(recipient.address);
|
|
await expect(
|
|
mailbox.connect(signer).dispatch(destDomain, recipientBytes, body),
|
|
)
|
|
.to.emit(mailbox, 'Dispatch')
|
|
.withArgs(signer.address, destDomain, recipientBytes, message)
|
|
.to.emit(mailbox, 'DispatchId')
|
|
.withArgs(utils.messageId(message));
|
|
});
|
|
|
|
it('Returns the id of the dispatched message', async () => {
|
|
const actualId = await mailbox
|
|
.connect(signer)
|
|
.callStatic.dispatch(
|
|
destDomain,
|
|
utils.addressToBytes32(recipient.address),
|
|
body,
|
|
);
|
|
|
|
expect(actualId).equals(id);
|
|
});
|
|
});
|
|
|
|
describe('#recipientIsm', () => {
|
|
let recipient: TestRecipient;
|
|
beforeEach(async () => {
|
|
const recipientF = new TestRecipient__factory(signer);
|
|
recipient = await recipientF.deploy();
|
|
});
|
|
|
|
it('Returns the default module when unspecified', async () => {
|
|
expect(await mailbox.recipientIsm(recipient.address)).to.equal(
|
|
await mailbox.defaultIsm(),
|
|
);
|
|
});
|
|
|
|
it('Returns the recipient module when specified', async () => {
|
|
const recipientIsm = mailbox.address;
|
|
await recipient.setInterchainSecurityModule(recipientIsm);
|
|
expect(await mailbox.recipientIsm(recipient.address)).to.equal(
|
|
recipientIsm,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('#process', () => {
|
|
const badRecipientFactories = [
|
|
BadRecipient1__factory,
|
|
BadRecipient2__factory,
|
|
BadRecipient3__factory,
|
|
BadRecipient5__factory,
|
|
BadRecipient6__factory,
|
|
];
|
|
let message: string, id: string, recipient: string;
|
|
|
|
beforeEach(async () => {
|
|
await module.setAccept(true);
|
|
const recipientF = new TestRecipient__factory(signer);
|
|
recipient = utils.addressToBytes32((await recipientF.deploy()).address);
|
|
({ message, id } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
originDomain,
|
|
recipient,
|
|
'message',
|
|
));
|
|
});
|
|
|
|
it('processes a message', async () => {
|
|
await expect(mailbox.process('0x', message))
|
|
.to.emit(mailbox, 'Process')
|
|
.withArgs(
|
|
originDomain,
|
|
utils.addressToBytes32(signer.address),
|
|
recipient,
|
|
)
|
|
.to.emit(mailbox, 'ProcessId')
|
|
.withArgs(id);
|
|
expect(await mailbox.delivered(id)).to.be.true;
|
|
});
|
|
|
|
it('Rejects an already-processed message', async () => {
|
|
await expect(mailbox.process('0x', message)).to.emit(mailbox, 'Process');
|
|
|
|
// Try to process message again
|
|
await expect(mailbox.process('0x', message)).to.be.revertedWith(
|
|
'delivered',
|
|
);
|
|
});
|
|
|
|
it('Fails to process message when rejected by module', async () => {
|
|
await module.setAccept(false);
|
|
await expect(mailbox.process('0x', message)).to.be.revertedWith(
|
|
'!module',
|
|
);
|
|
});
|
|
|
|
for (let i = 0; i < badRecipientFactories.length; i++) {
|
|
it(`Fails to process a message for a badly implemented recipient (${
|
|
i + 1
|
|
})`, async () => {
|
|
const factory = new badRecipientFactories[i](signer);
|
|
const badRecipient = await factory.deploy();
|
|
|
|
({ message } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
originDomain,
|
|
badRecipient.address,
|
|
'message',
|
|
));
|
|
await expect(mailbox.process('0x', message)).to.be.reverted;
|
|
});
|
|
}
|
|
|
|
// TODO: Fails to process with wrong version..
|
|
it('Fails to process message with wrong destination Domain', async () => {
|
|
({ message } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
originDomain + 1,
|
|
recipient,
|
|
'message',
|
|
));
|
|
|
|
await expect(mailbox.process('0x', message)).to.be.revertedWith(
|
|
'!destination',
|
|
);
|
|
});
|
|
|
|
it('Fails to process message with wrong version', async () => {
|
|
const version = await mailbox.VERSION();
|
|
({ message } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
originDomain,
|
|
recipient,
|
|
'message',
|
|
version + 1,
|
|
));
|
|
await expect(mailbox.process('0x', message)).to.be.revertedWith(
|
|
'!version',
|
|
);
|
|
});
|
|
|
|
it('Fails to process message sent to a non-existent contract address', async () => {
|
|
({ message } = await inferMessageValues(
|
|
mailbox,
|
|
signer.address,
|
|
originDomain,
|
|
'0x1234567890123456789012345678901234567890', // non-existent contract address
|
|
'message',
|
|
));
|
|
await expect(mailbox.process('0x', message)).to.be.reverted;
|
|
});
|
|
});
|
|
|
|
describe('#setDefaultIsm', async () => {
|
|
let newIsm: TestMultisigIsm;
|
|
before(async () => {
|
|
const moduleFactory = new TestMultisigIsm__factory(signer);
|
|
newIsm = await moduleFactory.deploy();
|
|
});
|
|
|
|
it('Allows owner to update the default ISM', async () => {
|
|
await expect(mailbox.setDefaultIsm(newIsm.address))
|
|
.to.emit(mailbox, 'DefaultIsmSet')
|
|
.withArgs(newIsm.address);
|
|
expect(await mailbox.defaultIsm()).to.equal(newIsm.address);
|
|
});
|
|
|
|
it('Does not allow non-owner to update the default ISM', async () => {
|
|
await expect(
|
|
mailbox.connect(nonOwner).setDefaultIsm(newIsm.address),
|
|
).to.be.revertedWith(ONLY_OWNER_REVERT_MSG);
|
|
});
|
|
|
|
it('Reverts if the provided ISM is not a contract', async () => {
|
|
await expect(mailbox.setDefaultIsm(signer.address)).to.be.revertedWith(
|
|
'!contract',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('#pause', () => {
|
|
it('should revert on non-owner', async () => {
|
|
await expect(mailbox.connect(nonOwner).pause()).to.be.revertedWith(
|
|
ONLY_OWNER_REVERT_MSG,
|
|
);
|
|
await expect(mailbox.connect(nonOwner).unpause()).to.be.revertedWith(
|
|
ONLY_OWNER_REVERT_MSG,
|
|
);
|
|
});
|
|
|
|
it('should emit events', async () => {
|
|
await expect(mailbox.pause()).to.emit(mailbox, 'Paused');
|
|
await expect(mailbox.unpause()).to.emit(mailbox, 'Unpaused');
|
|
});
|
|
|
|
it('should prevent dispatch and process', async () => {
|
|
await mailbox.pause();
|
|
await expect(
|
|
mailbox.dispatch(
|
|
destDomain,
|
|
utils.addressToBytes32(nonOwner.address),
|
|
'0x',
|
|
),
|
|
).to.be.revertedWith('paused');
|
|
await expect(mailbox.process('0x', '0x')).to.be.revertedWith('paused');
|
|
});
|
|
|
|
it('isPaused should be true', async () => {
|
|
await mailbox.pause();
|
|
const paused = await mailbox.isPaused();
|
|
expect(paused);
|
|
});
|
|
});
|
|
});
|
|
|