diff --git a/solidity/contracts/isms/MultisigIsm.sol b/solidity/contracts/isms/MultisigIsm.sol index ae7e4c354..901ae1ac0 100644 --- a/solidity/contracts/isms/MultisigIsm.sol +++ b/solidity/contracts/isms/MultisigIsm.sol @@ -25,6 +25,10 @@ contract MultisigIsm is IMultisigIsm, Ownable { using MultisigIsmMetadata for bytes; using MerkleLib for MerkleLib.Tree; + // ============ Constants ============ + + uint8 public constant moduleType = 3; + // ============ Mutable Storage ============ /// @notice The validator threshold for each remote domain. @@ -219,6 +223,25 @@ contract MultisigIsm is IMultisigIsm, Ownable { return _validators; } + /** + * @notice Returns the set of validators responsible for verifying _message + * and the number of signatures required + * @dev Can change based on the content of _message + * @param _message Hyperlane formatted interchain message + * @return validators The array of validator addresses + * @return threshold The number of validator signatures needed + */ + function validatorsAndThreshold(bytes calldata _message) + external + view + returns (address[] memory, uint8) + { + uint32 _origin = _message.origin(); + address[] memory _validators = validators(_origin); + uint8 _threshold = threshold[_origin]; + return (_validators, _threshold); + } + /** * @notice Returns the number of validators enrolled in the validator set. * @param _domain The remote domain of the validator set. diff --git a/solidity/contracts/test/TestIsm.sol b/solidity/contracts/test/TestIsm.sol index 9e32a0c45..03f0f4cd1 100644 --- a/solidity/contracts/test/TestIsm.sol +++ b/solidity/contracts/test/TestIsm.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.0; import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; contract TestIsm is IInterchainSecurityModule { + uint8 public constant moduleType = 0; bool public accept; function setAccept(bool _val) external { diff --git a/solidity/interfaces/IInterchainSecurityModule.sol b/solidity/interfaces/IInterchainSecurityModule.sol index 3a568ebf8..a30f7f8e9 100644 --- a/solidity/interfaces/IInterchainSecurityModule.sol +++ b/solidity/interfaces/IInterchainSecurityModule.sol @@ -2,7 +2,21 @@ pragma solidity >=0.6.11; interface IInterchainSecurityModule { - // Called by the Mailbox to determine whether or not the message should be accepted. + /** + * @notice Returns an enum that represents the type of security model + * encoded by this ISM. + * @dev Relayers infer how to fetch and format metadata. + */ + function moduleType() external view returns (uint8); + + /** + * @notice Defines a security model responsible for verifying interchain + * messages based on the provided metadata. + * @param _metadata Off-chain metadata provided by a relayer, specific to + * the security model encoded by the module (e.g. validator signatures) + * @param _message Hyperlane encoded interchain message + * @return True if the message was verified + */ function verify(bytes calldata _metadata, bytes calldata _message) external returns (bool); diff --git a/solidity/interfaces/IMultisigIsm.sol b/solidity/interfaces/IMultisigIsm.sol index 04fe8a585..427f2421b 100644 --- a/solidity/interfaces/IMultisigIsm.sol +++ b/solidity/interfaces/IMultisigIsm.sol @@ -4,15 +4,16 @@ pragma solidity >=0.6.0; import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol"; interface IMultisigIsm is IInterchainSecurityModule { - function isEnrolled(uint32 _domain, address _validator) + /** + * @notice Returns the set of validators responsible for verifying _message + * and the number of signatures required + * @dev Can change based on the content of _message + * @param _message Hyperlane formatted interchain message + * @return validators The array of validator addresses + * @return threshold The number of validator signatures needed + */ + function validatorsAndThreshold(bytes calldata _message) external view - returns (bool); - - function threshold(uint32 _domain) external view returns (uint8); - - function validators(uint32 _domain) - external - view - returns (address[] memory); + returns (address[] memory validators, uint8 threshold); } diff --git a/solidity/test/isms/multisigIsm.test.ts b/solidity/test/isms/multisigIsm.test.ts index eed76480f..da22c1482 100644 --- a/solidity/test/isms/multisigIsm.test.ts +++ b/solidity/test/isms/multisigIsm.test.ts @@ -3,7 +3,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { expect } from 'chai'; import { ethers } from 'hardhat'; -import { Validator, utils } from '@hyperlane-xyz/utils'; +import { Validator, types, utils } from '@hyperlane-xyz/utils'; import { TestMailbox, @@ -13,6 +13,7 @@ import { TestRecipient__factory, } from '../../types'; import { + dispatchMessage, dispatchMessageAndReturnMetadata, getCommitment, signCheckpoint, @@ -54,6 +55,14 @@ describe('MultisigIsm', async () => { }); }); + describe('#moduleType', () => { + it('returns the correct type', async () => { + expect(await multisigIsm.moduleType()).to.equal( + types.InterchainSecurityModuleType.MULTISIG, + ); + }); + }); + describe('#enrollValidators', () => { let validatorAddresses: string[]; const domains = [ORIGIN_DOMAIN, DESTINATION_DOMAIN]; @@ -317,6 +326,32 @@ describe('MultisigIsm', async () => { }); }); + describe('#validatorsAndThreshold', () => { + const threshold = 7; + let message: string; + beforeEach(async () => { + await multisigIsm.enrollValidators( + [ORIGIN_DOMAIN], + [validators.map((v) => v.address)], + ); + await multisigIsm.setThreshold(ORIGIN_DOMAIN, threshold); + const dispatch = await dispatchMessage( + mailbox, + DESTINATION_DOMAIN, + utils.addressToBytes32(multisigIsm.address), + 'hello', + ); + message = dispatch.message; + }); + + it('returns the validators and threshold', async () => { + expect(await multisigIsm.validatorsAndThreshold(message)).to.deep.equal([ + validators.map((v) => v.address), + threshold, + ]); + }); + }); + describe('#validatorCount', () => { beforeEach(async () => { // Must be done sequentially so gas estimation is correct. diff --git a/typescript/helloworld b/typescript/helloworld deleted file mode 160000 index 646f1e7be..000000000 --- a/typescript/helloworld +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 646f1e7beb18a77e5ca2535dba130f75793aea0f diff --git a/typescript/token b/typescript/token deleted file mode 160000 index a294fdf5e..000000000 --- a/typescript/token +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a294fdf5ea498a018178fd90744c9c09276d9686 diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 248d175ce..c72e80e72 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -58,3 +58,7 @@ export type ParsedMultisigIsmMetadata = { signatures: ethers.utils.BytesLike[]; validators: ethers.utils.BytesLike[]; }; + +export enum InterchainSecurityModuleType { + MULTISIG = 3, +} diff --git a/yarn.lock b/yarn.lock index 2eb97b493..d59f2229b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3713,71 +3713,16 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@1.0.0-beta3, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": - version: 0.0.0-use.local - resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" +"@hyperlane-xyz/helloworld@npm:1.0.0-beta3": + version: 1.0.0-beta3 + resolution: "@hyperlane-xyz/helloworld@npm:1.0.0-beta3" dependencies: "@hyperlane-xyz/sdk": 1.0.0-beta3 - "@nomiclabs/hardhat-ethers": ^2.0.5 - "@nomiclabs/hardhat-waffle": ^2.0.2 "@openzeppelin/contracts-upgradeable": ^4.8.0 - "@trivago/prettier-plugin-sort-imports": ^3.2.0 - "@typechain/ethers-v5": 10.0.0 - "@typechain/hardhat": ^6.0.0 - "@types/mocha": ^9.1.0 - "@typescript-eslint/eslint-plugin": ^5.27.0 - "@typescript-eslint/parser": ^5.27.0 - chai: ^4.3.0 - eslint: ^8.16.0 - eslint-config-prettier: ^8.5.0 - ethereum-waffle: ^3.4.4 ethers: ^5.6.8 - hardhat: ^2.8.4 - hardhat-gas-reporter: ^1.0.7 - prettier: ^2.4.1 - prettier-plugin-solidity: ^1.0.0-beta.5 - solhint: ^3.3.2 - solhint-plugin-prettier: ^0.0.5 - solidity-coverage: ^0.7.14 - ts-node: ^10.8.0 - typechain: 8.0.0 - typescript: ^4.7.2 - languageName: unknown - linkType: soft - -"@hyperlane-xyz/hyperlane-token@workspace:typescript/token": - version: 0.0.0-use.local - resolution: "@hyperlane-xyz/hyperlane-token@workspace:typescript/token" - dependencies: - "@hyperlane-xyz/core": 1.0.0-beta3 - "@hyperlane-xyz/sdk": 1.0.0-beta3 - "@hyperlane-xyz/utils": 1.0.0-beta3 - "@nomiclabs/hardhat-ethers": ^2.0.5 - "@nomiclabs/hardhat-waffle": ^2.0.2 - "@openzeppelin/contracts-upgradeable": ^4.8.0 - "@trivago/prettier-plugin-sort-imports": ^3.2.0 - "@typechain/ethers-v5": 10.0.0 - "@typechain/hardhat": ^6.0.0 - "@types/mocha": ^9.1.0 - "@typescript-eslint/eslint-plugin": ^5.27.0 - "@typescript-eslint/parser": ^5.27.0 - chai: ^4.3.0 - eslint: ^8.16.0 - eslint-config-prettier: ^8.5.0 - ethereum-waffle: ^3.4.4 - ethers: ^5.6.8 - hardhat: ^2.8.4 - hardhat-gas-reporter: ^1.0.7 - prettier: ^2.4.1 - prettier-plugin-solidity: ^1.0.0-beta.5 - solhint: ^3.3.2 - solhint-plugin-prettier: ^0.0.5 - solidity-coverage: ^0.7.14 - ts-node: ^10.8.0 - typechain: 8.0.0 - typescript: ^4.7.2 - languageName: unknown - linkType: soft + checksum: ee13cfc499f1ed636b3d05151bff5316737f87189551f517fb9781838c68dd48eb89c1f353994f2728d0e8e8d8303f2a7c83ffca03ef931f1e859065596c747f + languageName: node + linkType: hard "@hyperlane-xyz/infra@workspace:typescript/infra": version: 0.0.0-use.local