Skip to content

Commit

Permalink
op-node: batch-decoder: Support ecotone (ethereum-optimism#10577)
Browse files Browse the repository at this point in the history
  • Loading branch information
pcw109550 authored and rdovgan committed Jun 24, 2024
1 parent af1b415 commit d8f44a2
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 20 deletions.
84 changes: 67 additions & 17 deletions op-node/cmd/batch_decoder/fetch/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import (
"time"

"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/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"golang.org/x/sync/errgroup"
Expand All @@ -28,8 +31,8 @@ type TransactionWithMetadata struct {
Sender common.Address `json:"sender"`
ValidSender bool `json:"valid_sender"`
Frames []derive.Frame `json:"frames"`
FrameErr string `json:"frame_parse_error"`
ValidFrames bool `json:"valid_data"`
FrameErrs []string `json:"frame_parse_error"`
ValidFrames []bool `json:"valid_data"`
Tx *types.Transaction `json:"tx"`
}

Expand All @@ -45,7 +48,7 @@ type Config struct {
// Batches fetches & stores all transactions sent to the batch inbox address in
// the given block range (inclusive to exclusive).
// 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 {
log.Fatal(err)
}
Expand All @@ -61,7 +64,7 @@ func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid
}
number := i
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 {
return fmt.Errorf("error occurred while fetching block %d: %w", number, err)
}
Expand All @@ -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.
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)
invalidBatchCount := uint64(0)
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
Expand All @@ -87,6 +90,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
return 0, 0, err
}
fmt.Println("Fetched block: ", number)
blobIndex := 0 // index of each blob in the block's blob sidecar
for i, tx := range block.Transactions() {
if tx.To() != nil && *tx.To() == config.BatchInbox {
sender, err := signer.Sender(tx)
Expand All @@ -99,22 +103,66 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
invalidBatchCount += 1
validSender = false
}

validFrames := true
frameError := ""
frames, err := derive.ParseFrames(tx.Data())
if err != nil {
fmt.Printf("Found a transaction (%s) with invalid data: %v\n", tx.Hash().String(), err)
validFrames = false
frameError = err.Error()
var datas []hexutil.Bytes
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)
}
}

if validSender && validFrames {
var frameErrors []string
var frames []derive.Frame
var validFrames []bool
validBatch := true
for _, data := range datas {
validFrame := true
frameError := ""
framesPerData, err := derive.ParseFrames(data)
if err != nil {
fmt.Printf("Found a transaction (%s) with invalid data: %v\n", tx.Hash().String(), err)
validFrame = false
validBatch = false
frameError = err.Error()
} else {
frames = append(frames, framesPerData...)
}
frameErrors = append(frameErrors, frameError)
validFrames = append(validFrames, validFrame)
}
if validSender && validBatch {
validBatchCount += 1
} else {
invalidBatchCount += 1
}

txm := &TransactionWithMetadata{
Tx: tx,
Sender: sender,
Expand All @@ -126,7 +174,7 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
ChainId: config.ChainID.Uint64(),
InboxAddr: config.BatchInbox,
Frames: frames,
FrameErr: frameError,
FrameErrs: frameErrors,
ValidFrames: validFrames,
}
filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String()))
Expand All @@ -140,6 +188,8 @@ func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number
return 0, 0, err
}
file.Close()
} else {
blobIndex += len(tx.BlobHashes())
}
}
return validBatchCount, invalidBatchCount, nil
Expand Down
27 changes: 24 additions & 3 deletions op-node/cmd/batch_decoder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"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/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/ethclient"
"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -57,23 +59,42 @@ func main() {
Usage: "L1 RPC URL",
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{
Name: "concurrent-requests",
Value: 10,
Usage: "Concurrency level when fetching L1",
},
},
Action: func(cliCtx *cli.Context) error {
client, err := ethclient.Dial(cliCtx.String("l1"))
l1Client, err := ethclient.Dial(cliCtx.String("l1"))
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
chainID, err := client.ChainID(ctx)
chainID, err := l1Client.ChainID(ctx)
if err != nil {
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{
Start: uint64(cliCtx.Int("start")),
End: uint64(cliCtx.Int("end")),
Expand All @@ -85,7 +106,7 @@ func main() {
OutDirectory: cliCtx.String("out"),
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("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)
Expand Down

0 comments on commit d8f44a2

Please sign in to comment.