github.com/ethereum-optimism/optimism@v1.7.2/op-node/cmd/batch_decoder/reassemble/reassemble.go (about) 1 package reassemble 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "log" 8 "math/big" 9 "os" 10 "path" 11 "sort" 12 13 "github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/fetch" 14 "github.com/ethereum-optimism/optimism/op-node/rollup/derive" 15 "github.com/ethereum-optimism/optimism/op-service/eth" 16 "github.com/ethereum/go-ethereum/common" 17 ) 18 19 type ChannelWithMetadata struct { 20 ID derive.ChannelID `json:"id"` 21 IsReady bool `json:"is_ready"` 22 InvalidFrames bool `json:"invalid_frames"` 23 InvalidBatches bool `json:"invalid_batches"` 24 Frames []FrameWithMetadata `json:"frames"` 25 Batches []derive.Batch `json:"batches"` 26 BatchTypes []int `json:"batch_types"` 27 } 28 29 type FrameWithMetadata struct { 30 TxHash common.Hash `json:"transaction_hash"` 31 InclusionBlock uint64 `json:"inclusion_block"` 32 Timestamp uint64 `json:"timestamp"` 33 BlockHash common.Hash `json:"block_hash"` 34 Frame derive.Frame `json:"frame"` 35 } 36 37 type Config struct { 38 BatchInbox common.Address 39 InDirectory string 40 OutDirectory string 41 L2ChainID *big.Int 42 L2GenesisTime uint64 43 L2BlockTime uint64 44 } 45 46 func LoadFrames(directory string, inbox common.Address) []FrameWithMetadata { 47 txns := loadTransactions(directory, inbox) 48 // Sort first by block number then by transaction index inside the block number range. 49 // This is to match the order they are processed in derivation. 50 sort.Slice(txns, func(i, j int) bool { 51 if txns[i].BlockNumber == txns[j].BlockNumber { 52 return txns[i].TxIndex < txns[j].TxIndex 53 } else { 54 return txns[i].BlockNumber < txns[j].BlockNumber 55 } 56 57 }) 58 return transactionsToFrames(txns) 59 } 60 61 // Channels loads all transactions from the given input directory that are submitted to the 62 // specified batch inbox and then re-assembles all channels & writes the re-assembled channels 63 // to the out directory. 64 func Channels(config Config) { 65 if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { 66 log.Fatal(err) 67 } 68 frames := LoadFrames(config.InDirectory, config.BatchInbox) 69 framesByChannel := make(map[derive.ChannelID][]FrameWithMetadata) 70 for _, frame := range frames { 71 framesByChannel[frame.Frame.ID] = append(framesByChannel[frame.Frame.ID], frame) 72 } 73 for id, frames := range framesByChannel { 74 ch := processFrames(config, id, frames) 75 filename := path.Join(config.OutDirectory, fmt.Sprintf("%s.json", id.String())) 76 if err := writeChannel(ch, filename); err != nil { 77 log.Fatal(err) 78 } 79 } 80 } 81 82 func writeChannel(ch ChannelWithMetadata, filename string) error { 83 file, err := os.Create(filename) 84 if err != nil { 85 log.Fatal(err) 86 } 87 defer file.Close() 88 enc := json.NewEncoder(file) 89 return enc.Encode(ch) 90 } 91 92 func processFrames(cfg Config, id derive.ChannelID, frames []FrameWithMetadata) ChannelWithMetadata { 93 ch := derive.NewChannel(id, eth.L1BlockRef{Number: frames[0].InclusionBlock}) 94 invalidFrame := false 95 96 for _, frame := range frames { 97 if ch.IsReady() { 98 fmt.Printf("Channel %v is ready despite having more frames\n", id.String()) 99 invalidFrame = true 100 break 101 } 102 if err := ch.AddFrame(frame.Frame, eth.L1BlockRef{Number: frame.InclusionBlock}); err != nil { 103 fmt.Printf("Error adding to channel %v. Err: %v\n", id.String(), err) 104 invalidFrame = true 105 } 106 } 107 108 var batches []derive.Batch 109 var batchTypes []int 110 invalidBatches := false 111 if ch.IsReady() { 112 br, err := derive.BatchReader(ch.Reader()) 113 if err == nil { 114 for batchData, err := br(); err != io.EOF; batchData, err = br() { 115 if err != nil { 116 fmt.Printf("Error reading batchData for channel %v. Err: %v\n", id.String(), err) 117 invalidBatches = true 118 } else { 119 batchType := batchData.GetBatchType() 120 batchTypes = append(batchTypes, int(batchType)) 121 switch batchType { 122 case derive.SingularBatchType: 123 singularBatch, err := derive.GetSingularBatch(batchData) 124 if err != nil { 125 invalidBatches = true 126 fmt.Printf("Error converting singularBatch from batchData for channel %v. Err: %v\n", id.String(), err) 127 } 128 // singularBatch will be nil when errored 129 batches = append(batches, singularBatch) 130 case derive.SpanBatchType: 131 spanBatch, err := derive.DeriveSpanBatch(batchData, cfg.L2BlockTime, cfg.L2GenesisTime, cfg.L2ChainID) 132 if err != nil { 133 invalidBatches = true 134 fmt.Printf("Error deriving spanBatch from batchData for channel %v. Err: %v\n", id.String(), err) 135 } 136 // spanBatch will be nil when errored 137 batches = append(batches, spanBatch) 138 default: 139 fmt.Printf("unrecognized batch type: %d for channel %v.\n", batchData.GetBatchType(), id.String()) 140 } 141 } 142 } 143 } else { 144 fmt.Printf("Error creating batch reader for channel %v. Err: %v\n", id.String(), err) 145 } 146 } else { 147 fmt.Printf("Channel %v is not ready\n", id.String()) 148 } 149 150 return ChannelWithMetadata{ 151 ID: id, 152 Frames: frames, 153 IsReady: ch.IsReady(), 154 InvalidFrames: invalidFrame, 155 InvalidBatches: invalidBatches, 156 Batches: batches, 157 BatchTypes: batchTypes, 158 } 159 } 160 161 func transactionsToFrames(txns []fetch.TransactionWithMetadata) []FrameWithMetadata { 162 var out []FrameWithMetadata 163 for _, tx := range txns { 164 for _, frame := range tx.Frames { 165 fm := FrameWithMetadata{ 166 TxHash: tx.Tx.Hash(), 167 InclusionBlock: tx.BlockNumber, 168 BlockHash: tx.BlockHash, 169 Timestamp: tx.BlockTime, 170 Frame: frame, 171 } 172 out = append(out, fm) 173 } 174 } 175 return out 176 } 177 178 // if inbox is the zero address, it will load all frames 179 func loadTransactions(dir string, inbox common.Address) []fetch.TransactionWithMetadata { 180 files, err := os.ReadDir(dir) 181 if err != nil { 182 log.Fatal(err) 183 } 184 var out []fetch.TransactionWithMetadata 185 for _, file := range files { 186 f := path.Join(dir, file.Name()) 187 txm := loadTransactionsFile(f) 188 if (inbox == common.Address{} || txm.InboxAddr == inbox) && txm.ValidSender { 189 out = append(out, txm) 190 } 191 } 192 return out 193 } 194 195 func loadTransactionsFile(file string) fetch.TransactionWithMetadata { 196 f, err := os.Open(file) 197 if err != nil { 198 log.Fatal(err) 199 } 200 defer f.Close() 201 dec := json.NewDecoder(f) 202 var txm fetch.TransactionWithMetadata 203 if err := dec.Decode(&txm); err != nil { 204 log.Fatalf("Failed to decode %v. Err: %v\n", file, err) 205 } 206 return txm 207 }