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(&ethTypes.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(&ethTypes.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  }