OSDN Git Service

38b5101b1a23e0c780d36a2e9e700a132c08b72f
[bytom/bytom.git] / cmd / bytomcli / commands / transaction.go
1 package commands
2
3 import (
4         "encoding/hex"
5         "encoding/json"
6         "fmt"
7         "os"
8
9         "github.com/spf13/cobra"
10         jww "github.com/spf13/jwalterweatherman"
11
12         "github.com/bytom/api"
13         "github.com/bytom/blockchain/txbuilder"
14         chainjson "github.com/bytom/encoding/json"
15         "github.com/bytom/protocol/bc/types"
16         "github.com/bytom/util"
17 )
18
19 func init() {
20         buildTransactionCmd.PersistentFlags().StringVarP(&buildType, "type", "t", "", "transaction type, valid types: 'issue', 'spend'")
21         buildTransactionCmd.PersistentFlags().StringVarP(&receiverProgram, "receiver", "r", "", "program of receiver")
22         buildTransactionCmd.PersistentFlags().StringVarP(&address, "address", "a", "", "address of receiver")
23         buildTransactionCmd.PersistentFlags().StringVarP(&btmGas, "gas", "g", "20000000", "gas of this transaction")
24         buildTransactionCmd.PersistentFlags().BoolVar(&pretty, "pretty", false, "pretty print json result")
25         buildTransactionCmd.PersistentFlags().BoolVar(&alias, "alias", false, "use alias build transaction")
26
27         signTransactionCmd.PersistentFlags().StringVarP(&password, "password", "p", "", "password of the account which sign these transaction(s)")
28         signTransactionCmd.PersistentFlags().BoolVar(&pretty, "pretty", false, "pretty print json result")
29
30         listTransactionsCmd.PersistentFlags().StringVar(&txID, "id", "", "transaction id")
31         listTransactionsCmd.PersistentFlags().StringVar(&account, "account_id", "", "account id")
32         listTransactionsCmd.PersistentFlags().BoolVar(&detail, "detail", false, "list transactions details")
33 }
34
35 var (
36         buildType       = ""
37         btmGas          = ""
38         receiverProgram = ""
39         address         = ""
40         password        = ""
41         pretty          = false
42         alias           = false
43         txID            = ""
44         account         = ""
45         detail          = false
46 )
47
48 var buildIssueReqFmt = `
49         {"actions": [
50                 {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
51                 {"type": "issue", "asset_id": "%s", "amount": %s},
52                 {"type": "control_address", "asset_id": "%s", "amount": %s, "address": "%s"}
53         ]}`
54
55 var buildIssueReqFmtByAlias = `
56         {"actions": [
57                 {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"},
58                 {"type": "issue", "asset_alias": "%s", "amount": %s},
59                 {"type": "control_address", "asset_alias": "%s", "amount": %s, "address": "%s"}
60         ]}`
61
62 var buildSpendReqFmt = `
63         {"actions": [
64                 {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
65                 {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"},
66                 {"type": "control_receiver", "asset_id": "%s", "amount": %s, "receiver":{"control_program": "%s"}}
67         ]}`
68
69 var buildSpendReqFmtByAlias = `
70         {"actions": [
71                 {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"},
72                 {"type": "spend_account", "asset_alias": "%s","amount": %s,"account_alias": "%s"},
73                 {"type": "control_receiver", "asset_alias": "%s", "amount": %s, "receiver":{"control_program": "%s"}}
74         ]}`
75
76 var buildRetireReqFmt = `
77         {"actions": [
78                 {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
79                 {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"},
80                 {"type": "retire", "asset_id": "%s","amount": %s,"account_id": "%s"}
81         ]}`
82
83 var buildRetireReqFmtByAlias = `
84         {"actions": [
85                 {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"},
86                 {"type": "spend_account", "asset_alias": "%s","amount": %s,"account_alias": "%s"},
87                 {"type": "retire", "asset_alias": "%s","amount": %s,"account_alias": "%s"}
88         ]}`
89
90 var buildControlAddressReqFmt = `
91         {"actions": [
92                 {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"},
93                 {"type": "spend_account", "asset_id": "%s","amount": %s,"account_id": "%s"},
94                 {"type": "control_address", "asset_id": "%s", "amount": %s,"address": "%s"}
95         ]}`
96
97 var buildControlAddressReqFmtByAlias = `
98         {"actions": [
99                 {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"},
100                 {"type": "spend_account", "asset_alias": "%s","amount": %s, "account_alias": "%s"},
101                 {"type": "control_address", "asset_alias": "%s", "amount": %s,"address": "%s"}
102         ]}`
103
104 var buildTransactionCmd = &cobra.Command{
105         Use:   "build-transaction <accountID|alias> <assetID|alias> <amount>",
106         Short: "Build one transaction template,default use account id and asset id",
107         Args:  cobra.RangeArgs(3, 4),
108         PreRun: func(cmd *cobra.Command, args []string) {
109                 cmd.MarkFlagRequired("type")
110                 if buildType == "spend" {
111                         cmd.MarkFlagRequired("receiver")
112                 }
113         },
114         Run: func(cmd *cobra.Command, args []string) {
115                 var buildReqStr string
116                 accountInfo := args[0]
117                 assetInfo := args[1]
118                 amount := args[2]
119                 switch buildType {
120                 case "issue":
121                         if alias {
122                                 buildReqStr = fmt.Sprintf(buildIssueReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, assetInfo, amount, address)
123                                 break
124                         }
125                         buildReqStr = fmt.Sprintf(buildIssueReqFmt, btmGas, accountInfo, assetInfo, amount, assetInfo, amount, address)
126                 case "spend":
127                         if alias {
128                                 buildReqStr = fmt.Sprintf(buildSpendReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram)
129                                 break
130                         }
131                         buildReqStr = fmt.Sprintf(buildSpendReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram)
132                 case "retire":
133                         if alias {
134                                 buildReqStr = fmt.Sprintf(buildRetireReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, accountInfo)
135                                 break
136                         }
137                         buildReqStr = fmt.Sprintf(buildRetireReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, accountInfo)
138                 case "address":
139                         if alias {
140                                 buildReqStr = fmt.Sprintf(buildControlAddressReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, address)
141                                 break
142                         }
143                         buildReqStr = fmt.Sprintf(buildControlAddressReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, address)
144                 default:
145                         jww.ERROR.Println("Invalid transaction template type")
146                         os.Exit(util.ErrLocalExe)
147                 }
148
149                 var buildReq api.BuildRequest
150                 if err := json.Unmarshal([]byte(buildReqStr), &buildReq); err != nil {
151                         jww.ERROR.Println(err)
152                         os.Exit(util.ErrLocalExe)
153                 }
154
155                 data, exitCode := util.ClientCall("/build-transaction", &buildReq)
156                 if exitCode != util.Success {
157                         os.Exit(exitCode)
158                 }
159
160                 if pretty {
161                         printJSON(data)
162                         return
163                 }
164
165                 dataMap, ok := data.(map[string]interface{})
166                 if ok != true {
167                         jww.ERROR.Println("invalid type assertion")
168                         os.Exit(util.ErrLocalParse)
169                 }
170
171                 rawTemplate, err := json.Marshal(dataMap)
172                 if err != nil {
173                         jww.ERROR.Println(err)
174                         os.Exit(util.ErrLocalParse)
175                 }
176
177                 jww.FEEDBACK.Printf("Template Type: %s\n%s\n", buildType, string(rawTemplate))
178         },
179 }
180
181 var signTransactionCmd = &cobra.Command{
182         Use:   "sign-transaction  <json templates>",
183         Short: "Sign transaction templates with account password",
184         Args:  cobra.ExactArgs(1),
185         PreRun: func(cmd *cobra.Command, args []string) {
186                 cmd.MarkFlagRequired("password")
187         },
188         Run: func(cmd *cobra.Command, args []string) {
189                 template := txbuilder.Template{}
190
191                 err := json.Unmarshal([]byte(args[0]), &template)
192                 if err != nil {
193                         jww.ERROR.Println(err)
194                         os.Exit(util.ErrLocalExe)
195                 }
196
197                 var req = struct {
198                         Password string             `json:"password"`
199                         Txs      txbuilder.Template `json:"transaction"`
200                 }{Password: password, Txs: template}
201
202                 jww.FEEDBACK.Printf("\n\n")
203                 data, exitCode := util.ClientCall("/sign-transaction", &req)
204                 if exitCode != util.Success {
205                         os.Exit(exitCode)
206                 }
207
208                 if pretty {
209                         printJSON(data)
210                         return
211                 }
212
213                 dataMap, ok := data.(map[string]interface{})
214                 if ok != true {
215                         jww.ERROR.Println("invalid type assertion")
216                         os.Exit(util.ErrLocalParse)
217                 }
218
219                 rawSign, err := json.Marshal(dataMap)
220                 if err != nil {
221                         jww.ERROR.Println(err)
222                         os.Exit(util.ErrLocalParse)
223                 }
224                 jww.FEEDBACK.Printf("\nSign Template:\n%s\n", string(rawSign))
225         },
226 }
227
228 var submitTransactionCmd = &cobra.Command{
229         Use:   "submit-transaction  <signed json raw_transaction>",
230         Short: "Submit signed transaction",
231         Args:  cobra.ExactArgs(1),
232         Run: func(cmd *cobra.Command, args []string) {
233                 var ins = struct {
234                         Tx types.Tx `json:"raw_transaction"`
235                 }{}
236
237                 err := json.Unmarshal([]byte(args[0]), &ins)
238                 if err != nil {
239                         jww.ERROR.Println(err)
240                         os.Exit(util.ErrLocalExe)
241                 }
242
243                 data, exitCode := util.ClientCall("/submit-transaction", &ins)
244                 if exitCode != util.Success {
245                         os.Exit(exitCode)
246                 }
247
248                 printJSON(data)
249         },
250 }
251
252 var estimateTransactionGasCmd = &cobra.Command{
253         Use:   "estimate-transaction-gas  <json templates>",
254         Short: "estimate gas for build transaction",
255         Args:  cobra.ExactArgs(1),
256         Run: func(cmd *cobra.Command, args []string) {
257                 template := txbuilder.Template{}
258
259                 err := json.Unmarshal([]byte(args[0]), &template)
260                 if err != nil {
261                         jww.ERROR.Println(err)
262                         os.Exit(util.ErrLocalExe)
263                 }
264
265                 var req = struct {
266                         TxTemplate txbuilder.Template `json:"transaction_template"`
267                 }{TxTemplate: template}
268
269                 data, exitCode := util.ClientCall("/estimate-transaction-gas", &req)
270                 if exitCode != util.Success {
271                         os.Exit(exitCode)
272                 }
273
274                 printJSON(data)
275         },
276 }
277
278 var decodeRawTransactionCmd = &cobra.Command{
279         Use:   "decode-raw-transaction <raw_transaction>",
280         Short: "decode the raw transaction",
281         Args:  cobra.ExactArgs(1),
282         Run: func(cmd *cobra.Command, args []string) {
283                 var ins = struct {
284                         Tx types.Tx `json:"raw_transaction"`
285                 }{}
286
287                 err := ins.Tx.UnmarshalText([]byte(args[0]))
288                 if err != nil {
289                         jww.ERROR.Println(err)
290                         os.Exit(util.ErrLocalExe)
291                 }
292
293                 data, exitCode := util.ClientCall("/decode-raw-transaction", &ins)
294                 if exitCode != util.Success {
295                         os.Exit(exitCode)
296                 }
297
298                 printJSON(data)
299         },
300 }
301
302 var getTransactionCmd = &cobra.Command{
303         Use:   "get-transaction <hash>",
304         Short: "get the transaction by matching the given transaction hash",
305         Args:  cobra.ExactArgs(1),
306         Run: func(cmd *cobra.Command, args []string) {
307                 txInfo := &struct {
308                         TxID string `json:"tx_id"`
309                 }{TxID: args[0]}
310
311                 data, exitCode := util.ClientCall("/get-transaction", txInfo)
312                 if exitCode != util.Success {
313                         os.Exit(exitCode)
314                 }
315
316                 printJSON(data)
317         },
318 }
319
320 var listTransactionsCmd = &cobra.Command{
321         Use:   "list-transactions",
322         Short: "List the transactions",
323         Args:  cobra.NoArgs,
324         Run: func(cmd *cobra.Command, args []string) {
325                 filter := struct {
326                         ID        string `json:"id"`
327                         AccountID string `json:"account_id"`
328                         Detail    bool   `json:"detail"`
329                 }{ID: txID, AccountID: account, Detail: detail}
330
331                 data, exitCode := util.ClientCall("/list-transactions", &filter)
332                 if exitCode != util.Success {
333                         os.Exit(exitCode)
334                 }
335
336                 printJSONList(data)
337         },
338 }
339
340 var getUnconfirmedTransactionCmd = &cobra.Command{
341         Use:   "get-unconfirmed-transaction <hash>",
342         Short: "get unconfirmed transaction by matching the given transaction hash",
343         Args:  cobra.ExactArgs(1),
344         Run: func(cmd *cobra.Command, args []string) {
345                 txID, err := hex.DecodeString(args[0])
346                 if err != nil {
347                         jww.ERROR.Println(err)
348                         os.Exit(util.ErrLocalExe)
349                 }
350
351                 txInfo := &struct {
352                         TxID chainjson.HexBytes `json:"tx_id"`
353                 }{TxID: txID}
354
355                 data, exitCode := util.ClientCall("/get-unconfirmed-transaction", txInfo)
356                 if exitCode != util.Success {
357                         os.Exit(exitCode)
358                 }
359
360                 printJSON(data)
361         },
362 }
363
364 var listUnconfirmedTransactionsCmd = &cobra.Command{
365         Use:   "list-unconfirmed-transactions",
366         Short: "list unconfirmed transactions hashes",
367         Args:  cobra.NoArgs,
368         Run: func(cmd *cobra.Command, args []string) {
369                 data, exitCode := util.ClientCall("/list-unconfirmed-transactions")
370                 if exitCode != util.Success {
371                         os.Exit(exitCode)
372                 }
373
374                 printJSON(data)
375         },
376 }
377
378 var gasRateCmd = &cobra.Command{
379         Use:   "gas-rate",
380         Short: "Print the current gas rate",
381         Args:  cobra.NoArgs,
382         Run: func(cmd *cobra.Command, args []string) {
383                 data, exitCode := util.ClientCall("/gas-rate")
384                 if exitCode != util.Success {
385                         os.Exit(exitCode)
386                 }
387                 printJSON(data)
388         },
389 }