diff --git a/cmd/autoSend.go b/cmd/autoSend.go new file mode 100644 index 0000000..e7c8dad --- /dev/null +++ b/cmd/autoSend.go @@ -0,0 +1,61 @@ +// Copyright © 2020 Vulcanize, Inc +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "os" + "os/signal" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/vulcanize/tx_spammer/pkg/auto" +) + +// autoSendCmd represents the autoSend command +var autoSendCmd = &cobra.Command{ + Use: "autoSend", + Short: "Send large volumes of different tx types to different nodes for testing purposes", + Long: `Loads tx configuration from .toml config file +Generates txs from configuration and provided private keys and sends them to designated node according to set frequency and number +Support standard, optimism L2, optimism L1 to L2, and EIP1559 transactions`, + Run: func(cmd *cobra.Command, args []string) { + subCommand = cmd.CalledAs() + logWithCommand = *logrus.WithField("SubCommand", subCommand) + autoSend() + }, +} + +func autoSend() { + config, err := auto.NewConfig() + if err != nil { + logWithCommand.Fatal(err) + } + txSpammer := auto.NewTxSpammer(config) + quitChan := make(chan bool) + doneChan := txSpammer.Loop(quitChan) + + go func() { + shutdown := make(chan os.Signal) + signal.Notify(shutdown, os.Interrupt) + <-shutdown + close(quitChan) + }() + <-doneChan + +} +func init() { + rootCmd.AddCommand(autoSendCmd) +} \ No newline at end of file diff --git a/cmd/sendTxs.go b/cmd/sendTxs.go index c180e41..dbd6f8f 100644 --- a/cmd/sendTxs.go +++ b/cmd/sendTxs.go @@ -18,7 +18,6 @@ package cmd import ( "os" "os/signal" - "sync" "github.com/sirupsen/logrus" @@ -46,9 +45,8 @@ func sendTxs() { logWithCommand.Fatal(err) } txSpammer := manual.NewTxSpammer(params) - wg := new(sync.WaitGroup) quitChan := make(chan bool) - txSpammer.Loop(wg, quitChan) + doneChan := txSpammer.Loop(quitChan) go func() { shutdown := make(chan os.Signal) @@ -56,7 +54,7 @@ func sendTxs() { <-shutdown close(quitChan) }() - wg.Wait() + <-doneChan } func init() { diff --git a/pkg/auto/config.go b/pkg/auto/config.go index d7b5083..5f9a037 100644 --- a/pkg/auto/config.go +++ b/pkg/auto/config.go @@ -48,7 +48,7 @@ type Config struct { Client *rpc.Client // Key pairs for the accounts we will use to deploy contracts and send txs - SenderKeys []*ecdsa.PrivateKey + SenderKeys []*ecdsa.PrivateKey SenderAddrs []common.Address // Type of the txs we are working with @@ -92,8 +92,8 @@ type DeploymentConfig struct { // CallConfig holds the parameters for the contract calling txs type CallConfig struct { - GasLimit uint64 - GasPrice *big.Int + GasLimit uint64 + GasPrice *big.Int MethodName string ABI abi.ABI @@ -112,13 +112,12 @@ type SendConfig struct { Amount *big.Int DestinationAddresses []common.Address - Frequency time.Duration - Number uint64 + Frequency time.Duration + Number uint64 } // todo: EIP1559Config type EIP1559Config struct { - } func NewConfig() (*Config, error) { @@ -217,7 +216,7 @@ func NewConfig() (*Config, error) { } // NewOptimismConfig constructs and returns a new OptimismConfig -func NewOptimismConfig() *OptimismConfig{ +func NewOptimismConfig() *OptimismConfig { l1SenderStr := viper.GetString(ethOptimismL1Sender) var l1Sender *common.Address if l1SenderStr != "" { @@ -306,11 +305,11 @@ func NewSendConfig(destinationAddrs []common.Address) (*SendConfig, error) { } return &SendConfig{ DestinationAddresses: destinationAddrs, - Frequency: viper.GetDuration(ethSendFrequency), - Number: viper.GetUint64(ethSendTotalNumber), - Amount: amount, - GasPrice: gasPrice, - GasLimit: viper.GetUint64(ethSendGasLimit), + Frequency: viper.GetDuration(ethSendFrequency), + Number: viper.GetUint64(ethSendTotalNumber), + Amount: amount, + GasPrice: gasPrice, + GasLimit: viper.GetUint64(ethSendGasLimit), }, nil } diff --git a/pkg/auto/deployer.go b/pkg/auto/deployer.go new file mode 100644 index 0000000..d281de6 --- /dev/null +++ b/pkg/auto/deployer.go @@ -0,0 +1,36 @@ +// VulcanizeDB +// Copyright © 2020 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package auto + +import "crypto/ecdsa" + +type ContractDeployer struct { + SenderKeys []*ecdsa.PrivateKey + Config *DeploymentConfig +} + +func NewContractDeployer(config *Config) *ContractDeployer { + return &ContractDeployer{ + Config: config.DeploymentConfig, + SenderKeys: config.SenderKeys, + } +} + +func (cp *ContractDeployer) Deploy() error { + + return nil +} diff --git a/pkg/auto/env.go b/pkg/auto/env.go index 339b273..d783706 100644 --- a/pkg/auto/env.go +++ b/pkg/auto/env.go @@ -118,4 +118,4 @@ func bindEnv() { viper.BindEnv(ethSendAmount, ETH_SEND_AMOUNT) viper.BindEnv(ethSendGasLimit, ETH_SEND_GAS_LIMIT) viper.BindEnv(ethSendGasPrice, ETH_SEND_GAS_PRICE) -} \ No newline at end of file +} diff --git a/pkg/auto/sender.go b/pkg/auto/sender.go index b051f9b..36e85de 100644 --- a/pkg/auto/sender.go +++ b/pkg/auto/sender.go @@ -42,7 +42,7 @@ func (s *EthSender) Send(quitChan <-chan bool, txRlpChan <-chan []byte) (<-chan go func() { for { select { - case tx := <- txRlpChan: + case tx := <-txRlpChan: if err := shared.SendRawTransaction(s.client, tx); err != nil { errChan <- err } diff --git a/pkg/auto/service.go b/pkg/auto/service.go new file mode 100644 index 0000000..f99ee21 --- /dev/null +++ b/pkg/auto/service.go @@ -0,0 +1,56 @@ +// VulcanizeDB +// Copyright © 2020 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package auto + +import ( + "github.com/sirupsen/logrus" + "github.com/vulcanize/tx_spammer/pkg/shared" +) + +// Spammer underlying struct type for spamming service +type Spammer struct { + Deployer *ContractDeployer + Caller *ContractCaller + Sender *EthSender +} + +// NewTxSpammer creates a new tx spamming service +func NewTxSpammer(config *Config) shared.Service { + return &Spammer{ + Deployer: NewContractDeployer(config), + Caller: NewContractCaller(config), + Sender: NewEthSender(config), + } +} + +func (s *Spammer) Loop(quitChan <-chan bool) <-chan bool { + forwardQuit := make(chan bool) + doneChan, errChan := s.Sender.Send(forwardQuit) + go func() { + for { + select { + case err := <-errChan: + logrus.Error(err) + case forwardQuit <- <-quitChan: + return + case <-doneChan: + return + } + } + }() + return doneChan +} diff --git a/pkg/auto/tx_generator.go b/pkg/auto/tx_generator.go new file mode 100644 index 0000000..7f626b7 --- /dev/null +++ b/pkg/auto/tx_generator.go @@ -0,0 +1,120 @@ +// VulcanizeDB +// Copyright © 2020 Vulcanize + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package auto + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rlp" + "github.com/vulcanize/tx_spammer/pkg/shared" +) + +// TxGenerator generates and signs txs +type TxGenerator struct { + signer types.Signer + optimismConfig *OptimismConfig + eip1559Config *EIP1559Config + // keep track of account nonces locally so we aren't spamming to determine the nonce + // this assumes these accounts are not sending txs outside this process + nonces map[common.Address]*uint64 +} + +// NewTxGenerator creates a new tx generator +func NewTxGenerator(config Config) *TxGenerator { + nonces := make(map[common.Address]*uint64) + for _, addr := range config.SenderAddrs { + startingNonce := uint64(0) + nonces[addr] = &startingNonce + } + return &TxGenerator{ + signer: config.Signer, + nonces: nonces, + optimismConfig: config.OptimismConfig, + eip1559Config: config.EIP1559Config, + } +} + +// GenParams params for GenerateTx method calls +type GenParams struct { + Signer types.Signer + Sender common.Address + SenderKey *ecdsa.PrivateKey + To *common.Address + Amount *big.Int + GasLimit uint64 + GasPrice *big.Int + Data []byte +} + +func (tg TxGenerator) GenerateTxs(quitChan <-chan bool) (<-chan bool, <-chan []byte, <-chan error) { + +} + +// GenerateTx generates tx from the provided params +func (tg TxGenerator) GenerateTx(ty shared.TxType, params *GenParams) ([]byte, error) { + tx := make([]byte, 0) + switch ty { + case shared.OptimismL2: + return tg.genL2(params, tg.optimismConfig) + case shared.Standard: + return tg.gen(params) + case shared.EIP1559: + return tg.gen1559(params, tg.eip1559Config) + default: + return nil, fmt.Errorf("unsupported tx type: %s", ty.String()) + } + return tx, nil +} + +func (gen TxGenerator) genL2(params *GenParams, op *OptimismConfig) ([]byte, error) { + nonce := atomic.AddUint64(gen.nonces[params.Sender], 1) + tx := new(types.Transaction) + if params.To == nil { + tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, op.L1SenderAddr, op.L1RollupTxId, op.QueueOrigin) + if err := shared.WriteContractAddr(shared.DefaultDeploymentAddrLogPathPrefix, params.Sender, nonce); err != nil { + return nil, err + } + + } else { + tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, op.L1SenderAddr, op.L1RollupTxId, op.QueueOrigin, op.SigHashType) + } + signedTx, err := types.SignTx(tx, params.Signer, params.SenderKey) + if err != nil { + return nil, err + } + txRlp, err := rlp.EncodeToBytes(signedTx) + if err != nil { + return nil, err + } + return txRlp, nil +} + +func (gen TxGenerator) gen(params *GenParams) ([]byte, error) { + // TODO: support standard geth + return nil, errors.New("L1 support not yet available") +} + +func (gen TxGenerator) gen1559(params *GenParams, eip1559Config *EIP1559Config) ([]byte, error) { + // TODO: support EIP1559 + return nil, errors.New("1559 support not yet available") +} diff --git a/pkg/manual/config.go b/pkg/manual/config.go index 1ac0397..dc8d1cf 100644 --- a/pkg/manual/config.go +++ b/pkg/manual/config.go @@ -19,12 +19,13 @@ package manual import ( "crypto/ecdsa" "fmt" - "github.com/vulcanize/tx_spammer/pkg/shared" "math/big" "os" "strings" "time" + "github.com/vulcanize/tx_spammer/pkg/shared" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" diff --git a/pkg/manual/env.go b/pkg/manual/env.go index c0ba50a..4b607d6 100644 --- a/pkg/manual/env.go +++ b/pkg/manual/env.go @@ -54,4 +54,4 @@ const ( func bindEnv() { viper.BindEnv("eth.txs", ETH_TX_LIST) viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG) -} \ No newline at end of file +} diff --git a/pkg/manual/sender.go b/pkg/manual/sender.go index 926332a..629076d 100644 --- a/pkg/manual/sender.go +++ b/pkg/manual/sender.go @@ -18,10 +18,11 @@ package manual import ( "fmt" - "github.com/sirupsen/logrus" "sync" "time" + "github.com/sirupsen/logrus" + "github.com/vulcanize/tx_spammer/pkg/shared" ) @@ -86,4 +87,4 @@ func (s *TxSender) genAndSend(p TxParams) error { } logrus.Infof("sending tx %s", p.Name) return shared.SendRawTransaction(p.Client, tx) -} \ No newline at end of file +} diff --git a/pkg/shared/env.go b/pkg/shared/env.go index b09f951..79fd305 100644 --- a/pkg/shared/env.go +++ b/pkg/shared/env.go @@ -18,4 +18,4 @@ package shared const ( DefaultDeploymentAddrLogPathPrefix = "./accounts/addresses/" -) \ No newline at end of file +) diff --git a/pkg/shared/interface.go b/pkg/shared/interface.go index 5a570a4..c0cef64 100644 --- a/pkg/shared/interface.go +++ b/pkg/shared/interface.go @@ -19,4 +19,4 @@ package shared // Service looping interface type Service interface { Loop(quitChan <-chan bool) (doneChan <-chan bool) -} \ No newline at end of file +} diff --git a/pkg/shared/util.go b/pkg/shared/util.go index 0c9975e..832039a 100644 --- a/pkg/shared/util.go +++ b/pkg/shared/util.go @@ -19,14 +19,15 @@ package shared import ( "context" "fmt" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "math/big" "os" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/temp/common/hexutil" ) // ChainConfig returns the appropriate ethereum chain config for the provided chain id @@ -60,4 +61,4 @@ func WriteContractAddr(filePath string, senderAddr common.Address, nonce uint64) return err } return f.Close() -} \ No newline at end of file +}