Commit 6da12c7c authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge pull request #4914 from ethereum-optimism/bugfix/withdrawal-filtering

op-chain-ops: Correctly filter withdrawal hashes
parents 2e33b4e8 bf2eab6e
...@@ -284,11 +284,11 @@ func main() { ...@@ -284,11 +284,11 @@ func main() {
if !isFinalized { if !isFinalized {
// Get the ETH balance of the withdrawal target *before* the finalization // Get the ETH balance of the withdrawal target *before* the finalization
targetBalBefore, err := clients.L1Client.BalanceAt(context.Background(), *wd.Target, nil) targetBalBefore, err := clients.L1Client.BalanceAt(context.Background(), wd.XDomainTarget, nil)
if err != nil { if err != nil {
return err return err
} }
log.Debug("Balance before finalization", "balance", targetBalBefore, "account", *wd.Target) log.Debug("Balance before finalization", "balance", targetBalBefore, "account", wd.XDomainTarget)
log.Info("Finalizing withdrawal") log.Info("Finalizing withdrawal")
receipt, err := finalizeWithdrawalTransaction(contracts, clients, opts, wd, withdrawal) receipt, err := finalizeWithdrawalTransaction(contracts, clients, opts, wd, withdrawal)
...@@ -369,14 +369,14 @@ func main() { ...@@ -369,14 +369,14 @@ func main() {
if method != nil { if method != nil {
log.Info("withdrawal action", "function", method.Name, "value", wdValue) log.Info("withdrawal action", "function", method.Name, "value", wdValue)
} else { } else {
log.Info("unknown method", "to", wd.Target, "data", hexutil.Encode(wd.Data)) log.Info("unknown method", "to", wd.XDomainTarget, "data", hexutil.Encode(wd.XDomainData))
if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "unknown method"); err != nil { if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "unknown method"); err != nil {
return err return err
} }
} }
// check that the user's intents are actually executed // check that the user's intents are actually executed
if common.HexToAddress(callFrame.To) != *wd.Target { if common.HexToAddress(callFrame.To) != wd.XDomainTarget {
log.Info("target mismatch", "index", i) log.Info("target mismatch", "index", i)
if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "target mismatch"); err != nil { if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "target mismatch"); err != nil {
...@@ -384,7 +384,7 @@ func main() { ...@@ -384,7 +384,7 @@ func main() {
} }
continue continue
} }
if !bytes.Equal(hexutil.MustDecode(callFrame.Input), wd.Data) { if !bytes.Equal(hexutil.MustDecode(callFrame.Input), wd.XDomainData) {
log.Info("calldata mismatch", "index", i) log.Info("calldata mismatch", "index", i)
if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "calldata mismatch"); err != nil { if err := writeSuspicious(f, withdrawal, wd, finalizationTrace, i, "calldata mismatch"); err != nil {
...@@ -401,7 +401,7 @@ func main() { ...@@ -401,7 +401,7 @@ func main() {
} }
// Get the ETH balance of the withdrawal target *after* the finalization // Get the ETH balance of the withdrawal target *after* the finalization
targetBalAfter, err := clients.L1Client.BalanceAt(context.Background(), *wd.Target, nil) targetBalAfter, err := clients.L1Client.BalanceAt(context.Background(), wd.XDomainTarget, nil)
if err != nil { if err != nil {
return err return err
} }
...@@ -621,7 +621,7 @@ func finalizeWithdrawalTransaction( ...@@ -621,7 +621,7 @@ func finalizeWithdrawalTransaction(
wd *crossdomain.LegacyWithdrawal, wd *crossdomain.LegacyWithdrawal,
withdrawal *crossdomain.Withdrawal, withdrawal *crossdomain.Withdrawal,
) (*types.Receipt, error) { ) (*types.Receipt, error) {
if wd.Target == nil { if wd.XDomainTarget == (common.Address{}) {
return nil, errors.New("withdrawal target is nil, should never happen") return nil, errors.New("withdrawal target is nil, should never happen")
} }
...@@ -833,7 +833,7 @@ func newTransactor(ctx *cli.Context) (*bind.TransactOpts, error) { ...@@ -833,7 +833,7 @@ func newTransactor(ctx *cli.Context) (*bind.TransactOpts, error) {
// represents the user's intent. // represents the user's intent.
func findWithdrawalCall(trace *callFrame, wd *crossdomain.LegacyWithdrawal, l1xdm common.Address) *callFrame { func findWithdrawalCall(trace *callFrame, wd *crossdomain.LegacyWithdrawal, l1xdm common.Address) *callFrame {
isCall := trace.Type == "CALL" isCall := trace.Type == "CALL"
isTarget := common.HexToAddress(trace.To) == *wd.Target isTarget := common.HexToAddress(trace.To) == wd.XDomainTarget
isFrom := common.HexToAddress(trace.From) == l1xdm isFrom := common.HexToAddress(trace.From) == l1xdm
if isCall && isTarget && isFrom { if isCall && isTarget && isFrom {
return trace return trace
......
...@@ -37,8 +37,8 @@ func init() { ...@@ -37,8 +37,8 @@ func init() {
// EncodeCrossDomainMessageV0 will encode the calldata for // EncodeCrossDomainMessageV0 will encode the calldata for
// "relayMessage(address,address,bytes,uint256)", // "relayMessage(address,address,bytes,uint256)",
func EncodeCrossDomainMessageV0( func EncodeCrossDomainMessageV0(
target *common.Address, target common.Address,
sender *common.Address, sender common.Address,
message []byte, message []byte,
nonce *big.Int, nonce *big.Int,
) ([]byte, error) { ) ([]byte, error) {
...@@ -49,8 +49,8 @@ func EncodeCrossDomainMessageV0( ...@@ -49,8 +49,8 @@ func EncodeCrossDomainMessageV0(
// "relayMessage(uint256,address,address,uint256,uint256,bytes)", // "relayMessage(uint256,address,address,uint256,uint256,bytes)",
func EncodeCrossDomainMessageV1( func EncodeCrossDomainMessageV1(
nonce *big.Int, nonce *big.Int,
sender *common.Address, sender common.Address,
target *common.Address, target common.Address,
value *big.Int, value *big.Int,
gasLimit *big.Int, gasLimit *big.Int,
data []byte, data []byte,
......
...@@ -10,8 +10,8 @@ import ( ...@@ -10,8 +10,8 @@ import (
// HashCrossDomainMessageV0 computes the pre bedrock cross domain messaging // HashCrossDomainMessageV0 computes the pre bedrock cross domain messaging
// hashing scheme. // hashing scheme.
func HashCrossDomainMessageV0( func HashCrossDomainMessageV0(
target *common.Address, target common.Address,
sender *common.Address, sender common.Address,
data []byte, data []byte,
nonce *big.Int, nonce *big.Int,
) (common.Hash, error) { ) (common.Hash, error) {
...@@ -27,8 +27,8 @@ func HashCrossDomainMessageV0( ...@@ -27,8 +27,8 @@ func HashCrossDomainMessageV0(
// messaging hashing scheme. // messaging hashing scheme.
func HashCrossDomainMessageV1( func HashCrossDomainMessageV1(
nonce *big.Int, nonce *big.Int,
sender *common.Address, sender common.Address,
target *common.Address, target common.Address,
value *big.Int, value *big.Int,
gasLimit *big.Int, gasLimit *big.Int,
data []byte, data []byte,
......
...@@ -16,21 +16,28 @@ import ( ...@@ -16,21 +16,28 @@ import (
// LegacyWithdrawal represents a pre bedrock upgrade withdrawal. // LegacyWithdrawal represents a pre bedrock upgrade withdrawal.
type LegacyWithdrawal struct { type LegacyWithdrawal struct {
Target *common.Address `json:"target"` // MessageSender is the caller of the message passer
Sender *common.Address `json:"sender"` MessageSender common.Address `json:"who"`
Data hexutil.Bytes `json:"data"` // XDomainTarget is the L1 target of the withdrawal message
Nonce *big.Int `json:"nonce"` XDomainTarget common.Address `json:"target"`
// XDomainSender is the L2 withdrawing account
XDomainSender common.Address `json:"sender"`
// XDomainData represents the calldata of the withdrawal message
XDomainData hexutil.Bytes `json:"data"`
// XDomainNonce represents the nonce of the withdrawal
XDomainNonce *big.Int `json:"nonce"`
} }
var _ WithdrawalMessage = (*LegacyWithdrawal)(nil) var _ WithdrawalMessage = (*LegacyWithdrawal)(nil)
// NewLegacyWithdrawal will construct a LegacyWithdrawal // NewLegacyWithdrawal will construct a LegacyWithdrawal
func NewLegacyWithdrawal(target, sender *common.Address, data []byte, nonce *big.Int) *LegacyWithdrawal { func NewLegacyWithdrawal(msgSender, target, sender common.Address, data []byte, nonce *big.Int) *LegacyWithdrawal {
return &LegacyWithdrawal{ return &LegacyWithdrawal{
Target: target, MessageSender: msgSender,
Sender: sender, XDomainTarget: target,
Data: data, XDomainSender: sender,
Nonce: nonce, XDomainData: data,
XDomainNonce: nonce,
} }
} }
...@@ -39,7 +46,7 @@ func NewLegacyWithdrawal(target, sender *common.Address, data []byte, nonce *big ...@@ -39,7 +46,7 @@ func NewLegacyWithdrawal(target, sender *common.Address, data []byte, nonce *big
// through the standard optimism cross domain messaging system by hashing in // through the standard optimism cross domain messaging system by hashing in
// the L2CrossDomainMessenger address. // the L2CrossDomainMessenger address.
func (w *LegacyWithdrawal) Encode() ([]byte, error) { func (w *LegacyWithdrawal) Encode() ([]byte, error) {
enc, err := EncodeCrossDomainMessageV0(w.Target, w.Sender, []byte(w.Data), w.Nonce) enc, err := EncodeCrossDomainMessageV0(w.XDomainTarget, w.XDomainSender, []byte(w.XDomainData), w.XDomainNonce)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot encode LegacyWithdrawal: %w", err) return nil, fmt.Errorf("cannot encode LegacyWithdrawal: %w", err)
} }
...@@ -62,9 +69,6 @@ func (w *LegacyWithdrawal) Decode(data []byte) error { ...@@ -62,9 +69,6 @@ func (w *LegacyWithdrawal) Decode(data []byte) error {
} }
msgSender := data[len(data)-len(predeploys.L2CrossDomainMessengerAddr):] msgSender := data[len(data)-len(predeploys.L2CrossDomainMessengerAddr):]
if !bytes.Equal(msgSender, predeploys.L2CrossDomainMessengerAddr.Bytes()) {
return errors.New("invalid msg.sender")
}
raw := data[4 : len(data)-len(predeploys.L2CrossDomainMessengerAddr)] raw := data[4 : len(data)-len(predeploys.L2CrossDomainMessengerAddr)]
...@@ -97,10 +101,11 @@ func (w *LegacyWithdrawal) Decode(data []byte) error { ...@@ -97,10 +101,11 @@ func (w *LegacyWithdrawal) Decode(data []byte) error {
return errors.New("cannot abi decode nonce") return errors.New("cannot abi decode nonce")
} }
w.Target = &target w.MessageSender = common.BytesToAddress(msgSender)
w.Sender = &sender w.XDomainTarget = target
w.Data = hexutil.Bytes(msgData) w.XDomainSender = sender
w.Nonce = nonce w.XDomainData = msgData
w.XDomainNonce = nonce
return nil return nil
} }
...@@ -142,19 +147,15 @@ func (w *LegacyWithdrawal) Value() (*big.Int, error) { ...@@ -142,19 +147,15 @@ func (w *LegacyWithdrawal) Value() (*big.Int, error) {
value := new(big.Int) value := new(big.Int)
// Parse the 4byte selector // Parse the 4byte selector
method, err := abi.MethodById(w.Data) method, err := abi.MethodById(w.XDomainData)
// If it is an unknown selector, there is no value // If it is an unknown selector, there is no value
if err != nil { if err != nil {
return value, nil return value, nil
} }
if w.Sender == nil { isFromL2StandardBridge := w.XDomainSender == predeploys.L2StandardBridgeAddr
return nil, errors.New("sender is nil")
}
isFromL2StandardBridge := *w.Sender == predeploys.L2StandardBridgeAddr
if isFromL2StandardBridge && method.Name == "finalizeETHWithdrawal" { if isFromL2StandardBridge && method.Name == "finalizeETHWithdrawal" {
data, err := method.Inputs.Unpack(w.Data[4:]) data, err := method.Inputs.Unpack(w.XDomainData[4:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -177,11 +178,11 @@ func (w *LegacyWithdrawal) Value() (*big.Int, error) { ...@@ -177,11 +178,11 @@ func (w *LegacyWithdrawal) Value() (*big.Int, error) {
// the concept of value or gaslimit, so set them to 0. // the concept of value or gaslimit, so set them to 0.
func (w *LegacyWithdrawal) CrossDomainMessage() *CrossDomainMessage { func (w *LegacyWithdrawal) CrossDomainMessage() *CrossDomainMessage {
return &CrossDomainMessage{ return &CrossDomainMessage{
Nonce: w.Nonce, Nonce: w.XDomainNonce,
Sender: w.Sender, Sender: w.XDomainSender,
Target: w.Target, Target: w.XDomainTarget,
Value: new(big.Int), Value: new(big.Int),
GasLimit: new(big.Int), GasLimit: new(big.Int),
Data: []byte(w.Data), Data: []byte(w.XDomainData),
} }
} }
...@@ -133,8 +133,7 @@ func TestWithdrawalLegacyStorageSlot(t *testing.T) { ...@@ -133,8 +133,7 @@ func TestWithdrawalLegacyStorageSlot(t *testing.T) {
// Cast the cross domain message to a withdrawal. Note that // Cast the cross domain message to a withdrawal. Note that
// this only works for legacy style messages // this only works for legacy style messages
withdrawal, err := msg.ToWithdrawal() withdrawal := toWithdrawal(t, common.HexToAddress(call.From), msg)
require.Nil(t, err)
// Compute the legacy storage slot for the withdrawal // Compute the legacy storage slot for the withdrawal
slot, err := withdrawal.StorageSlot() slot, err := withdrawal.StorageSlot()
...@@ -160,12 +159,13 @@ func TestWithdrawalLegacyStorageSlot(t *testing.T) { ...@@ -160,12 +159,13 @@ func TestWithdrawalLegacyStorageSlot(t *testing.T) {
} }
func FuzzEncodeDecodeLegacyWithdrawal(f *testing.F) { func FuzzEncodeDecodeLegacyWithdrawal(f *testing.F) {
f.Fuzz(func(t *testing.T, _target, _sender, _nonce, data []byte) { f.Fuzz(func(t *testing.T, _msgSender, _target, _sender, _nonce, data []byte) {
msgSender := common.BytesToAddress(_msgSender)
target := common.BytesToAddress(_target) target := common.BytesToAddress(_target)
sender := common.BytesToAddress(_sender) sender := common.BytesToAddress(_sender)
nonce := new(big.Int).SetBytes(_nonce) nonce := new(big.Int).SetBytes(_nonce)
withdrawal := crossdomain.NewLegacyWithdrawal(&target, &sender, data, nonce) withdrawal := crossdomain.NewLegacyWithdrawal(msgSender, target, sender, data, nonce)
encoded, err := withdrawal.Encode() encoded, err := withdrawal.Encode()
require.Nil(t, err) require.Nil(t, err)
...@@ -174,10 +174,10 @@ func FuzzEncodeDecodeLegacyWithdrawal(f *testing.F) { ...@@ -174,10 +174,10 @@ func FuzzEncodeDecodeLegacyWithdrawal(f *testing.F) {
err = w.Decode(encoded) err = w.Decode(encoded)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, withdrawal.Nonce.Uint64(), w.Nonce.Uint64()) require.Equal(t, withdrawal.XDomainNonce.Uint64(), w.XDomainNonce.Uint64())
require.Equal(t, withdrawal.Sender, w.Sender) require.Equal(t, withdrawal.XDomainSender, w.XDomainSender)
require.Equal(t, withdrawal.Target, w.Target) require.Equal(t, withdrawal.XDomainTarget, w.XDomainTarget)
require.Equal(t, withdrawal.Data, w.Data) require.Equal(t, withdrawal.XDomainData, w.XDomainData)
}) })
} }
...@@ -221,8 +221,8 @@ func findCrossDomainMessage(receipt *types.Receipt) (*crossdomain.CrossDomainMes ...@@ -221,8 +221,8 @@ func findCrossDomainMessage(receipt *types.Receipt) (*crossdomain.CrossDomainMes
// Parse the legacy event // Parse the legacy event
if event.Name == "SentMessage" { if event.Name == "SentMessage" {
e, _ := l2xdm.ParseSentMessage(*log) e, _ := l2xdm.ParseSentMessage(*log)
msg.Target = &e.Target msg.Target = e.Target
msg.Sender = &e.Sender msg.Sender = e.Sender
msg.Data = e.Message msg.Data = e.Message
msg.Nonce = e.MessageNonce msg.Nonce = e.MessageNonce
msg.GasLimit = e.GasLimit msg.GasLimit = e.GasLimit
...@@ -336,3 +336,30 @@ func readStateDiff(hash string) (stateDiff, error) { ...@@ -336,3 +336,30 @@ func readStateDiff(hash string) (stateDiff, error) {
} }
return diff, nil return diff, nil
} }
// ToWithdrawal will turn a CrossDomainMessage into a Withdrawal.
// This only works for version 0 CrossDomainMessages as not all of
// the data is present for version 1 CrossDomainMessages to be turned
// into Withdrawals.
func toWithdrawal(t *testing.T, msgSender common.Address, c *crossdomain.CrossDomainMessage) *crossdomain.LegacyWithdrawal {
version := c.Version()
switch version {
case 0:
if c.Value != nil && c.Value.Cmp(common.Big0) != 0 {
t.Fatalf("version 0 messages must have 0 value")
}
w := &crossdomain.LegacyWithdrawal{
MessageSender: msgSender,
XDomainTarget: c.Target,
XDomainSender: c.Sender,
XDomainData: c.Data,
XDomainNonce: c.Nonce,
}
return w
case 1:
t.Fatalf("cannot convert version 1 messages to withdrawals")
default:
t.Fatalf("unknown message version: %d", version)
}
return nil
}
package crossdomain package crossdomain
import ( import (
"errors"
"fmt" "fmt"
"math/big" "math/big"
...@@ -14,18 +13,18 @@ import ( ...@@ -14,18 +13,18 @@ import (
// version 1 messages have a value and the most significant // version 1 messages have a value and the most significant
// byte of the nonce is a 1 // byte of the nonce is a 1
type CrossDomainMessage struct { type CrossDomainMessage struct {
Nonce *big.Int `json:"nonce"` Nonce *big.Int `json:"nonce"`
Sender *common.Address `json:"sender"` Sender common.Address `json:"sender"`
Target *common.Address `json:"target"` Target common.Address `json:"target"`
Value *big.Int `json:"value"` Value *big.Int `json:"value"`
GasLimit *big.Int `json:"gasLimit"` GasLimit *big.Int `json:"gasLimit"`
Data []byte `json:"data"` Data []byte `json:"data"`
} }
// NewCrossDomainMessage creates a CrossDomainMessage. // NewCrossDomainMessage creates a CrossDomainMessage.
func NewCrossDomainMessage( func NewCrossDomainMessage(
nonce *big.Int, nonce *big.Int,
sender, target *common.Address, sender, target common.Address,
value, gasLimit *big.Int, value, gasLimit *big.Int,
data []byte, data []byte,
) *CrossDomainMessage { ) *CrossDomainMessage {
...@@ -77,23 +76,3 @@ func (c *CrossDomainMessage) Hash() (common.Hash, error) { ...@@ -77,23 +76,3 @@ func (c *CrossDomainMessage) Hash() (common.Hash, error) {
func (c *CrossDomainMessage) HashV1() (common.Hash, error) { func (c *CrossDomainMessage) HashV1() (common.Hash, error) {
return HashCrossDomainMessageV1(c.Nonce, c.Sender, c.Target, c.Value, c.GasLimit, c.Data) return HashCrossDomainMessageV1(c.Nonce, c.Sender, c.Target, c.Value, c.GasLimit, c.Data)
} }
// ToWithdrawal will turn a CrossDomainMessage into a Withdrawal.
// This only works for version 0 CrossDomainMessages as not all of
// the data is present for version 1 CrossDomainMessages to be turned
// into Withdrawals.
func (c *CrossDomainMessage) ToWithdrawal() (WithdrawalMessage, error) {
version := c.Version()
switch version {
case 0:
if c.Value != nil && c.Value.Cmp(common.Big0) != 0 {
return nil, errors.New("version 0 messages must have 0 value")
}
w := NewLegacyWithdrawal(c.Target, c.Sender, c.Data, c.Nonce)
return w, nil
case 1:
return nil, errors.New("version 1 messages cannot be turned into withdrawals")
default:
return nil, fmt.Errorf("unknown version %d", version)
}
}
...@@ -18,8 +18,8 @@ func TestEncode(t *testing.T) { ...@@ -18,8 +18,8 @@ func TestEncode(t *testing.T) {
t.Run("V0", func(t *testing.T) { t.Run("V0", func(t *testing.T) {
msg := crossdomain.NewCrossDomainMessage( msg := crossdomain.NewCrossDomainMessage(
crossdomain.EncodeVersionedNonce(common.Big0, common.Big0), crossdomain.EncodeVersionedNonce(common.Big0, common.Big0),
&common.Address{}, common.Address{},
&common.Address{19: 0x01}, common.Address{19: 0x01},
big.NewInt(0), big.NewInt(0),
big.NewInt(5), big.NewInt(5),
[]byte{}, []byte{},
...@@ -37,8 +37,8 @@ func TestEncode(t *testing.T) { ...@@ -37,8 +37,8 @@ func TestEncode(t *testing.T) {
t.Run("V1", func(t *testing.T) { t.Run("V1", func(t *testing.T) {
msg := crossdomain.NewCrossDomainMessage( msg := crossdomain.NewCrossDomainMessage(
crossdomain.EncodeVersionedNonce(common.Big1, common.Big1), crossdomain.EncodeVersionedNonce(common.Big1, common.Big1),
&common.Address{19: 0x01}, common.Address{19: 0x01},
&common.Address{19: 0x02}, common.Address{19: 0x02},
big.NewInt(100), big.NewInt(100),
big.NewInt(555), big.NewInt(555),
[]byte{}, []byte{},
...@@ -63,8 +63,8 @@ func TestHash(t *testing.T) { ...@@ -63,8 +63,8 @@ func TestHash(t *testing.T) {
t.Run("V0", func(t *testing.T) { t.Run("V0", func(t *testing.T) {
msg := crossdomain.NewCrossDomainMessage( msg := crossdomain.NewCrossDomainMessage(
crossdomain.EncodeVersionedNonce(common.Big0, common.Big0), crossdomain.EncodeVersionedNonce(common.Big0, common.Big0),
&common.Address{}, common.Address{},
&common.Address{19: 0x01}, common.Address{19: 0x01},
big.NewInt(10), big.NewInt(10),
big.NewInt(5), big.NewInt(5),
[]byte{}, []byte{},
...@@ -82,8 +82,8 @@ func TestHash(t *testing.T) { ...@@ -82,8 +82,8 @@ func TestHash(t *testing.T) {
t.Run("V1", func(t *testing.T) { t.Run("V1", func(t *testing.T) {
msg := crossdomain.NewCrossDomainMessage( msg := crossdomain.NewCrossDomainMessage(
crossdomain.EncodeVersionedNonce(common.Big0, common.Big1), crossdomain.EncodeVersionedNonce(common.Big0, common.Big1),
&common.Address{}, common.Address{},
&common.Address{19: 0x01}, common.Address{19: 0x01},
big.NewInt(0), big.NewInt(0),
big.NewInt(5), big.NewInt(5),
[]byte{}, []byte{},
......
...@@ -19,7 +19,7 @@ var ( ...@@ -19,7 +19,7 @@ var (
) )
// MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB. // MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB.
func MigrateWithdrawals(withdrawals []*LegacyWithdrawal, db vm.StateDB, l1CrossDomainMessenger *common.Address, noCheck bool) error { func MigrateWithdrawals(withdrawals SafeFilteredWithdrawals, db vm.StateDB, l1CrossDomainMessenger *common.Address, noCheck bool) error {
for i, legacy := range withdrawals { for i, legacy := range withdrawals {
legacySlot, err := legacy.StorageSlot() legacySlot, err := legacy.StorageSlot()
if err != nil { if err != nil {
...@@ -66,17 +66,17 @@ func MigrateWithdrawal(withdrawal *LegacyWithdrawal, l1CrossDomainMessenger *com ...@@ -66,17 +66,17 @@ func MigrateWithdrawal(withdrawal *LegacyWithdrawal, l1CrossDomainMessenger *com
// Migrated withdrawals are specified as version 0. Both the // Migrated withdrawals are specified as version 0. Both the
// L2ToL1MessagePasser and the CrossDomainMessenger use the same // L2ToL1MessagePasser and the CrossDomainMessenger use the same
// versioning scheme. Both should be set to version 0 // versioning scheme. Both should be set to version 0
versionedNonce := EncodeVersionedNonce(withdrawal.Nonce, new(big.Int)) versionedNonce := EncodeVersionedNonce(withdrawal.XDomainNonce, new(big.Int))
// Encode the call to `relayMessage` on the `CrossDomainMessenger`. // Encode the call to `relayMessage` on the `CrossDomainMessenger`.
// The minGasLimit can safely be 0 here. // The minGasLimit can safely be 0 here.
data, err := abi.Pack( data, err := abi.Pack(
"relayMessage", "relayMessage",
versionedNonce, versionedNonce,
withdrawal.Sender, withdrawal.XDomainSender,
withdrawal.Target, withdrawal.XDomainTarget,
value, value,
new(big.Int), new(big.Int),
[]byte(withdrawal.Data), []byte(withdrawal.XDomainData),
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot abi encode relayMessage: %w", err) return nil, fmt.Errorf("cannot abi encode relayMessage: %w", err)
......
...@@ -17,10 +17,7 @@ func TestMigrateWithdrawal(t *testing.T) { ...@@ -17,10 +17,7 @@ func TestMigrateWithdrawal(t *testing.T) {
for _, receipt := range receipts { for _, receipt := range receipts {
msg, err := findCrossDomainMessage(receipt) msg, err := findCrossDomainMessage(receipt)
require.Nil(t, err) require.Nil(t, err)
withdrawal, err := msg.ToWithdrawal() legacyWithdrawal := toWithdrawal(t, predeploys.L2CrossDomainMessengerAddr, msg)
require.Nil(t, err)
legacyWithdrawal, ok := withdrawal.(*crossdomain.LegacyWithdrawal)
require.True(t, ok)
withdrawals = append(withdrawals, legacyWithdrawal) withdrawals = append(withdrawals, legacyWithdrawal)
} }
...@@ -31,7 +28,7 @@ func TestMigrateWithdrawal(t *testing.T) { ...@@ -31,7 +28,7 @@ func TestMigrateWithdrawal(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, withdrawal) require.NotNil(t, withdrawal)
require.Equal(t, legacy.Nonce.Uint64(), withdrawal.Nonce.Uint64()) require.Equal(t, legacy.XDomainNonce.Uint64(), withdrawal.Nonce.Uint64())
require.Equal(t, *withdrawal.Sender, predeploys.L2CrossDomainMessengerAddr) require.Equal(t, *withdrawal.Sender, predeploys.L2CrossDomainMessengerAddr)
require.Equal(t, *withdrawal.Target, l1CrossDomainMessenger) require.Equal(t, *withdrawal.Target, l1CrossDomainMessenger)
}) })
......
package crossdomain package crossdomain
import ( import (
"errors"
"fmt" "fmt"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
var (
ErrUnknownSlotInMessagePasser = errors.New("unknown slot in legacy message passer")
ErrMissingSlotInWitness = errors.New("missing storage slot in witness data")
)
// PreCheckWithdrawals checks that the given list of withdrawals represents all withdrawals made // PreCheckWithdrawals checks that the given list of withdrawals represents all withdrawals made
// in the legacy system and filters out any extra withdrawals not included in the legacy system. // in the legacy system and filters out any extra withdrawals not included in the legacy system.
func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]*LegacyWithdrawal, error) { func PreCheckWithdrawals(db *state.StateDB, withdrawals DangerousUnfilteredWithdrawals) (SafeFilteredWithdrawals, error) {
// Convert each withdrawal into a storage slot, and build a map of those slots. // Convert each withdrawal into a storage slot, and build a map of those slots.
slotsInp := make(map[common.Hash]*LegacyWithdrawal) slotsInp := make(map[common.Hash]*LegacyWithdrawal)
for _, wd := range withdrawals { for _, wd := range withdrawals {
...@@ -26,6 +31,7 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([] ...@@ -26,6 +31,7 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]
// Build a mapping of the slots of all messages actually sent in the legacy system. // Build a mapping of the slots of all messages actually sent in the legacy system.
var count int var count int
var innerErr error
slotsAct := make(map[common.Hash]bool) slotsAct := make(map[common.Hash]bool)
err := db.ForEachStorage(predeploys.LegacyMessagePasserAddr, func(key, value common.Hash) bool { err := db.ForEachStorage(predeploys.LegacyMessagePasserAddr, func(key, value common.Hash) bool {
// When a message is inserted into the LegacyMessagePasser, it is stored with the value // When a message is inserted into the LegacyMessagePasser, it is stored with the value
...@@ -33,7 +39,7 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([] ...@@ -33,7 +39,7 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]
// can safely ignore anything that is not "true". // can safely ignore anything that is not "true".
if value != abiTrue { if value != abiTrue {
// Should not happen! // Should not happen!
log.Error("found unknown slot in LegacyMessagePasser", "key", key.String(), "val", value.String()) innerErr = fmt.Errorf("%w: key: %s, val: %s", ErrUnknownSlotInMessagePasser, key.String(), value.String())
return true return true
} }
...@@ -45,6 +51,9 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([] ...@@ -45,6 +51,9 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot iterate over LegacyMessagePasser: %w", err) return nil, fmt.Errorf("cannot iterate over LegacyMessagePasser: %w", err)
} }
if innerErr != nil {
return nil, innerErr
}
// Log the number of messages we found. // Log the number of messages we found.
log.Info("Iterated legacy messages", "count", count) log.Info("Iterated legacy messages", "count", count)
...@@ -53,13 +62,13 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([] ...@@ -53,13 +62,13 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]
for slot := range slotsAct { for slot := range slotsAct {
_, ok := slotsInp[slot] _, ok := slotsInp[slot]
if !ok { if !ok {
return nil, fmt.Errorf("unknown storage slot in state: %s", slot) return nil, ErrMissingSlotInWitness
} }
} }
// Iterate over the list of input messages and check that we have a known slot for each one. // Iterate over the list of input messages and check that we have a known slot for each one.
// We'll filter out any extra messages that are not in the legacy system. // We'll filter out any extra messages that are not in the legacy system.
filtered := make([]*LegacyWithdrawal, 0) filtered := make(SafeFilteredWithdrawals, 0)
for slot := range slotsInp { for slot := range slotsInp {
_, ok := slotsAct[slot] _, ok := slotsAct[slot]
if !ok { if !ok {
...@@ -67,7 +76,13 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([] ...@@ -67,7 +76,13 @@ func PreCheckWithdrawals(db *state.StateDB, withdrawals []*LegacyWithdrawal) ([]
continue continue
} }
filtered = append(filtered, slotsInp[slot]) wd := slotsInp[slot]
if wd.MessageSender != predeploys.L2CrossDomainMessengerAddr {
log.Info("filtering out message from sender other than the L2XDM", "sender", wd.MessageSender)
continue
}
filtered = append(filtered, wd)
} }
// At this point, we know that the list of filtered withdrawals MUST be exactly the same as the // At this point, we know that the list of filtered withdrawals MUST be exactly the same as the
......
package crossdomain
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/trie"
"github.com/stretchr/testify/require"
)
func TestPreCheckWithdrawals_Filtering(t *testing.T) {
dbWds := []*LegacyWithdrawal{
// Random legacy WD to something other than the L2XDM.
{
MessageSender: common.Address{19: 0xFF},
XDomainTarget: common.Address{19: 0x01},
XDomainSender: common.Address{19: 0x02},
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(0),
},
// Random legacy WD to the L2XDM. Should be the only thing
// returned by the prechecker.
{
MessageSender: predeploys.L2CrossDomainMessengerAddr,
XDomainTarget: common.Address{19: 0x01},
XDomainSender: common.Address{19: 0x02},
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(1),
},
}
// Add an additional witness to the witnesses list to
// test how the prechecker handles witness data that
// isn't in state.
witnessWds := append([]*LegacyWithdrawal{
{
MessageSender: common.Address{19: 0xAA},
XDomainTarget: common.Address{19: 0x03},
XDomainSender: predeploys.L2CrossDomainMessengerAddr,
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(0),
},
}, dbWds...)
filteredWds, err := runPrecheck(t, dbWds, witnessWds)
require.NoError(t, err)
require.EqualValues(t, []*LegacyWithdrawal{dbWds[1]}, filteredWds)
}
func TestPreCheckWithdrawals_InvalidSlotInStorage(t *testing.T) {
rawDB := rawdb.NewMemoryDatabase()
rawStateDB := state.NewDatabaseWithConfig(rawDB, &trie.Config{
Preimages: true,
Cache: 1024,
})
stateDB, err := state.New(common.Hash{}, rawStateDB, nil)
require.NoError(t, err)
// Create account, and set a random storage slot to a value
// other than abiTrue.
stateDB.CreateAccount(predeploys.LegacyMessagePasserAddr)
stateDB.SetState(predeploys.LegacyMessagePasserAddr, common.Hash{0: 0xff}, common.Hash{0: 0xff})
root, err := stateDB.Commit(false)
require.NoError(t, err)
err = stateDB.Database().TrieDB().Commit(root, true, nil)
require.NoError(t, err)
_, err = PreCheckWithdrawals(stateDB, nil)
require.ErrorIs(t, err, ErrUnknownSlotInMessagePasser)
}
func TestPreCheckWithdrawals_MissingStorageSlot(t *testing.T) {
// Add a legacy WD to state that does not appear in witness data.
dbWds := []*LegacyWithdrawal{
{
XDomainTarget: common.Address{19: 0x01},
XDomainSender: predeploys.L2CrossDomainMessengerAddr,
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(1),
},
}
// Create some witness data that includes both a valid
// and an invalid witness, but neither of which correspond
// to the value above in state.
witnessWds := []*LegacyWithdrawal{
{
XDomainTarget: common.Address{19: 0x01},
XDomainSender: common.Address{19: 0x02},
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(0),
},
{
XDomainTarget: common.Address{19: 0x03},
XDomainSender: predeploys.L2CrossDomainMessengerAddr,
XDomainData: []byte{0x01, 0x02, 0x03},
XDomainNonce: big.NewInt(0),
},
}
_, err := runPrecheck(t, dbWds, witnessWds)
require.ErrorIs(t, err, ErrMissingSlotInWitness)
}
func runPrecheck(t *testing.T, dbWds []*LegacyWithdrawal, witnessWds []*LegacyWithdrawal) ([]*LegacyWithdrawal, error) {
rawDB := rawdb.NewMemoryDatabase()
rawStateDB := state.NewDatabaseWithConfig(rawDB, &trie.Config{
Preimages: true,
Cache: 1024,
})
stateDB, err := state.New(common.Hash{}, rawStateDB, nil)
require.NoError(t, err)
stateDB.CreateAccount(predeploys.LegacyMessagePasserAddr)
for _, wd := range dbWds {
slot, err := wd.StorageSlot()
require.NoError(t, err)
stateDB.SetState(predeploys.LegacyMessagePasserAddr, slot, abiTrue)
}
root, err := stateDB.Commit(false)
require.NoError(t, err)
err = stateDB.Database().TrieDB().Commit(root, true, nil)
require.NoError(t, err)
return PreCheckWithdrawals(stateDB, witnessWds)
}
...@@ -5,6 +5,15 @@ import ( ...@@ -5,6 +5,15 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
// DangerousUnfilteredWithdrawals is a list of raw withdrawal witness
// data. It has not been filtered for messages from sources other than
// the
type DangerousUnfilteredWithdrawals []*LegacyWithdrawal
// SafeFilteredWithdrawals is a list of withdrawals that have been filtered to only include
// withdrawals that were from the L2XDM.
type SafeFilteredWithdrawals []*LegacyWithdrawal
var ( var (
// Standard ABI types // Standard ABI types
Uint256Type, _ = abi.NewType("uint256", "", nil) Uint256Type, _ = abi.NewType("uint256", "", nil)
......
...@@ -87,8 +87,8 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end ...@@ -87,8 +87,8 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
msg := NewCrossDomainMessage( msg := NewCrossDomainMessage(
event.MessageNonce, event.MessageNonce,
&event.Sender, event.Sender,
&event.Target, event.Target,
common.Big0, common.Big0,
event.GasLimit, event.GasLimit,
event.Message, event.Message,
...@@ -116,10 +116,10 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end ...@@ -116,10 +116,10 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
withdrawal := PendingWithdrawal{ withdrawal := PendingWithdrawal{
LegacyWithdrawal: LegacyWithdrawal{ LegacyWithdrawal: LegacyWithdrawal{
Target: &event.Target, XDomainTarget: event.Target,
Sender: &event.Sender, XDomainSender: event.Sender,
Data: event.Message, XDomainData: event.Message,
Nonce: event.MessageNonce, XDomainNonce: event.MessageNonce,
}, },
TransactionHash: event.Raw.TxHash, TransactionHash: event.Raw.TxHash,
} }
......
...@@ -146,8 +146,8 @@ func sendCrossDomainMessage( ...@@ -146,8 +146,8 @@ func sendCrossDomainMessage(
// Parse the legacy event // Parse the legacy event
if event.Name == "SentMessage" { if event.Name == "SentMessage" {
e, _ := l2xdm.ParseSentMessage(*log) e, _ := l2xdm.ParseSentMessage(*log)
msg.Target = &e.Target msg.Target = e.Target
msg.Sender = &e.Sender msg.Sender = e.Sender
msg.Data = e.Message msg.Data = e.Message
msg.Nonce = e.MessageNonce msg.Nonce = e.MessageNonce
msg.GasLimit = e.GasLimit msg.GasLimit = e.GasLimit
...@@ -272,7 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) { ...@@ -272,7 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) {
for i, msg := range msgs[3:] { for i, msg := range msgs[3:] {
withdrawal := withdrawals[i] withdrawal := withdrawals[i]
require.Equal(t, msg.Target, *withdrawal.Target) require.Equal(t, msg.Target, withdrawal.XDomainTarget)
require.Equal(t, msg.Message, []byte(withdrawal.Data)) require.Equal(t, msg.Message, []byte(withdrawal.XDomainData))
} }
} }
...@@ -461,7 +461,8 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD ...@@ -461,7 +461,8 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD
// First, make a mapping between old withdrawal slots and new ones. // First, make a mapping between old withdrawal slots and new ones.
// This list can be a superset of what was actually migrated, since // This list can be a superset of what was actually migrated, since
// some witness data may references withdrawals that reverted. // some witness data may references withdrawals that reverted.
oldToNew := make(map[common.Hash]common.Hash) oldToNewSlots := make(map[common.Hash]common.Hash)
wdsByOldSlot := make(map[common.Hash]*crossdomain.LegacyWithdrawal)
for _, wd := range wds { for _, wd := range wds {
migrated, err := crossdomain.MigrateWithdrawal(wd, l1CrossDomainMessenger) migrated, err := crossdomain.MigrateWithdrawal(wd, l1CrossDomainMessenger)
if err != nil { if err != nil {
...@@ -477,7 +478,8 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD ...@@ -477,7 +478,8 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD
return fmt.Errorf("cannot compute migrated storage slot: %w", err) return fmt.Errorf("cannot compute migrated storage slot: %w", err)
} }
oldToNew[legacySlot] = migratedSlot oldToNewSlots[legacySlot] = migratedSlot
wdsByOldSlot[legacySlot] = wd
} }
// Now, iterate over each legacy withdrawal and check if there is a corresponding // Now, iterate over each legacy withdrawal and check if there is a corresponding
...@@ -498,17 +500,29 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD ...@@ -498,17 +500,29 @@ func CheckWithdrawalsAfter(db vm.StateDB, data migration.MigrationData, l1CrossD
} }
// Grab the migrated slot. // Grab the migrated slot.
migratedSlot := oldToNew[key] migratedSlot := oldToNewSlots[key]
if migratedSlot == (common.Hash{}) { if migratedSlot == (common.Hash{}) {
innerErr = fmt.Errorf("no migrated slot found for legacy slot %s", key) innerErr = fmt.Errorf("no migrated slot found for legacy slot %s", key)
return false return false
} }
// Look up the migrated slot in the DB, and make sure it is abiTrue. // Look up the migrated slot in the DB.
migratedValue := db.GetState(predeploys.L2ToL1MessagePasserAddr, migratedSlot) migratedValue := db.GetState(predeploys.L2ToL1MessagePasserAddr, migratedSlot)
if migratedValue != abiTrue {
innerErr = fmt.Errorf("expected migrated value to be true, but got %s", migratedValue) // If the sender is _not_ the L2XDM, the value should not be migrated.
return false wd := wdsByOldSlot[key]
if wd.XDomainSender == predeploys.L2CrossDomainMessengerAddr {
// Make sure the value is abiTrue if this withdrawal should be migrated.
if migratedValue != abiTrue {
innerErr = fmt.Errorf("expected migrated value to be true, but got %s", migratedValue)
return false
}
} else {
// Otherwise, ensure that withdrawals from senders other than the L2XDM are _not_ migrated.
if migratedValue != abiFalse {
innerErr = fmt.Errorf("a migration from a sender other than the L2XDM was migrated")
return false
}
} }
return true return true
......
...@@ -20,7 +20,8 @@ import ( ...@@ -20,7 +20,8 @@ import (
) )
var ( var (
abiTrue = common.Hash{31: 0x01} abiTrue = common.Hash{31: 0x01}
abiFalse = common.Hash{}
// BedrockTransitionBlockExtraData represents the extradata // BedrockTransitionBlockExtraData represents the extradata
// set in the very first bedrock block. This value must be // set in the very first bedrock block. This value must be
// less than 32 bytes long or it will create an invalid block. // less than 32 bytes long or it will create an invalid block.
...@@ -120,7 +121,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m ...@@ -120,7 +121,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
// We now need to check that we have all of the withdrawals that we expect to have. An error // We now need to check that we have all of the withdrawals that we expect to have. An error
// will be thrown if there are any missing messages, and any extra messages will be removed. // will be thrown if there are any missing messages, and any extra messages will be removed.
var filteredWithdrawals []*crossdomain.LegacyWithdrawal var filteredWithdrawals crossdomain.SafeFilteredWithdrawals
if !noCheck { if !noCheck {
log.Info("Checking withdrawals...") log.Info("Checking withdrawals...")
filteredWithdrawals, err = crossdomain.PreCheckWithdrawals(db, unfilteredWithdrawals) filteredWithdrawals, err = crossdomain.PreCheckWithdrawals(db, unfilteredWithdrawals)
...@@ -129,7 +130,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m ...@@ -129,7 +130,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
} }
} else { } else {
log.Info("Skipping checking withdrawals") log.Info("Skipping checking withdrawals")
filteredWithdrawals = unfilteredWithdrawals filteredWithdrawals = crossdomain.SafeFilteredWithdrawals(unfilteredWithdrawals)
} }
// We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract // We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract
......
...@@ -5,8 +5,6 @@ import ( ...@@ -5,8 +5,6 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
...@@ -119,12 +117,9 @@ type MigrationData struct { ...@@ -119,12 +117,9 @@ type MigrationData struct {
EvmMessages []*SentMessage EvmMessages []*SentMessage
} }
func (m *MigrationData) ToWithdrawals() ([]*crossdomain.LegacyWithdrawal, error) { func (m *MigrationData) ToWithdrawals() (crossdomain.DangerousUnfilteredWithdrawals, error) {
messages := make([]*crossdomain.LegacyWithdrawal, 0) messages := make(crossdomain.DangerousUnfilteredWithdrawals, 0)
for _, msg := range m.OvmMessages { for _, msg := range m.OvmMessages {
if msg.Who != predeploys.L2CrossDomainMessengerAddr {
continue
}
wd, err := msg.ToLegacyWithdrawal() wd, err := msg.ToLegacyWithdrawal()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -135,9 +130,6 @@ func (m *MigrationData) ToWithdrawals() ([]*crossdomain.LegacyWithdrawal, error) ...@@ -135,9 +130,6 @@ func (m *MigrationData) ToWithdrawals() ([]*crossdomain.LegacyWithdrawal, error)
} }
} }
for _, msg := range m.EvmMessages { for _, msg := range m.EvmMessages {
if msg.Who != predeploys.L2CrossDomainMessengerAddr {
continue
}
wd, err := msg.ToLegacyWithdrawal() wd, err := msg.ToLegacyWithdrawal()
if err != nil { if err != nil {
return nil, err return nil, err
......
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