Implement multiple message relay

multi-message-relay
Yorke Rhodes 2 weeks ago
parent db5875cc22
commit 4c77fe1319
No known key found for this signature in database
GPG Key ID: 9876451F25A3B38C
  1. 67
      typescript/cli/src/status/message.ts
  2. 10
      typescript/sdk/src/core/HyperlaneCore.ts
  3. 34
      typescript/sdk/src/core/HyperlaneRelayer.ts

@ -2,12 +2,10 @@ import type { TransactionReceipt } from '@ethersproject/providers';
import { input } from '@inquirer/prompts';
import { ChainName, HyperlaneCore, HyperlaneRelayer } from '@hyperlane-xyz/sdk';
import { assert, parseWarpRouteMessage } from '@hyperlane-xyz/utils';
import { WriteCommandContext } from '../context/types.js';
import { log, logBlue, logGray, logGreen, logRed } from '../logger.js';
import { log, logBlue, logGreen, logRed } from '../logger.js';
import { runSingleChainSelectionStep } from '../utils/chains.js';
import { stubMerkleTreeConfig } from '../utils/relay.js';
export async function checkMessageStatus({
context,
@ -31,15 +29,9 @@ export async function checkMessageStatus({
);
}
if (!messageId) {
messageId = await input({
message: 'Please specify the message id',
});
}
const chainAddresses = await context.registry.getAddresses();
const coreAddresses = await context.registry.getAddresses();
const core = HyperlaneCore.fromAddressesMap(
chainAddresses,
coreAddresses,
context.multiProvider,
);
@ -50,6 +42,9 @@ export async function checkMessageStatus({
.getProvider(origin)
.getTransactionReceipt(dispatchTx);
} else {
messageId ??= await input({
message: 'Please specify the message id',
});
try {
dispatchedReceipt = await core.getDispatchTx(origin, messageId);
} catch (e) {
@ -64,44 +59,24 @@ export async function checkMessageStatus({
}
}
const messages = core.getDispatchedMessages(dispatchedReceipt!);
const match = messages.find((m) => m.id === messageId);
assert(match, `Message ${messageId} not found in dispatch tx ${dispatchTx}`);
const message = match;
try {
const { amount, recipient } = parseWarpRouteMessage(message.parsed.body);
logGray(`Warping ${amount} to ${recipient}`);
// eslint-disable-next-line no-empty
} catch {}
const messages = core.getDispatchedMessages(dispatchedReceipt);
let deliveredTx: TransactionReceipt;
log(`Checking status of message ${messageId} on ${destination}`);
const delivered = await core.isDelivered(message);
if (delivered) {
logGreen(`Message ${messageId} was delivered`);
deliveredTx = await core.getProcessedReceipt(message);
} else {
logBlue(`Message ${messageId} was not yet delivered`);
if (!selfRelay) {
return;
let undelivered = [];
for (const message of messages) {
log(
`Checking status of message ${message.id} on ${message.parsed.destinationChain}`,
);
const delivered = await core.isDelivered(message);
if (delivered) {
logGreen(`Message ${message.id} was delivered`);
} else {
logBlue(`Message ${message.id} was not yet delivered`);
undelivered.push(message);
}
}
if (selfRelay) {
const relayer = new HyperlaneRelayer({ core });
const hookAddress = await core.getSenderHookAddress(message);
const merkleAddress = chainAddresses[origin].merkleTreeHook;
stubMerkleTreeConfig(relayer, origin, hookAddress, merkleAddress);
deliveredTx = await relayer.relayMessage(dispatchedReceipt);
await relayer.relayAll(dispatchedReceipt, undelivered);
}
logGreen(
`Message ${messageId} delivered in ${
context.multiProvider.tryGetExplorerTxUrl(message.parsed.destination, {
hash: deliveredTx.transactionHash,
}) ?? deliveredTx.transactionHash
}`,
);
}

@ -14,6 +14,7 @@ import {
ProtocolType,
addBufferToGasLimit,
addressToBytes32,
assert,
bytes32ToAddress,
isZeroishAddress,
messageId,
@ -307,7 +308,7 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
message: DispatchedMessage,
): Promise<ethers.ContractReceipt> {
const destinationChain = this.getDestination(message);
const mailbox = this.contractsMap[destinationChain].mailbox;
const mailbox = this.getContracts(destinationChain).mailbox;
const processedBlock = await mailbox.processedAt(message.id);
const events = await mailbox.queryFilter(
@ -315,6 +316,11 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
processedBlock,
processedBlock,
);
assert(
events.length === 1,
`Expected exactly one process event, got ${events.length}`,
);
const processedEvent = events[0];
return processedEvent.getTransactionReceipt();
}
@ -422,6 +428,8 @@ export class HyperlaneCore extends HyperlaneApp<CoreFactories> {
if (matching.length === 0) {
throw new Error(`No dispatch event found for message ${messageId}`);
}
assert(matching.length === 1, 'Multiple dispatch events found');
const event = matching[0]; // only 1 event per message ID
return event.getTransactionReceipt();
}

@ -17,7 +17,7 @@ import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js';
import { BaseMetadataBuilder } from '../ism/metadata/builder.js';
import { IsmConfigSchema } from '../ism/schemas.js';
import { MultiProvider } from '../providers/MultiProvider.js';
import { ChainName } from '../types.js';
import { ChainMap, ChainName } from '../types.js';
import { HyperlaneCore } from './HyperlaneCore.js';
import { DispatchedMessage } from './types.js';
@ -147,6 +147,38 @@ export class HyperlaneRelayer {
return this.getIsmConfig(destinationChain, ism, message);
}
async relayAll(
dispatchTx: providers.TransactionReceipt,
messages = HyperlaneCore.getDispatchedMessages(dispatchTx),
): Promise<ChainMap<ethers.ContractReceipt[]>> {
let destinationMap: ChainMap<DispatchedMessage[]> = {};
messages.forEach((message) => {
destinationMap[message.parsed.destination] ??= [];
destinationMap[message.parsed.destination].push(message);
});
// parallelize relaying to different destinations
return promiseObjAll(
objMap(destinationMap, async (_destination, messages) => {
let receipts: ethers.ContractReceipt[] = [];
// serially relay messages to the same destination
for (const message of messages) {
try {
const receipt = await this.relayMessage(
dispatchTx,
undefined,
message,
);
receipts.push(receipt);
} catch (e) {
this.logger.error(`Failed to relay message ${message.id}, ${e}`);
}
}
return receipts;
}),
);
}
async relayMessage(
dispatchTx: providers.TransactionReceipt,
messageIndex = 0,

Loading…
Cancel
Save