Make router domains enumerable (#1460)

* Make router domains enumerable

* Fix tests

* Make routers fn backwards compatible

* Add external domains fn
pull/1479/head
Yorke Rhodes 2 years ago committed by GitHub
parent 40a145d988
commit d0f14c1b88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 46
      solidity/contracts/Router.sol
  2. 72
      solidity/contracts/libs/EnumerableMapExtended.sol
  3. 2
      solidity/contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol
  4. 13
      solidity/test/router.test.ts

@ -6,14 +6,16 @@ import {HyperlaneConnectionClient} from "./HyperlaneConnectionClient.sol";
import {IInterchainGasPaymaster} from "../interfaces/IInterchainGasPaymaster.sol";
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IMailbox} from "../interfaces/IMailbox.sol";
import {EnumerableMapExtended} from "./libs/EnumerableMapExtended.sol";
abstract contract Router is HyperlaneConnectionClient, IMessageRecipient {
using EnumerableMapExtended for EnumerableMapExtended.UintToBytes32Map;
string constant NO_ROUTER_ENROLLED_REVERT_MESSAGE =
"No router enrolled for domain. Did you specify the right domain ID?";
// ============ Mutable Storage ============
mapping(uint32 => bytes32) public routers;
EnumerableMapExtended.UintToBytes32Map internal _routers;
uint256[49] private __GAP; // gap for upgrade safety
// ============ Events ============
@ -55,6 +57,22 @@ abstract contract Router is HyperlaneConnectionClient, IMessageRecipient {
}
// ============ External functions ============
function domains() external view returns (uint32[] memory) {
bytes32[] storage rawKeys = _routers.keys();
uint32[] memory keys = new uint32[](rawKeys.length);
for (uint256 i = 0; i < rawKeys.length; i++) {
keys[i] = uint32(uint256(rawKeys[i]));
}
return keys;
}
function routers(uint32 _domain) public view returns (bytes32) {
if (_routers.contains(_domain)) {
return _routers.get(_domain);
} else {
return bytes32(0); // for backwards compatibility with storage mapping
}
}
/**
* @notice Register the address of a Router contract for the same Application on a remote chain
@ -72,15 +90,15 @@ abstract contract Router is HyperlaneConnectionClient, IMessageRecipient {
/**
* @notice Batch version of `enrollRemoteRouter`
* @param _domains The domaisn of the remote Application Routers
* @param _routers The addresses of the remote Application Routers
* @param _addresses The addresses of the remote Application Routers
*/
function enrollRemoteRouters(
uint32[] calldata _domains,
bytes32[] calldata _routers
bytes32[] calldata _addresses
) external virtual onlyOwner {
require(_domains.length == _routers.length, "!length");
require(_domains.length == _addresses.length, "!length");
for (uint256 i = 0; i < _domains.length; i += 1) {
_enrollRemoteRouter(_domains[i], _routers[i]);
_enrollRemoteRouter(_domains[i], _addresses[i]);
}
}
@ -111,24 +129,24 @@ abstract contract Router is HyperlaneConnectionClient, IMessageRecipient {
/**
* @notice Set the router for a given domain
* @param _domain The domain
* @param _router The new router
* @param _address The new router
*/
function _enrollRemoteRouter(uint32 _domain, bytes32 _router) internal {
routers[_domain] = _router;
emit RemoteRouterEnrolled(_domain, _router);
function _enrollRemoteRouter(uint32 _domain, bytes32 _address) internal {
_routers.set(_domain, _address);
emit RemoteRouterEnrolled(_domain, _address);
}
/**
* @notice Return true if the given domain / router is the address of a remote Application Router
* @param _domain The domain of the potential remote Application Router
* @param _router The address of the potential remote Application Router
* @param _address The address of the potential remote Application Router
*/
function _isRemoteRouter(uint32 _domain, bytes32 _router)
function _isRemoteRouter(uint32 _domain, bytes32 _address)
internal
view
returns (bool)
{
return routers[_domain] == _router;
return routers(_domain) == _address;
}
/**
@ -141,7 +159,7 @@ abstract contract Router is HyperlaneConnectionClient, IMessageRecipient {
view
returns (bytes32 _router)
{
_router = routers[_domain];
_router = routers(_domain);
require(_router != bytes32(0), NO_ROUTER_ENROLLED_REVERT_MESSAGE);
}

@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
// ============ External Imports ============
import "@openzeppelin/contracts/utils/structs/EnumerableMap.sol";
// extends EnumerableMap with uint256 => bytes32 type
// modelled after https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.0/contracts/utils/structs/EnumerableMap.sol
library EnumerableMapExtended {
using EnumerableMap for EnumerableMap.Bytes32ToBytes32Map;
struct UintToBytes32Map {
EnumerableMap.Bytes32ToBytes32Map _inner;
}
// ============ Library Functions ============
function keys(UintToBytes32Map storage map)
internal
view
returns (bytes32[] storage)
{
return map._inner._keys._inner._values;
}
function set(
UintToBytes32Map storage map,
uint256 key,
bytes32 value
) internal {
map._inner.set(bytes32(key), value);
}
function get(UintToBytes32Map storage map, uint256 key)
internal
view
returns (bytes32)
{
return map._inner.get(bytes32(key));
}
function remove(UintToBytes32Map storage map, uint256 key)
internal
returns (bool)
{
return map._inner.remove(bytes32(key));
}
function contains(UintToBytes32Map storage map, uint256 key)
internal
view
returns (bool)
{
return map._inner.contains(bytes32(key));
}
function length(UintToBytes32Map storage map)
internal
view
returns (uint256)
{
return map._inner.length();
}
function at(UintToBytes32Map storage map, uint256 index)
internal
view
returns (uint256, bytes32)
{
(bytes32 key, bytes32 value) = map._inner.at(index);
return (uint256(key), value);
}
}

@ -96,7 +96,7 @@ contract CircleBridgeAdapter is ILiquidityLayerAdapter, Router {
uint32 _circleDomain = hyperlaneDomainToCircleDomain[
_destinationDomain
];
bytes32 _remoteRouter = routers[_destinationDomain];
bytes32 _remoteRouter = routers(_destinationDomain);
require(
_remoteRouter != bytes32(0),
"CircleBridgeAdapter: No router for domain"

@ -133,6 +133,19 @@ describe('Router', async () => {
expect(await router.mustHaveRemoteRouter(origin)).to.equal(remoteBytes);
});
describe('#domains', () => {
it('returns the domains', async () => {
await router.enrollRemoteRouters(
[origin, destination],
[
utils.addressToBytes32(nonOwner.address),
utils.addressToBytes32(nonOwner.address),
],
);
expect(await router.domains()).to.deep.equal([origin, destination]);
});
});
it('non-owner cannot enroll remote router', async () => {
await expect(
router

Loading…
Cancel
Save