Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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