finalize support for l1 and l2
This commit is contained in:
parent
e441254891
commit
54de12e12e
@ -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()
|
||||
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
25
pkg/util.go
25
pkg/util.go
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user