func txNumOfBlock(blockNum int64) int64 {
	reqData := fmt.Sprintf(`{"jsonrpc": "2.0", "method": "eth_getBlockTransactionCountByNumber", "id": 1, "params": ["0x%x"]}`, blockNum)
	recv := doPost(reqData)
	txNum := hexstr2dec(recv.Result)
	log.Debug(fmt.Sprintf("Block %-6d has %-6d TXs.", blockNum, txNum))
	return txNum
}
txNumOfBlock gets tx number of given block number.
package main
import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/DSiSc/craft/log"
	"io/ioutil"
	"math"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)
var (
	GlobalTestAccount  = "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"
	GlobalNonceCounter = struct {
		nonce uint64
		mu    sync.Mutex
	}{nonce: 0}
)
func initGlobalNonce() {
	reqTemplateStr := `{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["%s","%s"],"id":1}`
	pendingResp := doPost(fmt.Sprintf(reqTemplateStr, GlobalTestAccount, "pending"))
	pendingNonce := uint64(hexstr2dec(pendingResp.Result))
	latestResp := doPost(fmt.Sprintf(reqTemplateStr, GlobalTestAccount, "latest"))
	latestNonce := uint64(hexstr2dec(latestResp.Result))
	if pendingNonce > latestNonce {
		GlobalNonceCounter.nonce = pendingNonce
	} else {
		GlobalNonceCounter.nonce = latestNonce
	}
}
type RPCError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    string `json:"data,omitempty"`
}
type RPCResponse struct {
	JSONRPC string      `json:"jsonrpc"`
	ID      interface{} `json:"id"`
	Result  string      `json:"result,omitempty"`
	Error   *RPCError   `json:"error,omitempty"`
}
var (
	client    = &http.Client{}
	endpoints []string
	port      string
)
func main() {
	var durationInt, txsRate, blockStart, blockEnd uint
	var verbose, random, showHelp, statistic bool
	var timerWG sync.WaitGroup
	var filePath string
	flagSet := flag.NewFlagSet("jt-bench", flag.ExitOnError)
	flagSet.UintVar(&durationInt, "t", 60, "Exit after the specified amount of time in seconds(0 means forever).")
	flagSet.UintVar(&txsRate, "r", 200, "Txs per second to send in a connection.")
	flagSet.StringVar(&port, "p", "47768", "Port of api gateway (default to 47768).")
	flagSet.BoolVar(&verbose, "v", false, "Verbose output.")
	flagSet.BoolVar(&showHelp, "h", false, "Display help.")
	flagSet.BoolVar(&random, "R", false, "Random number of tx.")
	flagSet.BoolVar(&statistic, "s", false, "The switch of statistic and bench.")
	flagSet.UintVar(&blockStart, "b", 1, "The number of starting statistics.")
	flagSet.UintVar(&blockEnd, "e", 1, "The number of ending statistics.")
	flagSet.StringVar(&filePath, "f", "/var/log/justitia/justitia.log", "The absolute path of statistic file.")
	flagSet.Usage = func() {
		fmt.Println(`Justitia Repository benchmarking tool.
Usage:
    Send TXs:  go run bench.go [-t 60] [-r 200] [-p 47768] [-R] [-v] [host1,host2,host3,host4]
    Statistic: go run bench.go -s -b 100 -e 200 [-p 47768] [-v] [host]
Examples:
    There are two ways to use this tools.
    First is to bench, simulate send 200 txs/s in 60 second
    It will output starting, ending block number and tps, bps at the same time.
        go run bench.go -t 60 -r 200 -v 192.168.1.100,192.168.1.101,192.168.1.102,192.168.1.103
        Or keep sending random number of tx forever
        go run bench.go -t 0 -R
    Second is statistic output of the bench result, it will output log info
    when making block, and tps, bps at the same time.
        go run bench.go -s -b 120 -e 160
   `)
		fmt.Println("Flags:")
		flagSet.PrintDefaults()
	}
	flagSet.Parse(os.Args[1:])
	if showHelp {
		flagSet.Usage()
		return
	}
	if flagSet.NArg() == 0 {
		endpoints = []string{"127.0.0.1"}
	} else {
		endpoints = strings.Split(flagSet.Arg(0), ",")
	}
	log.RemoveAppender("stdout")
	log.AddAppender("console", os.Stdout, log.DebugLevel, log.TextFmt, false, false)
	if verbose {
		log.SetGlobalLogLevel(log.DebugLevel)
	} else {
		log.SetGlobalLogLevel(log.InfoLevel)
	}
	initGlobalNonce()
	if statistic {
		statisticBetweenBlock(int64(blockStart), int64(blockEnd))
		return
	}
	timerWG.Add(2)
	var ticker, totalTimer *time.Ticker
	var progress []byte
	stopped := false
	showProgress := true
	if durationInt == 0 {
		durationInt = math.MaxInt32
		showProgress = false
	} else if durationInt < 10 {
		log.Error("Duration too short, recommend a bigger integer.")
		return
	} else if durationInt > 3600 {
		showProgress = false
	}
	if showProgress {
		progress = make([]byte, durationInt)
		for i := range progress {
			progress[i] = ' '
		}
	}
	go func() {
		log.Info(fmt.Sprintf("Sending TX at rate of %d tx/sec for %d sec ...", txsRate, durationInt))
		ticker = time.NewTicker(1 * time.Second)
		sent := 0
		for i := 0; i < int(durationInt); i++ {
			go func() {
				if random {
					count := rand.Intn(int(txsRate) * 2)
					log.Debug(fmt.Sprintf("Sending %d TXs ...", count))
					sendTXs(count)
				} else {
					temp := i
					if showProgress {
						progress[temp] = '-'
						fmt.Fprintf(os.Stdout, "Sending... |%s| %2d%% started, %2d%% finished.\r", string(progress[:]), (i+1)*100/int(durationInt), (sent+1)*100/int(durationInt))
					}
					sendTXs(int(txsRate))
					sent = temp
					if showProgress && !stopped {
						progress[temp] = '='
						fmt.Fprintf(os.Stdout, "Sending... |%s| %2d%% started, %2d%% finished.\r", string(progress[:]), (i+1)*100/int(durationInt), (sent+1)*100/int(durationInt))
					}
				}
			}()
			<-ticker.C
		}
		fmt.Println()
		timerWG.Done()
	}()
	var beginningHeight, endingHeight int64
	go func() {
		totalTimer = time.NewTicker(time.Second * time.Duration(durationInt))
		beginningHeight = latestBlockNumber() + 1
		<-totalTimer.C
		endingHeight = latestBlockNumber()
		stopped = true
		timerWG.Done()
	}()
	timerWG.Wait()
	statisticBetweenBlock(beginningHeight, endingHeight)
}
func latestBlockNumber() int64 {
	reqData := `{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":8}`
	recv := doPost(reqData)
	return hexstr2dec(recv.Result)
}
func timestampOfBlockByNumber(blockNum int64) int64 {
	reqData := fmt.Sprintf(`{"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "id": 1, "params": ["0x%x", true]}`, blockNum)
	resp, err := http.Post(randomEndpoint(), "application/json", strings.NewReader(reqData))
	if err != nil {
		panic(nil)
	}
	defer resp.Body.Close()
	blob, _ := ioutil.ReadAll(resp.Body)
	bodystr := string(blob)
	index := strings.Index(bodystr, "timestamp")
	return hexstr2dec(bodystr[index+13 : index+23])
}
func randomEndpoint() string {
	return "http://" + endpoints[rand.Intn(len(endpoints))] + ":" + port
}