package main import ( "encoding/hex" "flag" "fmt" acm "github.com/tendermint/tendermint/account" . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" cclient "github.com/tendermint/tendermint/rpc/core_client" "github.com/tendermint/tendermint/types" ) const Version = "0.0.1" const sleepSeconds = 1 // Every second // Parse command-line options func parseFlags() (privKeyHex string, numAccounts int, remote string) { var version bool flag.StringVar(&privKeyHex, "priv-key", "", "Private key bytes in HEX") flag.IntVar(&numAccounts, "num-accounts", 1000, "Deterministically generates this many sub-accounts") flag.StringVar(&remote, "remote", "localhost:46657", "Remote RPC host:port") flag.BoolVar(&version, "version", false, "Version") flag.Parse() if version { Exit(Fmt("sim_txs version %v", Version)) } return } func main() { // Read options privKeyHex, numAccounts, remote := parseFlags() // Print args. // fmt.Println(privKeyHex, numAccounts, remote) privKeyBytes, err := hex.DecodeString(privKeyHex) if err != nil { panic(err) } root := acm.GenPrivAccountFromPrivKeyBytes(privKeyBytes) fmt.Println("Computed address: %X", root.Address) // Get root account. rootAccount, err := getAccount(remote, root.Address) if err != nil { fmt.Println(Fmt("Root account %X does not exist: %v", root.Address, err)) return } else { fmt.Println("Root account", rootAccount) } // Load all accounts accounts := make([]*acm.Account, numAccounts+1) accounts[0] = rootAccount privAccounts := make([]*acm.PrivAccount, numAccounts+1) privAccounts[0] = root for i := 1; i < numAccounts+1; i++ { privAccounts[i] = root.Generate(i) account, err := getAccount(remote, privAccounts[i].Address) if err != nil { fmt.Println("Error", err) return } else { accounts[i] = account } } // Test: send from root to accounts[1] sendTx := makeRandomTransaction(10, rootAccount.Sequence+1, root, 2, accounts) fmt.Println(sendTx) wsClient := cclient.NewWSClient("ws://" + remote + "/websocket") _, err = wsClient.Start() if err != nil { Exit(Fmt("Failed to establish websocket connection: %v", err)) } wsClient.Subscribe(types.EventStringAccInput(sendTx.Inputs[0].Address)) go func() { for { foo := <-wsClient.EventsCh fmt.Println("!!", foo) } }() err = broadcastSendTx(remote, sendTx) if err != nil { Exit(Fmt("Failed to broadcast SendTx: %v", err)) return } // Trap signal TrapSignal(func() { fmt.Println("sim_txs shutting down") }) } func getAccount(remote string, address []byte) (*acm.Account, error) { // var account *acm.Account = new(acm.Account) account, err := rpcclient.Call("http://"+remote, "get_account", []interface{}{address}, (*acm.Account)(nil)) if err != nil { return nil, err } if account.(*acm.Account) == nil { return &acm.Account{Address: address}, nil } else { return account.(*acm.Account), nil } } func broadcastSendTx(remote string, sendTx *types.SendTx) error { receipt, err := rpcclient.Call("http://"+remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil)) if err != nil { return err } fmt.Println("Broadcast receipt:", receipt) return nil } // Make a random send transaction from srcIndex to N other accounts. // balance: balance to send from input // sequence: sequence to sign with // inputPriv: input privAccount func makeRandomTransaction(balance int64, sequence int, inputPriv *acm.PrivAccount, sendCount int, accounts []*acm.Account) *types.SendTx { if sendCount >= len(accounts) { PanicSanity("Cannot make tx with sendCount >= len(accounts)") } // Remember which accounts were chosen accMap := map[string]struct{}{} accMap[string(inputPriv.Address)] = struct{}{} // Find a selection of accounts to send to outputs := []*acm.Account{} for i := 0; i < sendCount; i++ { for { idx := RandInt() % len(accounts) account := accounts[idx] if _, ok := accMap[string(account.Address)]; ok { continue } accMap[string(account.Address)] = struct{}{} outputs = append(outputs, account) break } } // Construct SendTx sendTx := types.NewSendTx() err := sendTx.AddInputWithNonce(inputPriv.PubKey, balance, sequence) if err != nil { panic(err) } for _, output := range outputs { sendTx.AddOutput(output.Address, balance/int64(len(outputs))) } // Sign SendTx sendTx.SignInput("tendermint_testnet_9", 0, inputPriv) return sendTx }