cmd: add tool to batch send txs
This commit is contained in:
parent
45dc130cbe
commit
1822929738
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
priKey *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
func GenerateAccount() (*Account, error) {
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Account{
|
||||
priKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// private has no '0x' prefix
|
||||
func AccountFromECDSA(private string) (*Account, error) {
|
||||
key, err := crypto.HexToECDSA(private)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Account{
|
||||
priKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *Account) PrivateKey() *ecdsa.PrivateKey {
|
||||
// hex.EncodeToString(crypto.FromECDSA(a.priKey))
|
||||
return a.priKey
|
||||
}
|
||||
|
||||
func (a *Account) PublicKey() *ecdsa.PublicKey {
|
||||
// hex.EncodeToString(crypto.FromECDSAPub(&a.priKey.PublicKey))
|
||||
return &a.priKey.PublicKey
|
||||
}
|
||||
|
||||
func (a *Account) Address() common.Address {
|
||||
return crypto.PubkeyToAddress(a.priKey.PublicKey)
|
||||
}
|
||||
|
||||
func (a *Account) String() string {
|
||||
return hex.EncodeToString(crypto.FromECDSA(a.priKey))
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import json
|
||||
from subprocess import call
|
||||
from subprocess import Popen
|
||||
import shlex
|
||||
import time
|
||||
import random
|
||||
|
||||
ADMIN='c921c91aa4c5f9886bd1e084a848e7e564644d5cc2f265beebb0d51bd251b7e7'
|
||||
ACCS=100
|
||||
TX_NUM=100
|
||||
PERIOD=600
|
||||
NODES=['http://127.0.0.1:8811', 'http://127.0.0.1:8822', 'http://127.0.0.1:8833', 'http://127.0.0.1:8844']
|
||||
|
||||
def file_reader(path):
|
||||
"""
|
||||
Read a file
|
||||
"""
|
||||
with open(path) as data_file:
|
||||
data = json.load(data_file)
|
||||
return data
|
||||
|
||||
def main():
|
||||
cmd = './sendtx prepare --addr {} --admin {} --number {}'.format(NODES[0], ADMIN, ACCS)
|
||||
call(cmd, shell=True)
|
||||
accs = file_reader('accs')
|
||||
# wait for money coming
|
||||
time.sleep(5)
|
||||
|
||||
processes = []
|
||||
n = 0
|
||||
for i in range(0,len(accs)):
|
||||
cmd = './sendtx batch --addr {} --admin {} --count {} --period {}'.format(NODES[n%len(NODES)], accs[i], TX_NUM, PERIOD)
|
||||
processes.append(Popen(shlex.split(cmd)))
|
||||
n = n+1
|
||||
time.sleep(random.randint(0,3)*0.2)
|
||||
|
||||
for p in processes:
|
||||
p.wait()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,239 @@
|
|||
// Copyright 2017 AMIS Technologies
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
GAS_PRICE = 20000000000
|
||||
GAS_LIMIT = 22000 // the gas of ether tx should be 21000
|
||||
// admin
|
||||
private = "c921c91aa4c5f9886bd1e084a848e7e564644d5cc2f265beebb0d51bd251b7e7"
|
||||
public = "1f0a3201bfa623be518eb7fb3742385f6f42e2e09a83e417f51f937c19c5388fc99e9472f5ac7e6c8baebcdf96719312d22fd8783d071c2e71bf713993ab2284"
|
||||
address = "507a198251ed29f421bd6cf667596f750d9b14ec"
|
||||
)
|
||||
|
||||
var (
|
||||
prepareCommand = cli.Command{
|
||||
Action: prepare,
|
||||
Name: "prepare",
|
||||
Usage: "Prepare accounts to nodes",
|
||||
ArgsUsage: "<prepare accounts>",
|
||||
Flags: []cli.Flag{
|
||||
NodeAddrFlag,
|
||||
AdminFlag,
|
||||
AccountsFlag,
|
||||
},
|
||||
Description: `To prepare accounts`,
|
||||
}
|
||||
|
||||
batchCommand = cli.Command{
|
||||
Action: batch,
|
||||
Name: "batch",
|
||||
Usage: "Batch send txs",
|
||||
ArgsUsage: "<batch send>",
|
||||
Flags: []cli.Flag{
|
||||
NodeAddrFlag,
|
||||
AdminFlag,
|
||||
TxsCountFlag,
|
||||
SendPeriodFlag,
|
||||
},
|
||||
Description: `To batch send txs`,
|
||||
}
|
||||
|
||||
// target
|
||||
toAddr = common.HexToAddress("0xcfe3b52266134683d6e9ee765d735431e35c7a54")
|
||||
)
|
||||
|
||||
func prepare(ctx *cli.Context) error {
|
||||
addr := ctx.String(NodeAddrFlag.Name)
|
||||
if addr == "" {
|
||||
return cli.NewExitError("Must provide node address", 0)
|
||||
}
|
||||
|
||||
key := ctx.String(AdminFlag.Name)
|
||||
if key == "" {
|
||||
return cli.NewExitError("Must provide admin's private key", 0)
|
||||
}
|
||||
|
||||
num := ctx.Int(AccountsFlag.Name)
|
||||
|
||||
conn, err := ethclient.Dial(addr)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to connect to ethclient:: %v", err), 1)
|
||||
}
|
||||
|
||||
admin, err := AccountFromECDSA(key)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to read private key: %v", err), 1)
|
||||
}
|
||||
|
||||
nonce, err := conn.NonceAt(context.Background(), admin.Address(), nil)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to get nonce:: %v", err), 1)
|
||||
}
|
||||
fmt.Println("nonce:", nonce)
|
||||
|
||||
// generate accounts
|
||||
prepareAccs := []*Account{}
|
||||
for i := 0; i < num; i++ {
|
||||
a, _ := GenerateAccount()
|
||||
prepareAccs = append(prepareAccs, a)
|
||||
fmt.Println("account", a)
|
||||
}
|
||||
|
||||
amount := big.NewInt(0).Exp(big.NewInt(int64(10)), big.NewInt(int64(20)), nil)
|
||||
// give money
|
||||
for _, pa := range prepareAccs {
|
||||
tx, err := sendEther(admin.PrivateKey(), pa.Address(), amount, nonce)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to gen tx, err:%v\n", err)
|
||||
continue
|
||||
}
|
||||
err = sendTx(conn, tx)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to send tx, err:%v\n", err)
|
||||
continue
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
writeAccounts(prepareAccs)
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendTx(conn *ethclient.Client, tx *types.Transaction) error {
|
||||
err := conn.SendTransaction(context.Background(), tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("tx", tx.Hash().String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendEther(prv *ecdsa.PrivateKey, to common.Address, amount *big.Int, nonce uint64) (*types.Transaction, error) {
|
||||
signer := types.FrontierSigner{}
|
||||
tx := types.NewTransaction(nonce, to, amount, big.NewInt(GAS_LIMIT), big.NewInt(GAS_PRICE), []byte{})
|
||||
// return tx, nil
|
||||
signedTx, err := types.SignTx(tx, signer, prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signedTx, nil
|
||||
}
|
||||
|
||||
func batch(ctx *cli.Context) error {
|
||||
addr := ctx.String(NodeAddrFlag.Name)
|
||||
if addr == "" {
|
||||
return cli.NewExitError("Must provide node address", 0)
|
||||
}
|
||||
|
||||
key := ctx.String(AdminFlag.Name)
|
||||
if key == "" {
|
||||
return cli.NewExitError("Must provide admin's private key", 0)
|
||||
}
|
||||
|
||||
conn, err := ethclient.Dial(addr)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to connect to ethclient:: %v", err), 1)
|
||||
}
|
||||
|
||||
admin, err := AccountFromECDSA(key)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to read private key: %v", err), 1)
|
||||
}
|
||||
|
||||
count := ctx.Int(TxsCountFlag.Name)
|
||||
secs := ctx.Int(SendPeriodFlag.Name)
|
||||
|
||||
nonce, err := conn.NonceAt(context.Background(), admin.Address(), nil)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to get nonce:: %v", err), 1)
|
||||
}
|
||||
fmt.Println("nonce:", nonce)
|
||||
|
||||
balance, err := conn.BalanceAt(context.Background(), admin.Address(), nil)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Sprintf("Failed to get nonce:: %v", err), 1)
|
||||
}
|
||||
fmt.Println("balance:", balance)
|
||||
|
||||
// give money
|
||||
batchSend(conn, admin.PrivateKey(), toAddr, nonce, count, time.Duration(secs)*time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func batchSend(conn *ethclient.Client, prv *ecdsa.PrivateKey, to common.Address, nonce uint64, count int, period time.Duration) {
|
||||
round := 0
|
||||
timer := time.NewTimer(period)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return
|
||||
default:
|
||||
fmt.Println("round", round)
|
||||
for i := 0; i < count; i++ {
|
||||
tx, err := sendEther(prv, to, common.Big1, nonce)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to gen tx, err:%v\n", err)
|
||||
continue
|
||||
}
|
||||
err = sendTx(conn, tx)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to send tx, err:%v\n", err)
|
||||
continue
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
round++
|
||||
<-time.After(time.Duration(20+rand.Int()%10) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeAccounts(accs []*Account) {
|
||||
keys := []string{}
|
||||
for _, acc := range accs {
|
||||
keys = append(keys, acc.String())
|
||||
}
|
||||
|
||||
b, err := json.Marshal(keys)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to json Marshal accounts, err:%v", err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile("accs", b, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to write file, err:%v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2017 AMIS Technologies
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
NodeAddrFlag = cli.StringFlag{
|
||||
Name: "addr",
|
||||
Usage: "Address of geth node",
|
||||
}
|
||||
AdminFlag = cli.StringFlag{
|
||||
Name: "admin",
|
||||
Usage: "Hex string of admin's private key",
|
||||
}
|
||||
AccountsFlag = cli.IntFlag{
|
||||
Name: "number",
|
||||
Usage: "Number of prepare accounts",
|
||||
Value: 4,
|
||||
}
|
||||
TxsCountFlag = cli.IntFlag{
|
||||
Name: "count",
|
||||
Usage: "Number of txs",
|
||||
Value: 1000,
|
||||
}
|
||||
SendPeriodFlag = cli.IntFlag{
|
||||
Name: "period",
|
||||
Usage: "Period in seconds to send txs",
|
||||
Value: 60,
|
||||
}
|
||||
)
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2017 AMIS Technologies
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/getamis/istanbul-tools/cmd/utils"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := utils.NewApp()
|
||||
app.Usage = "the istanbul-tools command line interface"
|
||||
|
||||
app.HideVersion = true // we have a command to print the version
|
||||
app.Copyright = "Copyright 2017 The Amis Authors"
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
prepareCommand,
|
||||
batchCommand,
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue