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.
283 lines
9.0 KiB
283 lines
9.0 KiB
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
|
|
import { expect } from 'chai';
|
|
import { ethers } from 'hardhat';
|
|
|
|
import { types, utils } from '@abacus-network/utils';
|
|
import { MessageStatus } from '@abacus-network/utils/dist/src/types';
|
|
|
|
import {
|
|
BadRecipient1__factory,
|
|
BadRecipient3__factory,
|
|
BadRecipient5__factory,
|
|
BadRecipient6__factory,
|
|
BadRecipientHandle__factory,
|
|
TestInbox,
|
|
TestInbox__factory,
|
|
TestRecipient__factory,
|
|
TestValidatorManager,
|
|
TestValidatorManager__factory,
|
|
} from '../types';
|
|
|
|
const proveAndProcessTestCases = require('../../../vectors/proveAndProcess.json');
|
|
const messageWithProof = require('../../../vectors/messageWithProof.json');
|
|
|
|
const localDomain = 3000;
|
|
const remoteDomain = 1000;
|
|
|
|
describe('Inbox', async () => {
|
|
const badRecipientFactories = [
|
|
BadRecipient1__factory,
|
|
BadRecipient3__factory,
|
|
BadRecipient5__factory,
|
|
BadRecipient6__factory,
|
|
];
|
|
|
|
let inbox: TestInbox,
|
|
signer: SignerWithAddress,
|
|
abacusMessageSender: SignerWithAddress,
|
|
validatorManager: TestValidatorManager;
|
|
|
|
before(async () => {
|
|
[signer, abacusMessageSender] = await ethers.getSigners();
|
|
// Inbox.initialize will ensure the validator manager is a contract.
|
|
// TestValidatorManager doesn't have any special logic, it just submits
|
|
// checkpoints without any signature verification.
|
|
const testValidatorManagerFactory = new TestValidatorManager__factory(
|
|
signer,
|
|
);
|
|
validatorManager = await testValidatorManagerFactory.deploy();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
const inboxFactory = new TestInbox__factory(signer);
|
|
inbox = await inboxFactory.deploy(localDomain);
|
|
await inbox.initialize(
|
|
remoteDomain,
|
|
validatorManager.address,
|
|
ethers.constants.HashZero,
|
|
0,
|
|
);
|
|
});
|
|
|
|
it('Cannot be initialized twice', async () => {
|
|
await expect(
|
|
inbox.initialize(
|
|
remoteDomain,
|
|
validatorManager.address,
|
|
ethers.constants.HashZero,
|
|
0,
|
|
),
|
|
).to.be.revertedWith('Initializable: contract is already initialized');
|
|
});
|
|
|
|
it('Accepts checkpoint from validator manager', async () => {
|
|
const root = ethers.utils.formatBytes32String('first new root');
|
|
const index = 1;
|
|
await validatorManager.checkpoint(inbox.address, root, index);
|
|
const [croot, cindex] = await inbox.latestCheckpoint();
|
|
expect(croot).to.equal(root);
|
|
expect(cindex).to.equal(index);
|
|
});
|
|
|
|
it('Rejects checkpoint from non-validator manager', async () => {
|
|
const root = ethers.utils.formatBytes32String('first new root');
|
|
const index = 1;
|
|
await expect(inbox.checkpoint(root, index)).to.be.revertedWith(
|
|
'!validatorManager',
|
|
);
|
|
});
|
|
|
|
it('Rejects old checkpoint from validator manager', async () => {
|
|
let root = ethers.utils.formatBytes32String('first new root');
|
|
let index = 10;
|
|
await validatorManager.checkpoint(inbox.address, root, index);
|
|
const [croot, cindex] = await inbox.latestCheckpoint();
|
|
expect(croot).to.equal(root);
|
|
expect(cindex).to.equal(index);
|
|
|
|
root = ethers.utils.formatBytes32String('second new root');
|
|
index = 9;
|
|
await expect(
|
|
validatorManager.checkpoint(inbox.address, root, index),
|
|
).to.be.revertedWith('old checkpoint');
|
|
});
|
|
|
|
it('Processes a valid message', async () => {
|
|
const signers = await ethers.getSigners();
|
|
const recipientF = new TestRecipient__factory(signers[signers.length - 1]);
|
|
const recipient = await recipientF.deploy();
|
|
await recipient.deployTransaction.wait();
|
|
|
|
const { index, proof, root, message } = messageWithProof;
|
|
await inbox.setCheckpoint(root, 1);
|
|
|
|
await inbox.process(message, proof, index, '0x');
|
|
const hash = utils.messageHash(message, index);
|
|
expect(await inbox.messages(hash)).to.eql(MessageStatus.PROCESSED);
|
|
});
|
|
|
|
it('Rejects an already-processed message', async () => {
|
|
const { leaf, index, proof, root, message } = messageWithProof;
|
|
|
|
await inbox.setCheckpoint(root, 1);
|
|
// Set message status as MessageStatus.Processed
|
|
await inbox.setMessageStatus(leaf, MessageStatus.PROCESSED);
|
|
|
|
// Try to process message again
|
|
await expect(inbox.process(message, proof, index, '0x')).to.be.revertedWith(
|
|
'!MessageStatus.None',
|
|
);
|
|
});
|
|
|
|
it('Rejects invalid message proof', async () => {
|
|
const { leaf, index, proof, root, message } = messageWithProof;
|
|
|
|
// Switch ordering of proof hashes
|
|
// NB: We copy 'path' here to avoid mutating the test cases for
|
|
// other tests.
|
|
const newProof = [...proof];
|
|
newProof[0] = proof[1];
|
|
newProof[1] = proof[0];
|
|
|
|
await inbox.setCheckpoint(root, 1);
|
|
|
|
expect(
|
|
inbox.process(message, newProof as types.BytesArray, index, '0x'),
|
|
).to.be.revertedWith('!checkpointed root');
|
|
expect(await inbox.messages(leaf)).to.equal(types.MessageStatus.NONE);
|
|
});
|
|
|
|
for (let i = 0; i < badRecipientFactories.length; i++) {
|
|
it(`Fails to process a message for a badly implemented recipient (${
|
|
i + 1
|
|
})`, async () => {
|
|
const sender = abacusMessageSender;
|
|
const factory = new badRecipientFactories[i](signer);
|
|
const badRecipient = await factory.deploy();
|
|
|
|
const leafIndex = 0;
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
sender.address,
|
|
|
|
localDomain,
|
|
badRecipient.address,
|
|
'0x',
|
|
);
|
|
|
|
await expect(inbox.testProcess(abacusMessage, leafIndex)).to.be.reverted;
|
|
});
|
|
}
|
|
|
|
it('Fails to process message with wrong destination Domain', async () => {
|
|
const [sender, recipient] = await ethers.getSigners();
|
|
const body = ethers.utils.formatBytes32String('message');
|
|
|
|
const leafIndex = 0;
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
sender.address,
|
|
// Wrong destination Domain
|
|
localDomain + 5,
|
|
recipient.address,
|
|
body,
|
|
);
|
|
|
|
await expect(
|
|
inbox.testProcess(abacusMessage, leafIndex),
|
|
).to.be.revertedWith('!destination');
|
|
});
|
|
|
|
it('Fails to process message sent to a non-existent contract address', async () => {
|
|
const body = ethers.utils.formatBytes32String('message');
|
|
|
|
const leafIndex = 0;
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
abacusMessageSender.address,
|
|
localDomain,
|
|
'0x1234567890123456789012345678901234567890', // non-existent contract address
|
|
body,
|
|
);
|
|
|
|
await expect(inbox.testProcess(abacusMessage, leafIndex)).to.be.reverted;
|
|
});
|
|
|
|
it('Fails to process a message for bad handler function', async () => {
|
|
const sender = abacusMessageSender;
|
|
const [recipient] = await ethers.getSigners();
|
|
const factory = new BadRecipientHandle__factory(recipient);
|
|
const testRecipient = await factory.deploy();
|
|
|
|
const leafIndex = 0;
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
sender.address,
|
|
localDomain,
|
|
testRecipient.address,
|
|
'0x',
|
|
);
|
|
|
|
// Ensure bad handler function causes process to fail
|
|
await expect(inbox.testProcess(abacusMessage, leafIndex)).to.be.reverted;
|
|
});
|
|
|
|
it('Processes a message directly', async () => {
|
|
const sender = abacusMessageSender;
|
|
const [recipient] = await ethers.getSigners();
|
|
const factory = new TestRecipient__factory(recipient);
|
|
const testRecipient = await factory.deploy();
|
|
|
|
const leafIndex = 0;
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
sender.address,
|
|
localDomain,
|
|
testRecipient.address,
|
|
'0x',
|
|
);
|
|
|
|
await inbox.testProcess(abacusMessage, leafIndex);
|
|
|
|
const hash = utils.messageHash(abacusMessage, leafIndex);
|
|
expect(await inbox.messages(hash)).to.eql(MessageStatus.PROCESSED);
|
|
});
|
|
|
|
it('Proves and processes a message', async () => {
|
|
const sender = abacusMessageSender;
|
|
const testRecipientFactory = new TestRecipient__factory(signer);
|
|
const testRecipient = await testRecipientFactory.deploy();
|
|
|
|
const leafIndex = 0;
|
|
// Note that hash of this message specifically matches leaf of 1st
|
|
// proveAndProcess test case
|
|
const abacusMessage = utils.formatMessage(
|
|
remoteDomain,
|
|
sender.address,
|
|
localDomain,
|
|
testRecipient.address,
|
|
'0x',
|
|
);
|
|
|
|
// Assert above message and test case have matching leaves
|
|
const { path, index } = proveAndProcessTestCases[0];
|
|
const hash = utils.messageHash(abacusMessage, leafIndex);
|
|
|
|
// Set inbox's current root to match newly computed root that includes
|
|
// the new leaf (normally root will have already been computed and path
|
|
// simply verifies leaf is in tree but because it is cryptographically
|
|
// impossible to find the inputs that create a pre-determined root, we
|
|
// simply recalculate root with the leaf using branchRoot)
|
|
const proofRoot = await inbox.testBranchRoot(
|
|
hash,
|
|
path as types.BytesArray,
|
|
index,
|
|
);
|
|
await inbox.setCheckpoint(proofRoot, 1);
|
|
|
|
await inbox.process(abacusMessage, path as types.BytesArray, index, '0x');
|
|
|
|
expect(await inbox.messages(hash)).to.equal(types.MessageStatus.PROCESSED);
|
|
});
|
|
});
|
|
|