OSDN Git Service

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