feat: do not exhaustively derive routing config in TS relayer (#4765)

### Description

CLI commands using HyperlaneRelayer (`--relay`) were very slow due to
exhaustive routing ISM/hook derivation. This short-circuits the
derivation when there is a known "message context" to evaluate the
routing hook and ISMs for.

### Related issues

<!--
- Fixes #[issue number here]
-->

### Backward compatibility

Yes

### Testing

Manual
pull/4772/head
Yorke Rhodes 4 weeks ago committed by GitHub
parent cd666d5d0c
commit 5dabdf3887
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      .changeset/cold-dingos-give.md
  2. 20
      typescript/sdk/src/core/HyperlaneRelayer.ts
  3. 8
      typescript/sdk/src/hook/EvmHookReader.ts
  4. 9
      typescript/sdk/src/ism/EvmIsmReader.ts

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': patch
---
Optimize HyperlaneRelayer routing config derivation

@ -75,12 +75,18 @@ export class HyperlaneRelayer {
async getHookConfig(
chain: ChainName,
hook: Address,
messageContext?: DispatchedMessage,
): Promise<DerivedHookConfig> {
let config: DerivedHookConfig | undefined;
if (this.cache?.hook[chain]?.[hook]) {
config = this.cache.hook[chain][hook] as DerivedHookConfig | undefined;
} else {
const evmHookReader = new EvmHookReader(this.multiProvider, chain);
const evmHookReader = new EvmHookReader(
this.multiProvider,
chain,
undefined,
messageContext,
);
config = await evmHookReader.deriveHookConfig(hook);
}
@ -98,12 +104,18 @@ export class HyperlaneRelayer {
async getIsmConfig(
chain: ChainName,
ism: Address,
messageContext?: DispatchedMessage,
): Promise<DerivedIsmConfig> {
let config: DerivedIsmConfig | undefined;
if (this.cache?.ism[chain]?.[ism]) {
config = this.cache.ism[chain][ism] as DerivedIsmConfig | undefined;
} else {
const evmIsmReader = new EvmIsmReader(this.multiProvider, chain);
const evmIsmReader = new EvmIsmReader(
this.multiProvider,
chain,
undefined,
messageContext,
);
config = await evmIsmReader.deriveIsmConfig(ism);
}
@ -124,7 +136,7 @@ export class HyperlaneRelayer {
): Promise<DerivedHookConfig> {
const originChain = this.core.getOrigin(message);
const hook = await this.core.getSenderHookAddress(message);
return this.getHookConfig(originChain, hook);
return this.getHookConfig(originChain, hook, message);
}
async getRecipientIsmConfig(
@ -132,7 +144,7 @@ export class HyperlaneRelayer {
): Promise<DerivedIsmConfig> {
const destinationChain = this.core.getDestination(message);
const ism = await this.core.getRecipientIsmAddress(message);
return this.getIsmConfig(destinationChain, ism);
return this.getIsmConfig(destinationChain, ism, message);
}
async relayMessage(

@ -26,6 +26,7 @@ import {
} from '@hyperlane-xyz/utils';
import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js';
import { DispatchedMessage } from '../core/types.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainNameOrId } from '../types.js';
import { HyperlaneReader } from '../utils/HyperlaneReader.js';
@ -96,6 +97,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
protected readonly concurrency: number = multiProvider.tryGetRpcConcurrency(
chain,
) ?? DEFAULT_CONTRACT_READ_CONCURRENCY,
protected readonly messageContext?: DispatchedMessage,
) {
super(multiProvider, chain);
}
@ -379,6 +381,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
address: Address,
): Promise<WithAddress<DomainRoutingHookConfig>> {
const hook = DomainRoutingHook__factory.connect(address, this.provider);
this.assertHookType(await hook.hookType(), OnchainHookType.ROUTING);
const owner = await hook.owner();
@ -403,6 +406,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
address,
this.provider,
);
this.assertHookType(
await hook.hookType(),
OnchainHookType.FALLBACK_ROUTING,
@ -430,7 +434,9 @@ export class EvmHookReader extends HyperlaneReader implements HookReader {
private async fetchDomainHooks(
hook: DomainRoutingHook | FallbackDomainRoutingHook,
): Promise<RoutingHookConfig['domains']> {
const domainIds = this.multiProvider.getKnownDomainIds();
const domainIds = this.messageContext
? [this.messageContext.parsed.destination]
: this.multiProvider.getKnownDomainIds();
const domainHooks: RoutingHookConfig['domains'] = {};
await concurrentMap(this.concurrency, domainIds, async (domainId) => {

@ -1,4 +1,4 @@
import { ethers } from 'ethers';
import { BigNumber, ethers } from 'ethers';
import {
ArbL2ToL1Ism__factory,
@ -21,6 +21,7 @@ import {
} from '@hyperlane-xyz/utils';
import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js';
import { DispatchedMessage } from '../core/types.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainNameOrId } from '../types.js';
import { HyperlaneReader } from '../utils/HyperlaneReader.js';
@ -66,6 +67,7 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader {
protected readonly concurrency: number = multiProvider.tryGetRpcConcurrency(
chain,
) ?? DEFAULT_CONTRACT_READ_CONCURRENCY,
protected readonly messageContext?: DispatchedMessage,
) {
super(multiProvider, chain);
}
@ -129,11 +131,14 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader {
address,
this.provider,
);
const owner = await ism.owner();
this.assertModuleType(await ism.moduleType(), ModuleType.ROUTING);
const domainIds = this.messageContext
? [BigNumber.from(this.messageContext.parsed.origin)]
: await ism.domains();
const domains: RoutingIsmConfig['domains'] = {};
const domainIds = await ism.domains();
await concurrentMap(this.concurrency, domainIds, async (domainId) => {
const chainName = this.multiProvider.tryGetChainName(domainId.toNumber());

Loading…
Cancel
Save