main.go 7.46 KB
Newer Older
1 2 3 4 5 6
package main

import (
	"context"
	"fmt"
	"log"
7
	"math/big"
8 9 10 11
	"os"
	"time"

	"github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/fetch"
12
	"github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/reassemble"
13
	"github.com/ethereum-optimism/optimism/op-node/rollup"
14
	"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
15 16
	"github.com/ethereum-optimism/optimism/op-service/client"
	"github.com/ethereum-optimism/optimism/op-service/sources"
17 18
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
19
	"github.com/urfave/cli/v2"
20 21 22 23 24 25
)

func main() {
	app := cli.NewApp()
	app.Name = "batch-decoder"
	app.Usage = "Optimism Batch Decoding Utility"
26
	app.Commands = []*cli.Command{
27 28 29 30
		{
			Name:  "fetch",
			Usage: "Fetches batches in the specified range",
			Flags: []cli.Flag{
31
				&cli.IntFlag{
32 33 34 35
					Name:     "start",
					Required: true,
					Usage:    "First block (inclusive) to fetch",
				},
36
				&cli.IntFlag{
37 38 39 40
					Name:     "end",
					Required: true,
					Usage:    "Last block (exclusive) to fetch",
				},
41
				&cli.StringFlag{
42 43 44 45
					Name:     "inbox",
					Required: true,
					Usage:    "Batch Inbox Address",
				},
46
				&cli.StringFlag{
47 48 49 50
					Name:     "sender",
					Required: true,
					Usage:    "Batch Sender Address",
				},
51
				&cli.StringFlag{
52 53 54 55
					Name:  "out",
					Value: "/tmp/batch_decoder/transactions_cache",
					Usage: "Cache directory for the found transactions",
				},
56
				&cli.StringFlag{
57 58 59
					Name:     "l1",
					Required: true,
					Usage:    "L1 RPC URL",
60
					EnvVars:  []string{"L1_RPC"},
61
				},
62 63 64 65 66 67
				&cli.StringFlag{
					Name:     "l1.beacon",
					Required: false,
					Usage:    "Address of L1 Beacon-node HTTP endpoint to use",
					EnvVars:  []string{"L1_BEACON"},
				},
68 69 70 71 72
				&cli.IntFlag{
					Name:  "concurrent-requests",
					Value: 10,
					Usage: "Concurrency level when fetching L1",
				},
73 74
			},
			Action: func(cliCtx *cli.Context) error {
75
				l1Client, err := ethclient.Dial(cliCtx.String("l1"))
76 77 78
				if err != nil {
					log.Fatal(err)
				}
79
				ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
80
				defer cancel()
81
				chainID, err := l1Client.ChainID(ctx)
82 83 84
				if err != nil {
					log.Fatal(err)
				}
85 86 87 88 89 90 91 92 93 94 95 96 97
				beaconAddr := cliCtx.String("l1.beacon")
				var beacon *sources.L1BeaconClient
				if beaconAddr != "" {
					beaconClient := sources.NewBeaconHTTPClient(client.NewBasicHTTPClient(beaconAddr, nil))
					beaconCfg := sources.L1BeaconClientConfig{FetchAllSidecars: false}
					beacon = sources.NewL1BeaconClient(beaconClient, beaconCfg)
					_, err := beacon.GetVersion(ctx)
					if err != nil {
						log.Fatal(fmt.Errorf("failed to check L1 Beacon API version: %w", err))
					}
				} else {
					fmt.Println("L1 Beacon endpoint not set. Unable to fetch post-ecotone channel frames")
				}
98 99 100 101 102
				config := fetch.Config{
					Start:   uint64(cliCtx.Int("start")),
					End:     uint64(cliCtx.Int("end")),
					ChainID: chainID,
					BatchSenders: map[common.Address]struct{}{
103
						common.HexToAddress(cliCtx.String("sender")): {},
104
					},
105 106 107
					BatchInbox:         common.HexToAddress(cliCtx.String("inbox")),
					OutDirectory:       cliCtx.String("out"),
					ConcurrentRequests: uint64(cliCtx.Int("concurrent-requests")),
108
				}
109
				totalValid, totalInvalid := fetch.Batches(l1Client, beacon, config)
110 111 112 113 114 115
				fmt.Printf("Fetched batches in range [%v,%v). Found %v valid & %v invalid batches\n", config.Start, config.End, totalValid, totalInvalid)
				fmt.Printf("Fetch Config: Chain ID: %v. Inbox Address: %v. Valid Senders: %v.\n", config.ChainID, config.BatchInbox, config.BatchSenders)
				fmt.Printf("Wrote transactions with batches to %v\n", config.OutDirectory)
				return nil
			},
		},
116 117
		{
			Name:  "reassemble",
118
			Usage: "Reassembles channels from fetched batch transactions and decode batches",
119
			Flags: []cli.Flag{
120
				&cli.StringFlag{
121 122 123 124
					Name:  "in",
					Value: "/tmp/batch_decoder/transactions_cache",
					Usage: "Cache directory for the found transactions",
				},
125
				&cli.StringFlag{
126 127 128 129
					Name:  "out",
					Value: "/tmp/batch_decoder/channel_cache",
					Usage: "Cache directory for the found channels",
				},
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
				&cli.Uint64Flag{
					Name:  "l2-chain-id",
					Value: 10,
					Usage: "L2 chain id for span batch derivation. Default value from op-mainnet.",
				},
				&cli.Uint64Flag{
					Name:  "l2-genesis-timestamp",
					Value: 1686068903,
					Usage: "L2 genesis time for span batch derivation. Default value from op-mainnet. " +
						"Superchain-registry prioritized when given value is inconsistent.",
				},
				&cli.Uint64Flag{
					Name:  "l2-block-time",
					Value: 2,
					Usage: "L2 block time for span batch derivation. Default value from op-mainnet. " +
						"Superchain-registry prioritized when given value is inconsistent.",
				},
				&cli.StringFlag{
					Name:  "inbox",
					Value: "0xFF00000000000000000000000000000000000010",
					Usage: "Batch Inbox Address. Default value from op-mainnet. " +
						"Superchain-registry prioritized when given value is inconsistent.",
				},
153 154
			},
			Action: func(cliCtx *cli.Context) error {
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
				var (
					L2GenesisTime     uint64         = cliCtx.Uint64("l2-genesis-timestamp")
					L2BlockTime       uint64         = cliCtx.Uint64("l2-block-time")
					BatchInboxAddress common.Address = common.HexToAddress(cliCtx.String("inbox"))
				)
				L2ChainID := new(big.Int).SetUint64(cliCtx.Uint64("l2-chain-id"))
				rollupCfg, err := rollup.LoadOPStackRollupConfig(L2ChainID.Uint64())
				if err == nil {
					// prioritize superchain config
					if L2GenesisTime != rollupCfg.Genesis.L2Time {
						L2GenesisTime = rollupCfg.Genesis.L2Time
						fmt.Printf("L2GenesisTime overridden: %v\n", L2GenesisTime)
					}
					if L2BlockTime != rollupCfg.BlockTime {
						L2BlockTime = rollupCfg.BlockTime
						fmt.Printf("L2BlockTime overridden: %v\n", L2BlockTime)
					}
					if BatchInboxAddress != rollupCfg.BatchInboxAddress {
						BatchInboxAddress = rollupCfg.BatchInboxAddress
						fmt.Printf("BatchInboxAddress overridden: %v\n", BatchInboxAddress)
					}
				}
177
				config := reassemble.Config{
178 179 180 181 182 183
					BatchInbox:    BatchInboxAddress,
					InDirectory:   cliCtx.String("in"),
					OutDirectory:  cliCtx.String("out"),
					L2ChainID:     L2ChainID,
					L2GenesisTime: L2GenesisTime,
					L2BlockTime:   L2BlockTime,
184
				}
185
				reassemble.Channels(config, rollupCfg)
186 187 188
				return nil
			},
		},
189 190 191 192
		{
			Name:  "force-close",
			Usage: "Create the tx data which will force close a channel",
			Flags: []cli.Flag{
193
				&cli.StringFlag{
194 195 196 197
					Name:     "id",
					Required: true,
					Usage:    "ID of the channel to close",
				},
198
				&cli.StringFlag{
199 200 201 202
					Name:  "inbox",
					Value: "0x0000000000000000000000000000000000000000",
					Usage: "(Optional) Batch Inbox Address",
				},
203
				&cli.StringFlag{
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
					Name:  "in",
					Value: "/tmp/batch_decoder/transactions_cache",
					Usage: "Cache directory for the found transactions",
				},
			},
			Action: func(cliCtx *cli.Context) error {
				var id derive.ChannelID
				if err := (&id).UnmarshalText([]byte(cliCtx.String("id"))); err != nil {
					log.Fatal(err)
				}
				frames := reassemble.LoadFrames(cliCtx.String("in"), common.HexToAddress(cliCtx.String("inbox")))
				var filteredFrames []derive.Frame
				for _, frame := range frames {
					if frame.Frame.ID == id {
						filteredFrames = append(filteredFrames, frame.Frame)
					}
				}
				data, err := derive.ForceCloseTxData(filteredFrames)
				if err != nil {
					log.Fatal(err)
				}
				fmt.Printf("%x\n", data)
				return nil
			},
		},
229 230 231 232 233 234
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}