From 9551c347bdc96942cf369629655d894817d68f7c Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Sun, 5 May 2024 14:13:29 -0400 Subject: [PATCH] Simplify core interface --- solidity/package.json | 1 - typescript/cli/src/status/message.ts | 4 +- typescript/sdk/src/core/HyperlaneCore.ts | 66 +++++++-------------- typescript/sdk/src/ism/metadata/multisig.ts | 36 ++++++----- 4 files changed, 45 insertions(+), 62 deletions(-) diff --git a/solidity/package.json b/solidity/package.json index 64cc859e9d..2f956544ae 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -37,7 +37,6 @@ "exports": { ".": "./dist/index.js", "./mailbox": "./dist/contracts/Mailbox.js", - "./merkle": "./dist/contracts/hooks/MerkleTreeHook.js", "./buildArtifact.js": "./dist/buildArtifact.js", "./buildArtifact.json": "./dist/buildArtifact.json", "./contracts": "./contracts" diff --git a/typescript/cli/src/status/message.ts b/typescript/cli/src/status/message.ts index 97a0526dbc..821d728673 100644 --- a/typescript/cli/src/status/message.ts +++ b/typescript/cli/src/status/message.ts @@ -39,6 +39,7 @@ export async function checkMessageStatus({ chainAddresses, context.multiProvider, ); + const mailbox = core.getContracts(destination).mailbox; log(`Checking status of message ${messageId} on ${destination}`); const delivered = await mailbox.delivered(messageId); @@ -65,8 +66,7 @@ export async function checkMessageStatus({ .getProvider(origin) .getTransactionReceipt(dispatchTx); - const messages = core.getDispatchedMessages(dispatchTxReceipt); - const tx = await core.relayMessage(messages[0], dispatchTxReceipt); + const tx = await core.relayMessage(dispatchTxReceipt); logGreen( `Message ${messageId} was relayed in ${context.multiProvider.getExplorerTxUrl( destination, diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index 010e39b2dd..07744d6ec8 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -3,7 +3,6 @@ import { ethers } from 'ethers'; import type { TransactionReceipt as ViemTxReceipt } from 'viem'; import { - IInterchainSecurityModule__factory, IMessageRecipient__factory, MailboxClient__factory, Mailbox__factory, @@ -217,8 +216,9 @@ export class HyperlaneCore extends HyperlaneApp { } async relayMessage( - message: DispatchedMessage, dispatchTx: TransactionReceipt, + index = 0, + message = HyperlaneCore.getDispatchedMessages(dispatchTx)[index], ): Promise { assert( this.multiProvider.hasChain(message.parsed.origin) && @@ -229,6 +229,12 @@ export class HyperlaneCore extends HyperlaneApp { const destinationChain = this.getDestination(message); const mailbox = this.getContracts(destinationChain).mailbox; + const isDelivered = await mailbox.delivered(message.id); + if (isDelivered) { + this.logger.debug(`Message ${message.id} already delivered`); + return this.getProcessedReceipt(message); + } + const recipient = IMessageRecipient__factory.connect( bytes32ToAddress(message.parsed.recipient), mailbox.provider, @@ -241,46 +247,20 @@ export class HyperlaneCore extends HyperlaneApp { { from: mailbox.address }, ); - const recipientIsm = await this.getRecipientIsmAddress(message); - const ism = IInterchainSecurityModule__factory.connect( - recipientIsm, - mailbox.provider, - ); - - return pollAsync( - async () => { - this.logger.debug( - { message, recipientIsm }, - `Building recipient ISM metadata`, - ); - const metadata = await this.buildMetadata(message, dispatchTx); - - this.logger.debug( - { message, metadata }, - `Simulating recipient ISM verification`, - ); - const verified = await ism.callStatic.verify(metadata, message.message); - assert(verified, 'ISM verification failed'); - - const isDelivered = await mailbox.delivered(message.id); - if (isDelivered) { - this.logger.debug(`Message ${message.id} already delivered`); - return this.getProcessedReceipt(message); - } - - this.logger.info( - { message, metadata }, - `Relaying message ${message.id} to ${destinationChain}`, - ); - - return this.multiProvider.handleTx( - destinationChain, - mailbox.process(metadata, message.message), - ); - }, + const metadata = await pollAsync( + () => this.buildMetadata(message, dispatchTx), 5 * 1000, // every 5 seconds 12, // 12 attempts ); + + this.logger.info( + { message, metadata }, + `Relaying message ${message.id} to ${destinationChain}`, + ); + return this.multiProvider.handleTx( + destinationChain, + mailbox.process(metadata, message.message), + ); } async relay( @@ -316,12 +296,11 @@ export class HyperlaneCore extends HyperlaneApp { this.multiProvider.tryGetChainName(destination); if (destinationChain) { this.logger.info( - { chain: originChain, sender, destination, recipient }, + { originChain, sender, destinationChain, recipient }, `Observed message from ${originChain} to ${destinationChain} attempting to relay`, ); - const dispatched = HyperlaneCore.parseDispatchedMessage(message); - const receipt = await event.getTransactionReceipt(); - await this.relayMessage(dispatched, receipt); + const dispatchReceipt = await event.getTransactionReceipt(); + await this.relayMessage(dispatchReceipt); } }, ); @@ -413,6 +392,7 @@ export class HyperlaneCore extends HyperlaneApp { messageId: string, ): Promise { const mailbox = this.contractsMap[originChain].mailbox; + // TODO: scan and chunk if necessary const filter = mailbox.filters.DispatchId(messageId); const matchingEvents = await mailbox.queryFilter(filter); if (matchingEvents.length === 0) { diff --git a/typescript/sdk/src/ism/metadata/multisig.ts b/typescript/sdk/src/ism/metadata/multisig.ts index 4822fb6a83..b6bc6e7443 100644 --- a/typescript/sdk/src/ism/metadata/multisig.ts +++ b/typescript/sdk/src/ism/metadata/multisig.ts @@ -2,7 +2,6 @@ import { TransactionReceipt } from '@ethersproject/providers'; import { joinSignature, splitSignature } from 'ethers/lib/utils.js'; import { MerkleTreeHook__factory } from '@hyperlane-xyz/core'; -import type { InsertedIntoTreeEvent } from '@hyperlane-xyz/core/merkle'; import { Address, Checkpoint, @@ -115,19 +114,25 @@ export class MultisigMetadataBuilder const originChain = this.core.multiProvider.getChainName(match.origin); const s3Validators = await this.s3Validators(originChain, validators); - const checkpointPromises = await Promise.allSettled( + const results = await Promise.allSettled( s3Validators.map((v) => v.getCheckpoint(match.index)), ); - const checkpoints = checkpointPromises + const checkpoints = results .filter( - (p): p is PromiseFulfilledResult => - p.status === 'fulfilled', + (result): result is PromiseFulfilledResult => + result.status === 'fulfilled' && result.value !== undefined, ) - .map((p) => p.value) - .filter((v): v is S3CheckpointWithId => v !== undefined); + .map((result) => result.value); this.logger.debug({ checkpoints }, 'Fetched checkpoints'); + if (checkpoints.length < validators.length) { + this.logger.debug( + { checkpoints, validators, match }, + `Found ${checkpoints.length} checkpoints out of ${validators.length} validators`, + ); + } + const matchingCheckpoints = checkpoints.filter( ({ value }) => eqAddress( @@ -162,24 +167,23 @@ export class MultisigMetadataBuilder 'Merkle proofs are not yet supported', ); + const merkleTree = context.hook.address; + const matchingInsertion = context.dispatchTx.logs - .filter((l) => eqAddressEvm(l.address, context.hook.address)) - .map( - (l) => - MerkleTreeInterface.parseLog(l) as unknown as InsertedIntoTreeEvent, - ) - .find((e) => e.args.messageId === message.id); + .filter((log) => eqAddressEvm(log.address, merkleTree)) + .map((log) => MerkleTreeInterface.parseLog(log)) + .find((event) => event.args.messageId === message.id); assert( matchingInsertion, - `No merkle tree insertion of ${message.id} to ${context.hook.address} found in dispatch tx`, + `No merkle tree insertion of ${message.id} to ${merkleTree} found in dispatch tx`, ); this.logger.debug({ matchingInsertion }, 'Found matching insertion event'); const checkpoints = await this.getS3Checkpoints(context.ism.validators, { origin: message.parsed.origin, - merkleTree: context.hook.address, messageId: message.id, + merkleTree, index: matchingInsertion.args.index, }); assert( @@ -193,7 +197,7 @@ export class MultisigMetadataBuilder ); const signatures = checkpoints - .map((c) => c.signature) + .map((checkpoint) => checkpoint.signature) .slice(0, context.ism.threshold); this.logger.debug(