You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
4.7 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/hex"
  5. "flag"
  6. "fmt"
  7. "time"
  8. acm "github.com/tendermint/tendermint/account"
  9. . "github.com/tendermint/tendermint/common"
  10. "github.com/tendermint/tendermint/rpc/client"
  11. ctypes "github.com/tendermint/tendermint/rpc/core/types"
  12. "github.com/tendermint/tendermint/types"
  13. )
  14. const Version = "0.0.1"
  15. const sleepSeconds = 1 // Every second
  16. // Parse command-line options
  17. func parseFlags() (privKeyHex string, numAccounts int, remote string, version bool) {
  18. flag.StringVar(&privKeyHex, "priv-key", "", "Private key bytes in HEX")
  19. flag.IntVar(&numAccounts, "num-accounts", 1000, "Deterministically generates this many sub-accounts")
  20. flag.StringVar(&remote, "remote", "http://localhost:46657", "Remote RPC host:port")
  21. flag.BoolVar(&version, "version", false, "Version")
  22. flag.Parse()
  23. return
  24. }
  25. func main() {
  26. // Read options
  27. privKeyHex, numAccounts, remote, version := parseFlags()
  28. if version {
  29. fmt.Println(Fmt("sim_txs version %v", Version))
  30. return
  31. }
  32. // Print args.
  33. // fmt.Println(privKeyHex, numAccounts, remote, version)
  34. privKeyBytes, err := hex.DecodeString(privKeyHex)
  35. if err != nil {
  36. panic(err)
  37. }
  38. var privKeyArray [64]byte
  39. copy(privKeyArray[:], privKeyBytes)
  40. root := acm.GenPrivAccountFromPrivKeyBytes(&privKeyArray)
  41. fmt.Println("Computed address: %X", root.Address)
  42. // Get root account.
  43. rootAccount, err := getAccount(remote, root.Address)
  44. if err != nil {
  45. fmt.Println(Fmt("Root account does not exist: %X", root.Address))
  46. return
  47. } else {
  48. fmt.Println("Root account", rootAccount)
  49. }
  50. go func() {
  51. // Construct a new send Tx
  52. accounts := make([]*acm.Account, numAccounts)
  53. privAccounts := make([]*acm.PrivAccount, numAccounts)
  54. for i := 0; i < numAccounts; i++ {
  55. privAccounts[i] = root.Generate(i)
  56. account, err := getAccount(remote, privAccounts[i].Address)
  57. if err != nil {
  58. fmt.Println("Error", err)
  59. return
  60. } else {
  61. accounts[i] = account
  62. }
  63. }
  64. for {
  65. sendTx := makeRandomTransaction(rootAccount, root, accounts, privAccounts)
  66. // Broadcast it.
  67. err := broadcastSendTx(remote, sendTx)
  68. if err != nil {
  69. Exit(Fmt("Failed to broadcast SendTx: %v", err))
  70. return
  71. }
  72. // Broadcast 1 tx!
  73. time.Sleep(10 * time.Millisecond)
  74. }
  75. }()
  76. // Trap signal
  77. TrapSignal(func() {
  78. fmt.Println("sim_txs shutting down")
  79. })
  80. }
  81. func getAccount(remote string, address []byte) (*acm.Account, error) {
  82. // var account *acm.Account = new(acm.Account)
  83. account, err := rpcclient.Call(remote, "get_account", []interface{}{address}, (*acm.Account)(nil))
  84. if err != nil {
  85. return nil, err
  86. }
  87. if account.(*acm.Account) == nil {
  88. return &acm.Account{Address: address}, nil
  89. } else {
  90. return account.(*acm.Account), nil
  91. }
  92. }
  93. func broadcastSendTx(remote string, sendTx *types.SendTx) error {
  94. receipt, err := rpcclient.Call(remote, "broadcast_tx", []interface{}{sendTx}, (*ctypes.Receipt)(nil))
  95. if err != nil {
  96. return err
  97. }
  98. fmt.Println("Broadcast receipt:", receipt)
  99. return nil
  100. }
  101. func makeRandomTransaction(rootAccount *acm.Account, rootPrivAccount *acm.PrivAccount, accounts []*acm.Account, privAccounts []*acm.PrivAccount) *types.SendTx {
  102. allAccounts := append(accounts, rootAccount)
  103. allPrivAccounts := append(privAccounts, rootPrivAccount)
  104. // Find accout with the most money
  105. inputBalance := int64(0)
  106. inputAccount := (*acm.Account)(nil)
  107. inputPrivAccount := (*acm.PrivAccount)(nil)
  108. for i, account := range allAccounts {
  109. if account == nil {
  110. continue
  111. }
  112. if inputBalance < account.Balance {
  113. inputBalance = account.Balance
  114. inputAccount = account
  115. inputPrivAccount = allPrivAccounts[i]
  116. }
  117. }
  118. if inputAccount == nil {
  119. Exit("No accounts have any money")
  120. return nil
  121. }
  122. // Find a selection of accounts to send to
  123. outputAccounts := map[string]*acm.Account{}
  124. for i := 0; i < 2; i++ {
  125. for {
  126. idx := RandInt() % len(accounts)
  127. if bytes.Equal(accounts[idx].Address, inputAccount.Address) {
  128. continue
  129. }
  130. if _, ok := outputAccounts[string(accounts[idx].Address)]; ok {
  131. continue
  132. }
  133. outputAccounts[string(accounts[idx].Address)] = accounts[idx]
  134. break
  135. }
  136. }
  137. // Construct SendTx
  138. sendTx := types.NewSendTx()
  139. err := sendTx.AddInputWithNonce(inputPrivAccount.PubKey, inputAccount.Balance, inputAccount.Sequence+1)
  140. if err != nil {
  141. panic(err)
  142. }
  143. for _, outputAccount := range outputAccounts {
  144. sendTx.AddOutput(outputAccount.Address, inputAccount.Balance/int64(len(outputAccounts)))
  145. // XXX FIXME???
  146. outputAccount.Balance += inputAccount.Balance / int64(len(outputAccounts))
  147. }
  148. // Sign SendTx
  149. sendTx.SignInput("tendermint_testnet_7", 0, inputPrivAccount)
  150. // Hack: Listen for events or create a new RPC call for this.
  151. // XXX FIXME
  152. inputAccount.Sequence += 1
  153. inputAccount.Balance = 0 // FIXME???
  154. return sendTx
  155. }