Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Message debug script #936

Merged
merged 7 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions typescript/infra/scripts/debug-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { ethers } from 'ethers';

import {
AbacusCore,
ChainName,
DispatchedMessage,
DomainIdToChainName,
MultiProvider,
chainConnectionConfigs,
} from '@abacus-network/sdk';
import { utils } from '@abacus-network/utils';

import { assertChain } from '../src/utils/utils';

import { getArgs, getEnvironment } from './utils';

async function main() {
const argv = await getArgs()
.string('tx-hash')
.coerce('tx-hash', (txHash: string) => {
txHash = utils.ensure0x(txHash);

// 0x + 64 hex chars
if (txHash.length !== 66) {
throw Error(`Invalid tx hash length`);
}
return txHash;
})
.describe(
'tx-hash',
'The hash of the tx with one or more message dispatches',
)
.demandOption('tx-hash')

.string('origin-chain')
.coerce('origin-chain', (originChain: string) => assertChain(originChain))
.describe('origin-chain', 'The chain of the dispatching transaction')
.demandOption('origin-chain').argv;

const environment = await getEnvironment();

// Intentionally use public RPC providers to avoid requiring access to our GCP secrets
// to run this script
const multiProvider = new MultiProvider(chainConnectionConfigs);

const core = AbacusCore.fromEnvironment(environment, multiProvider);

const originProvider = multiProvider.getChainProvider(argv.originChain);
const dispatchReceipt = await originProvider.getTransactionReceipt(
argv.txHash,
);
const dispatchedMessages = core.getDispatchedMessages(dispatchReceipt);

// 1 indexed for human friendly logs
let currentMessage = 1;
for (const message of dispatchedMessages) {
console.log(`Message ${currentMessage} of ${dispatchedMessages.length}...`);
await checkMessage(core, multiProvider, message);
console.log('==========');
currentMessage++;
}
console.log(`Evaluated ${dispatchedMessages.length} messages`);
}

async function checkMessage(
core: AbacusCore<any>,
multiProvider: MultiProvider<any>,
message: DispatchedMessage,
) {
console.log(`Leaf index: ${message.leafIndex.toString()}`);
console.log(`Raw bytes: ${message.message}`);
console.log('Parsed message:', message.parsed);

const destinationChain = DomainIdToChainName[message.parsed.destination];

if (destinationChain === undefined) {
console.error(
`ERROR: Unknown destination domain ${message.parsed.destination}`,
);
return;
}

console.log(`Destination chain: ${destinationChain}`);

if (!core.knownChain(destinationChain)) {
console.error(
`ERROR: destination chain ${destinationChain} unknown for environment`,
);
return;
}

const destinationInbox = core.getMailboxPair(
DomainIdToChainName[message.parsed.origin],
destinationChain,
).destinationInbox;

const messageHash = utils.messageHash(message.message, message.leafIndex);
console.log(`Message hash: ${messageHash}`);

const processed = await destinationInbox.messages(messageHash);
if (processed === 1) {
console.log('Message has already been processed');

// TODO: look for past events to find the exact tx in which the message was processed.

return;
} else {
console.log('Message not yet processed');
}

const recipientAddress = utils.bytes32ToAddress(message.parsed.recipient);
const recipientIsContract = await isContract(
multiProvider,
destinationChain,
recipientAddress,
);

if (!recipientIsContract) {
console.error(
`ERROR: recipient address ${recipientAddress} is not a contract, maybe a malformed bytes32 recipient?`,
);
return;
}

const destinationProvider = multiProvider.getChainProvider(destinationChain);
const messageHandlerInterface = new ethers.utils.Interface([
'function handle(uint32 origin, bytes32 sender, bytes message)',
]);

try {
await destinationProvider.call({
from: destinationInbox.address,
to: recipientAddress,
data: messageHandlerInterface.encodeFunctionData('handle', [
message.parsed.origin,
message.parsed.sender,
message.parsed.body,
]),
});
console.log(
'Calling recipient `handle` function from the inbox does not revert',
);
} catch (err: any) {
console.error(
`Error calling recipient \`handle\` function from the inbox`,
err,
);
}
}

async function isContract(
multiProvider: MultiProvider<any>,
chain: ChainName,
address: string,
) {
const provider = multiProvider.getChainProvider(chain);
const code = await provider.getCode(address);
// "Empty" code
return code !== '0x';
}

main().catch((err) => {
console.error('Error in main', err);
});
2 changes: 1 addition & 1 deletion typescript/sdk/src/core/AbacusCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type CoreContractsMap<Chain extends ChainName> = {
[local in Chain]: CoreContracts<Chain, local>;
};

type DispatchedMessage = {
export type DispatchedMessage = {
leafIndex: number;
message: string;
parsed: types.ParsedMessage;
Expand Down
6 changes: 5 additions & 1 deletion typescript/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export { ChainConnection } from './providers/ChainConnection';
export { MultiProvider } from './providers/MultiProvider';
export { RetryJsonRpcProvider, RetryProvider } from './providers/RetryProvider';

export { AbacusCore, CoreContractsMap } from './core/AbacusCore';
export {
AbacusCore,
CoreContractsMap,
DispatchedMessage,
} from './core/AbacusCore';
export {
CoreContracts,
coreFactories,
Expand Down