Commit 4812aa19 authored by Mark Tyneway's avatar Mark Tyneway

op-chain-ops: update script to submit pending withdrawals

An old an unused script for fetching withdrawals
is updated to instead submit pending withdrawals.
parent e92bc23c
package main package main
import ( import (
"encoding/json" "context"
"errors" "errors"
"math/big"
"os" "os"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/l2geth/crypto"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"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-optimism/optimism/op-chain-ops/genesis/migration"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
) )
// TODO(tynes): handle connecting directly to a LevelDB based StateDB
func main() { func main() {
log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd())))) log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd()))))
app := &cli.App{ app := &cli.App{
Name: "withdrawals", Name: "withdrawals",
Usage: "fetches all pending withdrawals", Usage: "submits pending withdrawals",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "l1-rpc-url", Name: "l1-rpc-url",
...@@ -32,64 +42,190 @@ func main() { ...@@ -32,64 +42,190 @@ func main() {
Value: "http://127.0.0.1:9545", Value: "http://127.0.0.1:9545",
Usage: "RPC URL for an L2 Node", Usage: "RPC URL for an L2 Node",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "l1-cross-domain-messenger-address", Name: "optimism-portal-address",
Usage: "Address of the OptimismPortal on L1",
},
&cli.StringFlag{
Name: "l1-crossdomain-messenger-address",
Usage: "Address of the L1CrossDomainMessenger", Usage: "Address of the L1CrossDomainMessenger",
}, },
&cli.Uint64Flag{ &cli.StringFlag{
Name: "start", Name: "ovm-messages",
Usage: "Start height to search for events", Usage: "Path to ovm-messages.json",
},
&cli.StringFlag{
Name: "evm-messages",
Usage: "Path to evm-messages.json",
}, },
&cli.Uint64Flag{ &cli.Uint64Flag{
Name: "end", Name: "bedrock-transition-block-number",
Usage: "End height to search for events", Usage: "The blocknumber of the bedrock transition block",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "outfile", Name: "private-key",
Usage: "Path to output file", Usage: "Key to sign transactions with",
Value: "out.json",
}, },
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
l1RpcURL := ctx.String("l1-rpc-url") l1RpcURL := ctx.String("l1-rpc-url")
l2RpcURL := ctx.String("l2-rpc-url")
l1Client, err := ethclient.Dial(l1RpcURL) l1Client, err := ethclient.Dial(l1RpcURL)
if err != nil { if err != nil {
return err return err
} }
l1ChainID, err := l1Client.ChainID(context.Background())
if err != nil {
return err
}
log.Info("Set up L1 RPC Client", "chain-id", l1ChainID)
l2RpcURL := ctx.String("l2-rpc-url")
l2Client, err := ethclient.Dial(l2RpcURL) l2Client, err := ethclient.Dial(l2RpcURL)
if err != nil { if err != nil {
return err return err
} }
l2ChainID, err := l2Client.ChainID(context.Background())
if err != nil {
return err
}
log.Info("Set up L2 RPC Client", "chain-id", l2ChainID)
l2RpcClient, err := rpc.DialContext(context.Background(), l2RpcURL)
if err != nil {
return err
}
gclient := gethclient.New(l2RpcClient)
log.Info("Set up L2 geth Client")
ovmMessages, err := migration.NewSentMessage(ctx.String("ovm-messages"))
if err != nil {
return err
}
evmMessages, err := migration.NewSentMessage(ctx.String("evm-messages"))
if err != nil {
return err
}
optimismPortalAddress := ctx.String("optimism-portal-address")
if optimismPortalAddress == "" {
return errors.New("OptimismPortal address not configured")
}
optimismPortalAddr := common.HexToAddress(optimismPortalAddress)
migrationData := migration.MigrationData{
OvmMessages: ovmMessages,
EvmMessages: evmMessages,
}
wds, err := migrationData.ToWithdrawals()
if err != nil {
return err
}
if len(wds) == 0 {
return errors.New("no withdrawals")
}
backends := crossdomain.NewBackends(l1Client, l2Client) l1xdmAddress := ctx.String("l1-crossdomain-messenger-address")
if l1xdmAddress == "" {
return errors.New("Must pass in --l1-crossdomain-messenger-address")
}
l1xdmAddr := common.HexToAddress(l1xdmAddress)
l1xDomainMessenger := ctx.String("l1-cross-domain-messenger-address") // TODO: temp, should iterate over all instead of taking the first
if l1xDomainMessenger == "" { wd := wds[0]
return errors.New("Must pass in L1CrossDomainMessenger address") withdrawal, err := crossdomain.MigrateWithdrawal(wd, &l1xdmAddr)
if err != nil {
return err
}
hash, err := withdrawal.Hash()
if err != nil {
return err
}
slot, err := withdrawal.StorageSlot()
if err != nil {
return err
}
transitionBlockNumber := new(big.Int).SetUint64(ctx.Uint64("bedrock-transition-block-number"))
bn, err := withdrawals.WaitForFinalizationPeriod(context.Background(), l1Client, optimismPortalAddr, transitionBlockNumber)
if err != nil {
return err
}
header, err := l2Client.HeaderByNumber(context.Background(), new(big.Int).SetUint64(bn))
if err != nil {
return err
}
proof, err := gclient.GetProof(context.Background(), predeploys.L2ToL1MessagePasserAddr, []string{slot.String()}, header.Number)
if err != nil {
return err
}
if len(proof.StorageProof) != 1 {
return errors.New("invalid amount of storage proofs")
}
trieNodes := make([][]byte, len(proof.StorageProof[0].Proof))
for i, s := range proof.StorageProof[0].Proof {
trieNodes[i] = common.FromHex(s)
} }
l1xDomainMessengerAddr := common.HexToAddress(l1xDomainMessenger) portal, err := bindings.NewOptimismPortal(optimismPortalAddr, l1Client)
messengers, err := crossdomain.NewMessengers(backends, l1xDomainMessengerAddr)
if err != nil { if err != nil {
return err return err
} }
start := ctx.Uint64("start") l2OracleAddr, err := portal.L2ORACLE(&bind.CallOpts{})
end := ctx.Uint64("end") oracle, err := bindings.NewL2OutputOracleCaller(l2OracleAddr, l1Client)
if err != nil {
return nil
}
l2OutputIndex, err := oracle.GetL2OutputIndexAfter(&bind.CallOpts{}, header.Number)
// All messages are expected to be version 0 messages if ctx.String("private-key") == "" {
withdrawals, err := crossdomain.GetPendingWithdrawals(messengers, common.Big0, start, end) return errors.New("No private key to transact with")
}
privateKey, err := crypto.HexToECDSA(strings.TrimPrefix(ctx.String("private-key"), "0x"))
if err != nil { if err != nil {
return err return err
} }
outfile := ctx.String("outfile") opts, err := bind.NewKeyedTransactorWithChainID(privateKey, l1ChainID)
if err := writeJSONFile(outfile, withdrawals); err != nil { if err != nil {
return err return err
} }
tx, err := portal.ProveWithdrawalTransaction(
opts,
bindings.TypesWithdrawalTransaction{
Nonce: withdrawal.Nonce,
Sender: *withdrawal.Sender,
Target: *withdrawal.Target,
Value: withdrawal.Value,
GasLimit: withdrawal.GasLimit,
Data: withdrawal.Data,
},
l2OutputIndex,
bindings.TypesOutputRootProof{
Version: [32]byte{},
StateRoot: header.Root,
MessagePasserStorageRoot: proof.StorageHash,
LatestBlockhash: header.Hash(),
},
trieNodes,
)
if err != nil {
return err
}
log.Info("Withdrawal proven", "tx-hash", tx.Hash(), "withdrawal-hash", hash)
// TODO: - warp forward the L1 timestamp
// - finalize the tx
return nil return nil
}, },
} }
...@@ -98,15 +234,3 @@ func main() { ...@@ -98,15 +234,3 @@ func main() {
log.Crit("error in migration", "err", err) log.Crit("error in migration", "err", err)
} }
} }
func writeJSONFile(outfile string, input interface{}) error {
f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer f.Close()
enc := json.NewEncoder(f)
enc.SetIndent("", " ")
return enc.Encode(input)
}
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