package main import ( "bytes" "encoding/hex" "flag" "fmt" "time" 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" "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, 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", "http://localhost:46657", "Remote RPC host:port") flag.BoolVar(&version, "version", false, "Version") flag.Parse() return } func main() { // Read options privKeyHex, numAccounts, remote, version := parseFlags() if version { fmt.Println(Fmt("sim_txs version %v", Version)) return } // Print args. // fmt.Println(privKeyHex, numAccounts, remote, version) privKeyBytes, err := hex.DecodeString(privKeyHex) if err != nil { panic(err) } var privKeyArray [64]byte copy(privKeyArray[:], privKeyBytes) root := acm.GenPrivAccountFromPrivKeyBytes(&privKeyArray) fmt.Println("Computed address: %X", root.Address) // Get root account. rootAccount, err := getAccount(remote, root.Address) if err != nil { fmt.Println(Fmt("Root account does not exist: %X", root.Address)) return } else { fmt.Println("Root account", rootAccount) } go func() { // Construct a new send Tx accounts := make([]*acm.Account, numAccounts) privAccounts := make([]*acm.PrivAccount, numAccounts) for i := 0; i < numAccounts; 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 } } for { sendTx := makeRandomTransaction(rootAccount, root, accounts, privAccounts) // Broadcast it. err := broadcastSendTx(remote, sendTx) if err != nil { Exit(Fmt("Failed to broadcast SendTx: %v", err)) return } // Broadcast 1 tx! time.Sleep(10 * time.Millisecond) } }() // 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(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(remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil)) if err != nil { return err } fmt.Println("Broadcast receipt:", receipt) return nil } func makeRandomTransaction(rootAccount *acm.Account, rootPrivAccount *acm.PrivAccount, accounts []*acm.Account, privAccounts []*acm.PrivAccount) *types.SendTx { allAccounts := append(accounts, rootAccount) allPrivAccounts := append(privAccounts, rootPrivAccount) // Find accout with the most money inputBalance := int64(0) inputAccount := (*acm.Account)(nil) inputPrivAccount := (*acm.PrivAccount)(nil) for i, account := range allAccounts { if account == nil { continue } if inputBalance < account.Balance { inputBalance = account.Balance inputAccount = account inputPrivAccount = allPrivAccounts[i] } } if inputAccount == nil { Exit("No accounts have any money") return nil } // Find a selection of accounts to send to outputAccounts := map[string]*acm.Account{} for i := 0; i < 2; i++ { for { idx := RandInt() % len(accounts) if bytes.Equal(accounts[idx].Address, inputAccount.Address) { continue } if _, ok := outputAccounts[string(accounts[idx].Address)]; ok { continue } outputAccounts[string(accounts[idx].Address)] = accounts[idx] break } } // Construct SendTx sendTx := types.NewSendTx() err := sendTx.AddInputWithNonce(inputPrivAccount.PubKey, inputAccount.Balance, inputAccount.Sequence+1) if err != nil { panic(err) } for _, outputAccount := range outputAccounts { sendTx.AddOutput(outputAccount.Address, inputAccount.Balance/int64(len(outputAccounts))) // XXX FIXME??? outputAccount.Balance += inputAccount.Balance / int64(len(outputAccounts)) } // Sign SendTx sendTx.SignInput("tendermint_testnet_7", 0, inputPrivAccount) // Hack: Listen for events or create a new RPC call for this. // XXX FIXME inputAccount.Sequence += 1 inputAccount.Balance = 0 // FIXME??? return sendTx }