auto tx generator

This commit is contained in:
Ian Norden 2020-10-28 11:03:41 -05:00
parent da9dd220d0
commit d73390966c
10 changed files with 268 additions and 161 deletions

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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()
}