Semi-deterministic, complex/large chain spammer #6

Merged
telackey merged 8 commits from auto_tx_gen_and_send into main 2020-10-30 03:09:39 +00:00
19 changed files with 1146 additions and 86 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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