diff --git a/environments/gen.toml b/environments/gen.toml index 561ef10..a988991 100644 --- a/environments/gen.toml +++ b/environments/gen.toml @@ -10,8 +10,8 @@ gasFeeCap = "1000000007" # gasFeeCap to use for the deployment txs - env: $ETH_DEPLOYMENT_GAS_FEE_CAP [contractSpammer] - frequency = 10 # how often to send a transaction (in milliseconds) - env: $ETH_CALL_FREQ - totalNumber = 5000 # total number of transactions to send (per sender) - env: $ETH_CALL_TOTAL_NUMBER + frequency = 0 # how often to send a transaction (in milliseconds) - env: $ETH_CALL_FREQ + totalNumber = 500000 # total number of transactions to send (per sender) - env: $ETH_CALL_TOTAL_NUMBER abiPath = "sol/build/Test.abi" # path to the abi file for the contract we are calling - env: $ETH_CALL_ABI_PATH # NOTE: we expect to be calling a method such as Put(address addr, uint256 val) where the first argument is an # integer than we can increment to store values at new locations in the contract trie (to grow it) and @@ -22,8 +22,8 @@ gasFeeCap = "1000000007" # gasFeeCap to use for the eth call txs - env: $ETH_CALL_GAS_FEE_CAP [sendSpammer] - frequency = 50 # how often to send a transaction (in milliseconds) - env: $ETH_SEND_FREQ - totalNumber = 1000 # total number of transactions to send (per sender) - env: $ETH_SEND_TOTAL_NUMBER + frequency = 0 # how often to send a transaction (in milliseconds) - env: $ETH_SEND_FREQ + totalNumber = 100000 # total number of transactions to send (per sender) - env: $ETH_SEND_TOTAL_NUMBER amount = "10000" # amount of wei (1x10^-18 ETH) to send in each tx (be mindful of the genesis allocations) - env: $ETH_SEND_AMOUNT gasLimit = 21000 # gasLimit to use for the eth transfer txs - env: $ETH_SEND_GAS_LIMIT gasTipCap = "1000000000" # gasTipCap to use for the eth transfer txs - env: $ETH_SEND_GAS_TIP_CAP diff --git a/pkg/auto/config.go b/pkg/auto/config.go index a463d4e..ca50fac 100644 --- a/pkg/auto/config.go +++ b/pkg/auto/config.go @@ -68,9 +68,6 @@ type Config struct { // Configuration for the eth transfer txs SendConfig *SendConfig - - // Configuration for EIP1559 - EIP1559Config *EIP1559Config } // DeploymentConfig holds the parameters for the contract deployment contracts @@ -108,13 +105,10 @@ type SendConfig struct { GasTipCap *big.Int Amount *big.Int - DestinationAddresses []common.Address - Frequency time.Duration + Frequency time.Duration + TotalNumber int } -// todo: EIP1559Config -type EIP1559Config struct{} - func NewConfig() (*Config, error) { // Initialize rpc client httpPathStr := viper.GetString(ethHttpPath) @@ -232,6 +226,15 @@ func NewCallConfig(chainID *big.Int) (*CallConfig, error) { if !exist { return nil, fmt.Errorf("method '%s' not found in provided abi", methodName) } + + var frequency time.Duration + tmpFreq := viper.GetInt(ethCallFrequency) + if tmpFreq <= 0 { + frequency = time.Microsecond + } else { + frequency = viper.GetDuration(ethCallFrequency) * time.Millisecond + } + return &CallConfig{ ChainID: chainID, GasLimit: viper.GetUint64(ethCallGasLimit), @@ -239,7 +242,7 @@ func NewCallConfig(chainID *big.Int) (*CallConfig, error) { GasTipCap: big.NewInt(viper.GetInt64(ethCallGasTipCap)), MethodName: methodName, ABI: parsedABI, - Frequency: viper.GetDuration(ethCallFrequency) * time.Millisecond, + Frequency: frequency, TotalNumber: viper.GetInt(ethCallTotalNumber), }, nil } @@ -251,18 +254,22 @@ func NewSendConfig(chainID *big.Int) (*SendConfig, error) { if !ok { return nil, fmt.Errorf("unable to convert amount string (%s) into big.Int", amountStr) } - number := viper.GetUint64(ethSendTotalNumber) - addrs := make([]common.Address, number) - for i := uint64(0); i < number; i++ { - addrs[i] = crypto.CreateAddress(receiverAddressSeed, i) + + var frequency time.Duration + tmpFreq := viper.GetInt(ethCallFrequency) + if tmpFreq <= 0 { + frequency = time.Microsecond + } else { + frequency = viper.GetDuration(ethCallFrequency) * time.Millisecond } + return &SendConfig{ - ChainID: chainID, - DestinationAddresses: addrs, - Frequency: viper.GetDuration(ethSendFrequency) * time.Millisecond, - Amount: amount, - GasLimit: viper.GetUint64(ethSendGasLimit), - GasFeeCap: big.NewInt(viper.GetInt64(ethSendGasFeeCap)), - GasTipCap: big.NewInt(viper.GetInt64(ethSendGasTipCap)), + ChainID: chainID, + Frequency: frequency, + Amount: amount, + GasLimit: viper.GetUint64(ethSendGasLimit), + GasFeeCap: big.NewInt(viper.GetInt64(ethSendGasFeeCap)), + GasTipCap: big.NewInt(viper.GetInt64(ethSendGasTipCap)), + TotalNumber: viper.GetInt(ethSendTotalNumber), }, nil } diff --git a/pkg/auto/sender.go b/pkg/auto/sender.go index b3718d5..ccca587 100644 --- a/pkg/auto/sender.go +++ b/pkg/auto/sender.go @@ -36,7 +36,7 @@ func NewEthSender(config *Config) *EthSender { } // Send awaits txs off the provided work queue and sends them -func (s *EthSender) Send(quitChan <-chan bool, txChan <-chan *types.Transaction) (<-chan bool, <-chan error) { +func (s *EthSender) Send(quitChan <-chan bool, txChan <-chan *types.Transaction, sentCh chan *types.Transaction) (<-chan bool, <-chan error) { // err channel returned to calling context errChan := make(chan error) doneChan := make(chan bool) @@ -49,6 +49,8 @@ func (s *EthSender) Send(quitChan <-chan bool, txChan <-chan *types.Transaction) counter += 1 if err := shared.SendTransaction(s.client, tx); err != nil { errChan <- err + } else { + sentCh <- tx } case <-quitChan: logrus.Infof("quitting Send loop (sent %d)", counter) diff --git a/pkg/auto/service.go b/pkg/auto/service.go index 5395eef..f277d9b 100644 --- a/pkg/auto/service.go +++ b/pkg/auto/service.go @@ -50,10 +50,12 @@ func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) { genQuit := make(chan bool) senderQuit := make(chan bool) doneChan := make(chan bool) + watcher := NewTxWatcher(s.config.EthClient) + watcher.Start() s.config.CallConfig.ContractAddrs = contractAddrs genDoneChan, txChan, genErrChan := s.TxGenerator.GenerateTxs(genQuit) - sendDoneChan, sendErrChan := s.Sender.Send(senderQuit, txChan) + sendDoneChan, sendErrChan := s.Sender.Send(senderQuit, txChan, watcher.PendingTxCh) go func() { defer close(doneChan) @@ -64,17 +66,24 @@ func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) { recoverClose(genQuit) <-genDoneChan recoverClose(senderQuit) + <-sendDoneChan + recoverClose(watcher.quitCh) case err := <-sendErrChan: logrus.Errorf("tx sending error: %v", err) recoverClose(genQuit) <-genDoneChan recoverClose(senderQuit) + <-sendDoneChan + recoverClose(watcher.quitCh) case <-quitChan: logrus.Info("shutting down tx spammer") recoverClose(genQuit) <-genDoneChan recoverClose(senderQuit) + <-sendDoneChan + recoverClose(watcher.quitCh) case <-sendDoneChan: + recoverClose(watcher.quitCh) return case <-genDoneChan: recoverClose(senderQuit) @@ -84,15 +93,6 @@ func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) { return doneChan, nil } -func recoverSend(ch chan bool, value bool) { - defer func() { - if recover() != nil { - } - }() - - ch <- value -} - func recoverClose(ch chan bool) (justClosed bool) { defer func() { if recover() != nil { diff --git a/pkg/auto/tx_generator.go b/pkg/auto/tx_generator.go index 6dd6c23..03eb46e 100644 --- a/pkg/auto/tx_generator.go +++ b/pkg/auto/tx_generator.go @@ -19,6 +19,7 @@ package auto import ( "context" "crypto/ecdsa" + "github.com/ethereum/go-ethereum/crypto" log "github.com/sirupsen/logrus" "math/big" "math/rand" @@ -79,7 +80,7 @@ func (gen *TxGenerator) GenerateTxs(quitChan <-chan bool) (<-chan bool, <-chan * errChan := make(chan error) wg := new(sync.WaitGroup) for i, sender := range gen.config.SenderKeys { - if len(gen.config.SendConfig.DestinationAddresses) > 0 { + if gen.config.SendConfig.TotalNumber > 0 { wg.Add(1) go gen.genSends(wg, txChan, errChan, quitChan, sender, gen.config.SenderAddrs[i], gen.config.SendConfig) } @@ -99,9 +100,10 @@ func (gen *TxGenerator) GenerateTxs(quitChan <-chan bool) (<-chan bool, <-chan * func (gen *TxGenerator) genSends(wg *sync.WaitGroup, txChan chan<- *types.Transaction, errChan chan<- error, quitChan <-chan bool, senderKey *ecdsa.PrivateKey, senderAddr common.Address, sendConfig *SendConfig) { defer wg.Done() ticker := time.NewTicker(sendConfig.Frequency) - for _, dst := range sendConfig.DestinationAddresses { + for i := 0; i < sendConfig.TotalNumber; i++ { select { case <-ticker.C: + dst := crypto.CreateAddress(receiverAddressSeed, uint64(i)) log.Debugf("Generating send from %s to %s.", senderAddr.Hex(), dst.Hex()) rawTx, _, err := gen.GenerateTx(&GenParams{ ChainID: sendConfig.ChainID, diff --git a/pkg/auto/tx_watcher.go b/pkg/auto/tx_watcher.go new file mode 100644 index 0000000..0558fa0 --- /dev/null +++ b/pkg/auto/tx_watcher.go @@ -0,0 +1,60 @@ +package auto + +import ( + "context" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/sirupsen/logrus" + "time" +) + +type TxWatcher struct { + PendingTxCh chan *types.Transaction + ethClient *ethclient.Client + quitCh chan bool + startedAt time.Time + counter uint +} + +func NewTxWatcher(ethClient *ethclient.Client) *TxWatcher { + return &TxWatcher{ + PendingTxCh: make(chan *types.Transaction, 1000), + ethClient: ethClient, + quitCh: make(chan bool), + } +} + +func (tw *TxWatcher) Start() { + tw.startedAt = time.Now() + go func() { + defer close(tw.PendingTxCh) + for { + select { + case tx := <-tw.PendingTxCh: + tw.counter += 1 + if 0 == tw.counter%10 { + logrus.Debugf("TxW: checking on TX %s (%d in channel)", tx.Hash().Hex(), len(tw.PendingTxCh)) + var receipt *types.Receipt = nil + sleep := time.Millisecond + start := time.Now() + for receipt == nil { + receipt, _ = tw.ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if nil == receipt { + time.Sleep(sleep) + sleep *= 2 + } else { + elapsed := time.Now().Sub(tw.startedAt) + logrus.Debugf("TxW: TX %s found in block %s after %dms.", tx.Hash().Hex(), + receipt.BlockNumber.String(), time.Now().Sub(start).Milliseconds()) + logrus.Infof("TxW: %d in %.0f seconds (%.2f/sec, %d pending)", + tw.counter, elapsed.Seconds(), float64(tw.counter)/elapsed.Seconds(), len(tw.PendingTxCh)) + } + } + } + case <-tw.quitCh: + logrus.Infof("TxW: quitting with %d in channel", len(tw.PendingTxCh)) + return + } + } + }() +} diff --git a/pkg/shared/util.go b/pkg/shared/util.go index adaba02..6f69e07 100644 --- a/pkg/shared/util.go +++ b/pkg/shared/util.go @@ -39,13 +39,13 @@ func TxSigner(chainID *big.Int) types.Signer { func SendTransaction(rpcClient *rpc.Client, tx *types.Transaction) error { msg, _ := tx.AsMessage(TxSigner(tx.ChainId()), big.NewInt(1)) if nil == tx.To() { - logrus.Infof("TX %s to create contract %s (sender %s)", + logrus.Debugf("TX %s to create contract %s (sender %s)", tx.Hash().Hex(), crypto.CreateAddress(msg.From(), tx.Nonce()), msg.From().Hex()) } else if nil == tx.Data() || len(tx.Data()) == 0 { - logrus.Infof("TX %s sending %s Wei to %s (sender %s)", + logrus.Debugf("TX %s sending %s Wei to %s (sender %s)", tx.Hash().Hex(), tx.Value().String(), msg.To().Hex(), msg.From().Hex()) } else { - logrus.Infof("TX %s calling contract %s (sender %s)", + logrus.Debugf("TX %s calling contract %s (sender %s)", tx.Hash().Hex(), msg.To().Hex(), msg.From().Hex()) }