OSDN Git Service

add send bulk tx
authorwz <mars@bytom.io>
Sat, 14 Apr 2018 03:04:31 +0000 (11:04 +0800)
committerwz <mars@bytom.io>
Thu, 19 Apr 2018 16:35:33 +0000 (00:35 +0800)
tools/sendbulktx/README.md [new file with mode: 0644]
tools/sendbulktx/config.toml [new file with mode: 0644]
tools/sendbulktx/core/def.go [new file with mode: 0644]
tools/sendbulktx/core/send_tx.go [new file with mode: 0644]
tools/sendbulktx/core/tool.go [new file with mode: 0644]
tools/sendbulktx/main.go [new file with mode: 0644]

diff --git a/tools/sendbulktx/README.md b/tools/sendbulktx/README.md
new file mode 100644 (file)
index 0000000..a6b28da
--- /dev/null
@@ -0,0 +1,36 @@
+
+# 发送远端交易
+## Example
+```
+$ go build
+$ export BYTOM_URL="http://192.168.199.62:9888"
+$ ./sendbulktx
+
+tx num: 600, use time: 3m8.8846942s
+height: 561 ,tx num: 67
+height: 562 ,tx num: 67
+height: 563 ,tx num: 51
+height: 564 ,tx num: 156
+height: 565 ,tx num: 82
+height: 559 ,tx num: 17
+height: 560 ,tx num: 160
+
+```
+available flags for `sendbulktx`:
+
+```
+      --assetnum int    Number of transactions asset (default 10)
+      --config string   config file (default "./config.toml")
+      --thdnum int      goroutine num (default 5)
+      --thdtxnum int     The number of transactions per goroutine (default 10)
+```
+
+# config.toml
+```
+send_acct_id = "0CMUIQ06G0A02"
+send_asset_id = "36017df0de65f4de249c966b9a98b8765ee9ecd438be14cdefce9b6467e7a752"
+#"issue", "spend", "address"
+build_type = "address"
+#asset_receiver = ["bm1qm97wwnjvgxarwzgd4q9saf38fj9r5jr8aelcvp"]
+asset_receiver = ["bm1q678m0eac5xcxxvalynzjp5cl4wq06rp039svzs","bm1qhgpjl0f7hzyxj866v0ztllscw6uqmh5gfcuqgy"]
+```
diff --git a/tools/sendbulktx/config.toml b/tools/sendbulktx/config.toml
new file mode 100644 (file)
index 0000000..c6316f0
--- /dev/null
@@ -0,0 +1,7 @@
+send_acct_id = "0CO2INHB00A02"
+password = "123456"
+send_asset_id = "f3891b6cdce683a559ae5087881db0d3164226d33d4b028083bd5e05db3ba203"
+#"issue", "spend", "address"
+build_type = "issue"
+asset_receiver = ["tm1q4g0mcfpxe9g2eh3qswj4qw6wdxdcg38v5szsr4"]
+#asset_receiver = ["bm1q678m0eac5xcxxvalynzjp5cl4wq06rp039svzs","bm1qhgpjl0f7hzyxj866v0ztllscw6uqmh5gfcuqgy"]
diff --git a/tools/sendbulktx/core/def.go b/tools/sendbulktx/core/def.go
new file mode 100644 (file)
index 0000000..9ea4dc8
--- /dev/null
@@ -0,0 +1,39 @@
+package core
+
+import (
+       "github.com/bytom/blockchain/txbuilder"
+       "github.com/bytom/crypto/ed25519/chainkd"
+)
+
+type keyIns struct {
+       Alias    string `json:"alias"`
+       Password string `json:"password"`
+}
+
+// Reveive use while CreateReceiver
+type Reveive struct {
+       AccountID    string `json:"account_id"`
+       AccountAlias string `json:"account_alias"`
+}
+
+type account struct {
+       RootXPubs   []chainkd.XPub         `json:"root_xpubs"`
+       Quorum      int                    `json:"quorum"`
+       Alias       string                 `json:"alias"`
+       Tags        map[string]interface{} `json:"tags"`
+       AccessToken string                 `json:"access_token"`
+}
+
+type asset struct {
+       RootXPubs   []chainkd.XPub         `json:"root_xpubs"`
+       Quorum      int                    `json:"quorum"`
+       Alias       string                 `json:"alias"`
+       Tags        map[string]interface{} `json:"tags"`
+       Definition  map[string]interface{} `json:"definition"`
+       AccessToken string                 `json:"access_token"`
+}
+
+type signResp struct {
+       Tx           *txbuilder.Template `json:"transaction"`
+       SignComplete bool                `json:"sign_complete"`
+}
diff --git a/tools/sendbulktx/core/send_tx.go b/tools/sendbulktx/core/send_tx.go
new file mode 100644 (file)
index 0000000..1b94731
--- /dev/null
@@ -0,0 +1,232 @@
+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, &param)
+       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
+       }
+}
diff --git a/tools/sendbulktx/core/tool.go b/tools/sendbulktx/core/tool.go
new file mode 100644 (file)
index 0000000..9a41260
--- /dev/null
@@ -0,0 +1,168 @@
+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)
+       }
+}
diff --git a/tools/sendbulktx/main.go b/tools/sendbulktx/main.go
new file mode 100644 (file)
index 0000000..ba5fa02
--- /dev/null
@@ -0,0 +1,7 @@
+package main
+
+import "github.com/bytom/tools/sendbulktx/core"
+
+func main() {
+       core.Execute()
+}