339 lines
9.0 KiB
Go
339 lines
9.0 KiB
Go
// 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 (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"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"
|
|
)
|
|
|
|
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
|
|
PositionStart uint64
|
|
PositionEnd uint64
|
|
StorageValue uint64
|
|
|
|
Frequency time.Duration
|
|
Number uint64
|
|
}
|
|
|
|
// 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
|
|
Number uint64
|
|
}
|
|
|
|
// 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 eth transfer destination addresses
|
|
addrs, err := loadAddresses()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Load tx type
|
|
txType, err := shared.TxTypeFromString(viper.GetString(ethType))
|
|
if err != nil {
|
|
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(addrs)
|
|
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)
|
|
}
|
|
|
|
return &CallConfig{
|
|
Number: viper.GetUint64(ethCallTotalNumber),
|
|
GasPrice: gasPrice,
|
|
GasLimit: viper.GetUint64(ethCallGasLimit),
|
|
MethodName: methodName,
|
|
ABI: parsedABI,
|
|
PositionEnd: viper.GetUint64(ethCallPositionEnd),
|
|
PositionStart: viper.GetUint64(ethCallPositionStart),
|
|
StorageValue: viper.GetUint64(ethCallStorageValue),
|
|
Frequency: viper.GetDuration(ethCallFrequency),
|
|
}, nil
|
|
}
|
|
|
|
// NewSendConfig constructs and returns a new SendConfig
|
|
func NewSendConfig(destinationAddrs []common.Address) (*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)
|
|
}
|
|
return &SendConfig{
|
|
DestinationAddresses: destinationAddrs,
|
|
Frequency: viper.GetDuration(ethSendFrequency),
|
|
Number: viper.GetUint64(ethSendTotalNumber),
|
|
Amount: amount,
|
|
GasPrice: gasPrice,
|
|
GasLimit: viper.GetUint64(ethSendGasLimit),
|
|
}, nil
|
|
}
|
|
|
|
// Load eth transfer destination addresses
|
|
func loadAddresses() ([]common.Address, error) {
|
|
addrs := make([]common.Address, 0)
|
|
addrFilePath := viper.GetString(ethAddrFilePath)
|
|
if addrFilePath == "" {
|
|
return nil, fmt.Errorf("missing %s", ethAddrFilePath)
|
|
}
|
|
addrFile, err := os.Open(addrFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer addrFile.Close()
|
|
scanner := bufio.NewScanner(addrFile)
|
|
for scanner.Scan() {
|
|
addrBytes := scanner.Bytes()
|
|
addr := common.BytesToAddress(addrBytes)
|
|
addrs = append(addrs, addr)
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return addrs, nil
|
|
}
|