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/optics-core/test/cross-chain/SimpleMessage.test.js

210 lines
6.8 KiB

const { waffle, ethers } = require('hardhat');
const { provider } = waffle;
const { expect } = require('chai');
const testUtils = require('../utils');
const { domainsToTestConfigs } = require('./generateTestChainConfigs');
const {
enqueueUpdateToReplica,
enqueueMessagesAndUpdateHome,
formatMessage,
formatCall,
} = require('./crossChainTestUtils');
const {
deployMultipleChains,
getHome,
getReplica,
getGovernanceRouter,
} = require('./deployCrossChainTest');
const {
testCases: proveAndProcessTestCases,
} = require('../../../../vectors/proveAndProcessTestCases.json');
/*
* Deploy the full Optics suite on two chains
* enqueue messages to Home
* sign and submit updates to Home
* relay updates to Replica
* confirm updates on Replica
* TODO prove and process messages on Replica
*/
describe('SimpleCrossChainMessage', async () => {
const domains = [1000, 2000];
const homeDomain = domains[0];
const replicaDomain = domains[1];
const walletProvider = new testUtils.WalletProvider(provider);
let randomSigner, chainDetails, firstRootEnqueuedToReplica;
let latestRoot = {},
latestUpdate = {};
before(async () => {
// generate TestChainConfigs for the given domains
const configs = await domainsToTestConfigs(domains);
// deploy the entire Optics suite on each chain
chainDetails = await deployMultipleChains(configs);
[randomSigner] = walletProvider.getWalletsPersistent(1);
});
it('All Homes have correct initial state', async () => {
// governorHome has 0 updates
const governorHome = getHome(chainDetails, homeDomain);
let length = await governorHome.queueLength();
expect(length).to.equal(0);
let [suggestedCurrent, suggestedNew] = await governorHome.suggestUpdate();
expect(suggestedCurrent).to.equal(ethers.utils.formatBytes32String(0));
expect(suggestedNew).to.equal(ethers.utils.formatBytes32String(0));
// nonGovernorHome has 1 update
const nonGovernorHome = getHome(chainDetails, replicaDomain);
length = await nonGovernorHome.queueLength();
expect(length).to.equal(1);
[suggestedCurrent, suggestedNew] = await nonGovernorHome.suggestUpdate();
const nullRoot = ethers.utils.formatBytes32String(0);
expect(suggestedCurrent).to.equal(nullRoot);
expect(suggestedNew).to.not.equal(nullRoot);
});
it('All Replicas have empty queue of pending updates', async () => {
for (let destinationDomain of domains) {
for (let remoteDomain of domains) {
if (destinationDomain !== remoteDomain) {
const replica = getReplica(
chainDetails,
destinationDomain,
remoteDomain,
);
const length = await replica.queueLength();
expect(length).to.equal(0);
const [pending, confirmAt] = await replica.nextPending();
expect(pending).to.equal(ethers.utils.formatBytes32String(0));
expect(confirmAt).to.equal(0);
}
}
}
});
it('Origin Home Accepts one valid update', async () => {
const messages = ['message'].map((message) =>
formatMessage(message, replicaDomain, randomSigner.address),
);
const update = await enqueueMessagesAndUpdateHome(
chainDetails,
homeDomain,
messages,
);
latestUpdate[homeDomain] = update;
latestRoot[homeDomain] = update.finalRoot;
});
it('Destination Replica Accepts the first update', async () => {
firstRootEnqueuedToReplica = await enqueueUpdateToReplica(
chainDetails,
latestUpdate[homeDomain],
homeDomain,
replicaDomain,
);
});
it('Origin Home Accepts an update with several batched messages', async () => {
const messages = ['message1', 'message2', 'message3'].map((message) =>
formatMessage(message, replicaDomain, randomSigner.address),
);
const update = await enqueueMessagesAndUpdateHome(
chainDetails,
homeDomain,
messages,
);
latestUpdate[homeDomain] = update;
latestRoot[homeDomain] = update.finalRoot;
});
it('Destination Replica Accepts the second update', async () => {
await enqueueUpdateToReplica(
chainDetails,
latestUpdate[homeDomain],
homeDomain,
replicaDomain,
);
});
it('Destination Replica shows first update as the next pending', async () => {
const replica = getReplica(chainDetails, replicaDomain, homeDomain);
const [pending] = await replica.nextPending();
expect(pending).to.equal(firstRootEnqueuedToReplica);
});
it('Destination Replica Batch-confirms several ready updates', async () => {
const replica = getReplica(chainDetails, replicaDomain, homeDomain);
// Increase time enough for both updates to be confirmable
const optimisticSeconds = chainDetails[replicaDomain].optimisticSeconds;
await testUtils.increaseTimestampBy(provider, optimisticSeconds * 2);
// Replica should be able to confirm updates
expect(await replica.canConfirm()).to.be.true;
await replica.confirm();
// after confirming, current root should be equal to the last submitted update
const { finalRoot } = latestUpdate[homeDomain];
expect(await replica.current()).to.equal(finalRoot);
});
it('Proves and processes a message on Replica', async () => {
// get governance routers
const governorRouter = getGovernanceRouter(chainDetails, homeDomain);
const nonGovernorRouter = getGovernanceRouter(chainDetails, replicaDomain);
const replica = getReplica(chainDetails, replicaDomain, homeDomain);
const TestRecipient = await optics.deployImplementation('TestRecipient');
// ensure `processed` has an initial value of false
expect(await TestRecipient.processed()).to.be.false;
// create Call message to test recipient that calls `processCall`
const arg = true;
const call = await formatCall(TestRecipient, 'processCall', [arg]);
const callMessage = optics.GovernanceRouter.formatCalls([call]);
// Create Optics message that is sent from the governor domain and governor
// to the nonGovernorRouter on the nonGovernorDomain
const sequence = (await replica.lastProcessed()).add(1);
const opticsMessage = optics.formatMessage(
1000,
governorRouter.address,
sequence,
2000,
nonGovernorRouter.address,
callMessage,
);
// get merkle proof
const { path, index } = proveAndProcessTestCases[0];
const leaf = optics.messageToLeaf(opticsMessage);
// set root
const proofRoot = await replica.testBranchRoot(leaf, path, index);
await replica.setCurrentRoot(proofRoot);
// prove and process message
await replica.proveAndProcess(opticsMessage, path, index);
// expect call to have been processed
expect(await TestRecipient.processed()).to.be.true;
expect(await replica.messages(leaf)).to.equal(
optics.MessageStatus.PROCESSED,
);
expect(await replica.lastProcessed()).to.equal(sequence);
});
});