Commit 7d92f417 authored by Park Changwan's avatar Park Changwan Committed by GitHub

op-node: batch-decoder: Support ecotone (#10577)

parent 4b86fd76
...@@ -12,7 +12,10 @@ import ( ...@@ -12,7 +12,10 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
...@@ -28,8 +31,8 @@ type TransactionWithMetadata struct { ...@@ -28,8 +31,8 @@ type TransactionWithMetadata struct {
Sender common.Address `json:"sender"` Sender common.Address `json:"sender"`
ValidSender bool `json:"valid_sender"` ValidSender bool `json:"valid_sender"`
Frames []derive.Frame `json:"frames"` Frames []derive.Frame `json:"frames"`
FrameErr string `json:"frame_parse_error"` FrameErrs []string `json:"frame_parse_error"`
ValidFrames bool `json:"valid_data"` ValidFrames []bool `json:"valid_data"`
Tx *types.Transaction `json:"tx"` Tx *types.Transaction `json:"tx"`
} }
...@@ -45,7 +48,7 @@ type Config struct { ...@@ -45,7 +48,7 @@ type Config struct {
// Batches fetches & stores all transactions sent to the batch inbox address in // Batches fetches & stores all transactions sent to the batch inbox address in
// the given block range (inclusive to exclusive). // the given block range (inclusive to exclusive).
// The transactions & metadata are written to the out directory. // The transactions & metadata are written to the out directory.
func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid uint64) { func Batches(client *ethclient.Client, beacon *sources.L1BeaconClient, config Config) (totalValid, totalInvalid uint64) {
if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { if err := os.MkdirAll(config.OutDirectory, 0750); err != nil {
log.Fatal(err) log.Fatal(err)
} }
...@@ -61,7 +64,7 @@ func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid ...@@ -61,7 +64,7 @@ func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid
} }
number := i number := i
g.Go(func() error { g.Go(func() error {
valid, invalid, err := fetchBatchesPerBlock(ctx, client, number, signer, config) valid, invalid, err := fetchBatchesPerBlock(ctx, client, beacon, number, signer, config)
if err != nil { if err != nil {
return fmt.Errorf("error occurred while fetching block %d: %w", number, err) return fmt.Errorf("error occurred while fetching block %d: %w", number, err)
} }
...@@ -77,7 +80,7 @@ func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid ...@@ -77,7 +80,7 @@ func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid
} }
// fetchBatchesPerBlock gets a block & the parses all of the transactions in the block. // fetchBatchesPerBlock gets a block & the parses all of the transactions in the block.
func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number uint64, signer types.Signer, config Config) (uint64, uint64, error) { func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, beacon *sources.L1BeaconClient, number uint64, signer types.Signer, config Config) (uint64, uint64, error) {
validBatchCount := uint64(0) validBatchCount := uint64(0)
invalidBatchCount := uint64(0) invalidBatchCount := uint64(0)
ctx, cancel := context.WithTimeout(ctx, 10*time.Second) ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
...@@ -87,6 +90,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number ...@@ -87,6 +90,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
return 0, 0, err return 0, 0, err
} }
fmt.Println("Fetched block: ", number) fmt.Println("Fetched block: ", number)
blobIndex := 0 // index of each blob in the block's blob sidecar
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
if tx.To() != nil && *tx.To() == config.BatchInbox { if tx.To() != nil && *tx.To() == config.BatchInbox {
sender, err := signer.Sender(tx) sender, err := signer.Sender(tx)
...@@ -99,22 +103,66 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number ...@@ -99,22 +103,66 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
invalidBatchCount += 1 invalidBatchCount += 1
validSender = false validSender = false
} }
var datas []hexutil.Bytes
validFrames := true if tx.Type() != types.BlobTxType {
datas = append(datas, tx.Data())
// no need to increment blobIndex because no blobs
} else {
if beacon == nil {
fmt.Printf("Unable to handle blob transaction (%s) because L1 Beacon API not provided\n", tx.Hash().String())
blobIndex += len(tx.BlobHashes())
continue
}
var hashes []eth.IndexedBlobHash
for _, h := range tx.BlobHashes() {
idh := eth.IndexedBlobHash{
Index: uint64(blobIndex),
Hash: h,
}
hashes = append(hashes, idh)
blobIndex += 1
}
blobs, err := beacon.GetBlobs(ctx, eth.L1BlockRef{
Hash: block.Hash(),
Number: block.Number().Uint64(),
ParentHash: block.ParentHash(),
Time: block.Time(),
}, hashes)
if err != nil {
log.Fatal(fmt.Errorf("failed to fetch blobs: %w", err))
}
for _, blob := range blobs {
data, err := blob.ToData()
if err != nil {
log.Fatal(fmt.Errorf("failed to parse blobs: %w", err))
}
datas = append(datas, data)
}
}
var frameErrors []string
var frames []derive.Frame
var validFrames []bool
validBatch := true
for _, data := range datas {
validFrame := true
frameError := "" frameError := ""
frames, err := derive.ParseFrames(tx.Data()) framesPerData, err := derive.ParseFrames(data)
if err != nil { if err != nil {
fmt.Printf("Found a transaction (%s) with invalid data: %v\n", tx.Hash().String(), err) fmt.Printf("Found a transaction (%s) with invalid data: %v\n", tx.Hash().String(), err)
validFrames = false validFrame = false
validBatch = false
frameError = err.Error() frameError = err.Error()
} else {
frames = append(frames, framesPerData...)
} }
frameErrors = append(frameErrors, frameError)
if validSender && validFrames { validFrames = append(validFrames, validFrame)
}
if validSender && validBatch {
validBatchCount += 1 validBatchCount += 1
} else { } else {
invalidBatchCount += 1 invalidBatchCount += 1
} }
txm := &TransactionWithMetadata{ txm := &TransactionWithMetadata{
Tx: tx, Tx: tx,
Sender: sender, Sender: sender,
...@@ -126,7 +174,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number ...@@ -126,7 +174,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
ChainId: config.ChainID.Uint64(), ChainId: config.ChainID.Uint64(),
InboxAddr: config.BatchInbox, InboxAddr: config.BatchInbox,
Frames: frames, Frames: frames,
FrameErr: frameError, FrameErrs: frameErrors,
ValidFrames: validFrames, ValidFrames: validFrames,
} }
filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String())) filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String()))
...@@ -140,6 +188,8 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number ...@@ -140,6 +188,8 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
return 0, 0, err return 0, 0, err
} }
file.Close() file.Close()
} else {
blobIndex += len(tx.BlobHashes())
} }
} }
return validBatchCount, invalidBatchCount, nil return validBatchCount, invalidBatchCount, nil
......
...@@ -12,6 +12,8 @@ import ( ...@@ -12,6 +12,8 @@ import (
"github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/reassemble" "github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/reassemble"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
...@@ -57,6 +59,12 @@ func main() { ...@@ -57,6 +59,12 @@ func main() {
Usage: "L1 RPC URL", Usage: "L1 RPC URL",
EnvVars: []string{"L1_RPC"}, EnvVars: []string{"L1_RPC"},
}, },
&cli.StringFlag{
Name: "l1.beacon",
Required: false,
Usage: "Address of L1 Beacon-node HTTP endpoint to use",
EnvVars: []string{"L1_BEACON"},
},
&cli.IntFlag{ &cli.IntFlag{
Name: "concurrent-requests", Name: "concurrent-requests",
Value: 10, Value: 10,
...@@ -64,16 +72,29 @@ func main() { ...@@ -64,16 +72,29 @@ func main() {
}, },
}, },
Action: func(cliCtx *cli.Context) error { Action: func(cliCtx *cli.Context) error {
client, err := ethclient.Dial(cliCtx.String("l1")) l1Client, err := ethclient.Dial(cliCtx.String("l1"))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel() defer cancel()
chainID, err := client.ChainID(ctx) chainID, err := l1Client.ChainID(ctx)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
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")
}
config := fetch.Config{ config := fetch.Config{
Start: uint64(cliCtx.Int("start")), Start: uint64(cliCtx.Int("start")),
End: uint64(cliCtx.Int("end")), End: uint64(cliCtx.Int("end")),
...@@ -85,7 +106,7 @@ func main() { ...@@ -85,7 +106,7 @@ func main() {
OutDirectory: cliCtx.String("out"), OutDirectory: cliCtx.String("out"),
ConcurrentRequests: uint64(cliCtx.Int("concurrent-requests")), ConcurrentRequests: uint64(cliCtx.Int("concurrent-requests")),
} }
totalValid, totalInvalid := fetch.Batches(client, config) totalValid, totalInvalid := fetch.Batches(l1Client, beacon, config)
fmt.Printf("Fetched batches in range [%v,%v). Found %v valid & %v invalid batches\n", config.Start, config.End, totalValid, totalInvalid) 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("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) fmt.Printf("Wrote transactions with batches to %v\n", config.OutDirectory)
......
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