github.com/cgcardona/r-subnet-evm@v0.1.5/cmd/simulator/load/loader.go (about)

     1  // Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package load
     5  
     6  import (
     7  	"context"
     8  	"crypto/ecdsa"
     9  	"fmt"
    10  	"math/big"
    11  
    12  	"github.com/cgcardona/r-subnet-evm/cmd/simulator/config"
    13  	"github.com/cgcardona/r-subnet-evm/cmd/simulator/key"
    14  	"github.com/cgcardona/r-subnet-evm/cmd/simulator/txs"
    15  	"github.com/cgcardona/r-subnet-evm/core/types"
    16  	"github.com/cgcardona/r-subnet-evm/ethclient"
    17  	"github.com/cgcardona/r-subnet-evm/params"
    18  	"github.com/ethereum/go-ethereum/common"
    19  	ethcrypto "github.com/ethereum/go-ethereum/crypto"
    20  	"github.com/ethereum/go-ethereum/log"
    21  	"golang.org/x/sync/errgroup"
    22  )
    23  
    24  // ExecuteLoader creates txSequences from [config] and has txAgents execute the specified simulation.
    25  func ExecuteLoader(ctx context.Context, config config.Config) error {
    26  	if config.Timeout > 0 {
    27  		var cancel context.CancelFunc
    28  		ctx, cancel = context.WithTimeout(ctx, config.Timeout)
    29  		defer cancel()
    30  	}
    31  
    32  	// Construct the arguments for the load simulator
    33  	clients := make([]ethclient.Client, 0, len(config.Endpoints))
    34  	for i := 0; i < config.Workers; i++ {
    35  		clientURI := config.Endpoints[i%len(config.Endpoints)]
    36  		client, err := ethclient.Dial(clientURI)
    37  		if err != nil {
    38  			return fmt.Errorf("failed to dial client at %s: %w", clientURI, err)
    39  		}
    40  		clients = append(clients, client)
    41  	}
    42  
    43  	keys, err := key.LoadAll(ctx, config.KeyDir)
    44  	if err != nil {
    45  		return err
    46  	}
    47  	// Ensure there are at least [config.Workers] keys and save any newly generated ones.
    48  	if len(keys) < config.Workers {
    49  		for i := 0; len(keys) < config.Workers; i++ {
    50  			newKey, err := key.Generate()
    51  			if err != nil {
    52  				return fmt.Errorf("failed to generate %d new key: %w", i, err)
    53  			}
    54  			if err := newKey.Save(config.KeyDir); err != nil {
    55  				return fmt.Errorf("failed to save %d new key: %w", i, err)
    56  			}
    57  			keys = append(keys, newKey)
    58  		}
    59  	}
    60  
    61  	// Each address needs: params.GWei * MaxFeeCap * params.TxGas * TxsPerWorker total wei
    62  	// to fund gas for all of their transactions.
    63  	maxFeeCap := new(big.Int).Mul(big.NewInt(params.GWei), big.NewInt(config.MaxFeeCap))
    64  	minFundsPerAddr := new(big.Int).Mul(maxFeeCap, big.NewInt(int64(config.TxsPerWorker*params.TxGas)))
    65  	log.Info("Distributing funds", "numTxsPerWorker", config.TxsPerWorker, "minFunds", minFundsPerAddr)
    66  	keys, err = DistributeFunds(ctx, clients[0], keys, config.Workers, minFundsPerAddr)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	log.Info("Distributed funds successfully")
    71  
    72  	pks := make([]*ecdsa.PrivateKey, 0, len(keys))
    73  	senders := make([]common.Address, 0, len(keys))
    74  	for _, key := range keys {
    75  		pks = append(pks, key.PrivKey)
    76  		senders = append(senders, key.Address)
    77  	}
    78  
    79  	bigGwei := big.NewInt(params.GWei)
    80  	gasTipCap := new(big.Int).Mul(bigGwei, big.NewInt(config.MaxTipCap))
    81  	gasFeeCap := new(big.Int).Mul(bigGwei, big.NewInt(config.MaxFeeCap))
    82  	client := clients[0]
    83  	chainID, err := client.ChainID(ctx)
    84  	if err != nil {
    85  		return fmt.Errorf("failed to fetch chainID: %w", err)
    86  	}
    87  	signer := types.LatestSignerForChainID(chainID)
    88  
    89  	log.Info("Creating transaction sequences...")
    90  	txGenerator := func(key *ecdsa.PrivateKey, nonce uint64) (*types.Transaction, error) {
    91  		addr := ethcrypto.PubkeyToAddress(key.PublicKey)
    92  		tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
    93  			ChainID:   chainID,
    94  			Nonce:     nonce,
    95  			GasTipCap: gasTipCap,
    96  			GasFeeCap: gasFeeCap,
    97  			Gas:       params.TxGas,
    98  			To:        &addr,
    99  			Data:      nil,
   100  			Value:     common.Big0,
   101  		})
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		return tx, nil
   106  	}
   107  	txSequences, err := txs.GenerateTxSequences(ctx, txGenerator, clients[0], pks, config.TxsPerWorker)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	log.Info("Constructing tx agents...", "numAgents", config.Workers)
   113  	agents := make([]txs.Agent[*types.Transaction], 0, config.Workers)
   114  	for i := 0; i < config.Workers; i++ {
   115  		agents = append(agents, txs.NewIssueNAgent[*types.Transaction](txSequences[i], NewSingleAddressTxWorker(ctx, clients[i], senders[i]), config.BatchSize))
   116  	}
   117  
   118  	log.Info("Starting tx agents...")
   119  	eg := errgroup.Group{}
   120  	for _, agent := range agents {
   121  		agent := agent
   122  		eg.Go(func() error {
   123  			return agent.Execute(ctx)
   124  		})
   125  	}
   126  
   127  	log.Info("Waiting for tx agents...")
   128  	if err := eg.Wait(); err != nil {
   129  		return err
   130  	}
   131  	log.Info("Tx agents completed successfully.")
   132  	return nil
   133  }