Skip to content

Commit

Permalink
Merge pull request #6042 from ethereum-optimism/willc/prebedrockbug
Browse files Browse the repository at this point in the history
fix(sdk): check for both v0 and v1 messages when looking for RelayedMessages
  • Loading branch information
OptimismBot authored Jun 29, 2023
2 parents 9510472 + 650eba7 commit ff6d298
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-lobsters-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/sdk': minor
---

Fixes issue with legacy withdrawal message status detection
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -626,12 +626,12 @@ jobs:
name: anvil-l1
background: true
# atm this is goerli but we should use mainnet after bedrock is live
command: anvil --fork-url $ANVIL_L1_FORK_URL --fork-block-number 9023108
command: anvil --fork-url $ANVIL_L1_FORK_URL --fork-block-number 9190101
- run:
name: anvil-l2
background: true
# atm this is goerli but we should use mainnet after bedrock is live
command: anvil --fork-url $ANVIL_L2_FORK_URL --port 9545 --fork-block-number 9504811
command: anvil --fork-url $ANVIL_L2_FORK_URL --port 9545 --fork-block-number 10756611
- run:
name: build
command: pnpm build
Expand Down
24 changes: 12 additions & 12 deletions packages/core-utils/src/optimism/hashing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,19 @@ export const hashCrossDomainMessage = (
target: string,
value: BigNumber,
gasLimit: BigNumber,
data: string
message: string
) => {
const { version } = decodeVersionedNonce(nonce)
if (version.eq(0)) {
return hashCrossDomainMessagev0(target, sender, data, nonce)
return hashCrossDomainMessagev0(target, sender, message, nonce)
} else if (version.eq(1)) {
return hashCrossDomainMessagev1(
nonce,
sender,
target,
value,
gasLimit,
data
message
)
}
throw new Error(`unknown version ${version.toString()}`)
Expand All @@ -85,16 +85,16 @@ export const hashCrossDomainMessage = (
*
* @param target The target of the cross domain message
* @param sender The sender of the cross domain message
* @param data The data passed along with the cross domain message
* @param message The message passed along with the cross domain message
* @param nonce The cross domain message nonce
*/
export const hashCrossDomainMessagev0 = (
target: string,
sender: string,
data: string,
message: string,
nonce: BigNumber
) => {
return keccak256(encodeCrossDomainMessageV0(target, sender, data, nonce))
return keccak256(encodeCrossDomainMessageV0(target, sender, message, nonce))
}

/**
Expand All @@ -105,18 +105,18 @@ export const hashCrossDomainMessagev0 = (
* @param target The target of the cross domain message
* @param value The value being sent with the cross domain message
* @param gasLimit The gas limit of the cross domain execution
* @param data The data passed along with the cross domain message
* @param message The message passed along with the cross domain message
*/
export const hashCrossDomainMessagev1 = (
nonce: BigNumber,
sender: string,
target: string,
value: BigNumberish,
gasLimit: BigNumberish,
data: string
message: string
) => {
return keccak256(
encodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data)
encodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, message)
)
}

Expand All @@ -128,15 +128,15 @@ export const hashCrossDomainMessagev1 = (
* @param target The target of the cross domain message
* @param value The value being sent with the cross domain message
* @param gasLimit The gas limit of the cross domain execution
* @param data The data passed along with the cross domain message
* @param message The message passed along with the cross domain message
*/
export const hashWithdrawal = (
nonce: BigNumber,
sender: string,
target: string,
value: BigNumber,
gasLimit: BigNumber,
data: string
message: string
): string => {
const types = ['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes']
const encoded = defaultAbiCoder.encode(types, [
Expand All @@ -145,7 +145,7 @@ export const hashWithdrawal = (
target,
value,
gasLimit,
data,
message,
])
return keccak256(encoded)
}
Expand Down
38 changes: 31 additions & 7 deletions packages/sdk/src/cross-chain-messenger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
decodeVersionedNonce,
encodeVersionedNonce,
getChainId,
hashCrossDomainMessagev0,
hashCrossDomainMessagev1,
} from '@eth-optimism/core-utils'
import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import * as rlp from 'rlp'
Expand Down Expand Up @@ -716,7 +718,18 @@ export class CrossChainMessenger {
message: MessageLike
): Promise<MessageReceipt> {
const resolved = await this.toCrossChainMessage(message)
const messageHash = hashCrossDomainMessage(
// legacy withdrawals relayed prebedrock are v1
const messageHashV0 = hashCrossDomainMessagev0(
resolved.target,
resolved.sender,
resolved.message,
resolved.messageNonce
)
// bedrock withdrawals are v1
// legacy withdrawals relayed postbedrock are v1
// there is no good way to differentiate between the two types of legacy
// so what we will check for both
const messageHashV1 = hashCrossDomainMessagev1(
resolved.messageNonce,
resolved.sender,
resolved.target,
Expand All @@ -731,9 +744,15 @@ export class CrossChainMessenger {
? this.contracts.l2.L2CrossDomainMessenger
: this.contracts.l1.L1CrossDomainMessenger

const relayedMessageEvents = await messenger.queryFilter(
messenger.filters.RelayedMessage(messageHash)
)
// this is safe because we can guarantee only one of these filters max will return something
const relayedMessageEvents = [
...(await messenger.queryFilter(
messenger.filters.RelayedMessage(messageHashV0)
)),
...(await messenger.queryFilter(
messenger.filters.RelayedMessage(messageHashV1)
)),
]

// Great, we found the message. Convert it into a transaction receipt.
if (relayedMessageEvents.length === 1) {
Expand All @@ -749,9 +768,14 @@ export class CrossChainMessenger {

// We didn't find a transaction that relayed the message. We now attempt to find
// FailedRelayedMessage events instead.
const failedRelayedMessageEvents = await messenger.queryFilter(
messenger.filters.FailedRelayedMessage(messageHash)
)
const failedRelayedMessageEvents = [
...(await messenger.queryFilter(
messenger.filters.FailedRelayedMessage(messageHashV0)
)),
...(await messenger.queryFilter(
messenger.filters.FailedRelayedMessage(messageHashV1)
)),
]

// A transaction can fail to be relayed multiple times. We'll always return the last
// transaction that attempted to relay the message.
Expand Down
32 changes: 32 additions & 0 deletions packages/sdk/test-next/messageStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expect, it } from 'vitest'

import { CrossChainMessenger, MessageStatus } from '../src'
import { l1Provider, l2Provider } from './testUtils/ethersProviders'

const crossChainMessenger = new CrossChainMessenger({
l1SignerOrProvider: l1Provider,
l2SignerOrProvider: l2Provider,
l1ChainId: 5,
l2ChainId: 420,
bedrock: true,
})

describe('prove message', () => {
it(`should be able to correctly find a finalized withdrawal`, async () => {
/**
* Tx hash of legacy withdrawal that was claimed
*
* @see https://goerli-optimism.etherscan.io/tx/0xda9e9c8dfc7718bc1499e1e64d8df6cddbabc46e819475a6c755db286a41b9fa
*/
const txWithdrawalHash =
'0xda9e9c8dfc7718bc1499e1e64d8df6cddbabc46e819475a6c755db286a41b9fa'

const txReceipt = await l2Provider.getTransactionReceipt(txWithdrawalHash)

expect(txReceipt).toBeDefined()

expect(await crossChainMessenger.getMessageStatus(txWithdrawalHash)).toBe(
MessageStatus.RELAYED
)
}, 20_000)
})

0 comments on commit ff6d298

Please sign in to comment.