diff --git a/environment/example.toml b/environment/example.toml index d72d394..d1b4384 100644 --- a/environment/example.toml +++ b/environment/example.toml @@ -1,5 +1,6 @@ [eth] - txs = ["L2ContractDeployment", "L2ContractPutCall", "L2ContractGetCall"] + txs = ["L2ContractDeployment", "L2ContractPutCall", "L2ContractGetCall"] # $ETH_TX_LIST + addrLogPath = "" # ETH_ADDR_LOG [L2ContractDeployment] type = "L2" @@ -12,6 +13,7 @@ data = "" senderKeyPath = "" writeSenderPath = "" + writeDeploymentAddrPath = "" frequency = 1 totalNumber = 1 delay = 0 diff --git a/pkg/config.go b/pkg/config.go index f31ef72..51e16f7 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -20,6 +20,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "os" "strings" "time" @@ -31,6 +32,7 @@ import ( "github.com/spf13/viper" ) +// TxParams holds the parameters for a given transaction type TxParams struct { // Name of this tx in the .toml file Name string @@ -63,8 +65,9 @@ type TxParams struct { FeeCap *big.Int // Sender key, if left the senderKeyPath is empty we generate a new key - SenderKey *ecdsa.PrivateKey - StartingNonce uint64 + SenderKey *ecdsa.PrivateKey + StartingNonce uint64 + ContractAddrWritePath string // Sending params // How often we send a tx of this type @@ -76,7 +79,11 @@ type TxParams struct { } const ( - ETH_TX_LIST = "ETH_TX_LIST" + ETH_TX_LIST = "ETH_TX_LIST" + ETH_ADDR_LOG = "ETH_ADDR_LOG" + + defaultGenKeyWritePathPrefix = "./accounts/keys/" + defaultAddrLogPath = "./accounts/addresses/accounts" typeSuffix = ".type" httpPathSuffix = ".http" @@ -98,12 +105,15 @@ const ( startingNonceSuffix = ".startingNonce" queueOriginSuffix = ".queueOrigin" chainIDSuffix = ".chainID" + contractWriteSuffix = ".writeDeploymentAddrPath" ) // NewConfig returns a new tx spammer config func NewTxParams() ([]TxParams, error) { viper.BindEnv("eth.txs", ETH_TX_LIST) + viper.BindEnv("eth.addrLogPath", ETH_ADDR_LOG) + addrLogPath := viper.GetString("eth.addrLogPath") txs := viper.GetStringSlice("eth.txs") txParams := make([]TxParams, len(txs)) for i, txName := range txs { @@ -129,7 +139,6 @@ func NewTxParams() ([]TxParams, error) { if err != nil { return nil, err } - chainID := viper.GetUint64(txName + chainIDSuffix) // Get basic fields toStr := viper.GetString(txName + toSuffix) @@ -174,11 +183,16 @@ func NewTxParams() ([]TxParams, error) { return nil, fmt.Errorf("unable to generate ecdsa key for tx %s", txName) } writePath := viper.GetString(txName + writeSenderPathSuffix) - if writePath != "" { - if err := crypto.SaveECDSA(writePath, key); err != nil { - return nil, err - } + if writePath == "" { + writePath = defaultGenKeyWritePathPrefix + txName } + if err := crypto.SaveECDSA(writePath, key); err != nil { + return nil, err + } + } + sender := crypto.PubkeyToAddress(key.PublicKey) + if err := writeSenderAddr(addrLogPath, sender); err != nil { + return nil, err } // Attempt to load Optimism fields @@ -214,33 +228,44 @@ func NewTxParams() ([]TxParams, error) { } } - // 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: &l1rtid, - SigHashType: (types.SignatureHashType)(uint8(sigHashType)), - Frequency: frequency, - TotalNumber: totalNumber, - Delay: delay, - StartingNonce: startingNonce, - QueueOrigin: (types.QueueOrigin)(queueOrigin), - ChainID: chainID, + Name: txName, + Client: rpcClient, + Type: txType, + ChainID: viper.GetUint64(txName + chainIDSuffix), + To: toAddr, + GasLimit: gasLimit, + GasPrice: gasPrice, + Amount: amount, + Data: data, + Sender: sender, + L1SenderAddr: l1Sender, + L1RollupTxId: &l1rtid, + SigHashType: (types.SignatureHashType)(uint8(sigHashType)), + QueueOrigin: (types.QueueOrigin)(queueOrigin), + GasPremium: gasPremium, + FeeCap: feeCap, + SenderKey: key, + StartingNonce: viper.GetUint64(txName + startingNonceSuffix), + ContractAddrWritePath: viper.GetString(txName + contractWriteSuffix), + Frequency: viper.GetDuration(txName + frequencySuffix), + TotalNumber: viper.GetUint64(txName + totalNumberSuffix), + Delay: viper.GetDuration(txName + delaySuffix), } } return txParams, nil } + +func writeSenderAddr(filePath string, senderAddr common.Address) error { + if filePath == "" { + filePath = defaultAddrLogPath + } + f, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return err + } + if _, err := f.WriteString(senderAddr.Hex() + "\n"); err != nil { + return err + } + return f.Close() +} diff --git a/pkg/tx_generator.go b/pkg/tx_generator.go index 4c7f37f..2fc6949 100644 --- a/pkg/tx_generator.go +++ b/pkg/tx_generator.go @@ -18,24 +18,31 @@ package tx_spammer import ( "fmt" + "os" + "sync/atomic" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/rlp" ) +const ( + defaultDeploymentAddrLogPathPrefix = "./accounts/addresses/" +) + // TxGenerator generates and signs txs type TxGenerator struct { // 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 + nonces map[common.Address]*uint64 } // NewTxGenerator creates a new tx generator func NewTxGenerator(params []TxParams) *TxGenerator { - nonces := make(map[common.Address]uint64) + nonces := make(map[common.Address]*uint64) for _, p := range params { - nonces[p.Sender] = p.StartingNonce + nonces[p.Sender] = &p.StartingNonce } return &TxGenerator{ nonces: nonces, @@ -57,10 +64,14 @@ func (tg TxGenerator) GenerateTx(params TxParams) ([]byte, error) { } func (gen TxGenerator) gen(params TxParams) ([]byte, error) { - nonce := gen.nonces[params.Sender] + nonce := atomic.AddUint64(gen.nonces[params.Sender], 1) 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) + if err := writeContractAddr(params.ContractAddrWritePath, params.Sender, nonce); err != nil { + return nil, err + } + } else { tx = types.NewTransaction(nonce, *params.To, params.Amount, params.GasLimit, params.GasPrice, params.Data, params.L1SenderAddr, params.L1RollupTxId, params.QueueOrigin, params.SigHashType) } @@ -76,7 +87,6 @@ func (gen TxGenerator) gen(params TxParams) ([]byte, error) { if err != nil { return nil, err } - gen.nonces[params.Sender]++ return txRlp, nil } @@ -84,3 +94,18 @@ 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") } + +func writeContractAddr(filePath string, senderAddr common.Address, nonce uint64) 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 err + } + if _, err := f.WriteString(contractAddr.Hex() + "\n"); err != nil { + return err + } + return f.Close() +}