finalize support for l1 and l2

This commit is contained in:
Ian Norden 2020-10-15 22:10:18 -05:00
parent e441254891
commit 54de12e12e
8 changed files with 201 additions and 133 deletions

View File

@ -27,9 +27,10 @@ import (
// sendTxsCmd represents the sendTxs command
var sendTxsCmd = &cobra.Command{
Use: "sendTxs",
Short: "send large volumes of different tx types to different nodes",
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 sends them to designated node according to set frequency and number`,
Generates txs from configuration 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) {
sendTxs()
},
@ -45,10 +46,12 @@ func sendTxs() {
quitChan := make(chan bool)
txSpammer.Loop(wg, quitChan)
shutdown := make(chan os.Signal)
signal.Notify(shutdown, os.Interrupt)
<-shutdown
close(quitChan)
go func() {
shutdown := make(chan os.Signal)
signal.Notify(shutdown, os.Interrupt)
<-shutdown
close(quitChan)
}()
wg.Wait()
}

View File

@ -4,6 +4,7 @@
[L2ContractDeployment]
type = "L2"
httpPath = ""
chainID = 420
to = ""
amount = "0"
gasLimit = 0
@ -13,11 +14,16 @@
writeSenderPath = ""
frequency = 1
totalNumber = 1
chainID = 420
delay = 0
l1Sender = ""
l1RollupTxId = 0
sigHashType = 0
queueOrigin = 0
[L2ContractPutCall]
type = "L2"
httpPath = ""
chainID = 420
to = ""
amount = "0"
gasLimit = 0
@ -26,54 +32,17 @@
senderKeyPath = ""
writeSenderPath = ""
frequency = 15
totalNumber = 1500
chainID = 420
totalNumber = 1
delay = 60
l1Sender = ""
l1RollupTxId = 0
sigHashType = 0
queueOrigin = 0
[L2ContractGetCall]
type = "L2"
httpPath = ""
to = ""
amount = "0"
gasLimit = 0
gasPrice = "0"
data = ""
senderKeyPath = ""
writeSenderPath = ""
frequency = 15
totalNumber = 1500
chainID = 420
[GethTx]
type = "geth"
httpPath = ""
to = ""
amount = "0"
gasLimit = 0
gasPrice = "0"
data = ""
senderKeyPath = ""
writeSenderPath = ""
frequency = 0
totalNumber = 0
chainID = 1
[EIP1559]
type = "1559"
httpPath = ""
to = ""
amount = "0"
gasLimit = 0
gasPremium = "0"
feeCap = "0"
data = ""
senderKeyPath = ""
writeSenderPath = ""
frequency = 0
totalNumber = 0
[L1ToL2]
type = "L2"
httpPath = ""
to = ""
amount = "0"
gasLimit = 0
@ -81,11 +50,13 @@
data = ""
senderKeyPath = ""
writeSenderPath = ""
frequency = 60
totalNumber = 2
delay = 30
l1Sender = ""
l1RollupTxId = 0
sigHashType = 0
frequency = 0
totalNumber = 0
queueOrigin = 0
[log]
level = "info"

View File

@ -24,6 +24,8 @@ import (
"time"
"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"
@ -39,6 +41,9 @@ type TxParams struct {
// Type of the tx
Type TxType
// Chain ID
ChainID uint64
// Universal tx fields
To *common.Address
GasLimit uint64
@ -49,23 +54,25 @@ type TxParams struct {
// Optimism-specific metadata fields
L1SenderAddr *common.Address
L1RollupTxId uint64
SigHashType uint8
L1RollupTxId *hexutil.Uint64
SigHashType types.SignatureHashType
QueueOrigin types.QueueOrigin
// EIP1559-specific fields
GasPremium *big.Int
FeeCap *big.Int
// Sender key, if left the senderKeyPath is empty we generate a new key
SenderKey *ecdsa.PrivateKey
SenderKey *ecdsa.PrivateKey
StartingNonce uint64
// Sending params
// How often we send a tx of this type
Frequency time.Duration
// Total number of txs of this type to send
TotalNumber uint64
// Txs of different types will be sent according to their order (starting at 0)
Order uint64
// Delay before beginning to send
Delay time.Duration
}
const (
@ -87,6 +94,10 @@ const (
sigHashTypeSuffix = ".sigHashType"
frequencySuffix = ".frequency"
totalNumberSuffix = ".totalNumber"
delaySuffix = ".delay"
startingNonceSuffix = ".startingNonce"
queueOriginSuffix = ".queueOrigin"
chainIDSuffix = ".chainID"
)
// NewConfig returns a new tx spammer config
@ -109,7 +120,7 @@ func NewTxParams() ([]TxParams, error) {
return nil, err
}
// Get tx type
// Get tx type and chain id
txTypeStr := viper.GetString(txName + typeSuffix)
if txTypeStr == "" {
return nil, fmt.Errorf("need tx type for tx %s", txName)
@ -118,6 +129,7 @@ func NewTxParams() ([]TxParams, error) {
if err != nil {
return nil, err
}
chainID := viper.GetUint64(txName + chainIDSuffix)
// Get basic fields
toStr := viper.GetString(txName + toSuffix)
@ -176,9 +188,10 @@ func NewTxParams() ([]TxParams, error) {
sender := common.HexToAddress(l1SenderStr)
l1Sender = &sender
}
l1RollupTxId := viper.GetUint64(txName + l1RollupTxIdSuffix)
l1rtid := (hexutil.Uint64)(l1RollupTxId)
sigHashType := viper.GetUint(txName + sigHashTypeSuffix)
queueOrigin := viper.GetInt64(txName + queueOriginSuffix)
// If gasPrice was empty, attempt to load EIP1559 fields
var feeCap, gasPremium *big.Int
@ -201,26 +214,32 @@ func NewTxParams() ([]TxParams, error) {
}
}
// Load the sending paramas
// Load starting nonce and sending params
startingNonce := viper.GetUint64(txName + startingNonceSuffix)
frequency := viper.GetDuration(txName + frequencySuffix)
totalNumber := viper.GetUint64(txName + totalNumberSuffix)
delay := viper.GetDuration(txName + delaySuffix)
txParams[i] = TxParams{
Client: rpcClient,
Type: txType,
Name: txName,
To: toAddr,
Amount: amount,
GasLimit: gasLimit,
GasPrice: gasPrice,
GasPremium: gasPremium,
FeeCap: feeCap,
Data: data,
L1SenderAddr: l1Sender,
L1RollupTxId: l1RollupTxId,
SigHashType: uint8(sigHashType),
Frequency: frequency,
TotalNumber: totalNumber,
Client: rpcClient,
Type: txType,
Name: txName,
To: toAddr,
Amount: amount,
GasLimit: gasLimit,
GasPrice: gasPrice,
GasPremium: gasPremium,
FeeCap: feeCap,
Data: data,
L1SenderAddr: l1Sender,
L1RollupTxId: &l1rtid,
SigHashType: (types.SignatureHashType)(uint8(sigHashType)),
Frequency: frequency,
TotalNumber: totalNumber,
Delay: delay,
StartingNonce: startingNonce,
QueueOrigin: (types.QueueOrigin)(queueOrigin),
ChainID: chainID,
}
}
return txParams, nil

View File

@ -18,40 +18,78 @@ package tx_spammer
import (
"context"
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
"github.com/sirupsen/logrus"
)
// TxSender type for
type TxSender struct {
TxGen *TxGenerator
TxGen *TxGenerator
TxParams []TxParams
}
// NewTxSender returns a new tx sender
func NewTxSender(params []TxParams) *TxSender {
return &TxSender{
TxGen: NewTxGenerator(params),
TxGen: NewTxGenerator(params),
TxParams: params,
}
}
func (s *TxSender) Send(quitChan <-chan bool) <-chan error {
func (s *TxSender) Send(quitChan <-chan bool) (<-chan bool, <-chan error) {
// done channel to signal completion of all jobs
doneChan := make(chan bool)
// err channel returned to calling context
errChan := make(chan error)
go func() {
for s.TxGen.Next() {
select {
case <-quitChan:
// for each tx param set, spin up a goroutine to generate and send the tx at the specified delay and frequency
wg := new(sync.WaitGroup)
for _, txParams := range s.TxParams {
wg.Add(1)
go func(p TxParams) {
defer wg.Done()
// send the first tx after the delay
timer := time.NewTimer(p.Delay)
<-timer.C
if err := s.genAndSend(p); err != nil {
errChan <- fmt.Errorf("tx %s initial genAndSend error: %v", p.Name, err)
return
default:
}
if err := sendRawTransaction(s.TxGen.Current()); err != nil {
errChan <- err
// send any remaining ones at the provided frequency, also check for quit signal
ticker := time.NewTicker(p.Frequency)
for i := uint64(1); i < p.TotalNumber; i++ {
select {
case <-ticker.C:
if err := s.genAndSend(p); err != nil {
errChan <- fmt.Errorf("tx %s number %d genAndSend error: %v", p.Name, i, err)
return
}
case <-quitChan:
return
}
}
}
if s.TxGen.Error() != nil {
errChan <- s.TxGen.Error()
}
}(txParams)
}
go func() {
wg.Wait()
close(doneChan)
}()
return errChan
return doneChan, errChan
}
func sendRawTransaction(rpcClient *rpc.Client, txRlp []byte) error {
func (s *TxSender) genAndSend(p TxParams) error {
tx, err := s.TxGen.GenerateTx(p)
if err != nil {
return err
}
return sendRawTransaction(p.Client, tx, p.Name)
}
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

@ -38,9 +38,9 @@ func NewTxSpammer(params []TxParams) Service {
func (s *Spammer) Loop(wg *sync.WaitGroup, quitChan <-chan bool) {
forwardQuit := make(chan bool)
errChan := s.Sender.Send(forwardQuit)
doneChan, errChan := s.Sender.Send(forwardQuit)
wg.Add(1)
go func() {
wg.Add(1)
defer wg.Done()
for {
select {
@ -48,6 +48,8 @@ func (s *Spammer) Loop(wg *sync.WaitGroup, quitChan <-chan bool) {
logrus.Error(err)
case forwardQuit <- <-quitChan:
return
case <-doneChan:
return
}
}
}()

View File

@ -16,34 +16,71 @@
package tx_spammer
import "github.com/ethereum/go-ethereum/rpc"
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
)
// TxGenerator generates and signs txs
type TxGenerator struct {
TxParams []TxParams
currentTx []byte
currentClient *rpc.Client
err error
// 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(params []TxParams) *TxGenerator {
nonces := make(map[common.Address]uint64)
for _, p := range params {
nonces[p.Sender] = p.StartingNonce
}
return &TxGenerator{
TxParams: params,
nonces: nonces,
}
}
func (gen TxGenerator) Next() bool {
return false
}
func (gen TxGenerator) Current() (*rpc.Client, []byte) {
return gen.currentClient, gen.currentTx
}
func (gen TxGenerator) Error() error {
return gen.err
// GenerateTx generates tx from the provided params
func (tg TxGenerator) GenerateTx(params TxParams) ([]byte, error) {
tx := make([]byte, 0)
switch params.Type {
case Standard, OptimismL1ToL2, OptimismL2:
return tg.gen(params)
case EIP1559:
return tg.gen1559(params)
default:
return nil, fmt.Errorf("unsupported tx type: %s", params.Type.String())
}
return tx, nil
}
func (gen TxGenerator) gen(params TxParams) ([]byte, error) {
return nil, nil
nonce := gen.nonces[params.Sender]
tx := new(types.Transaction)
if params.To == nil {
tx = types.NewContractCreation(nonce, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin)
} else {
tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin, params.SigHashType)
}
signer, err := TxSigner(params.Type, params.ChainID)
if err != nil {
return nil, err
}
signedTx, err := types.SignTx(tx, signer, params.SenderKey)
if err != nil {
return nil, err
}
txRlp, err := rlp.EncodeToBytes(signedTx)
if err != nil {
return nil, err
}
gen.nonces[params.Sender]++
return txRlp, nil
}
func (gen TxGenerator) gen1559(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
return nil, fmt.Errorf("1559 support not yet available")
}

View File

@ -24,13 +24,28 @@ import (
type TxType int
const (
Unkown TxType = iota
Unsupported TxType = iota
Standard
OptimismL2
OptimismL1ToL2
EIP1559
)
func (tt TxType) String() string {
switch tt {
case Standard:
return "Standard"
case OptimismL2:
return "L2"
case OptimismL1ToL2:
return "L1toL2"
case EIP1559:
return "EIP1559"
default:
return "Unsupported"
}
}
// TxTypeFromString returns the tx enum type from provided string
func TxTypeFromString(str string) (TxType, error) {
switch strings.ToLower(str) {
@ -43,6 +58,6 @@ func TxTypeFromString(str string) (TxType, error) {
case "eip1559":
return EIP1559, nil
default:
return Unkown, fmt.Errorf("unsupported tx type: %s", str)
return Unsupported, fmt.Errorf("unsupported tx type: %s", str)
}
}

View File

@ -21,31 +21,14 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
func ChainConfig(chainID uint64) (*params.ChainConfig, error) {
switch chainID {
case 1:
return params.MainnetChainConfig, nil
case 3:
return params.TestnetChainConfig, nil // Ropsten
case 4:
return params.RinkebyChainConfig, nil
case 5, 420:
return params.GoerliChainConfig, nil
default:
return nil, fmt.Errorf("chain config for chainid %d not available", chainID)
}
}
// ChainConfig returns the appropriate ethereum chain config for the provided chain id
func TxSigner(chainID uint64) (types.Signer, error) {
switch chainID {
case 1, 3, 4, 5:
func TxSigner(kind TxType, chainID uint64) (types.Signer, error) {
switch kind {
case Standard, EIP1559:
return types.NewEIP155Signer(new(big.Int).SetUint64(chainID)), nil
case 420:
case OptimismL2, OptimismL1ToL2:
return types.NewOVMSigner(new(big.Int).SetUint64(chainID)), nil
default:
return nil, fmt.Errorf("chain config for chainid %d not available", chainID)