Skip to content
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
8 changes: 4 additions & 4 deletions op-chain-ops/crossdomain/legacy_withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import (

// LegacyWithdrawal represents a pre bedrock upgrade withdrawal.
type LegacyWithdrawal struct {
Target *common.Address
Sender *common.Address
Data []byte
Nonce *big.Int
Target *common.Address `json:"target"`
Sender *common.Address `json:"sender"`
Data []byte `json:"data"`
Nonce *big.Int `json:"nonce"`
}

var _ WithdrawalMessage = (*LegacyWithdrawal)(nil)
Expand Down
116 changes: 116 additions & 0 deletions op-chain-ops/crossdomain/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package crossdomain

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)

var (
abiTrue = common.Hash{31: 0x01}
errLegacyStorageSlotNotFound = errors.New("cannot find storage slot")
)

// MigrateWithdrawals will migrate a list of pending withdrawals given a StateDB.
func MigrateWithdrawals(withdrawals []*PendingWithdrawal, db vm.StateDB, l1CrossDomainMessenger, l1StandardBridge *common.Address) error {
for _, legacy := range withdrawals {
legacySlot, err := legacy.StorageSlot()
if err != nil {
return err
}

legacyValue := db.GetState(predeploys.LegacyMessagePasserAddr, legacySlot)
if legacyValue != abiTrue {
return fmt.Errorf("%w: %s", errLegacyStorageSlotNotFound, legacyValue)
}

withdrawal, err := MigrateWithdrawal(&legacy.LegacyWithdrawal, l1CrossDomainMessenger, l1StandardBridge)
if err != nil {
return err
}

slot, err := withdrawal.StorageSlot()
if err != nil {
return err
}

db.SetState(predeploys.L2ToL1MessagePasserAddr, slot, abiTrue)
}
return nil
}

// MigrateWithdrawal will turn a LegacyWithdrawal into a bedrock
// style Withdrawal.
func MigrateWithdrawal(withdrawal *LegacyWithdrawal, l1CrossDomainMessenger, l1StandardBridge *common.Address) (*Withdrawal, error) {
value := new(big.Int)

isFromL2StandardBridge := *withdrawal.Sender == predeploys.L2StandardBridgeAddr

if withdrawal.Target == nil {
return nil, errors.New("withdrawal target cannot be nil")
}

isToL1StandardBridge := *withdrawal.Target == *l1StandardBridge

if isFromL2StandardBridge && isToL1StandardBridge {
abi, err := bindings.L1StandardBridgeMetaData.GetAbi()
if err != nil {
return nil, err
}

method, err := abi.MethodById(withdrawal.Data)
if err != nil {
return nil, err
}
if method.Name == "finalizeETHWithdrawal" {
data, err := method.Inputs.Unpack(withdrawal.Data[4:])
if err != nil {
return nil, err
}
// bounds check
if len(data) < 3 {
return nil, errors.New("not enough data")
}
var ok bool
value, ok = data[2].(*big.Int)
if !ok {
return nil, errors.New("not big.Int")
}
}
}

abi, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}

versionedNonce := EncodeVersionedNonce(withdrawal.Nonce, common.Big1)
data, err := abi.Pack(
"relayMessage",
versionedNonce,
withdrawal.Sender,
withdrawal.Target,
value,
new(big.Int),
withdrawal.Data,
)
if err != nil {
return nil, fmt.Errorf("cannot abi encode relayMessage: %w", err)
}

w := NewWithdrawal(
withdrawal.Nonce,
&predeploys.L2CrossDomainMessengerAddr,
l1CrossDomainMessenger,
value,
new(big.Int),
data,
)
return w, nil
}
41 changes: 41 additions & 0 deletions op-chain-ops/crossdomain/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package crossdomain_test

import (
"fmt"
"testing"

"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum/go-ethereum/common"

"github.com/stretchr/testify/require"
)

func TestMigrateWithdrawal(t *testing.T) {
withdrawals := make([]*crossdomain.LegacyWithdrawal, 0)

for _, receipt := range receipts {
msg, err := findCrossDomainMessage(receipt)
require.Nil(t, err)
withdrawal, err := msg.ToWithdrawal()
require.Nil(t, err)
legacyWithdrawal, ok := withdrawal.(*crossdomain.LegacyWithdrawal)
require.True(t, ok)
withdrawals = append(withdrawals, legacyWithdrawal)
}

l1CrossDomainMessenger := common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1")
l1StandardBridge := common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1")

for i, legacy := range withdrawals {
t.Run(fmt.Sprintf("test%d", i), func(t *testing.T) {
withdrawal, err := crossdomain.MigrateWithdrawal(legacy, &l1CrossDomainMessenger, &l1StandardBridge)
require.Nil(t, err)
require.NotNil(t, withdrawal)

require.Equal(t, legacy.Nonce.Uint64(), withdrawal.Nonce.Uint64())
require.Equal(t, *withdrawal.Sender, predeploys.L2CrossDomainMessengerAddr)
require.Equal(t, *withdrawal.Target, l1CrossDomainMessenger)
})
}
}
12 changes: 6 additions & 6 deletions op-chain-ops/crossdomain/withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ var _ WithdrawalMessage = (*Withdrawal)(nil)

// Withdrawal represents a withdrawal transaction on L2
type Withdrawal struct {
Nonce *big.Int
Sender *common.Address
Target *common.Address
Value *big.Int
GasLimit *big.Int
Data []byte
Nonce *big.Int `json:"nonce"`
Sender *common.Address `json:"sender"`
Target *common.Address `json:"target"`
Value *big.Int `json:"value"`
GasLimit *big.Int `json:"gasLimit"`
Data []byte `json:"data"`
}

// NewWithdrawal will create a Withdrawal
Expand Down
19 changes: 8 additions & 11 deletions op-chain-ops/crossdomain/withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,8 @@ import (
// A PendingWithdrawal represents a withdrawal that has
// not been finalized on L1
type PendingWithdrawal struct {
Target common.Address `json:"target"`
Sender common.Address `json:"sender"`
Message []byte `json:"message"`
MessageNonce *big.Int `json:"nonce"`
GasLimit *big.Int `json:"gasLimit"`
TransactionHash common.Hash `json:"transactionHash"`
LegacyWithdrawal `json:"withdrawal"`
TransactionHash common.Hash `json:"transactionHash"`
}

// Backends represents a set of backends for L1 and L2.
Expand Down Expand Up @@ -119,11 +115,12 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
log.Info("%s not yet relayed", event.Raw.TxHash)

withdrawal := PendingWithdrawal{
Target: event.Target,
Sender: event.Sender,
Message: event.Message,
MessageNonce: event.MessageNonce,
GasLimit: event.GasLimit,
LegacyWithdrawal: LegacyWithdrawal{
Target: &event.Target,
Sender: &event.Sender,
Data: event.Message,
Nonce: event.MessageNonce,
},
TransactionHash: event.Raw.TxHash,
}

Expand Down
5 changes: 2 additions & 3 deletions op-chain-ops/crossdomain/withdrawals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) {
for i, msg := range msgs[3:] {
withdrawal := withdrawals[i]

require.Equal(t, msg.Target, withdrawal.Target)
require.Equal(t, msg.Message, withdrawal.Message)
require.Equal(t, uint64(msg.MinGasLimit), withdrawal.GasLimit.Uint64())
require.Equal(t, msg.Target, *withdrawal.Target)
require.Equal(t, msg.Message, withdrawal.Data)
}
}