Commit 499925b7 authored by Mark Tyneway's avatar Mark Tyneway

op-chain-ops: withdrawal hash migration

parent 3dcf11ba
...@@ -14,10 +14,10 @@ import ( ...@@ -14,10 +14,10 @@ import (
// LegacyWithdrawal represents a pre bedrock upgrade withdrawal. // LegacyWithdrawal represents a pre bedrock upgrade withdrawal.
type LegacyWithdrawal struct { type LegacyWithdrawal struct {
Target *common.Address Target *common.Address `json:"target"`
Sender *common.Address Sender *common.Address `json:"sender"`
Data []byte Data []byte `json:"data"`
Nonce *big.Int Nonce *big.Int `json:"nonce"`
} }
var _ WithdrawalMessage = (*LegacyWithdrawal)(nil) var _ WithdrawalMessage = (*LegacyWithdrawal)(nil)
......
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")
)
// This takes a state db and a list of withdrawals
func MigrateWithdrawals(withdrawals []*PendingWithdrawal, db vm.StateDB) 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)
}
// TODO: pass in args
withdrawal, err := MigrateWithdrawal(&legacy.LegacyWithdrawal, &common.Address{}, &common.Address{})
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
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
}
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)
})
}
}
...@@ -13,12 +13,12 @@ var _ WithdrawalMessage = (*Withdrawal)(nil) ...@@ -13,12 +13,12 @@ var _ WithdrawalMessage = (*Withdrawal)(nil)
// Withdrawal represents a withdrawal transaction on L2 // Withdrawal represents a withdrawal transaction on L2
type Withdrawal struct { type Withdrawal struct {
Nonce *big.Int Nonce *big.Int `json:"nonce"`
Sender *common.Address Sender *common.Address `json:"sender"`
Target *common.Address Target *common.Address `json:"target"`
Value *big.Int Value *big.Int `json:"value"`
GasLimit *big.Int GasLimit *big.Int `json:"gasLimit"`
Data []byte Data []byte `json:"data"`
} }
// NewWithdrawal will create a Withdrawal // NewWithdrawal will create a Withdrawal
......
...@@ -15,12 +15,8 @@ import ( ...@@ -15,12 +15,8 @@ import (
// A PendingWithdrawal represents a withdrawal that has // A PendingWithdrawal represents a withdrawal that has
// not been finalized on L1 // not been finalized on L1
type PendingWithdrawal struct { type PendingWithdrawal struct {
Target common.Address `json:"target"` LegacyWithdrawal `json:"withdrawal"`
Sender common.Address `json:"sender"` TransactionHash common.Hash `json:"transactionHash"`
Message []byte `json:"message"`
MessageNonce *big.Int `json:"nonce"`
GasLimit *big.Int `json:"gasLimit"`
TransactionHash common.Hash `json:"transactionHash"`
} }
// Backends represents a set of backends for L1 and L2. // Backends represents a set of backends for L1 and L2.
...@@ -119,11 +115,12 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end ...@@ -119,11 +115,12 @@ func GetPendingWithdrawals(messengers *Messengers, version *big.Int, start, end
log.Info("%s not yet relayed", event.Raw.TxHash) log.Info("%s not yet relayed", event.Raw.TxHash)
withdrawal := PendingWithdrawal{ withdrawal := PendingWithdrawal{
Target: event.Target, LegacyWithdrawal: LegacyWithdrawal{
Sender: event.Sender, Target: &event.Target,
Message: event.Message, Sender: &event.Sender,
MessageNonce: event.MessageNonce, Data: event.Message,
GasLimit: event.GasLimit, Nonce: event.MessageNonce,
},
TransactionHash: event.Raw.TxHash, TransactionHash: event.Raw.TxHash,
} }
......
...@@ -272,8 +272,7 @@ func TestGetPendingWithdrawals(t *testing.T) { ...@@ -272,8 +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.Target)
require.Equal(t, msg.Message, withdrawal.Message) require.Equal(t, msg.Message, withdrawal.Data)
require.Equal(t, uint64(msg.MinGasLimit), withdrawal.GasLimit.Uint64())
} }
} }
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