github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/test/scripts/batchsender/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math/big" 8 "os" 9 "strconv" 10 "time" 11 12 "github.com/0xPolygon/supernets2-node/config" 13 "github.com/0xPolygon/supernets2-node/etherman" 14 ethmanTypes "github.com/0xPolygon/supernets2-node/etherman/types" 15 "github.com/0xPolygon/supernets2-node/log" 16 "github.com/0xPolygon/supernets2-node/test/operations" 17 "github.com/ethereum/go-ethereum" 18 "github.com/ethereum/go-ethereum/common" 19 ethTypes "github.com/ethereum/go-ethereum/core/types" 20 "github.com/mitchellh/mapstructure" 21 "github.com/spf13/viper" 22 "github.com/urfave/cli/v2" 23 ) 24 25 const ( 26 flagSequencesName = "sequences" 27 flagWaitName = "wait" 28 flagVerboseName = "verbose" 29 ) 30 31 var ( 32 flagSequences = cli.Uint64Flag{ 33 Name: flagSequencesName, 34 Aliases: []string{"s"}, 35 Usage: "send batches for the provided number of sequences.", 36 Required: false, 37 } 38 flagWait = cli.BoolFlag{ 39 Name: flagWaitName, 40 Aliases: []string{"w"}, 41 Usage: "wait batch transaction to be confirmed", 42 Required: false, 43 } 44 flagVerbose = cli.BoolFlag{ 45 Name: flagVerboseName, 46 Aliases: []string{"v"}, 47 Usage: "output verbose logs", 48 Required: false, 49 } 50 ) 51 52 func main() { 53 batchsender := cli.NewApp() 54 batchsender.Name = "batchsender" 55 batchsender.Usage = "send batch transactions to L1" 56 batchsender.Description = `This tool allows to send a specified number of batch transactions to L1. 57 Optionally it can wait for the batches to be validated.` 58 batchsender.DefaultCommand = "send" 59 batchsender.Flags = []cli.Flag{&flagSequences, &flagWait, &flagVerbose} 60 batchsender.Commands = []*cli.Command{ 61 { 62 Before: setLogLevel, 63 Name: "send", 64 Aliases: []string{}, 65 Usage: "Sends the specified number of batch transactions to L1", 66 Description: `This command sends the specified number of batches to L1. 67 If --sequences flag is used, the number of batches is repeated for the number of sequences provided. 68 If --wait flag is used, it waits for the corresponding validation transaction.`, 69 ArgsUsage: "number of batches to be sent (default: 1)", 70 Action: sendBatches, 71 }, 72 } 73 74 err := batchsender.Run(os.Args) 75 if err != nil { 76 log.Fatal(err) 77 os.Exit(1) 78 } 79 } 80 81 func setLogLevel(ctx *cli.Context) error { 82 logLevel := "info" 83 if ctx.Bool(flagVerboseName) { 84 logLevel = "debug" 85 } 86 87 log.Init(log.Config{ 88 Level: logLevel, 89 Outputs: []string{"stderr"}, 90 }) 91 return nil 92 } 93 94 func sendBatches(cliCtx *cli.Context) error { 95 ctx := cliCtx.Context 96 97 // retrieve default configuration 98 var cfg config.Config 99 viper.SetConfigType("toml") 100 err := viper.ReadConfig(bytes.NewBuffer([]byte(config.DefaultValues))) 101 if err != nil { 102 return err 103 } 104 err = viper.Unmarshal(&cfg, viper.DecodeHook(mapstructure.TextUnmarshallerHookFunc())) 105 if err != nil { 106 return err 107 } 108 109 auth, err := operations.GetAuth(operations.DefaultSequencerPrivateKey, operations.DefaultL1ChainID) 110 if err != nil { 111 return err 112 } 113 114 ethMan, err := etherman.NewClient(cfg.Etherman, cfg.NetworkConfig.L1Config) 115 if err != nil { 116 return err 117 } 118 119 err = ethMan.AddOrReplaceAuth(*auth) 120 if err != nil { 121 return err 122 } 123 log.Info("Using address: ", auth.From) 124 125 wait := cliCtx.Bool(flagWaitName) 126 127 nBatches := 1 // send 1 batch by default 128 if cliCtx.NArg() > 0 { 129 nBatchesArgStr := cliCtx.Args().Get(0) 130 nBatchesArg, err := strconv.Atoi(nBatchesArgStr) 131 if err == nil { 132 nBatches = nBatchesArg 133 } 134 } 135 136 nSequences := int(cliCtx.Uint64(flagSequencesName)) 137 138 var sentTxs []*ethTypes.Transaction 139 sentTxsMap := make(map[common.Hash]struct{}) 140 141 var duration time.Duration 142 if nBatches > 1 { 143 duration = 500 144 } 145 146 // here the behavior is different: 147 // - if the `--sequences` flag is used we send ns sequences filled with nb batches each 148 // - if the flag is not used we send one sequence for each batch 149 var ns, nb int 150 if nSequences == 0 { 151 ns = nBatches 152 nb = 1 153 } else { 154 ns = nSequences 155 nb = nBatches 156 } 157 158 nonce, err := ethMan.CurrentNonce(ctx, auth.From) 159 if err != nil { 160 err := fmt.Errorf("failed to get current nonce: %w", err) 161 log.Error(err.Error()) 162 return err 163 } 164 165 for i := 0; i < ns; i++ { 166 currentBlock, err := ethMan.EthClient.BlockByNumber(ctx, nil) 167 if err != nil { 168 return err 169 } 170 log.Debug("currentBlock.Time(): ", currentBlock.Time()) 171 172 seqs := make([]ethmanTypes.Sequence, 0, nBatches) 173 for i := 0; i < nb; i++ { 174 // empty rollup 175 seqs = append(seqs, ethmanTypes.Sequence{ 176 GlobalExitRoot: common.HexToHash("0x"), 177 BatchL2Data: []byte{}, 178 Timestamp: int64(currentBlock.Time() - 1), // fit in latest-sequence < > current-block rage 179 }) 180 } 181 182 // send to L1 183 to, data, err := ethMan.BuildSequenceBatchesTxData(auth.From, seqs, nil) 184 if err != nil { 185 return err 186 } 187 tx := ethTypes.NewTx(ðTypes.LegacyTx{ 188 To: to, 189 Data: data, 190 }) 191 signedTx, err := ethMan.SignTx(ctx, auth.From, tx) 192 if err != nil { 193 return err 194 } 195 err = ethMan.SendTx(ctx, signedTx) 196 if err != nil { 197 return err 198 } 199 gas, err := ethMan.EstimateGas(ctx, auth.From, to, nil, data) 200 if err != nil { 201 err := fmt.Errorf("failed to estimate gas: %w", err) 202 log.Error(err.Error()) 203 return err 204 } 205 // get gas price 206 gasPrice, err := ethMan.SuggestedGasPrice(ctx) 207 if err != nil { 208 err := fmt.Errorf("failed to get suggested gas price: %w", err) 209 log.Error(err.Error()) 210 return err 211 } 212 tx = ethTypes.NewTx(ðTypes.LegacyTx{ 213 Nonce: nonce, 214 Gas: gas + uint64(i), 215 GasPrice: gasPrice, 216 To: to, 217 Data: data, 218 }) 219 signedTx, err = ethMan.SignTx(ctx, auth.From, tx) 220 if err != nil { 221 log.Error(err.Error()) 222 return err 223 } 224 err = ethMan.SendTx(ctx, signedTx) 225 if err != nil { 226 log.Error(err.Error()) 227 return err 228 } 229 230 log.Info("TxHash: ", signedTx.Hash()) 231 sentTxs = append(sentTxs, signedTx) 232 sentTxsMap[signedTx.Hash()] = struct{}{} 233 234 time.Sleep(duration * time.Millisecond) 235 } 236 237 sentBatches := len(sentTxs) 238 239 if wait { // wait proofs 240 log.Info("Waiting for transactions to be confirmed...") 241 time.Sleep(time.Second) 242 243 virtualBatches := make(map[uint64]common.Hash) 244 verifiedBatches := make(map[uint64]struct{}) 245 loggedBatches := make(map[uint64]struct{}) 246 247 miningTimeout := 180 * time.Second //nolint:gomnd 248 waitTimeout := time.Duration(180*len(sentTxs)) * time.Second //nolint:gomnd 249 done := make(chan struct{}) 250 251 for _, tx := range sentTxs { 252 err := operations.WaitTxToBeMined(ctx, ethMan.EthClient, tx, miningTimeout) 253 if err != nil { 254 return err 255 } 256 } 257 258 for { 259 select { 260 case <-time.After(waitTimeout): 261 return errors.New("deadline exceeded") 262 case <-done: 263 success(sentBatches) 264 return nil 265 default: 266 txLoop: 267 for _, tx := range sentTxs { 268 // get rollup tx block number 269 receipt, err := ethMan.EthClient.TransactionReceipt(ctx, tx.Hash()) 270 if err != nil { 271 return err 272 } 273 274 fromBlock := receipt.BlockNumber 275 toBlock := new(big.Int).Add(fromBlock, new(big.Int).SetUint64(cfg.Synchronizer.SyncChunkSize)) 276 query := ethereum.FilterQuery{ 277 FromBlock: fromBlock, 278 ToBlock: toBlock, 279 Addresses: ethMan.SCAddresses, 280 } 281 logs, err := ethMan.EthClient.FilterLogs(ctx, query) 282 if err != nil { 283 return err 284 } 285 for _, vLog := range logs { 286 switch vLog.Topics[0] { 287 case etherman.SequencedBatchesSigHash(): 288 if vLog.TxHash == tx.Hash() { // ignore other txs happening on L1 289 sb, err := ethMan.Supernets2.ParseSequenceBatches(vLog) 290 if err != nil { 291 return err 292 } 293 294 virtualBatches[sb.NumBatch] = vLog.TxHash 295 296 if _, logged := loggedBatches[sb.NumBatch]; !logged { 297 log.Infof("Batch [%d] virtualized in TxHash [%v]", sb.NumBatch, vLog.TxHash) 298 loggedBatches[sb.NumBatch] = struct{}{} 299 } 300 } 301 case etherman.TrustedVerifyBatchesSigHash(): 302 vb, err := ethMan.Supernets2.ParseVerifyBatchesTrustedAggregator(vLog) 303 if err != nil { 304 return err 305 } 306 307 if _, verified := verifiedBatches[vb.NumBatch]; !verified { 308 log.Infof("Batch [%d] verified in TxHash [%v]", vb.NumBatch, vLog.TxHash) 309 verifiedBatches[vb.NumBatch] = struct{}{} 310 } 311 312 // batch is verified, remove it from the txs set 313 delete(sentTxsMap, virtualBatches[vb.NumBatch]) 314 if len(sentTxsMap) == 0 { 315 close(done) 316 break txLoop 317 } 318 } 319 } 320 321 // wait for verifications 322 time.Sleep(time.Second) //nolint:gomnd 323 } 324 } 325 } 326 } 327 328 success(sentBatches) 329 return nil 330 } 331 332 func success(nBatches int) { 333 if nBatches > 1 { 334 log.Infof("Successfully sent %d batches", nBatches) 335 } else { 336 log.Info("Successfully sent 1 batch") 337 } 338 }