Commit 59abd46f authored by Will Cory's avatar Will Cory Committed by GitHub

Merge branch 'develop' into update_eco_bridge

parents 9d2270ba 6856455c
---
'@eth-optimism/sdk': minor
---
Fixes issue with legacy withdrawal message status detection
...@@ -627,11 +627,13 @@ jobs: ...@@ -627,11 +627,13 @@ jobs:
background: true background: true
# atm this is goerli but we should use mainnet after bedrock is live # atm this is goerli but we should use mainnet after bedrock is live
command: anvil --fork-url $ANVIL_L1_FORK_URL --fork-block-number 9256679 command: anvil --fork-url $ANVIL_L1_FORK_URL --fork-block-number 9256679
- run: - run:
name: anvil-l2 name: anvil-l2
background: true background: true
# atm this is goerli but we should use mainnet after bedrock is live # 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 11276409 command: anvil --fork-url $ANVIL_L2_FORK_URL --port 9545 --fork-block-number 11276409
- run: - run:
name: build name: build
command: pnpm build command: pnpm build
......
...@@ -38,7 +38,7 @@ jobs: ...@@ -38,7 +38,7 @@ jobs:
cache: pnpm cache: pnpm
- name: Install Dependencies - name: Install Dependencies
run: pnpm --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Install Foundry - name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1 uses: foundry-rs/foundry-toolchain@v1
......
This source diff could not be displayed because it is too large. You can view the blob instead.
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.22.19.cjs"
...@@ -121,6 +121,10 @@ func (s *l2VerifierBackend) StopSequencer(ctx context.Context) (common.Hash, err ...@@ -121,6 +121,10 @@ func (s *l2VerifierBackend) StopSequencer(ctx context.Context) (common.Hash, err
return common.Hash{}, errors.New("stopping the L2Verifier sequencer is not supported") return common.Hash{}, errors.New("stopping the L2Verifier sequencer is not supported")
} }
func (s *l2VerifierBackend) SequencerActive(ctx context.Context) (bool, error) {
return false, nil
}
func (s *L2Verifier) L2Finalized() eth.L2BlockRef { func (s *L2Verifier) L2Finalized() eth.L2BlockRef {
return s.derivation.Finalized() return s.derivation.Finalized()
} }
......
...@@ -26,18 +26,30 @@ func TestStopStartSequencer(t *testing.T) { ...@@ -26,18 +26,30 @@ func TestStopStartSequencer(t *testing.T) {
nodeRPC, err := rpc.DialContext(context.Background(), rollupNode.HTTPEndpoint()) nodeRPC, err := rpc.DialContext(context.Background(), rollupNode.HTTPEndpoint())
require.Nil(t, err, "Error dialing node") require.Nil(t, err, "Error dialing node")
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(nodeRPC))
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
active, err := rollupClient.SequencerActive(ctx)
require.NoError(t, err)
require.True(t, active, "sequencer should be active")
blockBefore := latestBlock(t, l2Seq) blockBefore := latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second) time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter := latestBlock(t, l2Seq) blockAfter := latestBlock(t, l2Seq)
require.Greaterf(t, blockAfter, blockBefore, "Chain did not advance") require.Greaterf(t, blockAfter, blockBefore, "Chain did not advance")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
blockHash := common.Hash{} blockHash, err := rollupClient.StopSequencer(ctx)
err = nodeRPC.CallContext(ctx, &blockHash, "admin_stopSequencer")
require.Nil(t, err, "Error stopping sequencer") require.Nil(t, err, "Error stopping sequencer")
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
active, err = rollupClient.SequencerActive(ctx)
require.NoError(t, err)
require.False(t, active, "sequencer should be inactive")
blockBefore = latestBlock(t, l2Seq) blockBefore = latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second) time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq) blockAfter = latestBlock(t, l2Seq)
...@@ -45,9 +57,15 @@ func TestStopStartSequencer(t *testing.T) { ...@@ -45,9 +57,15 @@ func TestStopStartSequencer(t *testing.T) {
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
err = nodeRPC.CallContext(ctx, nil, "admin_startSequencer", blockHash) err = rollupClient.StartSequencer(ctx, blockHash)
require.Nil(t, err, "Error starting sequencer") require.Nil(t, err, "Error starting sequencer")
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
active, err = rollupClient.SequencerActive(ctx)
require.NoError(t, err)
require.True(t, active, "sequencer should be active again")
blockBefore = latestBlock(t, l2Seq) blockBefore = latestBlock(t, l2Seq)
time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second) time.Sleep(time.Duration(cfg.DeployConfig.L2BlockTime+1) * time.Second)
blockAfter = latestBlock(t, l2Seq) blockAfter = latestBlock(t, l2Seq)
......
...@@ -28,6 +28,7 @@ type driverClient interface { ...@@ -28,6 +28,7 @@ type driverClient interface {
ResetDerivationPipeline(context.Context) error ResetDerivationPipeline(context.Context) error
StartSequencer(ctx context.Context, blockHash common.Hash) error StartSequencer(ctx context.Context, blockHash common.Hash) error
StopSequencer(context.Context) (common.Hash, error) StopSequencer(context.Context) (common.Hash, error)
SequencerActive(context.Context) (bool, error)
} }
type rpcMetrics interface { type rpcMetrics interface {
...@@ -65,6 +66,12 @@ func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) { ...@@ -65,6 +66,12 @@ func (n *adminAPI) StopSequencer(ctx context.Context) (common.Hash, error) {
return n.dr.StopSequencer(ctx) return n.dr.StopSequencer(ctx)
} }
func (n *adminAPI) SequencerActive(ctx context.Context) (bool, error) {
recordDur := n.m.RecordRPCServerRequest("admin_sequencerActive")
defer recordDur()
return n.dr.SequencerActive(ctx)
}
type nodeAPI struct { type nodeAPI struct {
config *rollup.Config config *rollup.Config
client l2EthClient client l2EthClient
......
...@@ -227,3 +227,7 @@ func (c *mockDriverClient) StartSequencer(ctx context.Context, blockHash common. ...@@ -227,3 +227,7 @@ func (c *mockDriverClient) StartSequencer(ctx context.Context, blockHash common.
func (c *mockDriverClient) StopSequencer(ctx context.Context) (common.Hash, error) { func (c *mockDriverClient) StopSequencer(ctx context.Context) (common.Hash, error) {
return c.Mock.MethodCalled("StopSequencer").Get(0).(common.Hash), nil return c.Mock.MethodCalled("StopSequencer").Get(0).(common.Hash), nil
} }
func (c *mockDriverClient) SequencerActive(ctx context.Context) (bool, error) {
return c.Mock.MethodCalled("SequencerActive").Get(0).(bool), nil
}
...@@ -127,6 +127,7 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, al ...@@ -127,6 +127,7 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, al
forceReset: make(chan chan struct{}, 10), forceReset: make(chan chan struct{}, 10),
startSequencer: make(chan hashAndErrorChannel, 10), startSequencer: make(chan hashAndErrorChannel, 10),
stopSequencer: make(chan chan hashAndError, 10), stopSequencer: make(chan chan hashAndError, 10),
sequencerActive: make(chan chan bool, 10),
sequencerNotifs: sequencerStateListener, sequencerNotifs: sequencerStateListener,
config: cfg, config: cfg,
driverConfig: driverCfg, driverConfig: driverCfg,
......
...@@ -47,6 +47,11 @@ type Driver struct { ...@@ -47,6 +47,11 @@ type Driver struct {
// It tells the caller that the sequencer stopped by returning the latest sequenced L2 block hash. // It tells the caller that the sequencer stopped by returning the latest sequenced L2 block hash.
stopSequencer chan chan hashAndError stopSequencer chan chan hashAndError
// Upon receiving a channel in this channel, the current sequencer status is queried.
// It tells the caller the status by outputting a boolean to the provided channel:
// true when the sequencer is active, false when it is not.
sequencerActive chan chan bool
// sequencerNotifs is notified when the sequencer is started or stopped // sequencerNotifs is notified when the sequencer is started or stopped
sequencerNotifs SequencerStateListener sequencerNotifs SequencerStateListener
...@@ -373,6 +378,8 @@ func (s *Driver) eventLoop() { ...@@ -373,6 +378,8 @@ func (s *Driver) eventLoop() {
s.driverConfig.SequencerStopped = true s.driverConfig.SequencerStopped = true
respCh <- hashAndError{hash: s.derivation.UnsafeL2Head().Hash} respCh <- hashAndError{hash: s.derivation.UnsafeL2Head().Hash}
} }
case respCh := <-s.sequencerActive:
respCh <- !s.driverConfig.SequencerStopped
case <-s.done: case <-s.done:
return return
} }
...@@ -436,6 +443,24 @@ func (s *Driver) StopSequencer(ctx context.Context) (common.Hash, error) { ...@@ -436,6 +443,24 @@ func (s *Driver) StopSequencer(ctx context.Context) (common.Hash, error) {
} }
} }
func (s *Driver) SequencerActive(ctx context.Context) (bool, error) {
if !s.driverConfig.SequencerEnabled {
return false, nil
}
respCh := make(chan bool, 1)
select {
case <-ctx.Done():
return false, ctx.Err()
case s.sequencerActive <- respCh:
select {
case <-ctx.Done():
return false, ctx.Err()
case active := <-respCh:
return active, nil
}
}
}
// syncStatus returns the current sync status, and should only be called synchronously with // syncStatus returns the current sync status, and should only be called synchronously with
// the driver event loop to avoid retrieval of an inconsistent status. // the driver event loop to avoid retrieval of an inconsistent status.
func (s *Driver) syncStatus() *eth.SyncStatus { func (s *Driver) syncStatus() *eth.SyncStatus {
......
...@@ -52,3 +52,9 @@ func (r *RollupClient) StopSequencer(ctx context.Context) (common.Hash, error) { ...@@ -52,3 +52,9 @@ func (r *RollupClient) StopSequencer(ctx context.Context) (common.Hash, error) {
err := r.rpc.CallContext(ctx, &result, "admin_stopSequencer") err := r.rpc.CallContext(ctx, &result, "admin_stopSequencer")
return result, err return result, err
} }
func (r *RollupClient) SequencerActive(ctx context.Context) (bool, error) {
var result bool
err := r.rpc.CallContext(ctx, &result, "admin_sequencerActive")
return result, err
}
...@@ -4,26 +4,8 @@ ...@@ -4,26 +4,8 @@
"author": "Optimism PBC", "author": "Optimism PBC",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"workspaces": {
"packages": [
"packages/*",
"endpoint-monitor"
],
"nohoist": [
"**/typechain/*",
"**/@typechain/*",
"@eth-optimism/contracts-bedrock/ds-test",
"@eth-optimism/contracts-bedrock/forge-std",
"@eth-optimism/contracts-bedrock/@rari-capital/solmate",
"@eth-optimism/contracts-bedrock/clones-with-immutable-args",
"**/@openzeppelin/*",
"@eth-optimism/contracts-periphery/ds-test",
"@eth-optimism/contracts-periphery/forge-std",
"@eth-optimism/contracts-periphery/@rari-capital/solmate",
"forta-agent"
]
},
"engines": { "engines": {
"node": ">=16",
"pnpm": ">=8" "pnpm": ">=8"
}, },
"scripts": { "scripts": {
......
...@@ -62,11 +62,11 @@ export const hashCrossDomainMessage = ( ...@@ -62,11 +62,11 @@ export const hashCrossDomainMessage = (
target: string, target: string,
value: BigNumber, value: BigNumber,
gasLimit: BigNumber, gasLimit: BigNumber,
data: string message: string
) => { ) => {
const { version } = decodeVersionedNonce(nonce) const { version } = decodeVersionedNonce(nonce)
if (version.eq(0)) { if (version.eq(0)) {
return hashCrossDomainMessagev0(target, sender, data, nonce) return hashCrossDomainMessagev0(target, sender, message, nonce)
} else if (version.eq(1)) { } else if (version.eq(1)) {
return hashCrossDomainMessagev1( return hashCrossDomainMessagev1(
nonce, nonce,
...@@ -74,7 +74,7 @@ export const hashCrossDomainMessage = ( ...@@ -74,7 +74,7 @@ export const hashCrossDomainMessage = (
target, target,
value, value,
gasLimit, gasLimit,
data message
) )
} }
throw new Error(`unknown version ${version.toString()}`) throw new Error(`unknown version ${version.toString()}`)
...@@ -85,16 +85,16 @@ export const hashCrossDomainMessage = ( ...@@ -85,16 +85,16 @@ export const hashCrossDomainMessage = (
* *
* @param target The target of the cross domain message * @param target The target of the cross domain message
* @param sender The sender 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 * @param nonce The cross domain message nonce
*/ */
export const hashCrossDomainMessagev0 = ( export const hashCrossDomainMessagev0 = (
target: string, target: string,
sender: string, sender: string,
data: string, message: string,
nonce: BigNumber nonce: BigNumber
) => { ) => {
return keccak256(encodeCrossDomainMessageV0(target, sender, data, nonce)) return keccak256(encodeCrossDomainMessageV0(target, sender, message, nonce))
} }
/** /**
...@@ -105,7 +105,7 @@ export const hashCrossDomainMessagev0 = ( ...@@ -105,7 +105,7 @@ export const hashCrossDomainMessagev0 = (
* @param target The target of the cross domain message * @param target The target of the cross domain message
* @param value The value being sent with 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 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 = ( export const hashCrossDomainMessagev1 = (
nonce: BigNumber, nonce: BigNumber,
...@@ -113,10 +113,10 @@ export const hashCrossDomainMessagev1 = ( ...@@ -113,10 +113,10 @@ export const hashCrossDomainMessagev1 = (
target: string, target: string,
value: BigNumberish, value: BigNumberish,
gasLimit: BigNumberish, gasLimit: BigNumberish,
data: string message: string
) => { ) => {
return keccak256( return keccak256(
encodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data) encodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, message)
) )
} }
...@@ -128,7 +128,7 @@ export const hashCrossDomainMessagev1 = ( ...@@ -128,7 +128,7 @@ export const hashCrossDomainMessagev1 = (
* @param target The target of the cross domain message * @param target The target of the cross domain message
* @param value The value being sent with 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 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 = ( export const hashWithdrawal = (
nonce: BigNumber, nonce: BigNumber,
...@@ -136,7 +136,7 @@ export const hashWithdrawal = ( ...@@ -136,7 +136,7 @@ export const hashWithdrawal = (
target: string, target: string,
value: BigNumber, value: BigNumber,
gasLimit: BigNumber, gasLimit: BigNumber,
data: string message: string
): string => { ): string => {
const types = ['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes'] const types = ['uint256', 'address', 'address', 'uint256', 'uint256', 'bytes']
const encoded = defaultAbiCoder.encode(types, [ const encoded = defaultAbiCoder.encode(types, [
...@@ -145,7 +145,7 @@ export const hashWithdrawal = ( ...@@ -145,7 +145,7 @@ export const hashWithdrawal = (
target, target,
value, value,
gasLimit, gasLimit,
data, message,
]) ])
return keccak256(encoded) return keccak256(encoded)
} }
......
...@@ -27,6 +27,8 @@ import { ...@@ -27,6 +27,8 @@ import {
decodeVersionedNonce, decodeVersionedNonce,
encodeVersionedNonce, encodeVersionedNonce,
getChainId, getChainId,
hashCrossDomainMessagev0,
hashCrossDomainMessagev1,
} from '@eth-optimism/core-utils' } from '@eth-optimism/core-utils'
import { getContractInterface, predeploys } from '@eth-optimism/contracts' import { getContractInterface, predeploys } from '@eth-optimism/contracts'
import * as rlp from 'rlp' import * as rlp from 'rlp'
...@@ -716,7 +718,18 @@ export class CrossChainMessenger { ...@@ -716,7 +718,18 @@ export class CrossChainMessenger {
message: MessageLike message: MessageLike
): Promise<MessageReceipt> { ): Promise<MessageReceipt> {
const resolved = await this.toCrossChainMessage(message) 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.messageNonce,
resolved.sender, resolved.sender,
resolved.target, resolved.target,
...@@ -731,9 +744,15 @@ export class CrossChainMessenger { ...@@ -731,9 +744,15 @@ export class CrossChainMessenger {
? this.contracts.l2.L2CrossDomainMessenger ? this.contracts.l2.L2CrossDomainMessenger
: this.contracts.l1.L1CrossDomainMessenger : this.contracts.l1.L1CrossDomainMessenger
const relayedMessageEvents = await messenger.queryFilter( // this is safe because we can guarantee only one of these filters max will return something
messenger.filters.RelayedMessage(messageHash) 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. // Great, we found the message. Convert it into a transaction receipt.
if (relayedMessageEvents.length === 1) { if (relayedMessageEvents.length === 1) {
...@@ -749,9 +768,14 @@ export class CrossChainMessenger { ...@@ -749,9 +768,14 @@ export class CrossChainMessenger {
// We didn't find a transaction that relayed the message. We now attempt to find // We didn't find a transaction that relayed the message. We now attempt to find
// FailedRelayedMessage events instead. // FailedRelayedMessage events instead.
const failedRelayedMessageEvents = await messenger.queryFilter( const failedRelayedMessageEvents = [
messenger.filters.FailedRelayedMessage(messageHash) ...(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 // A transaction can fail to be relayed multiple times. We'll always return the last
// transaction that attempted to relay the message. // transaction that attempted to relay the message.
......
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)
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment