github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/cmd/ctool/core/tx_stability.go (about)

     1  package core
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"encoding/gob"
     6  	"encoding/json"
     7  	"fmt"
     8  	"github.com/PlatONnetwork/PlatON-Go/core/types"
     9  	"github.com/PlatONnetwork/PlatON-Go/crypto"
    10  	"github.com/PlatONnetwork/PlatON-Go/crypto/secp256k1"
    11  	"gopkg.in/urfave/cli.v1"
    12  	"io/ioutil"
    13  	"math/rand"
    14  	"os"
    15  	"path/filepath"
    16  	"sync"
    17  	"time"
    18  )
    19  
    20  var (
    21  	accountPool         = make(map[string]*PriAccount)
    22  	StressTransferValue = 1000
    23  	txCh                = make(chan *types.Transaction, 10)
    24  	wg                  = &sync.WaitGroup{}
    25  
    26  	DefaultPrivateKeyFilePath  = "./test/privateKeys.txt"
    27  	DefaultAccountAddrFilePath = "./test/addr.json"
    28  
    29  	StabilityCmd = cli.Command{
    30  		Name:    "stability",
    31  		Aliases: []string{"stab"},
    32  		Usage:   "start stability test ",
    33  		Action:  stabilityTest,
    34  		Flags:   stabilityCmdFlags,
    35  	}
    36  	StabPrepareCmd = cli.Command{
    37  		Name:    "prepare",
    38  		Aliases: []string{"pre"},
    39  		Usage:   "prepare some accounts are used for stability test ",
    40  		Action:  prepareAccount,
    41  		Flags:   stabPrepareCmdFlags,
    42  	}
    43  )
    44  
    45  func prepareAccount(c *cli.Context) {
    46  	pkFile := c.String(PKFilePathFlag.Name)
    47  	size := c.Int(AccountSizeFlag.Name)
    48  	value := c.String(TransferValueFlag.Name)
    49  
    50  	parseConfigJson(c.String(ConfigPathFlag.Name))
    51  
    52  	err := PrepareAccount(size, pkFile, value)
    53  	if err != nil {
    54  		panic(fmt.Errorf("send raw transaction error,%s", err.Error()))
    55  	}
    56  }
    57  
    58  func stabilityTest(c *cli.Context) {
    59  	pkFile := c.String(PKFilePathFlag.Name)
    60  	times := c.Int(StabExecTimesFlag.Name)
    61  	interval := c.Int(SendTxIntervalFlag.Name)
    62  
    63  	parseConfigJson(c.String(ConfigPathFlag.Name))
    64  
    65  	err := StabilityTest(pkFile, times, interval)
    66  	if err != nil {
    67  		panic(fmt.Errorf("stress test error,%s", err.Error()))
    68  	}
    69  }
    70  
    71  type PriAccount struct {
    72  	Priv  *ecdsa.PrivateKey
    73  	Nonce uint64
    74  }
    75  
    76  func generateAccount(size int, pkFile string) {
    77  	addrs := make([]string, size)
    78  	for i := 0; i < size; i++ {
    79  		privateKey, _ := crypto.GenerateKey()
    80  		address := crypto.PubkeyToAddress(privateKey.PublicKey).Hex()
    81  		accountPool[address] = &PriAccount{privateKey, 0}
    82  		addrs[i] = address
    83  	}
    84  	savePrivateKeyPool(pkFile)
    85  	saveAddrs(addrs, pkFile)
    86  }
    87  
    88  func savePrivateKeyPool(pkFile string) {
    89  	if pkFile == "" {
    90  		pkFile = DefaultPrivateKeyFilePath
    91  	}
    92  	gob.Register(&secp256k1.BitCurve{})
    93  	file, err := os.Create(pkFile)
    94  	if err != nil {
    95  		panic(fmt.Errorf("save private key err,%s,%s", pkFile, err.Error()))
    96  	}
    97  	os.Truncate(pkFile, 0)
    98  	enc := gob.NewEncoder(file)
    99  	err = enc.Encode(accountPool)
   100  	if err != nil {
   101  		panic(err.Error())
   102  	}
   103  }
   104  
   105  func saveAddrs(addrs []string, pkFile string) {
   106  	addrsPath := DefaultAccountAddrFilePath
   107  	if pkFile != "" {
   108  		addrsPath = filepath.Dir(pkFile) + "/addr.json"
   109  	}
   110  	os.Truncate(DefaultAccountAddrFilePath, 0)
   111  	byts, err := json.MarshalIndent(addrs, "", "\t")
   112  	_, err = os.Create(addrsPath)
   113  	if err != nil {
   114  		panic(fmt.Errorf("create addr.json error%s \n", err.Error()))
   115  	}
   116  	err = ioutil.WriteFile(addrsPath, byts, 0644)
   117  	if err != nil {
   118  		panic(fmt.Errorf("write to addr.json error%s \n", err.Error()))
   119  	}
   120  }
   121  
   122  func PrepareAccount(size int, pkFile, value string) error {
   123  
   124  	if len(accountPool) == 0 {
   125  		generateAccount(size, pkFile)
   126  	}
   127  
   128  	for addr := range accountPool {
   129  		hash, err := SendTransaction("", addr, value)
   130  		if err != nil {
   131  			return fmt.Errorf("prepare error,send from coinbase error,%s", err.Error())
   132  		}
   133  		fmt.Printf("transfer hash: %s \n", hash)
   134  	}
   135  	fmt.Printf("prepare %d account finish...", size)
   136  	return nil
   137  }
   138  
   139  func StabilityTest(pkFile string, times, interval int) error {
   140  	if len(accountPool) == 0 {
   141  		parsePkFile(pkFile)
   142  	}
   143  
   144  	addrs := getAllAddress(pkFile)
   145  
   146  	for i := 0; i < times; i++ {
   147  		if interval != 0 {
   148  			time.Sleep(time.Duration(interval) * time.Millisecond)
   149  		}
   150  		from, to := getRandomAddr(addrs)
   151  		if from == "" || to == "" {
   152  			continue
   153  		}
   154  
   155  		acc, ok := accountPool[from]
   156  		if !ok {
   157  			return fmt.Errorf("private key not found,addr:%s", from)
   158  		}
   159  
   160  		wg.Add(1)
   161  		go getTransactionGo(acc, from, to)
   162  
   163  		wg.Add(1)
   164  		go sendTransactionGo()
   165  	}
   166  
   167  	wg.Wait()
   168  
   169  	savePrivateKeyPool(pkFile)
   170  
   171  	return nil
   172  }
   173  
   174  func sendTransactionGo() {
   175  	defer func() {
   176  		if err := recover(); err != nil {
   177  			fmt.Printf("send raw transaction failed:%s \n", err)
   178  			//debug.PrintStack()
   179  			wg.Done()
   180  		}
   181  	}()
   182  
   183  	hash, _ := sendRawTransaction(<-txCh)
   184  	fmt.Printf("tx hash:%s\n", hash)
   185  	wg.Done()
   186  }
   187  
   188  func getTransactionGo(acc *PriAccount, from, to string) {
   189  	defer func() {
   190  		if err := recover(); err != nil {
   191  			fmt.Println("get transaction error:", err)
   192  			//debug.PrintStack()
   193  			wg.Done()
   194  		}
   195  	}()
   196  
   197  	newTx := getSignedTransaction(from, to, int64(StressTransferValue), acc.Priv, acc.Nonce)
   198  	lock := &sync.Mutex{}
   199  	lock.Lock()
   200  
   201  	acc.Nonce++
   202  	fmt.Printf("add =%s,Nonce:%d \n", from, acc.Nonce)
   203  
   204  	lock.Unlock()
   205  
   206  	txCh <- newTx
   207  	wg.Done()
   208  }
   209  
   210  func parsePkFile(pkFile string) {
   211  	if pkFile == "" {
   212  		dir, _ := os.Getwd()
   213  		pkFile = dir + DefaultPrivateKeyFilePath
   214  	}
   215  	gob.Register(&secp256k1.BitCurve{})
   216  	file, err := os.Open(pkFile)
   217  	dec := gob.NewDecoder(file)
   218  	err2 := dec.Decode(&accountPool)
   219  	if err2 != nil {
   220  		panic(err.Error())
   221  	}
   222  }
   223  
   224  func getAllAddress(pkFile string) []string {
   225  	addrsPath := ""
   226  	if pkFile != "" {
   227  		addrsPath = filepath.Dir(pkFile) + "/addr.json"
   228  	} else {
   229  		dir, _ := os.Getwd()
   230  		addrsPath = dir + DefaultAccountAddrFilePath
   231  	}
   232  
   233  	bytes, err := ioutil.ReadFile(addrsPath)
   234  	if err != nil {
   235  		panic(fmt.Errorf("get all address array error,%s \n", err.Error()))
   236  	}
   237  	var addrs []string
   238  	err = json.Unmarshal(bytes, &addrs)
   239  	if err != nil {
   240  		panic(fmt.Errorf("parse address to array error,%s \n", err.Error()))
   241  	}
   242  
   243  	return addrs
   244  }
   245  
   246  func getRandomAddr(addrs []string) (string, string) {
   247  	if len(addrs) == 0 {
   248  		return "", ""
   249  	}
   250  	fromIndex := rand.Intn(len(addrs))
   251  	toIndex := rand.Intn(len(addrs))
   252  	for toIndex == fromIndex {
   253  		toIndex = rand.Intn(len(addrs))
   254  	}
   255  	return addrs[fromIndex], addrs[toIndex]
   256  }