package main import ( "crypto/rand" "encoding/binary" "encoding/hex" "flag" "fmt" "os" "strconv" "strings" "sync" "time" "github.com/tendermint/go-rpc/client" rpctypes "github.com/tendermint/go-rpc/types" ) func main() { flag.Parse() args := flag.Args() if len(args) < 2 { fmt.Println("transact.go expects at least two arguments (ntxs, hosts)") os.Exit(1) } nTxS, hostS := args[0], args[1] nTxs, err := strconv.Atoi(nTxS) if err != nil { fmt.Println("ntxs must be an integer:", err) os.Exit(1) } hosts := strings.Split(hostS, ",") errCh := make(chan error, 1000) wg := new(sync.WaitGroup) wg.Add(len(hosts)) start := time.Now() fmt.Printf("Sending %d txs on every host %v\n", nTxs, hosts) for i, host := range hosts { go broadcastTxsToHost(wg, errCh, i, host, nTxs, 0) } wg.Wait() fmt.Println("Done broadcasting txs. Took", time.Since(start)) } func broadcastTxsToHost(wg *sync.WaitGroup, errCh chan error, valI int, valHost string, nTxs int, txCount int) { reconnectSleepSeconds := time.Second * 1 // thisStart := time.Now() // cli := rpcclient.NewClientURI(valHost + ":26657") fmt.Println("Connecting to host to broadcast txs", valI, valHost) cli := rpcclient.NewWSClient(valHost, "/websocket") if _, err := cli.Start(); err != nil { if nTxs == 0 { time.Sleep(reconnectSleepSeconds) broadcastTxsToHost(wg, errCh, valI, valHost, nTxs, txCount) return } fmt.Printf("Error starting websocket connection to val%d (%s): %v\n", valI, valHost, err) os.Exit(1) } reconnect := make(chan struct{}) go func(count int) { LOOP: for { ticker := time.NewTicker(reconnectSleepSeconds) select { case <-cli.ResultsCh: count += 1 // nTxs == 0 means just loop forever if nTxs > 0 && count == nTxs { break LOOP } case err := <-cli.ErrorsCh: fmt.Println("err: val", valI, valHost, err) case <-cli.Quit: broadcastTxsToHost(wg, errCh, valI, valHost, nTxs, count) return case <-reconnect: broadcastTxsToHost(wg, errCh, valI, valHost, nTxs, count) return case <-ticker.C: if nTxs == 0 { cli.Stop() broadcastTxsToHost(wg, errCh, valI, valHost, nTxs, count) return } } } fmt.Printf("Received all responses from node %d (%s)\n", valI, valHost) wg.Done() }(txCount) var i = 0 for { /* if i%(nTxs/4) == 0 { fmt.Printf("Have sent %d txs to node %d. Total time so far: %v\n", i, valI, time.Since(thisStart)) }*/ if !cli.IsRunning() { return } tx := generateTx(i, valI) if err := cli.WriteJSON(rpctypes.RPCRequest{ JSONRPC: "2.0", ID: "", Method: "broadcast_tx_async", Params: []interface{}{hex.EncodeToString(tx)}, }); err != nil { fmt.Printf("Error sending tx %d to validator %d: %v. Attempt reconnect\n", i, valI, err) reconnect <- struct{}{} return } i += 1 if nTxs > 0 && i >= nTxs { break } else if nTxs == 0 { time.Sleep(time.Millisecond * 1) } } fmt.Printf("Done sending %d txs to node s%d (%s)\n", nTxs, valI, valHost) } func generateTx(i, valI int) []byte { // a tx encodes the validator index, the tx number, and some random junk // TODO: read random bytes into more of the tx tx := make([]byte, 250) binary.PutUvarint(tx[:32], uint64(valI)) binary.PutUvarint(tx[32:64], uint64(i)) if _, err := rand.Read(tx[234:]); err != nil { fmt.Println("err reading from crypto/rand", err) os.Exit(1) } return tx }