--- /dev/null
+package core
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/bytom/api"
+ "github.com/bytom/blockchain/txbuilder"
+ "github.com/bytom/crypto/ed25519/chainkd"
+ "github.com/bytom/protocol/bc/types"
+ "github.com/bytom/util"
+)
+
+const (
+ CreateKey = "create_key"
+ ListKeys = "list_keys"
+ CreateAccount = "create_account"
+ CreateAsset = "create_asset"
+ CreateReceiver = "CreateReceiver"
+ BuildTx = "build_tx"
+ BuildSpend = "spend"
+ BuildIssue = "issue"
+ BuildCtlAddr = "address"
+ SignTx = "Sign_tx"
+ SubmitTx = "submit_tx"
+ GetTransaction = "get_transaction"
+)
+
+var buildIssueReqFmt = `
+ {"actions": [
+ {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
+ {"type": "issue", "asset_id": "%s", "amount": %s},
+ {"type": "control_address", "asset_id": "%s", "amount": %s, "address": "%s"}
+ ]}`
+
+var buildSpendReqFmt = `
+ {"actions": [
+ {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
+ {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"},
+ {"type": "control_receiver", "asset_id": "%s", "amount": %s, "receiver":{"control_program": "%s"}}
+ ]}`
+
+var buildControlAddressReqFmt = `
+ {"actions": [
+ {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
+ {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"},
+ {"type": "control_address", "asset_id": "%s", "amount": %s,"address": "%s"}
+ ]}`
+
+var (
+ buildType = ""
+ btmGas = "20000000"
+ accountQuorum = 1
+ passwd = "123456"
+)
+
+// RestoreStruct Restore data
+func RestoreStruct(data interface{}, out interface{}) {
+ dataMap, ok := data.(map[string]interface{})
+ if ok != true {
+ fmt.Println("invalid type assertion")
+ os.Exit(util.ErrLocalParse)
+ }
+
+ rawData, err := json.MarshalIndent(dataMap, "", " ")
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(util.ErrLocalParse)
+ }
+ json.Unmarshal(rawData, out)
+}
+
+// SendReq genetate tx and send data
+func SendReq(method string, args []string) (interface{}, bool) {
+ var param interface{}
+ var methodPath string
+ switch method {
+ case CreateKey:
+ ins := keyIns{Alias: args[0], Password: args[1]}
+ param = ins
+ methodPath = "/create-key"
+ case ListKeys:
+ methodPath = "/list-keys"
+ case CreateAccount:
+ ins := account{}
+ xpub := chainkd.XPub{}
+ if err := xpub.UnmarshalText([]byte(args[1])); err != nil {
+ fmt.Println("CreateAccount error: ", err)
+ os.Exit(util.ErrLocalExe)
+ }
+ ins.RootXPubs = append(ins.RootXPubs, xpub)
+ ins.Quorum = accountQuorum
+ ins.Alias = args[0]
+ ins.AccessToken = ""
+ param = ins
+ methodPath = "/create-account"
+ case CreateAsset:
+ ins := asset{}
+ xpub := chainkd.XPub{}
+ if err := xpub.UnmarshalText([]byte(args[1])); err != nil {
+ fmt.Println("CreateAsset error: ", err)
+ os.Exit(util.ErrLocalExe)
+ }
+ ins.RootXPubs = append(ins.RootXPubs, xpub)
+ ins.Quorum = 1
+ ins.Alias = args[0]
+ ins.AccessToken = ""
+ param = ins
+ methodPath = "/create-asset"
+ case CreateReceiver:
+ var ins = Reveive{AccountAlias: args[0]}
+ param = ins
+ methodPath = "/create-account-receiver"
+ case BuildTx:
+ accountInfo := args[0]
+ assetInfo := args[1]
+ amount := args[2]
+ receiverProgram := args[3]
+ buildType := args[4]
+ var buildReqStr string
+
+ switch buildType {
+ case BuildSpend:
+ buildReqStr = fmt.Sprintf(buildSpendReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram)
+ case BuildIssue:
+ address := args[3]
+ buildReqStr = fmt.Sprintf(buildIssueReqFmt, btmGas, accountInfo, assetInfo, amount, assetInfo, amount, address)
+ case BuildCtlAddr:
+ address := args[3]
+ buildReqStr = fmt.Sprintf(buildControlAddressReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, address)
+ default:
+ buildReqStr = fmt.Sprintf(buildSpendReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram)
+
+ }
+ var ins api.BuildRequest
+ if err := json.Unmarshal([]byte(buildReqStr), &ins); err != nil {
+ fmt.Println("generate build tx is error: ", err)
+ os.Exit(util.ErrLocalExe)
+ }
+ param = ins
+ methodPath = "/build-transaction"
+ case SignTx:
+ template := txbuilder.Template{}
+
+ err := json.Unmarshal([]byte(args[0]), &template)
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(util.ErrLocalExe)
+ }
+
+ var ins = struct {
+ Password string `json:"password"`
+ Txs txbuilder.Template `json:"transaction"`
+ }{Password: cfg.Password, Txs: template}
+
+ param = ins
+ methodPath = "/sign-transaction"
+ case SubmitTx:
+ var ins = struct {
+ Tx types.Tx `json:"raw_transaction"`
+ }{}
+ json.Unmarshal([]byte(args[0]), &ins)
+ methodPath = "/submit-transaction"
+ data, exitCode := util.ClientCall(methodPath, &ins)
+ if exitCode != util.Success {
+ return "", false
+ }
+ return data, true
+ case GetTransaction:
+ ins := &struct {
+ TxID string `json:"tx_id"`
+ }{TxID: args[0]}
+ param = ins
+ methodPath = "/get-transaction"
+ default:
+ return "", false
+ }
+ data, exitCode := util.ClientCall(methodPath, ¶m)
+ if exitCode != util.Success {
+ return "", false
+ }
+ return data, true
+}
+
+// Sendbulktx send asset tx
+func Sendbulktx(threadTxNum int, txBtmNum string, sendAcct string, sendasset string, controlPrograms []string, txidChan chan string) {
+ arrayLen := len(controlPrograms)
+ for i := 0; i < threadTxNum; i++ {
+ //build tx
+ receiver := controlPrograms[i%arrayLen]
+ if strings.EqualFold(receiver, "") {
+ txidChan <- ""
+ continue
+ }
+
+ param := []string{sendAcct, sendasset, txBtmNum, receiver, cfg.BuildType}
+ resp, b := SendReq(BuildTx, param)
+ if !b {
+ txidChan <- ""
+ continue
+ }
+ rawTemplate, _ := json.Marshal(resp)
+ //sign
+ param = []string{string(rawTemplate)}
+ resp, b = SendReq(SignTx, param)
+ if !b {
+ fmt.Println("SignTx fail")
+ txidChan <- ""
+ continue
+ }
+ // submit
+ rawTemplate, _ = json.Marshal(resp)
+ var data signResp
+ json.Unmarshal(rawTemplate, &data)
+ rawTemplate, _ = json.Marshal(*data.Tx)
+ param = []string{string(rawTemplate)}
+ resp, b = SendReq(SubmitTx, param)
+ if !b {
+ fmt.Println("SubmitTx fail")
+ txidChan <- ""
+ continue
+ }
+ type txID struct {
+ Txid string `json:"tx_id"`
+ }
+ var out txID
+ RestoreStruct(resp, &out)
+ txidChan <- out.Txid
+ }
+}
--- /dev/null
+package core
+
+import (
+ "container/list"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/pelletier/go-toml"
+
+ "github.com/spf13/cobra"
+
+ "github.com/bytom/blockchain/query"
+ "github.com/bytom/util"
+)
+
+type config struct {
+ SendAcct string `toml:"send_acct_id"`
+ Sendasset string `toml:"send_asset_id"`
+ AssetAddr string `toml:"asset_address"`
+ BuildType string `toml:"build_type"`
+ AssetReceiver []string `toml:"asset_receiver"`
+ Password string `toml:"password"`
+}
+
+func init() {
+ sendTxCmd.PersistentFlags().IntVar(&thdTxNum, "thdtxnum", 10, " The number of transactions per goroutine")
+ sendTxCmd.PersistentFlags().IntVar(&thdNum, "thdnum", 5, "goroutine num")
+ sendTxCmd.PersistentFlags().IntVar(&assetNum, "assetnum", 10, "Number of transactions asset")
+ sendTxCmd.PersistentFlags().StringVar(&configFile, "config", "./config.toml", "config file")
+}
+
+var (
+ acctNum int
+ thdTxNum int
+ thdNum int
+ assetNum int
+ sendAcct string
+ sendasset string
+ configFile string
+ cfg config
+ m sync.Mutex
+ sucess = 0
+ fail = 0
+)
+
+var sendTxCmd = &cobra.Command{
+ Use: "sendbulktx",
+ Short: "send bulk tx",
+ Args: cobra.RangeArgs(0, 4),
+ Run: func(cmd *cobra.Command, args []string) {
+ bs, err := ioutil.ReadFile(configFile)
+ if err = toml.Unmarshal(bs, &cfg); err != nil {
+ fmt.Println(err)
+ return
+ }
+ sendAcct = cfg.SendAcct
+ sendasset = cfg.Sendasset
+ acctNum = len(cfg.AssetReceiver)
+ controlPrograms := make([]string, acctNum)
+ txidChan := make(chan string)
+ switch cfg.BuildType {
+ case "issue", "spend", "address":
+ for i, value := range cfg.AssetReceiver {
+ controlPrograms[i] = value
+ }
+ default:
+ fmt.Println("Invalid transaction template type")
+ os.Exit(util.ErrLocalExe)
+ }
+ txBtm := fmt.Sprintf("%d", assetNum)
+ fmt.Println("*****************send tx start*****************")
+ // send btm to account
+ for i := 0; i < thdNum; i++ {
+ go Sendbulktx(thdTxNum, txBtm, sendAcct, sendasset, controlPrograms, txidChan)
+ }
+
+ txs := list.New()
+ go recvTxID(txs, txidChan)
+ num := 0
+ start := time.Now()
+ blockTxNum := make(map[uint64]uint32)
+ for {
+ var n *list.Element
+ for e := txs.Front(); e != nil; e = n {
+ //fmt.Println(e.Value) //输出list的值,01234
+ value := fmt.Sprintf("%s", e.Value)
+ param := []string{value}
+ if resp, ok := SendReq(GetTransaction, param); ok {
+ var tx query.AnnotatedTx
+ RestoreStruct(resp, &tx)
+ if _, ok := blockTxNum[tx.BlockHeight]; ok {
+ blockTxNum[tx.BlockHeight]++
+ } else {
+ blockTxNum[tx.BlockHeight] = 1
+ }
+ n = e.Next()
+ m.Lock()
+ txs.Remove(e)
+ m.Unlock()
+ num++
+ continue
+ } else {
+ n = e.Next()
+ }
+
+ }
+ if num >= sucess && (sucess+fail) >= thdTxNum*thdNum {
+ end := time.Now()
+ fmt.Printf("tx num: %d, use time: %v\n", num, end.Sub(start))
+ var keys []uint64
+ for k := range blockTxNum {
+ keys = append(keys, k)
+ }
+ for _, key := range keys {
+ fmt.Println("height:", key, ",tx num:", blockTxNum[key])
+ }
+ os.Exit(0)
+ }
+ time.Sleep(time.Second * 60)
+ }
+
+ },
+}
+
+func recvTxID(txs *list.List, txidChan chan string) {
+
+ file, error := os.OpenFile("./txid.txt", os.O_RDWR|os.O_CREATE, 0766)
+ if error != nil {
+ fmt.Println(error)
+ }
+
+ for {
+ select {
+ case txid := <-txidChan:
+ if strings.EqualFold(txid, "") {
+ fail++
+ } else {
+ sucess++
+ m.Lock()
+ txs.PushBack(txid)
+ m.Unlock()
+ file.WriteString(txid)
+ file.WriteString("\n")
+ }
+ default:
+ if fail >= (thdTxNum * thdNum) {
+ os.Exit(1)
+ }
+ if (sucess + fail) >= (thdTxNum * thdNum) {
+ file.Close()
+ return
+ }
+
+ time.Sleep(time.Second * 1)
+ }
+ }
+}
+
+// Execute send tx
+func Execute() {
+ if _, err := sendTxCmd.ExecuteC(); err != nil {
+ os.Exit(util.ErrLocalExe)
+ }
+}