Skip to content

Commit

Permalink
Move common withdrawal logic to separate utility
Browse files Browse the repository at this point in the history
  • Loading branch information
mdehoog committed Oct 11, 2024
1 parent 8a11df4 commit 93cbaef
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 79 deletions.
93 changes: 14 additions & 79 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"math/big"
Expand All @@ -12,9 +11,7 @@ import (

bindings2 "github.com/ethereum-optimism/optimism/op-e2e/bindings"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum-optimism/optimism/op-service/predeploys"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -23,8 +20,11 @@ import (
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/mdehoog/op-enclave/bindings"
"github.com/mdehoog/op-enclave/op-withdrawer/withdrawals"
)

const pollInterval = 250 * time.Millisecond

func main() {
chainID := 1709200504
deployedJSON := fmt.Sprintf("deployments/84532-%d-deployed.json", chainID)
Expand Down Expand Up @@ -57,7 +57,7 @@ func main() {
redeposit := false

withdrawalTxHash := common.HexToHash("0x")
withdrawalTxBlock := uint64(0)
withdrawalTxBlock := big.NewInt(0)

ctx, cancel := context.WithCancel(context.Background())
l1, err := ethclient.DialContext(ctx, "https://sepolia.base.org")
Expand Down Expand Up @@ -174,27 +174,27 @@ func AwaitDeposit(ctx context.Context, l2 *ethclient.Client, receipt *types.Rece
log.Fatalf("Expected 1 deposit, got %d", len(deposits))
}

receipt, err = waitForConfirmation(ctx, l2, types.NewTx(deposits[0]).Hash())
receipt, err = withdrawals.WaitForReceipt(ctx, l2, types.NewTx(deposits[0]).Hash(), pollInterval)
if err != nil {
log.Fatalf("Error waiting for confirmation: %v", err)
}
fmt.Printf("Deposit confirmed: %s\n", receipt.TxHash)
}

func Withdraw(ctx context.Context, l2 *ethclient.Client, opts *bind.TransactOpts, l2Bridge common.Address) (value *big.Int, withdrawalTxHash common.Hash, withdrawalTxBlock uint64) {
func Withdraw(ctx context.Context, l2 *ethclient.Client, opts *bind.TransactOpts, l2Bridge common.Address) (value *big.Int, withdrawalTxHash common.Hash, withdrawalTxBlock *big.Int) {
fmt.Printf("Withdrawing entire balance from L3\n")
tx, receipt, err := send(ctx, l2, l2Bridge, opts, nil, nil, true)
if err != nil {
log.Fatalf("Error sending withdrawal: %v", err)
}
value = tx.Value()
withdrawalTxHash = receipt.TxHash
withdrawalTxBlock = receipt.BlockNumber.Uint64()
withdrawalTxBlock = receipt.BlockNumber
fmt.Printf("Withdrew %s wei: %s (block %d)\n", tx.Value(), withdrawalTxHash, withdrawalTxBlock)
return
}

func WithdrawDeposit(ctx context.Context, l1, l2 *ethclient.Client, optsFactory func(l2 bool) *bind.TransactOpts, l1Bridge, l2Messenger common.Address) (withdrawalTxHash common.Hash, withdrawalTxBlock uint64) {
func WithdrawDeposit(ctx context.Context, l1, l2 *ethclient.Client, optsFactory func(l2 bool) *bind.TransactOpts, l1Bridge, l2Messenger common.Address) (withdrawalTxHash common.Hash, withdrawalTxBlock *big.Int) {
bridge, err := bindings2.NewL1StandardBridge(l1Bridge, l1)
if err != nil {
log.Fatalf("Error binding to L1 bridge: %v", err)
Expand Down Expand Up @@ -223,67 +223,21 @@ func WithdrawDeposit(ctx context.Context, l1, l2 *ethclient.Client, optsFactory
log.Fatalf("Error sending withdrawal: %v", err)
}
withdrawalTxHash = receipt.TxHash
withdrawalTxBlock = receipt.BlockNumber.Uint64()
withdrawalTxBlock = receipt.BlockNumber
fmt.Printf("Deposit withdrawal sent: %s (block %d)\n", withdrawalTxHash, withdrawalTxBlock)
return
}

func ProveWithdrawal(ctx context.Context, l1, l2 *ethclient.Client, l2g *gethclient.Client, opts *bind.TransactOpts, outputOracle *bindings.OutputOracle, portal *bindings.Portal, withdrawalTxHash common.Hash, withdrawalTxBlock uint64) *types.Receipt {
func ProveWithdrawal(ctx context.Context, l1, l2 *ethclient.Client, l2g *gethclient.Client, opts *bind.TransactOpts, outputOracle *bindings.OutputOracle, portal *bindings.Portal, withdrawalTxHash common.Hash, withdrawalTxBlock *big.Int) *types.Receipt {
fmt.Printf("Waiting for TEE proof of block %d... ", withdrawalTxBlock)
var l2OutputBlock *big.Int
for {
var err error
l2OutputBlock, err = outputOracle.LatestBlockNumber(&bind.CallOpts{})
if err != nil {
log.Fatalf("Error getting latest L2 output block: %v", err)
}
if l2OutputBlock.Uint64() >= withdrawalTxBlock {
break
}
time.Sleep(200 * time.Millisecond)
}
l2OutputBlock, err := withdrawals.WaitForOutputBlock(ctx, outputOracle, withdrawalTxBlock, pollInterval)
fmt.Println("done")

header, err := l2.HeaderByNumber(ctx, l2OutputBlock)
if err != nil {
log.Fatalf("Error getting L2 header: %v", err)
}
l2OutputIndex, err := outputOracle.GetL2OutputIndexAfter(&bind.CallOpts{}, header.Number)
if err != nil {
log.Fatalf("Error getting L2 output index: %v", err)
}
l2BlockNumber := header.Number

withdrawal, err := withdrawals.ProveWithdrawalParametersForBlock(ctx, l2g, l2, l2, withdrawalTxHash, l2BlockNumber, l2OutputIndex)
if err != nil {
log.Fatalf("Error proving withdrawal parameters: %v", err)
}

outputRootProof := bindings.TypesOutputRootProof{
Version: withdrawal.OutputRootProof.Version,
StateRoot: withdrawal.OutputRootProof.StateRoot,
MessagePasserStorageRoot: withdrawal.OutputRootProof.MessagePasserStorageRoot,
LatestBlockhash: withdrawal.OutputRootProof.LatestBlockhash,
}

tx, err := portal.ProveAndFinalizeWithdrawalTransaction(
opts,
bindings.TypesWithdrawalTransaction{
Nonce: withdrawal.Nonce,
Sender: withdrawal.Sender,
Target: withdrawal.Target,
Value: withdrawal.Value,
GasLimit: withdrawal.GasLimit,
Data: withdrawal.Data,
},
withdrawal.L2OutputIndex,
outputRootProof,
withdrawal.WithdrawalProof,
)
tx, err := withdrawals.ProveAndFinalizeWithdrawal(ctx, l2g, l2, opts, outputOracle, portal, withdrawalTxHash, l2OutputBlock)
if err != nil {
log.Fatalf("Error proving and finalizing withdrawal: %v", err)
}
receipt, err := waitForConfirmation(ctx, l1, tx.Hash())
receipt, err := withdrawals.WaitForReceipt(ctx, l1, tx.Hash(), pollInterval)
if err != nil {
log.Fatalf("Error waiting for confirmation: %v", err)
}
Expand Down Expand Up @@ -335,32 +289,13 @@ func send(ctx context.Context, client *ethclient.Client, to common.Address, opts
return nil, nil, err
}

receipt, err := waitForConfirmation(ctx, client, tx.Hash())
receipt, err := withdrawals.WaitForReceipt(ctx, client, tx.Hash(), pollInterval)
if err != nil {
return nil, nil, err
}
return tx, receipt, nil
}

func waitForConfirmation(ctx context.Context, client *ethclient.Client, tx common.Hash) (*types.Receipt, error) {
for {
receipt, err := client.TransactionReceipt(ctx, tx)
if errors.Is(err, ethereum.NotFound) {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(1 * time.Second):
}
} else if err != nil {
return nil, err
} else if receipt.Status != types.ReceiptStatusSuccessful {
return nil, errors.New("unsuccessful receipt status")
} else {
return receipt, nil
}
}
}

func calculateBatchInbox(chainID *big.Int) common.Address {
a1 := common.HexToAddress(chainID.Text(10))
a1[0] = a1[0] | 0xff
Expand Down
158 changes: 158 additions & 0 deletions op-withdrawer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package main

import (
"context"
"fmt"
"math/big"
"os"
"time"

"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/ethereum-optimism/optimism/op-service/ctxinterrupt"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/mdehoog/op-enclave/bindings"
"github.com/mdehoog/op-enclave/op-withdrawer/withdrawals"
"github.com/urfave/cli/v2"
)

var (
L1URLFlag = &cli.StringFlag{
Name: "l1-url",
Usage: "URL of an L1 RPC host",
EnvVars: []string{"L1_URL"},
Required: true,
}
L2URLFlag = &cli.StringFlag{
Name: "l2-url",
Usage: "URL of an L2 RPC host",
EnvVars: []string{"L2_URL"},
Required: true,
}
PortalAddressFlag = &cli.StringFlag{
Name: "portal-address",
Usage: "Path to the config file",
EnvVars: []string{"PORTAL_ADDRESS"},
Required: true,
}
WithdrawalTxHashFlag = &cli.StringFlag{
Name: "withdrawal-tx-hash",
Usage: "Hash of the withdrawal transaction",
EnvVars: []string{"WITHDRAWAL_TX_HASH"},
Required: true,
}
PrivateKeyFlag = &cli.StringFlag{
Name: "private-key",
Usage: "Private key to sign the transaction",
EnvVars: []string{"PRIVATE_KEY"},
Required: true,
}
)

var Flags = []cli.Flag{
L1URLFlag,
L2URLFlag,
PortalAddressFlag,
WithdrawalTxHashFlag,
PrivateKeyFlag,
}

func main() {
oplog.SetupDefaults()

app := cli.NewApp()
app.Flags = cliapp.ProtectFlags(Flags)
app.Name = "withdrawer"
app.Usage = "Withdraws funds from L2 to L1"
app.Action = Main

ctx := ctxinterrupt.WithSignalWaiterMain(context.Background())
err := app.RunContext(ctx, os.Args)
if err != nil {
log.Crit("Application failed", "message", err)
}
}

func Main(cliCtx *cli.Context) error {
l1URL := cliCtx.String(L1URLFlag.Name)
l2URL := cliCtx.String(L2URLFlag.Name)
portalAddress := common.HexToAddress(cliCtx.String(PortalAddressFlag.Name))
withdrawalTxHash := common.HexToHash(cliCtx.String(WithdrawalTxHashFlag.Name))
privateKey, err := crypto.HexToECDSA(cliCtx.String(PrivateKeyFlag.Name))
if err != nil {
return err
}

ctx := context.Background()
l1, err := ethclient.DialContext(ctx, l1URL)
if err != nil {
return err
}
l2, err := ethclient.DialContext(ctx, l2URL)
if err != nil {
return err
}
l2g := gethclient.New(l2.Client())

chainID, err := l1.ChainID(ctx)
if err != nil {
return err
}
opts, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
if err != nil {
return err
}

portal, err := bindings.NewPortal(portalAddress, l1)
if err != nil {
return err
}

receipt, err := withdrawals.WaitForReceipt(ctx, l2, withdrawalTxHash, 1*time.Second)
if err != nil {
return err
}

receipt, err = ProveWithdrawal(ctx, l1, l2, l2g, opts, portal, withdrawalTxHash, receipt.BlockNumber)
if err != nil {
return err
}

fmt.Printf("Withdrawal proved: %s\n", receipt.TxHash)

return nil
}

func ProveWithdrawal(ctx context.Context, l1, l2 *ethclient.Client, l2g *gethclient.Client, opts *bind.TransactOpts, portal *bindings.Portal, withdrawalTxHash common.Hash, withdrawalTxBlock *big.Int) (*types.Receipt, error) {
pollInterval := 1 * time.Second

outputOracleAddress, err := portal.L2Oracle(&bind.CallOpts{})
if err != nil {
return nil, err
}
outputOracle, err := bindings.NewOutputOracle(outputOracleAddress, l1)
if err != nil {
return nil, err
}

fmt.Printf("Waiting for TEE proof of block %d... ", withdrawalTxBlock)
l2OutputBlock, err := withdrawals.WaitForOutputBlock(ctx, outputOracle, withdrawalTxBlock, pollInterval)
fmt.Println("done")

tx, err := withdrawals.ProveAndFinalizeWithdrawal(ctx, l2g, l2, opts, outputOracle, portal, withdrawalTxHash, l2OutputBlock)
if err != nil {
return nil, err
}
receipt, err := withdrawals.WaitForReceipt(ctx, l1, tx.Hash(), pollInterval)
if err != nil {
return nil, err
}
fmt.Printf("Message proved: %s\n", receipt.TxHash)
return receipt, nil
}
Loading

0 comments on commit 93cbaef

Please sign in to comment.