github.com/ethereum-optimism/optimism@v1.7.2/op-node/cmd/batch_decoder/fetch/fetch.go (about) 1 package fetch 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "log" 8 "math/big" 9 "os" 10 "path" 11 "sync/atomic" 12 "time" 13 14 "github.com/ethereum-optimism/optimism/op-node/rollup/derive" 15 "github.com/ethereum/go-ethereum/common" 16 "github.com/ethereum/go-ethereum/core/types" 17 "github.com/ethereum/go-ethereum/ethclient" 18 "golang.org/x/sync/errgroup" 19 ) 20 21 type TransactionWithMetadata struct { 22 TxIndex uint64 `json:"tx_index"` 23 InboxAddr common.Address `json:"inbox_address"` 24 BlockNumber uint64 `json:"block_number"` 25 BlockHash common.Hash `json:"block_hash"` 26 BlockTime uint64 `json:"block_time"` 27 ChainId uint64 `json:"chain_id"` 28 Sender common.Address `json:"sender"` 29 ValidSender bool `json:"valid_sender"` 30 Frames []derive.Frame `json:"frames"` 31 FrameErr string `json:"frame_parse_error"` 32 ValidFrames bool `json:"valid_data"` 33 Tx *types.Transaction `json:"tx"` 34 } 35 36 type Config struct { 37 Start, End uint64 38 ChainID *big.Int 39 BatchInbox common.Address 40 BatchSenders map[common.Address]struct{} 41 OutDirectory string 42 ConcurrentRequests uint64 43 } 44 45 // Batches fetches & stores all transactions sent to the batch inbox address in 46 // the given block range (inclusive to exclusive). 47 // The transactions & metadata are written to the out directory. 48 func Batches(client *ethclient.Client, config Config) (totalValid, totalInvalid uint64) { 49 if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { 50 log.Fatal(err) 51 } 52 signer := types.LatestSignerForChainID(config.ChainID) 53 concurrentRequests := int(config.ConcurrentRequests) 54 55 g, ctx := errgroup.WithContext(context.Background()) 56 g.SetLimit(concurrentRequests) 57 58 for i := config.Start; i < config.End; i++ { 59 if err := ctx.Err(); err != nil { 60 break 61 } 62 number := i 63 g.Go(func() error { 64 valid, invalid, err := fetchBatchesPerBlock(ctx, client, number, signer, config) 65 if err != nil { 66 return fmt.Errorf("error occurred while fetching block %d: %w", number, err) 67 } 68 atomic.AddUint64(&totalValid, valid) 69 atomic.AddUint64(&totalInvalid, invalid) 70 return nil 71 }) 72 } 73 if err := g.Wait(); err != nil { 74 log.Fatal(err) 75 } 76 return 77 } 78 79 // fetchBatchesPerBlock gets a block & the parses all of the transactions in the block. 80 func fetchBatchesPerBlock(ctx context.Context, client *ethclient.Client, number uint64, signer types.Signer, config Config) (uint64, uint64, error) { 81 validBatchCount := uint64(0) 82 invalidBatchCount := uint64(0) 83 ctx, cancel := context.WithTimeout(ctx, 10*time.Second) 84 defer cancel() 85 block, err := client.BlockByNumber(ctx, new(big.Int).SetUint64(number)) 86 if err != nil { 87 return 0, 0, err 88 } 89 fmt.Println("Fetched block: ", number) 90 for i, tx := range block.Transactions() { 91 if tx.To() != nil && *tx.To() == config.BatchInbox { 92 sender, err := signer.Sender(tx) 93 if err != nil { 94 return 0, 0, err 95 } 96 validSender := true 97 if _, ok := config.BatchSenders[sender]; !ok { 98 fmt.Printf("Found a transaction (%s) from an invalid sender (%s)\n", tx.Hash().String(), sender.String()) 99 invalidBatchCount += 1 100 validSender = false 101 } 102 103 validFrames := true 104 frameError := "" 105 frames, err := derive.ParseFrames(tx.Data()) 106 if err != nil { 107 fmt.Printf("Found a transaction (%s) with invalid data: %v\n", tx.Hash().String(), err) 108 validFrames = false 109 frameError = err.Error() 110 } 111 112 if validSender && validFrames { 113 validBatchCount += 1 114 } else { 115 invalidBatchCount += 1 116 } 117 118 txm := &TransactionWithMetadata{ 119 Tx: tx, 120 Sender: sender, 121 ValidSender: validSender, 122 TxIndex: uint64(i), 123 BlockNumber: block.NumberU64(), 124 BlockHash: block.Hash(), 125 BlockTime: block.Time(), 126 ChainId: config.ChainID.Uint64(), 127 InboxAddr: config.BatchInbox, 128 Frames: frames, 129 FrameErr: frameError, 130 ValidFrames: validFrames, 131 } 132 filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", tx.Hash().String())) 133 file, err := os.Create(filename) 134 if err != nil { 135 return 0, 0, err 136 } 137 defer file.Close() 138 enc := json.NewEncoder(file) 139 if err := enc.Encode(txm); err != nil { 140 return 0, 0, err 141 } 142 } 143 } 144 return validBatchCount, invalidBatchCount, nil 145 }