Run forge tests in CI (#1355)

* Move foundry workflow so it is picked up by github

* Require ISM in router config

Co-authored-by: Asa Oines <asaoines@gmail.com>
pull/1378/head
Yorke Rhodes 2 years ago committed by GitHub
parent 5b5ce88b72
commit c25cd842b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      .github/workflows/node.yml
  2. 4
      solidity/contracts/HyperlaneConnectionClient.sol
  3. 2
      solidity/contracts/Mailbox.sol
  4. 16
      solidity/contracts/middleware/InterchainAccountRouter.sol
  5. 16
      solidity/contracts/middleware/InterchainQueryRouter.sol
  6. 8
      solidity/contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol
  7. 21
      solidity/contracts/mock/MockHyperlaneEnvironment.sol
  8. 4
      solidity/contracts/test/TestSendReceiver.sol
  9. 26
      solidity/lib/forge-std/.github/workflows/tests.yml
  10. 2
      solidity/package.json
  11. 35
      solidity/test/InterchainAccountRouter.t.sol
  12. 13
      solidity/test/LiquidityLayerRouter.t.sol
  13. 21
      solidity/test/Messaging.t.sol
  14. 21
      typescript/sdk/src/core/TestCoreDeployer.ts
  15. 6
      typescript/sdk/src/deploy/middleware/LiquidityLayerRouterDeployer.ts
  16. 12
      typescript/sdk/src/deploy/middleware/deploy.ts
  17. 2
      typescript/sdk/src/router.ts

@ -106,5 +106,13 @@ jobs:
path: ./* path: ./*
key: ${{ github.sha }} key: ${{ github.sha }}
- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly
- name: Install dependencies
run: cd solidity && forge install
- name: core - name: core
run: yarn workspace @hyperlane-xyz/core run test run: yarn workspace @hyperlane-xyz/core run test

@ -18,7 +18,7 @@ abstract contract HyperlaneConnectionClient is
IMailbox public mailbox; IMailbox public mailbox;
// Interchain Gas Paymaster contract. The relayer associated with this contract // Interchain Gas Paymaster contract. The relayer associated with this contract
// must be willing to relay messages dispatched from the current Outbox contract, // must be willing to relay messages dispatched from the current Mailbox contract,
// otherwise payments made to the paymaster will not result in relayed messages. // otherwise payments made to the paymaster will not result in relayed messages.
IInterchainGasPaymaster public interchainGasPaymaster; IInterchainGasPaymaster public interchainGasPaymaster;
@ -136,7 +136,7 @@ abstract contract HyperlaneConnectionClient is
} }
/** /**
* @notice Modify the contract the Application uses to validate Inbox contracts * @notice Modify the contract the Application uses to validate Mailbox contracts
* @param _mailbox The address of the mailbox contract * @param _mailbox The address of the mailbox contract
*/ */
function _setMailbox(address _mailbox) internal onlyContract(_mailbox) { function _setMailbox(address _mailbox) internal onlyContract(_mailbox) {

@ -211,7 +211,7 @@ contract Mailbox is
/** /**
* @notice Returns a checkpoint representing the current merkle tree. * @notice Returns a checkpoint representing the current merkle tree.
* @return root The root of the Outbox's merkle tree. * @return root The root of the Mailbox's merkle tree.
* @return index The index of the last element in the tree. * @return index The index of the last element in the tree.
*/ */
function latestCheckpoint() public view returns (bytes32, uint32) { function latestCheckpoint() public view returns (bytes32, uint32) {

@ -26,16 +26,16 @@ contract InterchainAccountRouter is Router, IInterchainAccountRouter {
); );
function initialize( function initialize(
address _owner,
address _mailbox, address _mailbox,
address _interchainGasPaymaster address _interchainGasPaymaster,
address _interchainSecurityModule
) public initializer { ) public initializer {
// Transfer ownership of the contract to deployer // Transfer ownership of the contract to `msg.sender`
_transferOwnership(_owner); __HyperlaneConnectionClient_initialize(
// Set the addresses for the Mailbox and IGP _mailbox,
// Alternatively, this could be done later in an initialize method _interchainGasPaymaster,
_setMailbox(_mailbox); _interchainSecurityModule
_setInterchainGasPaymaster(_interchainGasPaymaster); );
} }
function dispatch(uint32 _destinationDomain, Call[] calldata calls) function dispatch(uint32 _destinationDomain, Call[] calldata calls)

@ -32,16 +32,16 @@ contract InterchainQueryRouter is
); );
function initialize( function initialize(
address _owner,
address _mailbox, address _mailbox,
address _interchainGasPaymaster address _interchainGasPaymaster,
address _interchainSecurityModule
) public initializer { ) public initializer {
// Transfer ownership of the contract to deployer // Transfer ownership of the contract to `msg.sender`
_transferOwnership(_owner); __HyperlaneConnectionClient_initialize(
// Set the addresses for the Mailbox and IGP _mailbox,
// Alternatively, this could be done later in an initialize method _interchainGasPaymaster,
_setMailbox(_mailbox); _interchainSecurityModule
_setInterchainGasPaymaster(_interchainGasPaymaster); );
} }
/** /**

@ -20,15 +20,15 @@ contract LiquidityLayerRouter is Router {
event LiquidityLayerAdapterSet(string indexed bridge, address adapter); event LiquidityLayerAdapterSet(string indexed bridge, address adapter);
function initialize( function initialize(
address _owner,
address _mailbox, address _mailbox,
address _interchainGasPaymaster address _interchainGasPaymaster,
address _interchainSecurityModule
) public initializer { ) public initializer {
__HyperlaneConnectionClient_initialize( __HyperlaneConnectionClient_initialize(
_mailbox, _mailbox,
_interchainGasPaymaster _interchainGasPaymaster,
_interchainSecurityModule
); );
_transferOwnership(_owner);
} }
function dispatchWithTokens( function dispatchWithTokens(

@ -3,6 +3,8 @@ pragma solidity ^0.8.13;
import "./MockMailbox.sol"; import "./MockMailbox.sol";
import "../middleware/InterchainQueryRouter.sol"; import "../middleware/InterchainQueryRouter.sol";
import "../InterchainGasPaymaster.sol";
import "../test/TestIsm.sol";
import {TypeCasts} from "../libs/TypeCasts.sol"; import {TypeCasts} from "../libs/TypeCasts.sol";
@ -11,6 +13,8 @@ contract MockHyperlaneEnvironment {
uint32 destinationDomain; uint32 destinationDomain;
mapping(uint32 => MockMailbox) public mailboxes; mapping(uint32 => MockMailbox) public mailboxes;
mapping(uint32 => InterchainGasPaymaster) public igps;
mapping(uint32 => IInterchainSecurityModule) public isms;
mapping(uint32 => InterchainQueryRouter) public queryRouters; mapping(uint32 => InterchainQueryRouter) public queryRouters;
constructor(uint32 _originDomain, uint32 _destinationDomain) { constructor(uint32 _originDomain, uint32 _destinationDomain) {
@ -20,6 +24,15 @@ contract MockHyperlaneEnvironment {
MockMailbox originMailbox = new MockMailbox(_originDomain); MockMailbox originMailbox = new MockMailbox(_originDomain);
MockMailbox destinationMailbox = new MockMailbox(_destinationDomain); MockMailbox destinationMailbox = new MockMailbox(_destinationDomain);
originMailbox.addRemoteMailbox(_destinationDomain, destinationMailbox);
destinationMailbox.addRemoteMailbox(_originDomain, originMailbox);
igps[originDomain] = new InterchainGasPaymaster();
igps[destinationDomain] = new InterchainGasPaymaster();
isms[originDomain] = new TestIsm();
isms[destinationDomain] = new TestIsm();
mailboxes[_originDomain] = originMailbox; mailboxes[_originDomain] = originMailbox;
mailboxes[_destinationDomain] = destinationMailbox; mailboxes[_destinationDomain] = destinationMailbox;
@ -27,14 +40,14 @@ contract MockHyperlaneEnvironment {
InterchainQueryRouter destinationQueryRouter = new InterchainQueryRouter(); InterchainQueryRouter destinationQueryRouter = new InterchainQueryRouter();
originQueryRouter.initialize( originQueryRouter.initialize(
address(this),
address(originMailbox), address(originMailbox),
address(0) address(igps[originDomain]),
address(isms[originDomain])
); );
destinationQueryRouter.initialize( destinationQueryRouter.initialize(
address(this),
address(destinationMailbox), address(destinationMailbox),
address(0) address(igps[destinationDomain]),
address(isms[destinationDomain])
); );
originQueryRouter.enrollRemoteRouter( originQueryRouter.enrollRemoteRouter(

@ -15,12 +15,12 @@ contract TestSendReceiver is IMessageRecipient {
event Handled(bytes32 blockHash); event Handled(bytes32 blockHash);
function dispatchToSelf( function dispatchToSelf(
IMailbox _outbox, IMailbox _mailbox,
IInterchainGasPaymaster _paymaster, IInterchainGasPaymaster _paymaster,
uint32 _destinationDomain, uint32 _destinationDomain,
bytes calldata _messageBody bytes calldata _messageBody
) external payable { ) external payable {
bytes32 _messageId = _outbox.dispatch( bytes32 _messageId = _mailbox.dispatch(
_destinationDomain, _destinationDomain,
address(this).addressToBytes32(), address(this).addressToBytes32(),
_messageBody _messageBody

@ -1,26 +0,0 @@
name: Tests
on: [push, pull_request]
jobs:
check:
name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install Foundry
uses: onbjerg/foundry-toolchain@v1
with:
version: nightly
- name: Install dependencies
run: forge install
- name: Run tests
run: forge test -vvv
- name: Build Test with older solc versions
run: |
forge build --contracts src/Test.sol --use solc:0.8.0
forge build --contracts src/Test.sol --use solc:0.7.0
forge build --contracts src/Test.sol --use solc:0.6.0

@ -47,7 +47,7 @@
"clean": "hardhat clean && rm -rf ./dist ./cache", "clean": "hardhat clean && rm -rf ./dist ./cache",
"coverage": "hardhat coverage", "coverage": "hardhat coverage",
"prettier": "prettier --write ./contracts ./interfaces ./test", "prettier": "prettier --write ./contracts ./interfaces ./test",
"test": "hardhat test" "test": "hardhat test && forge test -vvv"
}, },
"types": "dist/index.d.ts" "types": "dist/index.d.ts"
} }

@ -2,17 +2,16 @@
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import "forge-std/Test.sol"; import "forge-std/Test.sol";
import "../contracts/mock/MockOutbox.sol"; import "../contracts/mock/MockMailbox.sol";
import "../contracts/mock/MockInbox.sol"; import "../contracts/HyperlaneConnectionClient.sol";
import "../contracts/AbacusConnectionManager.sol"; import "../contracts/mock/MockHyperlaneEnvironment.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol"; import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import "../contracts/test/TestRecipient.sol"; import "../contracts/test/TestRecipient.sol";
import "../contracts/middleware/InterchainAccountRouter.sol"; import "../contracts/middleware/InterchainAccountRouter.sol";
import {OwnableMulticall, Call} from "../contracts/OwnableMulticall.sol"; import {OwnableMulticall, Call} from "../contracts/OwnableMulticall.sol";
contract InterchainAccountRouterTest is Test { contract InterchainAccountRouterTest is Test {
MockOutbox outbox; MockHyperlaneEnvironment environment;
MockInbox inbox;
uint32 originDomain = 1; uint32 originDomain = 1;
uint32 remoteDomain = 2; uint32 remoteDomain = 2;
@ -20,20 +19,10 @@ contract InterchainAccountRouterTest is Test {
InterchainAccountRouter originRouter; InterchainAccountRouter originRouter;
InterchainAccountRouter remoteRouter; InterchainAccountRouter remoteRouter;
AbacusConnectionManager originManager;
AbacusConnectionManager remoteManager;
TestRecipient recipient; TestRecipient recipient;
function setUp() public { function setUp() public {
inbox = new MockInbox(); environment = new MockHyperlaneEnvironment(originDomain, remoteDomain);
outbox = new MockOutbox(originDomain, address(inbox));
originManager = new AbacusConnectionManager();
remoteManager = new AbacusConnectionManager();
originManager.setOutbox(address(outbox));
remoteManager.enrollInbox(remoteDomain, address(inbox));
recipient = new TestRecipient(); recipient = new TestRecipient();
@ -41,14 +30,14 @@ contract InterchainAccountRouterTest is Test {
remoteRouter = new InterchainAccountRouter(); remoteRouter = new InterchainAccountRouter();
originRouter.initialize( originRouter.initialize(
address(this), address(environment.mailboxes(originDomain)),
address(originManager), address(environment.igps(originDomain)),
address(0) address(environment.isms(originDomain))
); );
remoteRouter.initialize( remoteRouter.initialize(
address(this), address(environment.mailboxes(remoteDomain)),
address(remoteManager), address(environment.igps(remoteDomain)),
address(0) address(environment.isms(remoteDomain))
); );
originRouter.enrollRemoteRouter( originRouter.enrollRemoteRouter(
@ -68,7 +57,7 @@ contract InterchainAccountRouterTest is Test {
data: abi.encodeCall(recipient.fooBar, (1, "Test")) data: abi.encodeCall(recipient.fooBar, (1, "Test"))
}); });
originRouter.dispatch(remoteDomain, calls); originRouter.dispatch(remoteDomain, calls);
inbox.processNextPendingMessage(); environment.processNextPendingMessage();
assertEq(recipient.lastCallMessage(), "Test"); assertEq(recipient.lastCallMessage(), "Test");
} }
} }

@ -53,16 +53,15 @@ contract LiquidityLayerRouterTest is Test {
destinationDomain destinationDomain
); );
// TODO: set IGP?
originLiquidityLayerRouter.initialize( originLiquidityLayerRouter.initialize(
address(this), address(testEnvironment.mailboxes(originDomain)),
address(testEnvironment.connectionManager(originDomain)), address(testEnvironment.igps(originDomain)),
address(0) address(testEnvironment.isms(originDomain))
); );
destinationLiquidityLayerRouter.initialize( destinationLiquidityLayerRouter.initialize(
address(this), address(testEnvironment.mailboxes(destinationDomain)),
address(testEnvironment.connectionManager(destinationDomain)), address(testEnvironment.igps(destinationDomain)),
address(0) address(testEnvironment.isms(destinationDomain))
); );
originLiquidityLayerRouter.enrollRemoteRouter( originLiquidityLayerRouter.enrollRemoteRouter(

@ -2,16 +2,16 @@
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import "forge-std/Test.sol"; import "forge-std/Test.sol";
import "../contracts/mock/MockOutbox.sol"; import "../contracts/mock/MockMailbox.sol";
import "../contracts/mock/MockInbox.sol"; import "../contracts/mock/MockMailbox.sol";
import "../contracts/test/TestRecipient.sol"; import "../contracts/test/TestRecipient.sol";
import "../interfaces/IInbox.sol"; import "../interfaces/IMailbox.sol";
import "../interfaces/IOutbox.sol"; import "../interfaces/IMailbox.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol"; import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
contract MessagingTest is Test { contract MessagingTest is Test {
MockOutbox outbox; MockMailbox originMailbox;
MockInbox inbox; MockMailbox remoteMailbox;
TestRecipient receiver; TestRecipient receiver;
@ -19,19 +19,20 @@ contract MessagingTest is Test {
uint32 remoteDomain = 2; uint32 remoteDomain = 2;
function setUp() public { function setUp() public {
inbox = new MockInbox(); originMailbox = new MockMailbox(originDomain);
outbox = new MockOutbox(originDomain, address(inbox)); remoteMailbox = new MockMailbox(remoteDomain);
originMailbox.addRemoteMailbox(remoteDomain, remoteMailbox);
receiver = new TestRecipient(); receiver = new TestRecipient();
} }
function testSendMessage(string calldata _message) public { function testSendMessage(string calldata _message) public {
outbox.dispatch( originMailbox.dispatch(
remoteDomain, remoteDomain,
TypeCasts.addressToBytes32(address(receiver)), TypeCasts.addressToBytes32(address(receiver)),
bytes(_message) bytes(_message)
); );
inbox.processNextPendingMessage(); remoteMailbox.processNextInboundMessage();
assertEq(string(receiver.lastData()), _message); assertEq(string(receiver.lastData()), _message);
} }
} }

@ -1,10 +1,12 @@
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { import {
MultisigIsm,
TestIsm__factory, TestIsm__factory,
TestMailbox, TestMailbox,
TestMailbox__factory, TestMailbox__factory,
} from '@hyperlane-xyz/core'; } from '@hyperlane-xyz/core';
import { types } from '@hyperlane-xyz/utils';
import { chainMetadata } from '../consts/chainMetadata'; import { chainMetadata } from '../consts/chainMetadata';
import { HyperlaneCoreDeployer } from '../deploy/core/HyperlaneCoreDeployer'; import { HyperlaneCoreDeployer } from '../deploy/core/HyperlaneCoreDeployer';
@ -51,12 +53,10 @@ export class TestCoreDeployer<
super(multiProvider, configs, testCoreFactories); super(multiProvider, configs, testCoreFactories);
} }
// skip proxying // deploy a test ISM in place of a multisig ISM
async deployMailbox<LocalChain extends TestChain>( async deployMultisigIsm<LocalChain extends TestChain>(
chain: LocalChain, chain: LocalChain,
): Promise<ProxiedContract<TestMailbox, BeaconProxyAddresses>> { ): Promise<MultisigIsm> {
const localDomain = chainMetadata[chain].id;
const testIsm = await this.deployContractFromFactory( const testIsm = await this.deployContractFromFactory(
chain, chain,
testCoreFactories.testIsm, testCoreFactories.testIsm,
@ -64,9 +64,18 @@ export class TestCoreDeployer<
[], [],
); );
await testIsm.setAccept(true); await testIsm.setAccept(true);
return testIsm as unknown as MultisigIsm;
}
// skip proxying
async deployMailbox<LocalChain extends TestChain>(
chain: LocalChain,
defaultIsmAddress: types.Address,
): Promise<ProxiedContract<TestMailbox, BeaconProxyAddresses>> {
const localDomain = chainMetadata[chain].id;
const mailbox = await this.deployContract(chain, 'mailbox', [localDomain]); const mailbox = await this.deployContract(chain, 'mailbox', [localDomain]);
await mailbox.initialize(testIsm.address); await mailbox.initialize(defaultIsmAddress);
return new ProxiedContract(mailbox, { return new ProxiedContract(mailbox, {
kind: ProxyKind.UpgradeBeacon, kind: ProxyKind.UpgradeBeacon,
proxy: mailbox.address, proxy: mailbox.address,

@ -80,7 +80,11 @@ export class LiquidityLayerDeployer<
const initCalldata = const initCalldata =
LiquidityLayerRouter__factory.createInterface().encodeFunctionData( LiquidityLayerRouter__factory.createInterface().encodeFunctionData(
'initialize', 'initialize',
[config.owner, config.mailbox, config.interchainGasPaymaster], [
config.mailbox,
config.interchainGasPaymaster,
config.interchainSecurityModule,
],
); );
const router = await this.deployContract(chain, 'router', [], { const router = await this.deployContract(chain, 'router', [], {
create2Salt: this.create2salt, create2Salt: this.create2salt,

@ -45,7 +45,11 @@ export class InterchainAccountDeployer<
const initCalldata = const initCalldata =
InterchainAccountRouter__factory.createInterface().encodeFunctionData( InterchainAccountRouter__factory.createInterface().encodeFunctionData(
'initialize', 'initialize',
[config.owner, config.mailbox, config.interchainGasPaymaster], [
config.mailbox,
config.interchainGasPaymaster,
config.interchainSecurityModule,
],
); );
const router = await this.deployContract(chain, 'router', [], { const router = await this.deployContract(chain, 'router', [], {
create2Salt: this.create2salt + 'router', create2Salt: this.create2salt + 'router',
@ -86,7 +90,11 @@ export class InterchainQueryDeployer<
const initCalldata = const initCalldata =
InterchainQueryRouter__factory.createInterface().encodeFunctionData( InterchainQueryRouter__factory.createInterface().encodeFunctionData(
'initialize', 'initialize',
[config.owner, config.mailbox, config.interchainGasPaymaster], [
config.mailbox,
config.interchainGasPaymaster,
config.interchainSecurityModule,
],
); );
const router = await this.deployContract(chain, 'router', [], { const router = await this.deployContract(chain, 'router', [], {
create2Salt: this.create2salt + 'router', create2Salt: this.create2salt + 'router',

@ -23,7 +23,7 @@ export type RouterFactories<RouterContract extends Router = Router> =
export type ConnectionClientConfig = { export type ConnectionClientConfig = {
mailbox: types.Address; mailbox: types.Address;
interchainGasPaymaster: types.Address; interchainGasPaymaster: types.Address;
interchainSecurityModule?: types.Address; interchainSecurityModule: types.Address;
}; };
export { Router } from '@hyperlane-xyz/core'; export { Router } from '@hyperlane-xyz/core';

Loading…
Cancel
Save