auto tx generator
This commit is contained in:
parent
da9dd220d0
commit
d73390966c
58
README.md
58
README.md
@ -1,2 +1,58 @@
|
||||
# tx_spammer
|
||||
Util for sending large numbers of txs for testing purposes
|
||||
Tools to enable the semi-reproducible growth of a large and complex chain over RPC, for testing and benchmarking purposes
|
||||
|
||||
Usage:
|
||||
|
||||
`./tx_spammer autoSend --config=./environments/gen.toml`
|
||||
|
||||
The `autoSend` command takes as input a .toml config of the below format, the fields can be overridden with the env variables in the comments.
|
||||
It uses the provided key pairs and configuraiton parameters to generate and deploy a number of contracts with a simple interface for `Put`ing to a dynamic data structure.
|
||||
It can then spam these contracts with `Put` operations at deterministically generated storage locations in order to grow the storage tries indefinitely and in a reproducible manner.
|
||||
Additionally, it can be configured to send large numbers of eth value transfers to a set of manually designated or deterministically derived addresses,
|
||||
in order to grow the state trie.
|
||||
|
||||
This process is semi-reproducible, in that given the same input it will generate and send the same set of contract deployment, eth transfer,
|
||||
and contract calling transactions. The precise ordering of transactions is not guaranteed, and uncertainty is introduced by the
|
||||
miner processing the transactions.
|
||||
|
||||
```toml
|
||||
[eth]
|
||||
keyDirPath = "" # path to the directory with all of the key pairs to use - env: $ETH_KEY_DIR_PATH
|
||||
addrFilePath = "" # path to a file with a newline seperated list of addresses we want to send value transfers to - env: $ETH_ADDR_DIR_PATH
|
||||
httpPath = "" # http url for the node we wish to send all our transactions to - env: $ETH_HTTP_PATH
|
||||
chainID = 421 # chain id - env: $ETH_CHAIN_ID
|
||||
type = "L2" # tx type (EIP1559, Standard, or L2) - env: $ETH_TX_TYPE
|
||||
|
||||
[deployment]
|
||||
number = 1 # number of contracts we will deploy for each key at keyPath - env: $ETH_DEPLOYMENT_NUMBER
|
||||
hexData = "" # hex data for the contracts we will deploy - env: $ETH_DEPLOYMENT_HEX_DATA
|
||||
gasLimit = 0 # gasLimit to use for the deployment txs - env: $ETH_DEPLOYMENT_GAS_LIMIT
|
||||
gasPrice = "0" # gasPrice to use for the deployment txs - env: $ETH_DEPLOYMENT_GAS_PRICE
|
||||
|
||||
[optimism]
|
||||
l1Sender = "" # l1 sender address hex to use for all txs - env: $ETH_OPTIMISM_L1_SENDER
|
||||
l1RollupTxId = 0 # rollup tx id to use for all txs - env: $ETH_OPTIMISM_ROLLUP_TX_ID
|
||||
sigHashType = 0 # sig hash type to use for all txs - env: $ETH_OPTIMISM_SIG_HASH_TYPE
|
||||
queueOrigin = 0 # queue origin id to use for all txs - env: $ETH_OPTIMISM_QUEUE_ORIGIN
|
||||
|
||||
[contractSpammer]
|
||||
frequency = 30 # how often to send a transaction (in seconds) - env: $ETH_CALL_FREQ
|
||||
totalNumber = 10000 # total number of transactions to send (across all senders) - env: $ETH_CALL_TOTAL_NUMBER
|
||||
abiPath = "" # 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
|
||||
# the second argument is an integer value that we store at these positions
|
||||
methodName = "Put" # the method name we are calling - env: $ETH_CALL_METHOD_NAME
|
||||
storageValue = 1337 # the value we store at each position - env: $ETH_CALL_STORAGE_VALUE
|
||||
gasLimit = 0 # gasLimit to use for the eth call txs - env: $ETH_CALL_GAS_LIMIT
|
||||
gasPrice = "0" # gasPrice to use for the eth call txs - env: $ETH_CALL_GAS_PRICE
|
||||
|
||||
[sendSpammer]
|
||||
frequency = 30 # how often to send a transaction (in seconds) - env: $ETH_SEND_FREQ
|
||||
totalNumber = 10000 # total number of transactions to send (across all senders) - env: $ETH_SEND_TOTAL_NUMBER
|
||||
amount = "1" # amount of wei (1x10^-18 ETH) to send in each tx (be mindful of the genesis allocations) - env: $ETH_SEND_AMOUNT
|
||||
gasLimit = 0 # gasLimit to use for the eth transfer txs - env: $ETH_SEND_GAS_LIMIT
|
||||
gasPrice = "0" # gasPrice to use for the eth transfer txs - env: $ETH_SEND_GAS_PRICE
|
||||
```
|
||||
|
||||
TODO: better documentation and document the other commands
|
@ -1,6 +1,5 @@
|
||||
[eth]
|
||||
keyDirPath = "" # path to the directory with all of the key pairs to use - env: $ETH_KEY_DIR_PATH
|
||||
addrFilePath = "" # path to a file with a newline seperated list of addresses we want to send value transfers to - env: $ETH_ADDR_DIR_PATH
|
||||
httpPath = "" # http url for the node we wish to send all our transactions to - env: $ETH_HTTP_PATH
|
||||
chainID = 421 # chain id - env: $ETH_CHAIN_ID
|
||||
type = "L2" # tx type (EIP1559, Standard, or L2) - env: $ETH_TX_TYPE
|
||||
@ -19,7 +18,7 @@
|
||||
|
||||
[contractSpammer]
|
||||
frequency = 30 # how often to send a transaction (in seconds) - env: $ETH_CALL_FREQ
|
||||
totalNumber = 10000 # total number of transactions to send (across all senders) - env: $ETH_CALL_TOTAL_NUMBER
|
||||
totalNumber = 10000 # total number of transactions to send (per sender) - env: $ETH_CALL_TOTAL_NUMBER
|
||||
abiPath = "" # 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
|
||||
@ -31,7 +30,7 @@
|
||||
|
||||
[sendSpammer]
|
||||
frequency = 30 # how often to send a transaction (in seconds) - env: $ETH_SEND_FREQ
|
||||
totalNumber = 10000 # total number of transactions to send (across all senders) - env: $ETH_SEND_TOTAL_NUMBER
|
||||
totalNumber = 10000 # total number of transactions to send (per sender) - env: $ETH_SEND_TOTAL_NUMBER
|
||||
amount = "1" # amount of wei (1x10^-18 ETH) to send in each tx (be mindful of the genesis allocations) - env: $ETH_SEND_AMOUNT
|
||||
gasLimit = 0 # gasLimit to use for the eth transfer txs - env: $ETH_SEND_GAS_LIMIT
|
||||
gasPrice = "0" # gasPrice to use for the eth transfer txs - env: $ETH_SEND_GAS_PRICE
|
||||
|
@ -17,13 +17,11 @@
|
||||
package auto
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@ -38,6 +36,11 @@ import (
|
||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
receiverAddressSeed = common.HexToAddress("0xe48C9A989438606a79a7560cfba3d34BAfBAC38E")
|
||||
storageAddressSeed = common.HexToAddress("0x029298Ac95662F2b54A7F1116f3F8105eb2b00F5")
|
||||
)
|
||||
|
||||
func init() {
|
||||
bindEnv()
|
||||
}
|
||||
@ -95,14 +98,12 @@ type CallConfig struct {
|
||||
GasLimit uint64
|
||||
GasPrice *big.Int
|
||||
|
||||
MethodName string
|
||||
ABI abi.ABI
|
||||
PositionStart uint64
|
||||
PositionEnd uint64
|
||||
StorageValue uint64
|
||||
MethodName string
|
||||
ABI abi.ABI
|
||||
StorageValue uint64
|
||||
StorageAddrs []common.Address
|
||||
|
||||
Frequency time.Duration
|
||||
Number uint64
|
||||
}
|
||||
|
||||
// SendConfig holds the parameters for the eth transfer txs
|
||||
@ -113,12 +114,10 @@ type SendConfig struct {
|
||||
|
||||
DestinationAddresses []common.Address
|
||||
Frequency time.Duration
|
||||
Number uint64
|
||||
}
|
||||
|
||||
// todo: EIP1559Config
|
||||
type EIP1559Config struct {
|
||||
}
|
||||
type EIP1559Config struct{}
|
||||
|
||||
func NewConfig() (*Config, error) {
|
||||
// Initialize rpc client
|
||||
@ -158,12 +157,6 @@ func NewConfig() (*Config, error) {
|
||||
senderAddrs = append(senderAddrs, crypto.PubkeyToAddress(key.PublicKey))
|
||||
}
|
||||
|
||||
// Load eth transfer destination addresses
|
||||
addrs, err := loadAddresses()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load tx type
|
||||
txType, err := shared.TxTypeFromString(viper.GetString(ethType))
|
||||
if err != nil {
|
||||
@ -196,7 +189,7 @@ func NewConfig() (*Config, error) {
|
||||
}
|
||||
|
||||
// Load send config
|
||||
sendConfig, err := NewSendConfig(addrs)
|
||||
sendConfig, err := NewSendConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -277,20 +270,24 @@ func NewCallConfig() (*CallConfig, error) {
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("method '%s' not found in provided abi", methodName)
|
||||
}
|
||||
|
||||
number := viper.GetUint64(ethCallTotalNumber)
|
||||
addrs := make([]common.Address, number)
|
||||
for i := uint64(0); i < number; i++ {
|
||||
addrs[i] = crypto.CreateAddress(storageAddressSeed, i)
|
||||
}
|
||||
return &CallConfig{
|
||||
Number: viper.GetUint64(ethCallTotalNumber),
|
||||
GasPrice: gasPrice,
|
||||
GasLimit: viper.GetUint64(ethCallGasLimit),
|
||||
MethodName: methodName,
|
||||
ABI: parsedABI,
|
||||
StorageValue: viper.GetUint64(ethCallStorageValue),
|
||||
Frequency: viper.GetDuration(ethCallFrequency),
|
||||
GasPrice: gasPrice,
|
||||
GasLimit: viper.GetUint64(ethCallGasLimit),
|
||||
MethodName: methodName,
|
||||
ABI: parsedABI,
|
||||
StorageValue: viper.GetUint64(ethCallStorageValue),
|
||||
Frequency: viper.GetDuration(ethCallFrequency),
|
||||
StorageAddrs: addrs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSendConfig constructs and returns a new SendConfig
|
||||
func NewSendConfig(destinationAddrs []common.Address) (*SendConfig, error) {
|
||||
func NewSendConfig() (*SendConfig, error) {
|
||||
amountStr := viper.GetString(ethSendAmount)
|
||||
amount, ok := new(big.Int).SetString(amountStr, 10)
|
||||
if !ok {
|
||||
@ -301,36 +298,16 @@ func NewSendConfig(destinationAddrs []common.Address) (*SendConfig, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to convert gasPrice string (%s) into big.Int", gasPriceStr)
|
||||
}
|
||||
number := viper.GetUint64(ethSendTotalNumber)
|
||||
addrs := make([]common.Address, number)
|
||||
for i := uint64(0); i < number; i++ {
|
||||
addrs[i] = crypto.CreateAddress(receiverAddressSeed, i)
|
||||
}
|
||||
return &SendConfig{
|
||||
DestinationAddresses: destinationAddrs,
|
||||
DestinationAddresses: addrs,
|
||||
Frequency: viper.GetDuration(ethSendFrequency),
|
||||
Number: viper.GetUint64(ethSendTotalNumber),
|
||||
Amount: amount,
|
||||
GasPrice: gasPrice,
|
||||
GasLimit: viper.GetUint64(ethSendGasLimit),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Load eth transfer destination addresses
|
||||
func loadAddresses() ([]common.Address, error) {
|
||||
addrs := make([]common.Address, 0)
|
||||
addrFilePath := viper.GetString(ethAddrFilePath)
|
||||
if addrFilePath == "" {
|
||||
return nil, fmt.Errorf("missing %s", ethAddrFilePath)
|
||||
}
|
||||
addrFile, err := os.Open(addrFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer addrFile.Close()
|
||||
scanner := bufio.NewScanner(addrFile)
|
||||
for scanner.Scan() {
|
||||
addrBytes := scanner.Bytes()
|
||||
addr := common.BytesToAddress(addrBytes)
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
@ -32,46 +32,48 @@ const (
|
||||
// ContractDeployer is responsible for deploying contracts
|
||||
type ContractDeployer struct {
|
||||
client *rpc.Client
|
||||
ty shared.TxType
|
||||
ty shared.TxType
|
||||
txGenerator *TxGenerator
|
||||
senderKeys []*ecdsa.PrivateKey
|
||||
senderKeys []*ecdsa.PrivateKey
|
||||
senderAddrs []common.Address
|
||||
config *DeploymentConfig
|
||||
config *DeploymentConfig
|
||||
}
|
||||
|
||||
// NewContractDeployer returns a new ContractDeployer
|
||||
func NewContractDeployer(config *Config, gen *TxGenerator) *ContractDeployer {
|
||||
return &ContractDeployer{
|
||||
client: config.Client,
|
||||
ty: config.Type,
|
||||
client: config.Client,
|
||||
ty: config.Type,
|
||||
txGenerator: gen,
|
||||
config: config.DeploymentConfig,
|
||||
senderKeys: config.SenderKeys,
|
||||
config: config.DeploymentConfig,
|
||||
senderKeys: config.SenderKeys,
|
||||
senderAddrs: config.SenderAddrs,
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy deploys the contracts according to the config provided at construction
|
||||
func (cp *ContractDeployer) Deploy() error {
|
||||
func (cp *ContractDeployer) Deploy() ([]common.Address, error) {
|
||||
contractAddrs := make([]common.Address, 0, cp.config.Number*uint64(len(cp.senderKeys)))
|
||||
ticker := time.NewTicker(contractDeploymentDelay)
|
||||
defer ticker.Stop()
|
||||
for i := uint64(0); i < cp.config.Number; i++ {
|
||||
<- ticker.C
|
||||
<-ticker.C
|
||||
for i, key := range cp.senderKeys {
|
||||
txBytes, err := cp.txGenerator.GenerateTx(cp.ty, &GenParams{
|
||||
Sender: cp.senderAddrs[i],
|
||||
txBytes, contractAddr, err := cp.txGenerator.GenerateTx(cp.ty, &GenParams{
|
||||
Sender: cp.senderAddrs[i],
|
||||
SenderKey: key,
|
||||
GasLimit: cp.config.GasLimit,
|
||||
GasPrice: cp.config.GasPrice,
|
||||
Data: cp.config.Data,
|
||||
GasLimit: cp.config.GasLimit,
|
||||
GasPrice: cp.config.GasPrice,
|
||||
Data: cp.config.Data,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if err := shared.SendRawTransaction(cp.client, txBytes); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contractAddrs = append(contractAddrs, contractAddr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return contractAddrs, nil
|
||||
}
|
||||
|
@ -20,11 +20,10 @@ import "github.com/spf13/viper"
|
||||
|
||||
const (
|
||||
// env variables
|
||||
ETH_KEY_DIR_PATH = "ETH_KEY_DIR_PATH"
|
||||
ETH_ADDR_DIR_PATH = "ETH_ADDR_DIR_PATH"
|
||||
ETH_HTTP_PATH = "ETH_HTTP_PATH"
|
||||
ETH_CHAIN_ID = "ETH_CHAIN_ID"
|
||||
ETH_TX_TYPE = "ETH_TX_TYPE"
|
||||
ETH_KEY_DIR_PATH = "ETH_KEY_DIR_PATH"
|
||||
ETH_HTTP_PATH = "ETH_HTTP_PATH"
|
||||
ETH_CHAIN_ID = "ETH_CHAIN_ID"
|
||||
ETH_TX_TYPE = "ETH_TX_TYPE"
|
||||
|
||||
ETH_DEPLOYMENT_NUMBER = "ETH_DEPLOYMENT_NUMBER"
|
||||
ETH_DEPLOYMENT_HEX_DATA = "ETH_DEPLOYMENT_HEX_DATA"
|
||||
@ -36,13 +35,13 @@ const (
|
||||
ETH_OPTIMISM_SIG_HASH_TYPE = "ETH_OPTIMISM_SIG_HASH_TYPE"
|
||||
ETH_OPTIMISM_QUEUE_ORIGIN = "ETH_OPTIMISM_QUEUE_ORIGIN"
|
||||
|
||||
ETH_CALL_FREQ = "ETH_CALL_FREQ"
|
||||
ETH_CALL_TOTAL_NUMBER = "ETH_CALL_TOTAL_NUMBER"
|
||||
ETH_CALL_ABI_PATH = "ETH_CALL_ABI_PATH"
|
||||
ETH_CALL_METHOD_NAME = "ETH_CALL_METHOD_NAME"
|
||||
ETH_CALL_STORAGE_VALUE = "ETH_CALL_STORAGE_VALUE"
|
||||
ETH_CALL_GAS_LIMIT = "ETH_CALL_GAS_LIMIT"
|
||||
ETH_CALL_GAS_PRICE = "ETH_CALL_GAS_PRICE"
|
||||
ETH_CALL_FREQ = "ETH_CALL_FREQ"
|
||||
ETH_CALL_TOTAL_NUMBER = "ETH_CALL_TOTAL_NUMBER"
|
||||
ETH_CALL_ABI_PATH = "ETH_CALL_ABI_PATH"
|
||||
ETH_CALL_METHOD_NAME = "ETH_CALL_METHOD_NAME"
|
||||
ETH_CALL_STORAGE_VALUE = "ETH_CALL_STORAGE_VALUE"
|
||||
ETH_CALL_GAS_LIMIT = "ETH_CALL_GAS_LIMIT"
|
||||
ETH_CALL_GAS_PRICE = "ETH_CALL_GAS_PRICE"
|
||||
|
||||
ETH_SEND_FREQ = "ETH_SEND_FREQ"
|
||||
ETH_SEND_TOTAL_NUMBER = "ETH_SEND_TOTAL_NUMBER"
|
||||
@ -51,11 +50,10 @@ const (
|
||||
ETH_SEND_GAS_PRICE = "ETH_SEND_GAS_PRICE"
|
||||
|
||||
// toml bindings
|
||||
ethKeyDirPath = "eth.keyDirPath"
|
||||
ethAddrFilePath = "eth.addrFilePath"
|
||||
ethHttpPath = "eth.httpPath"
|
||||
ethChainID = "eth.chainID"
|
||||
ethType = "eth.type"
|
||||
ethKeyDirPath = "eth.keyDirPath"
|
||||
ethHttpPath = "eth.httpPath"
|
||||
ethChainID = "eth.chainID"
|
||||
ethType = "eth.type"
|
||||
|
||||
ethDeploymentNumber = "deployment.number"
|
||||
ethDeploymentData = "deployment.hexData"
|
||||
@ -67,13 +65,13 @@ const (
|
||||
ethOptimismSigHashType = "optimism.sigHashType"
|
||||
ethOptimismQueueOrigin = "optimism.queueOrigin"
|
||||
|
||||
ethCallFrequency = "contractSpammer.frequency"
|
||||
ethCallTotalNumber = "contractSpammer.totalNumber"
|
||||
ethCallABIPath = "contractSpammer.abiPath"
|
||||
ethCallMethodName = "contractSpammer.methodName"
|
||||
ethCallStorageValue = "contractSpammer.storageValue"
|
||||
ethCallGasLimit = "contractSpammer.gasLimit"
|
||||
ethCallGasPrice = "contractSpammer.gasPrice"
|
||||
ethCallFrequency = "contractSpammer.frequency"
|
||||
ethCallTotalNumber = "contractSpammer.totalNumber"
|
||||
ethCallABIPath = "contractSpammer.abiPath"
|
||||
ethCallMethodName = "contractSpammer.methodName"
|
||||
ethCallStorageValue = "contractSpammer.storageValue"
|
||||
ethCallGasLimit = "contractSpammer.gasLimit"
|
||||
ethCallGasPrice = "contractSpammer.gasPrice"
|
||||
|
||||
ethSendFrequency = "sendSpammer.frequency"
|
||||
ethSendTotalNumber = "sendSpammer.totalNumber"
|
||||
@ -84,7 +82,6 @@ const (
|
||||
|
||||
func bindEnv() {
|
||||
viper.BindEnv(ethKeyDirPath, ETH_KEY_DIR_PATH)
|
||||
viper.BindEnv(ethAddrFilePath, ETH_ADDR_DIR_PATH)
|
||||
viper.BindEnv(ethHttpPath, ETH_HTTP_PATH)
|
||||
viper.BindEnv(ethType, ETH_TX_TYPE)
|
||||
viper.BindEnv(ethChainID, ETH_CHAIN_ID)
|
||||
|
@ -36,11 +36,11 @@ func NewEthSender(config *Config) *EthSender {
|
||||
|
||||
// Send awaits txs off the provided work queue and sends them
|
||||
func (s *EthSender) Send(quitChan <-chan bool, txRlpChan <-chan []byte) (<-chan bool, <-chan error) {
|
||||
// done channel to signal quit signal has been received (any ongoing jobs were finished)
|
||||
doneChan := make(chan bool)
|
||||
// err channel returned to calling context
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan bool)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
for {
|
||||
select {
|
||||
case tx := <-txRlpChan:
|
||||
@ -48,8 +48,7 @@ func (s *EthSender) Send(quitChan <-chan bool, txRlpChan <-chan []byte) (<-chan
|
||||
errChan <- err
|
||||
}
|
||||
case <-quitChan:
|
||||
logrus.Info("quitting the Send loop")
|
||||
close(doneChan)
|
||||
logrus.Info("quitting Send loop")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -17,52 +17,61 @@
|
||||
package auto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||
)
|
||||
|
||||
// Spammer underlying struct type for spamming service
|
||||
type Spammer struct {
|
||||
Deployer *ContractDeployer
|
||||
Sender *EthSender
|
||||
Deployer *ContractDeployer
|
||||
Sender *EthSender
|
||||
TxGenerator *TxGenerator
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewTxSpammer creates a new tx spamming service
|
||||
func NewTxSpammer(config *Config) shared.Service {
|
||||
gen := NewTxGenerator(config)
|
||||
return &Spammer{
|
||||
Deployer: NewContractDeployer(config, gen),
|
||||
Sender: NewEthSender(config),
|
||||
Deployer: NewContractDeployer(config, gen),
|
||||
Sender: NewEthSender(config),
|
||||
TxGenerator: gen,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) {
|
||||
if err := s.Deployer.Deploy(); err != nil {
|
||||
return nil, err
|
||||
contractAddrs, err := s.Deployer.Deploy()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("contract deployment error: %v", err)
|
||||
}
|
||||
genQuit := make(chan bool)
|
||||
senderQuit := make(chan bool)
|
||||
generatorQuit := make(chan bool)
|
||||
genDoneChan, txRlpChan, genErrChan := s.TxGenerator.GenerateTxs(generatorQuit)
|
||||
|
||||
doneChan, errChan := s.Sender.Send(senderQuit, txRlpChan)
|
||||
doneChan := make(chan bool)
|
||||
genDoneChan, txRlpChan, genErrChan := s.TxGenerator.GenerateTxs(genQuit, contractAddrs)
|
||||
sendDoneChan, sendErrChan := s.Sender.Send(senderQuit, txRlpChan)
|
||||
go func() {
|
||||
defer close(doneChan)
|
||||
for {
|
||||
select {
|
||||
case <-genDoneChan:
|
||||
logrus.Info("all txs have been generated, beginning shut down sequence")
|
||||
senderQuit <- true
|
||||
case err := <-genErrChan:
|
||||
logrus.Error(err)
|
||||
senderQuit <- true
|
||||
case err := <-errChan:
|
||||
logrus.Error(err)
|
||||
senderQuit <- true // NOTE: sender will close doneChan when it receives a quit signal
|
||||
logrus.Errorf("tx generation error: %v", err)
|
||||
close(genQuit)
|
||||
<-genDoneChan
|
||||
close(senderQuit)
|
||||
case err := <-sendErrChan:
|
||||
logrus.Errorf("tx sending error: %v", err)
|
||||
close(genQuit)
|
||||
<-genDoneChan
|
||||
close(senderQuit)
|
||||
case <-quitChan:
|
||||
senderQuit <- true
|
||||
case <-doneChan: // NOTE CONT: which will be received here so that this context can close down only once the sender and generator have
|
||||
generatorQuit <- true
|
||||
logrus.Error("shutting down tx spammer")
|
||||
close(genQuit)
|
||||
<-genDoneChan
|
||||
close(senderQuit)
|
||||
case <-sendDoneChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
// 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.
|
||||
// GNU Affero General Public License for more detailgen.
|
||||
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@ -21,7 +21,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -31,9 +33,7 @@ import (
|
||||
|
||||
// TxGenerator generates and signs txs
|
||||
type TxGenerator struct {
|
||||
signer types.Signer
|
||||
optimismConfig *OptimismConfig
|
||||
eip1559Config *EIP1559Config
|
||||
config *Config
|
||||
// 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
|
||||
@ -47,10 +47,8 @@ func NewTxGenerator(config *Config) *TxGenerator {
|
||||
nonces[addr] = &startingNonce
|
||||
}
|
||||
return &TxGenerator{
|
||||
signer: config.Signer,
|
||||
nonces: nonces,
|
||||
optimismConfig: config.OptimismConfig,
|
||||
eip1559Config: config.EIP1559Config,
|
||||
nonces: nonces,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,57 +63,127 @@ type GenParams struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// GenerateTxs loops and generates txs according the configuration passed in during construction
|
||||
func (tg TxGenerator) GenerateTxs(quitChan <-chan bool) (<-chan bool, <-chan []byte, <-chan error) {
|
||||
// TODO: this
|
||||
return nil, nil, nil
|
||||
func (gen *TxGenerator) GenerateTxs(quitChan <-chan bool, contractAddrs []common.Address) (<-chan bool, <-chan []byte, <-chan error) {
|
||||
txRlpChan := make(chan []byte)
|
||||
errChan := make(chan error)
|
||||
wg := new(sync.WaitGroup)
|
||||
for i, sender := range gen.config.SenderKeys {
|
||||
if len(gen.config.SendConfig.DestinationAddresses) > 0 {
|
||||
wg.Add(1)
|
||||
go gen.genSends(wg, gen.config.Type, txRlpChan, errChan, quitChan, sender, gen.config.SenderAddrs[i], gen.config.SendConfig)
|
||||
}
|
||||
if len(gen.config.CallConfig.StorageAddrs) > 0 {
|
||||
wg.Add(1)
|
||||
go gen.genCalls(wg, gen.config.Type, txRlpChan, errChan, quitChan, sender, gen.config.SenderAddrs[i], gen.config.CallConfig)
|
||||
}
|
||||
}
|
||||
doneChan := make(chan bool)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(doneChan)
|
||||
}()
|
||||
return doneChan, txRlpChan, errChan
|
||||
}
|
||||
|
||||
func (gen *TxGenerator) genSends(wg *sync.WaitGroup, ty shared.TxType, txRlpChan chan<- []byte, 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 {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
txRlp, _, err := gen.GenerateTx(ty, &GenParams{
|
||||
Sender: senderAddr,
|
||||
SenderKey: senderKey,
|
||||
GasLimit: sendConfig.GasLimit,
|
||||
GasPrice: sendConfig.GasPrice,
|
||||
Amount: sendConfig.Amount,
|
||||
To: &dst,
|
||||
})
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
txRlpChan <- txRlp
|
||||
case <-quitChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gen *TxGenerator) genCalls(wg *sync.WaitGroup, ty shared.TxType, txRlpChan chan<- []byte, errChan chan<- error, quitChan <-chan bool, senderKey *ecdsa.PrivateKey, senderAddr common.Address, callConfig *CallConfig) {
|
||||
defer wg.Done()
|
||||
ticker := time.NewTicker(callConfig.Frequency)
|
||||
for _, addr := range callConfig.StorageAddrs {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
data, err := callConfig.ABI.Pack(callConfig.MethodName, addr, callConfig.StorageValue)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
txRlp, _, err := gen.GenerateTx(ty, &GenParams{
|
||||
Sender: senderAddr,
|
||||
SenderKey: senderKey,
|
||||
GasLimit: callConfig.GasLimit,
|
||||
GasPrice: callConfig.GasPrice,
|
||||
Data: data,
|
||||
})
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
continue
|
||||
}
|
||||
txRlpChan <- txRlp
|
||||
case <-quitChan:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateTx generates tx from the provided params
|
||||
func (tg TxGenerator) GenerateTx(ty shared.TxType, params *GenParams) ([]byte, error) {
|
||||
tx := make([]byte, 0)
|
||||
func (gen TxGenerator) GenerateTx(ty shared.TxType, params *GenParams) ([]byte, common.Address, error) {
|
||||
switch ty {
|
||||
case shared.OptimismL2:
|
||||
return tg.genL2(params, tg.optimismConfig)
|
||||
return gen.genL2(params, gen.config.OptimismConfig)
|
||||
case shared.Standard:
|
||||
return tg.gen(params)
|
||||
return gen.gen(params)
|
||||
case shared.EIP1559:
|
||||
return tg.gen1559(params, tg.eip1559Config)
|
||||
return gen.gen1559(params, gen.config.EIP1559Config)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported tx type: %s", ty.String())
|
||||
return nil, common.Address{}, fmt.Errorf("unsupported tx type: %s", ty.String())
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
func (gen TxGenerator) genL2(params *GenParams, op *OptimismConfig) ([]byte, error) {
|
||||
func (gen TxGenerator) genL2(params *GenParams, op *OptimismConfig) ([]byte, common.Address, error) {
|
||||
nonce := atomic.AddUint64(gen.nonces[params.Sender], 1)
|
||||
tx := new(types.Transaction)
|
||||
var contractAddr common.Address
|
||||
var err error
|
||||
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
|
||||
contractAddr, err = shared.WriteContractAddr(shared.DefaultDeploymentAddrLogPathPrefix, params.Sender, nonce)
|
||||
if err != nil {
|
||||
return nil, common.Address{}, 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, gen.signer, params.SenderKey)
|
||||
signedTx, err := types.SignTx(tx, gen.config.Signer, params.SenderKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, common.Address{}, err
|
||||
}
|
||||
txRlp, err := rlp.EncodeToBytes(signedTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, common.Address{}, err
|
||||
}
|
||||
return txRlp, nil
|
||||
return txRlp, contractAddr, err
|
||||
}
|
||||
|
||||
func (gen TxGenerator) gen(params *GenParams) ([]byte, error) {
|
||||
func (gen TxGenerator) gen(params *GenParams) ([]byte, common.Address, error) {
|
||||
// TODO: support standard geth
|
||||
return nil, errors.New("L1 support not yet available")
|
||||
return nil, common.Address{}, errors.New("L1 support not yet available")
|
||||
}
|
||||
|
||||
func (gen TxGenerator) gen1559(params *GenParams, eip1559Config *EIP1559Config) ([]byte, error) {
|
||||
func (gen TxGenerator) gen1559(params *GenParams, eip1559Config *EIP1559Config) ([]byte, common.Address, error) {
|
||||
// TODO: support EIP1559
|
||||
return nil, errors.New("1559 support not yet available")
|
||||
return nil, common.Address{}, errors.New("1559 support not yet available")
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (gen TxGenerator) genL2(params TxParams) ([]byte, error) {
|
||||
tx := new(types.Transaction)
|
||||
if params.To == nil {
|
||||
tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin)
|
||||
if err := shared.WriteContractAddr(params.ContractAddrWritePath, params.Sender, nonce); err != nil {
|
||||
if _, err := shared.WriteContractAddr(params.ContractAddrWritePath, params.Sender, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -48,17 +48,17 @@ func SendRawTransaction(rpcClient *rpc.Client, txRlp []byte) error {
|
||||
}
|
||||
|
||||
// WriteContractAddr appends a contract addr to an out file
|
||||
func WriteContractAddr(filePath string, senderAddr common.Address, nonce uint64) error {
|
||||
func WriteContractAddr(filePath string, senderAddr common.Address, nonce uint64) (common.Address, error) {
|
||||
if filePath == "" {
|
||||
filePath = DefaultDeploymentAddrLogPathPrefix + senderAddr.Hex()
|
||||
}
|
||||
contractAddr := crypto.CreateAddress(senderAddr, nonce)
|
||||
f, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
return common.Address{}, err
|
||||
}
|
||||
if _, err := f.WriteString(contractAddr.Hex() + "\n"); err != nil {
|
||||
return err
|
||||
return common.Address{}, err
|
||||
}
|
||||
return f.Close()
|
||||
return contractAddr, f.Close()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user