Semi-deterministic, complex/large chain spammer #6
58
README.md
58
README.md
@ -1,2 +1,58 @@
|
|||||||
# tx_spammer
|
# 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
|
64
cmd/autoSend.go
Normal file
64
cmd/autoSend.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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, err := txSpammer.Loop(quitChan)
|
||||||
|
if err != nil {
|
||||||
|
logWithCommand.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
shutdown := make(chan os.Signal)
|
||||||
|
signal.Notify(shutdown, os.Interrupt)
|
||||||
|
<-shutdown
|
||||||
|
close(quitChan)
|
||||||
|
}()
|
||||||
|
<-doneChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(autoSendCmd)
|
||||||
|
}
|
@ -83,7 +83,7 @@ func init() {
|
|||||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
// flags
|
// shared flags
|
||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
|
||||||
|
|
||||||
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
|
rootCmd.PersistentFlags().String("log-level", log.InfoLevel.String(), "Log level (trace, debug, info, warn, error, fatal, panic")
|
||||||
|
@ -18,7 +18,6 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
@ -46,9 +45,11 @@ func sendTxs() {
|
|||||||
logWithCommand.Fatal(err)
|
logWithCommand.Fatal(err)
|
||||||
}
|
}
|
||||||
txSpammer := manual.NewTxSpammer(params)
|
txSpammer := manual.NewTxSpammer(params)
|
||||||
wg := new(sync.WaitGroup)
|
|
||||||
quitChan := make(chan bool)
|
quitChan := make(chan bool)
|
||||||
txSpammer.Loop(wg, quitChan)
|
doneChan, err := txSpammer.Loop(quitChan)
|
||||||
|
if err != nil {
|
||||||
|
logWithCommand.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
shutdown := make(chan os.Signal)
|
shutdown := make(chan os.Signal)
|
||||||
@ -56,7 +57,7 @@ func sendTxs() {
|
|||||||
<-shutdown
|
<-shutdown
|
||||||
close(quitChan)
|
close(quitChan)
|
||||||
}()
|
}()
|
||||||
wg.Wait()
|
<-doneChan
|
||||||
|
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
|
36
environments/gen.toml
Normal file
36
environments/gen.toml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
[eth]
|
||||||
|
keyDirPath = "" # path to the directory with all of the key pairs to use - env: $ETH_KEY_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 (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
|
||||||
|
# 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 (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
|
313
pkg/auto/config.go
Normal file
313
pkg/auto/config.go
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
receiverAddressSeed = common.HexToAddress("0xe48C9A989438606a79a7560cfba3d34BAfBAC38E")
|
||||||
|
storageAddressSeed = common.HexToAddress("0x029298Ac95662F2b54A7F1116f3F8105eb2b00F5")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bindEnv()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config holds all the parameters for the auto tx spammer
|
||||||
|
type Config struct {
|
||||||
|
// HTTP client for sending transactions
|
||||||
|
Client *rpc.Client
|
||||||
|
|
||||||
|
// Key pairs for the accounts we will use to deploy contracts and send txs
|
||||||
|
SenderKeys []*ecdsa.PrivateKey
|
||||||
|
SenderAddrs []common.Address
|
||||||
|
|
||||||
|
// Type of the txs we are working with
|
||||||
|
Type shared.TxType
|
||||||
|
|
||||||
|
// Tx signer for the chain we are working with
|
||||||
|
Signer types.Signer
|
||||||
|
|
||||||
|
// Configuration for Optimism L2
|
||||||
|
OptimismConfig *OptimismConfig
|
||||||
|
|
||||||
|
// Configuration for the initial contract deployment
|
||||||
|
DeploymentConfig *DeploymentConfig
|
||||||
|
|
||||||
|
// Configuration for the contract calling txs
|
||||||
|
CallConfig *CallConfig
|
||||||
|
|
||||||
|
// Configuration for the eth transfer txs
|
||||||
|
SendConfig *SendConfig
|
||||||
|
|
||||||
|
// Configuration for EIP1559
|
||||||
|
EIP1559Config *EIP1559Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptimismConfig holds the tx paramaters specific to Optimism L2
|
||||||
|
type OptimismConfig struct {
|
||||||
|
L1SenderAddr *common.Address
|
||||||
|
L1RollupTxId *hexutil.Uint64
|
||||||
|
SigHashType types.SignatureHashType
|
||||||
|
QueueOrigin types.QueueOrigin
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeploymentConfig holds the parameters for the contract deployment contracts
|
||||||
|
type DeploymentConfig struct {
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice *big.Int
|
||||||
|
Data []byte
|
||||||
|
|
||||||
|
Number uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallConfig holds the parameters for the contract calling txs
|
||||||
|
type CallConfig struct {
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice *big.Int
|
||||||
|
|
||||||
|
MethodName string
|
||||||
|
ABI abi.ABI
|
||||||
|
StorageValue uint64
|
||||||
|
StorageAddrs []common.Address
|
||||||
|
|
||||||
|
Frequency time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendConfig holds the parameters for the eth transfer txs
|
||||||
|
type SendConfig struct {
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice *big.Int
|
||||||
|
Amount *big.Int
|
||||||
|
|
||||||
|
DestinationAddresses []common.Address
|
||||||
|
Frequency time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: EIP1559Config
|
||||||
|
type EIP1559Config struct{}
|
||||||
|
|
||||||
|
func NewConfig() (*Config, error) {
|
||||||
|
// Initialize rpc client
|
||||||
|
httpPathStr := viper.GetString(ethHttpPath)
|
||||||
|
if httpPathStr == "" {
|
||||||
|
return nil, fmt.Errorf("missing %s", ethHttpPath)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(httpPathStr, "http://") {
|
||||||
|
httpPathStr = "http://" + httpPathStr
|
||||||
|
}
|
||||||
|
rpcClient, err := rpc.Dial(httpPathStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load keys
|
||||||
|
keyDirPath := viper.GetString(ethKeyDirPath)
|
||||||
|
if keyDirPath == "" {
|
||||||
|
return nil, fmt.Errorf("missing %s", ethKeyDirPath)
|
||||||
|
}
|
||||||
|
keyFiles, err := ioutil.ReadDir(keyDirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keys := make([]*ecdsa.PrivateKey, 0)
|
||||||
|
senderAddrs := make([]common.Address, 0)
|
||||||
|
for _, keyFile := range keyFiles {
|
||||||
|
if keyFile.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := filepath.Join(keyDirPath, keyFile.Name())
|
||||||
|
key, err := crypto.LoadECDSA(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to load ecdsa key file at %s", filePath)
|
||||||
|
}
|
||||||
|
keys = append(keys, key)
|
||||||
|
senderAddrs = append(senderAddrs, crypto.PubkeyToAddress(key.PublicKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load tx type
|
||||||
|
txType, err := shared.TxTypeFromString(viper.GetString(ethType))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load signer
|
||||||
|
chainID := viper.GetUint64(ethChainID)
|
||||||
|
signer, err := shared.TxSigner(txType, chainID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load optimism config
|
||||||
|
var optimismConfig *OptimismConfig
|
||||||
|
if txType == shared.OptimismL1ToL2 || txType == shared.OptimismL2 {
|
||||||
|
optimismConfig = NewOptimismConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load deployment config
|
||||||
|
deploymentConfig, err := NewDeploymentConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load call config
|
||||||
|
callConfig, err := NewCallConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load send config
|
||||||
|
sendConfig, err := NewSendConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble and return
|
||||||
|
return &Config{
|
||||||
|
Client: rpcClient,
|
||||||
|
SenderKeys: keys,
|
||||||
|
SenderAddrs: senderAddrs,
|
||||||
|
Type: txType,
|
||||||
|
Signer: signer,
|
||||||
|
OptimismConfig: optimismConfig,
|
||||||
|
DeploymentConfig: deploymentConfig,
|
||||||
|
CallConfig: callConfig,
|
||||||
|
SendConfig: sendConfig,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOptimismConfig constructs and returns a new OptimismConfig
|
||||||
|
func NewOptimismConfig() *OptimismConfig {
|
||||||
|
l1SenderStr := viper.GetString(ethOptimismL1Sender)
|
||||||
|
var l1Sender *common.Address
|
||||||
|
if l1SenderStr != "" {
|
||||||
|
sender := common.HexToAddress(l1SenderStr)
|
||||||
|
l1Sender = &sender
|
||||||
|
}
|
||||||
|
l1RollupTxId := viper.GetUint64(ethOptimismRollupTxID)
|
||||||
|
l1rtid := (hexutil.Uint64)(l1RollupTxId)
|
||||||
|
sigHashType := viper.GetUint(ethOptimismSigHashType)
|
||||||
|
queueOrigin := viper.GetInt64(ethOptimismQueueOrigin)
|
||||||
|
return &OptimismConfig{
|
||||||
|
L1SenderAddr: l1Sender,
|
||||||
|
L1RollupTxId: &l1rtid,
|
||||||
|
SigHashType: (types.SignatureHashType)(uint8(sigHashType)),
|
||||||
|
QueueOrigin: (types.QueueOrigin)(queueOrigin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDeploymentConfig constructs and returns a new DeploymentConfig
|
||||||
|
func NewDeploymentConfig() (*DeploymentConfig, error) {
|
||||||
|
hexData := viper.GetString(ethDeploymentData)
|
||||||
|
data := common.Hex2Bytes(hexData)
|
||||||
|
gasPriceStr := viper.GetString(ethDeploymentGasPrice)
|
||||||
|
gasPrice, ok := new(big.Int).SetString(gasPriceStr, 10)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unable to convert gasPrice string (%s) into big.Int", gasPriceStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DeploymentConfig{
|
||||||
|
Number: viper.GetUint64(ethDeploymentNumber),
|
||||||
|
Data: data,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
GasLimit: viper.GetUint64(ethDeploymentGasLimit),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCallConfig constructs and returns a new CallConfig
|
||||||
|
func NewCallConfig() (*CallConfig, error) {
|
||||||
|
gasPriceStr := viper.GetString(ethCallGasPrice)
|
||||||
|
gasPrice, ok := new(big.Int).SetString(gasPriceStr, 10)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unable to convert gasPrice string (%s) into big.Int", gasPriceStr)
|
||||||
|
}
|
||||||
|
abiPath := viper.GetString(ethCallABIPath)
|
||||||
|
if abiPath == "" {
|
||||||
|
return nil, fmt.Errorf("missing contractSpammer.abiPath")
|
||||||
|
}
|
||||||
|
abiBytes, err := ioutil.ReadFile(abiPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedABI, err := abi.JSON(bytes.NewReader(abiBytes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
methodName := viper.GetString(ethCallMethodName)
|
||||||
|
_, exist := parsedABI.Methods[methodName]
|
||||||
|
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{
|
||||||
|
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() (*SendConfig, error) {
|
||||||
|
amountStr := viper.GetString(ethSendAmount)
|
||||||
|
amount, ok := new(big.Int).SetString(amountStr, 10)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unable to convert amount string (%s) into big.Int", amountStr)
|
||||||
|
}
|
||||||
|
gasPriceStr := viper.GetString(ethSendGasPrice)
|
||||||
|
gasPrice, ok := new(big.Int).SetString(gasPriceStr, 10)
|
||||||
|
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: addrs,
|
||||||
|
Frequency: viper.GetDuration(ethSendFrequency),
|
||||||
|
Amount: amount,
|
||||||
|
GasPrice: gasPrice,
|
||||||
|
GasLimit: viper.GetUint64(ethSendGasLimit),
|
||||||
|
}, nil
|
||||||
|
}
|
79
pkg/auto/deployer.go
Normal file
79
pkg/auto/deployer.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
contractDeploymentDelay = time.Duration(15) * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContractDeployer is responsible for deploying contracts
|
||||||
|
type ContractDeployer struct {
|
||||||
|
client *rpc.Client
|
||||||
|
ty shared.TxType
|
||||||
|
txGenerator *TxGenerator
|
||||||
|
senderKeys []*ecdsa.PrivateKey
|
||||||
|
senderAddrs []common.Address
|
||||||
|
config *DeploymentConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractDeployer returns a new ContractDeployer
|
||||||
|
func NewContractDeployer(config *Config, gen *TxGenerator) *ContractDeployer {
|
||||||
|
return &ContractDeployer{
|
||||||
|
client: config.Client,
|
||||||
|
ty: config.Type,
|
||||||
|
txGenerator: gen,
|
||||||
|
config: config.DeploymentConfig,
|
||||||
|
senderKeys: config.SenderKeys,
|
||||||
|
senderAddrs: config.SenderAddrs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deploy deploys the contracts according to the config provided at construction
|
||||||
|
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
|
||||||
|
for i, key := range cp.senderKeys {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := shared.SendRawTransaction(cp.client, txBytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
contractAddrs = append(contractAddrs, contractAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contractAddrs, nil
|
||||||
|
}
|
112
pkg/auto/env.go
Normal file
112
pkg/auto/env.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import "github.com/spf13/viper"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// env variables
|
||||||
|
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"
|
||||||
|
ETH_DEPLOYMENT_GAS_LIMIT = "ETH_DEPLOYMENT_GAS_LIMIT"
|
||||||
|
ETH_DEPLOYMENT_GAS_PRICE = "ETH_DEPLOYMENT_GAS_PRICE"
|
||||||
|
|
||||||
|
ETH_OPTIMISM_L1_SENDER = "ETH_OPTIMISM_L1_SENDER"
|
||||||
|
ETH_OPTIMISM_ROLLUP_TX_ID = "ETH_OPTIMISM_ROLLUP_TX_ID"
|
||||||
|
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_SEND_FREQ = "ETH_SEND_FREQ"
|
||||||
|
ETH_SEND_TOTAL_NUMBER = "ETH_SEND_TOTAL_NUMBER"
|
||||||
|
ETH_SEND_AMOUNT = "ETH_SEND_AMOUNT"
|
||||||
|
ETH_SEND_GAS_LIMIT = "ETH_SEND_GAS_LIMIT"
|
||||||
|
ETH_SEND_GAS_PRICE = "ETH_SEND_GAS_PRICE"
|
||||||
|
|
||||||
|
// toml bindings
|
||||||
|
ethKeyDirPath = "eth.keyDirPath"
|
||||||
|
ethHttpPath = "eth.httpPath"
|
||||||
|
ethChainID = "eth.chainID"
|
||||||
|
ethType = "eth.type"
|
||||||
|
|
||||||
|
ethDeploymentNumber = "deployment.number"
|
||||||
|
ethDeploymentData = "deployment.hexData"
|
||||||
|
ethDeploymentGasPrice = "deployment.gasPrice"
|
||||||
|
ethDeploymentGasLimit = "deployment.gasLimit"
|
||||||
|
|
||||||
|
ethOptimismL1Sender = "optimism.l1Sender"
|
||||||
|
ethOptimismRollupTxID = "optimism.l1RollupTxId"
|
||||||
|
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"
|
||||||
|
|
||||||
|
ethSendFrequency = "sendSpammer.frequency"
|
||||||
|
ethSendTotalNumber = "sendSpammer.totalNumber"
|
||||||
|
ethSendAmount = "sendSpammer.amount"
|
||||||
|
ethSendGasLimit = "sendSpammer.gasLimit"
|
||||||
|
ethSendGasPrice = "sendSpammer.gasPrice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindEnv() {
|
||||||
|
viper.BindEnv(ethKeyDirPath, ETH_KEY_DIR_PATH)
|
||||||
|
viper.BindEnv(ethHttpPath, ETH_HTTP_PATH)
|
||||||
|
viper.BindEnv(ethType, ETH_TX_TYPE)
|
||||||
|
viper.BindEnv(ethChainID, ETH_CHAIN_ID)
|
||||||
|
|
||||||
|
viper.BindEnv(ethOptimismL1Sender, ETH_OPTIMISM_L1_SENDER)
|
||||||
|
viper.BindEnv(ethOptimismQueueOrigin, ETH_OPTIMISM_QUEUE_ORIGIN)
|
||||||
|
viper.BindEnv(ethOptimismRollupTxID, ETH_OPTIMISM_ROLLUP_TX_ID)
|
||||||
|
viper.BindEnv(ethOptimismSigHashType, ETH_OPTIMISM_SIG_HASH_TYPE)
|
||||||
|
|
||||||
|
viper.BindEnv(ethDeploymentNumber, ETH_DEPLOYMENT_NUMBER)
|
||||||
|
viper.BindEnv(ethDeploymentData, ETH_DEPLOYMENT_HEX_DATA)
|
||||||
|
viper.BindEnv(ethDeploymentGasLimit, ETH_DEPLOYMENT_GAS_LIMIT)
|
||||||
|
viper.BindEnv(ethDeploymentGasPrice, ETH_DEPLOYMENT_GAS_PRICE)
|
||||||
|
|
||||||
|
viper.BindEnv(ethCallABIPath, ETH_CALL_ABI_PATH)
|
||||||
|
viper.BindEnv(ethCallFrequency, ETH_CALL_FREQ)
|
||||||
|
viper.BindEnv(ethCallGasLimit, ETH_CALL_GAS_LIMIT)
|
||||||
|
viper.BindEnv(ethCallGasPrice, ETH_CALL_GAS_PRICE)
|
||||||
|
viper.BindEnv(ethCallMethodName, ETH_CALL_METHOD_NAME)
|
||||||
|
viper.BindEnv(ethCallStorageValue, ETH_CALL_STORAGE_VALUE)
|
||||||
|
viper.BindEnv(ethCallTotalNumber, ETH_CALL_TOTAL_NUMBER)
|
||||||
|
|
||||||
|
viper.BindEnv(ethSendFrequency, ETH_SEND_FREQ)
|
||||||
|
viper.BindEnv(ethSendTotalNumber, ETH_SEND_TOTAL_NUMBER)
|
||||||
|
viper.BindEnv(ethSendAmount, ETH_SEND_AMOUNT)
|
||||||
|
viper.BindEnv(ethSendGasLimit, ETH_SEND_GAS_LIMIT)
|
||||||
|
viper.BindEnv(ethSendGasPrice, ETH_SEND_GAS_PRICE)
|
||||||
|
}
|
57
pkg/auto/sender.go
Normal file
57
pkg/auto/sender.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EthSender sends eth value transfer txs
|
||||||
|
type EthSender struct {
|
||||||
|
client *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEthSender returns a new EthSender
|
||||||
|
func NewEthSender(config *Config) *EthSender {
|
||||||
|
return &EthSender{
|
||||||
|
client: config.Client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// err channel returned to calling context
|
||||||
|
errChan := make(chan error)
|
||||||
|
doneChan := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
defer close(doneChan)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case tx := <-txRlpChan:
|
||||||
|
if err := shared.SendRawTransaction(s.client, tx); err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
case <-quitChan:
|
||||||
|
logrus.Info("quitting Send loop")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return doneChan, errChan
|
||||||
|
}
|
80
pkg/auto/service.go
Normal file
80
pkg/auto/service.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
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),
|
||||||
|
TxGenerator: gen,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) {
|
||||||
|
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)
|
||||||
|
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 err := <-genErrChan:
|
||||||
|
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:
|
||||||
|
logrus.Error("shutting down tx spammer")
|
||||||
|
close(genQuit)
|
||||||
|
<-genDoneChan
|
||||||
|
close(senderQuit)
|
||||||
|
case <-sendDoneChan:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return doneChan, nil
|
||||||
|
}
|
189
pkg/auto/tx_generator.go
Normal file
189
pkg/auto/tx_generator.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// 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 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/>.
|
||||||
|
|
||||||
|
package auto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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{
|
||||||
|
nonces: nonces,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenParams params for GenerateTx method calls
|
||||||
|
type GenParams struct {
|
||||||
|
Sender common.Address
|
||||||
|
SenderKey *ecdsa.PrivateKey
|
||||||
|
To *common.Address
|
||||||
|
Amount *big.Int
|
||||||
|
GasLimit uint64
|
||||||
|
GasPrice *big.Int
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (gen TxGenerator) GenerateTx(ty shared.TxType, params *GenParams) ([]byte, common.Address, error) {
|
||||||
|
switch ty {
|
||||||
|
case shared.OptimismL2:
|
||||||
|
return gen.genL2(params, gen.config.OptimismConfig)
|
||||||
|
case shared.Standard:
|
||||||
|
return gen.gen(params)
|
||||||
|
case shared.EIP1559:
|
||||||
|
return gen.gen1559(params, gen.config.EIP1559Config)
|
||||||
|
default:
|
||||||
|
return nil, common.Address{}, fmt.Errorf("unsupported tx type: %s", ty.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
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.config.Signer, params.SenderKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, common.Address{}, err
|
||||||
|
}
|
||||||
|
txRlp, err := rlp.EncodeToBytes(signedTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, common.Address{}, err
|
||||||
|
}
|
||||||
|
return txRlp, contractAddr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen TxGenerator) gen(params *GenParams) ([]byte, common.Address, error) {
|
||||||
|
// TODO: support standard geth
|
||||||
|
return nil, common.Address{}, errors.New("L1 support not yet available")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gen TxGenerator) gen1559(params *GenParams, eip1559Config *EIP1559Config) ([]byte, common.Address, error) {
|
||||||
|
// TODO: support EIP1559
|
||||||
|
return nil, common.Address{}, errors.New("1559 support not yet available")
|
||||||
|
}
|
@ -19,12 +19,13 @@ package manual
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -33,36 +34,6 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
ETH_TX_LIST = "ETH_TX_LIST"
|
|
||||||
ETH_ADDR_LOG = "ETH_ADDR_LOG"
|
|
||||||
|
|
||||||
defaultGenKeyWritePathPrefix = "./accounts/keys/"
|
|
||||||
defaultAddrLogPath = "./accounts/addresses/accounts"
|
|
||||||
|
|
||||||
typeSuffix = ".type"
|
|
||||||
httpPathSuffix = ".http"
|
|
||||||
toSuffix = ".to"
|
|
||||||
amountSuffix = ".amount"
|
|
||||||
gasLimitSuffix = ".gasLimit"
|
|
||||||
gasPriceSuffix = ".gasPrice"
|
|
||||||
gasPremiumSuffix = ".gasPremium"
|
|
||||||
feeCapSuffix = ".feeCap"
|
|
||||||
dataSuffix = ".data"
|
|
||||||
senderKeyPathSuffix = ".senderKeyPath"
|
|
||||||
writeSenderPathSuffix = ".writeSenderPath"
|
|
||||||
l1SenderSuffix = ".l1Sender"
|
|
||||||
l1RollupTxIdSuffix = ".l1RollupTxId"
|
|
||||||
sigHashTypeSuffix = ".sigHashType"
|
|
||||||
frequencySuffix = ".frequency"
|
|
||||||
totalNumberSuffix = ".totalNumber"
|
|
||||||
delaySuffix = ".delay"
|
|
||||||
startingNonceSuffix = ".startingNonce"
|
|
||||||
queueOriginSuffix = ".queueOrigin"
|
|
||||||
chainIDSuffix = ".chainID"
|
|
||||||
contractWriteSuffix = ".writeDeploymentAddrPath"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TxParams holds the parameters for a given transaction
|
// TxParams holds the parameters for a given transaction
|
||||||
type TxParams struct {
|
type TxParams struct {
|
||||||
// Name of this tx in the .toml file
|
// Name of this tx in the .toml file
|
||||||
@ -111,9 +82,7 @@ type TxParams struct {
|
|||||||
|
|
||||||
// NewConfig returns a new tx spammer config
|
// NewConfig returns a new tx spammer config
|
||||||
func NewTxParams() ([]TxParams, error) {
|
func NewTxParams() ([]TxParams, error) {
|
||||||
viper.BindEnv("eth.txs", ETH_TX_LIST)
|
bindEnv()
|
||||||
viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG)
|
|
||||||
|
|
||||||
addrLogPath := viper.GetString("eth.addrLogPath")
|
addrLogPath := viper.GetString("eth.addrLogPath")
|
||||||
txs := viper.GetStringSlice("eth.txs")
|
txs := viper.GetStringSlice("eth.txs")
|
||||||
txParams := make([]TxParams, len(txs))
|
txParams := make([]TxParams, len(txs))
|
||||||
|
57
pkg/manual/env.go
Normal file
57
pkg/manual/env.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package manual
|
||||||
|
|
||||||
|
import "github.com/spf13/viper"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// env variables
|
||||||
|
ETH_TX_LIST = "ETH_TX_LIST"
|
||||||
|
ETH_ADDR_LOG = "ETH_ADDR_LOG"
|
||||||
|
|
||||||
|
// write paths
|
||||||
|
defaultGenKeyWritePathPrefix = "./accounts/keys/out/"
|
||||||
|
defaultAddrLogPath = "./accounts/addresses/accounts"
|
||||||
|
|
||||||
|
// .toml binding suffixes
|
||||||
|
typeSuffix = ".type"
|
||||||
|
httpPathSuffix = ".http"
|
||||||
|
toSuffix = ".to"
|
||||||
|
amountSuffix = ".amount"
|
||||||
|
gasLimitSuffix = ".gasLimit"
|
||||||
|
gasPriceSuffix = ".gasPrice"
|
||||||
|
gasPremiumSuffix = ".gasPremium"
|
||||||
|
feeCapSuffix = ".feeCap"
|
||||||
|
dataSuffix = ".data"
|
||||||
|
senderKeyPathSuffix = ".senderKeyPath"
|
||||||
|
writeSenderPathSuffix = ".writeSenderPath"
|
||||||
|
l1SenderSuffix = ".l1Sender"
|
||||||
|
l1RollupTxIdSuffix = ".l1RollupTxId"
|
||||||
|
sigHashTypeSuffix = ".sigHashType"
|
||||||
|
frequencySuffix = ".frequency"
|
||||||
|
totalNumberSuffix = ".totalNumber"
|
||||||
|
delaySuffix = ".delay"
|
||||||
|
startingNonceSuffix = ".startingNonce"
|
||||||
|
queueOriginSuffix = ".queueOrigin"
|
||||||
|
chainIDSuffix = ".chainID"
|
||||||
|
contractWriteSuffix = ".writeDeploymentAddrPath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func bindEnv() {
|
||||||
|
viper.BindEnv("eth.txs", ETH_TX_LIST)
|
||||||
|
viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG)
|
||||||
|
}
|
@ -17,14 +17,13 @@
|
|||||||
package manual
|
package manual
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxSender type for
|
// TxSender type for
|
||||||
@ -86,10 +85,6 @@ func (s *TxSender) genAndSend(p TxParams) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sendRawTransaction(p.Client, tx, p.Name)
|
logrus.Infof("sending tx %s", p.Name)
|
||||||
}
|
return shared.SendRawTransaction(p.Client, tx)
|
||||||
|
|
||||||
func sendRawTransaction(rpcClient *rpc.Client, txRlp []byte, name string) error {
|
|
||||||
logrus.Infof("sending tx %s", name)
|
|
||||||
return rpcClient.CallContext(context.Background(), nil, "eth_sendRawTransaction", hexutil.Encode(txRlp))
|
|
||||||
}
|
}
|
||||||
|
@ -17,31 +17,24 @@
|
|||||||
package manual
|
package manual
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service interface {
|
|
||||||
Loop(wg *sync.WaitGroup, quitChan <-chan bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Spammer struct {
|
type Spammer struct {
|
||||||
Sender *TxSender
|
Sender *TxSender
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTxSpammer(params []TxParams) Service {
|
func NewTxSpammer(params []TxParams) shared.Service {
|
||||||
return &Spammer{
|
return &Spammer{
|
||||||
Sender: NewTxSender(params),
|
Sender: NewTxSender(params),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Spammer) Loop(wg *sync.WaitGroup, quitChan <-chan bool) {
|
func (s *Spammer) Loop(quitChan <-chan bool) (<-chan bool, error) {
|
||||||
forwardQuit := make(chan bool)
|
forwardQuit := make(chan bool)
|
||||||
doneChan, errChan := s.Sender.Send(forwardQuit)
|
doneChan, errChan := s.Sender.Send(forwardQuit)
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
@ -53,4 +46,5 @@ func (s *Spammer) Loop(wg *sync.WaitGroup, quitChan <-chan bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
return doneChan, nil
|
||||||
}
|
}
|
||||||
|
@ -17,19 +17,14 @@
|
|||||||
package manual
|
package manual
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vulcanize/tx_spammer/pkg/shared"
|
|
||||||
"os"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
"github.com/vulcanize/tx_spammer/pkg/shared"
|
||||||
|
|
||||||
const (
|
|
||||||
defaultDeploymentAddrLogPathPrefix = "./accounts/addresses/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxGenerator generates and signs txs
|
// TxGenerator generates and signs txs
|
||||||
@ -54,7 +49,9 @@ func NewTxGenerator(params []TxParams) *TxGenerator {
|
|||||||
func (tg TxGenerator) GenerateTx(params TxParams) ([]byte, error) {
|
func (tg TxGenerator) GenerateTx(params TxParams) ([]byte, error) {
|
||||||
tx := make([]byte, 0)
|
tx := make([]byte, 0)
|
||||||
switch params.Type {
|
switch params.Type {
|
||||||
case shared.Standard, shared.OptimismL1ToL2, shared.OptimismL2:
|
case shared.OptimismL2:
|
||||||
|
return tg.genL2(params)
|
||||||
|
case shared.Standard:
|
||||||
return tg.gen(params)
|
return tg.gen(params)
|
||||||
case shared.EIP1559:
|
case shared.EIP1559:
|
||||||
return tg.gen1559(params)
|
return tg.gen1559(params)
|
||||||
@ -64,19 +61,19 @@ func (tg TxGenerator) GenerateTx(params TxParams) ([]byte, error) {
|
|||||||
return tx, nil
|
return tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gen TxGenerator) gen(params TxParams) ([]byte, error) {
|
func (gen TxGenerator) genL2(params TxParams) ([]byte, error) {
|
||||||
nonce := atomic.AddUint64(gen.nonces[params.Sender], 1)
|
nonce := atomic.AddUint64(gen.nonces[params.Sender], 1)
|
||||||
tx := new(types.Transaction)
|
tx := new(types.Transaction)
|
||||||
if params.To == nil {
|
if params.To == nil {
|
||||||
tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin)
|
tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin)
|
||||||
if err := writeContractAddr(params.ContractAddrWritePath, params.Sender, nonce); err != nil {
|
if _, err := shared.WriteContractAddr(params.ContractAddrWritePath, params.Sender, nonce); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin, params.SigHashType)
|
tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin, params.SigHashType)
|
||||||
}
|
}
|
||||||
signer, err := shared.TxSigner(params.Type, params.ChainID)
|
signer, err := shared.TxSigner(shared.OptimismL2, params.ChainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -91,22 +88,12 @@ func (gen TxGenerator) gen(params TxParams) ([]byte, error) {
|
|||||||
return txRlp, nil
|
return txRlp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gen TxGenerator) gen1559(params TxParams) ([]byte, error) {
|
func (gen TxGenerator) gen(params TxParams) ([]byte, error) {
|
||||||
// TODO: support EIP1559; new to make a new major version, vendor it, or release with different pkg name so that we can import both optimism and eip1559 geth
|
// TODO: support standard geth
|
||||||
return nil, fmt.Errorf("1559 support not yet available")
|
return nil, errors.New("L1 support not yet available")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeContractAddr(filePath string, senderAddr common.Address, nonce uint64) error {
|
func (gen TxGenerator) gen1559(params TxParams) ([]byte, error) {
|
||||||
if filePath == "" {
|
// TODO: support EIP1559
|
||||||
filePath = defaultDeploymentAddrLogPathPrefix + senderAddr.Hex()
|
return nil, errors.New("1559 support not yet available")
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if _, err := f.WriteString(contractAddr.Hex() + "\n"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return f.Close()
|
|
||||||
}
|
}
|
||||||
|
21
pkg/shared/env.go
Normal file
21
pkg/shared/env.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package shared
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultDeploymentAddrLogPathPrefix = "./accounts/addresses/"
|
||||||
|
)
|
22
pkg/shared/interface.go
Normal file
22
pkg/shared/interface.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package shared
|
||||||
|
|
||||||
|
// Service looping interface
|
||||||
|
type Service interface {
|
||||||
|
Loop(quitChan <-chan bool) (doneChan <-chan bool, err error)
|
||||||
|
}
|
@ -17,10 +17,17 @@
|
|||||||
package shared
|
package shared
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"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/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
|
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
|
||||||
@ -34,3 +41,24 @@ func TxSigner(kind TxType, chainID uint64) (types.Signer, error) {
|
|||||||
return nil, fmt.Errorf("chain config for chainid %d not available", chainID)
|
return nil, fmt.Errorf("chain config for chainid %d not available", chainID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendRawTransaction sends a raw, signed tx using the provided client
|
||||||
|
func SendRawTransaction(rpcClient *rpc.Client, txRlp []byte) error {
|
||||||
|
return rpcClient.CallContext(context.Background(), nil, "eth_sendRawTransaction", hexutil.Encode(txRlp))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteContractAddr appends a contract addr to an out file
|
||||||
|
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 common.Address{}, err
|
||||||
|
}
|
||||||
|
if _, err := f.WriteString(contractAddr.Hex() + "\n"); err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
return contractAddr, f.Close()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user