package commands import ( "encoding/hex" "encoding/json" "fmt" "os" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" "github.com/vapor/api" "github.com/vapor/blockchain/txbuilder" chainjson "github.com/vapor/encoding/json" "github.com/vapor/protocol/bc/types" "github.com/vapor/util" ) func init() { buildTransactionCmd.PersistentFlags().StringVarP(&buildType, "type", "t", "", "transaction type, valid types: 'issue', 'spend', 'address', 'retire', 'unlock'") buildTransactionCmd.PersistentFlags().StringVarP(&receiverProgram, "receiver", "r", "", "program of receiver when type is spend") buildTransactionCmd.PersistentFlags().StringVarP(&address, "address", "a", "", "address of receiver when type is address") buildTransactionCmd.PersistentFlags().StringVarP(&program, "program", "p", "", "program of receiver when type is program") buildTransactionCmd.PersistentFlags().StringVarP(&arbitrary, "arbitrary", "v", "", "additional arbitrary data when type is retire") buildTransactionCmd.PersistentFlags().StringVarP(&btmGas, "gas", "g", "20000000", "gas of this transaction") buildTransactionCmd.PersistentFlags().StringVarP(&contractName, "contract-name", "c", "", "name of template contract, currently supported: 'LockWithPublicKey', 'LockWithMultiSig', 'LockWithPublicKeyHash',"+ "\n\t\t\t 'RevealPreimage', 'TradeOffer', 'Escrow', 'CallOption', 'LoanCollateral'") buildTransactionCmd.PersistentFlags().BoolVar(&pretty, "pretty", false, "pretty print json result") buildTransactionCmd.PersistentFlags().BoolVar(&alias, "alias", false, "use alias build transaction") signTransactionCmd.PersistentFlags().StringVarP(&password, "password", "p", "", "password of the account which sign these transaction(s)") signTransactionCmd.PersistentFlags().BoolVar(&pretty, "pretty", false, "pretty print json result") listTransactionsCmd.PersistentFlags().StringVar(&txID, "id", "", "transaction id") listTransactionsCmd.PersistentFlags().StringVar(&account, "account_id", "", "account id") listTransactionsCmd.PersistentFlags().BoolVar(&detail, "detail", false, "list transactions details") listTransactionsCmd.PersistentFlags().BoolVar(&unconfirmed, "unconfirmed", false, "list unconfirmed transactions") } var ( buildType = "" btmGas = "" receiverProgram = "" address = "" password = "" pretty = false alias = false txID = "" account = "" detail = false unconfirmed = false arbitrary = "" program = "" contractName = "" ) 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 buildIssueReqFmtByAlias = ` {"actions": [ {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"}, {"type": "issue", "asset_alias": "%s", "amount": %s}, {"type": "control_address", "asset_alias": "%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_program", "asset_id": "%s", "amount": %s, "control_program": "%s"} ]}` var buildSpendReqFmtByAlias = ` {"actions": [ {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"}, {"type": "spend_account", "asset_alias": "%s","amount": %s,"account_alias": "%s"}, {"type": "control_program", "asset_alias": "%s", "amount": %s, "control_program": "%s"} ]}` var buildRetireReqFmt = ` {"actions": [ {"type": "spend_account", "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "amount":%s, "account_id": "%s"}, {"type": "spend_account", "asset_id": "%s", "amount": %s, "account_id": "%s"}, {"type": "retire", "asset_id": "%s", "amount": %s, "arbitrary": "%s"} ]}` var buildRetireReqFmtByAlias = ` {"actions": [ {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"}, {"type": "spend_account", "asset_alias": "%s", "amount": %s, "account_alias": "%s"}, {"type": "retire", "asset_alias": "%s", "amount": %s, "arbitrary": "%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 buildControlAddressReqFmtByAlias = ` {"actions": [ {"type": "spend_account", "asset_alias": "BTM", "amount":%s, "account_alias": "%s"}, {"type": "spend_account", "asset_alias": "%s","amount": %s, "account_alias": "%s"}, {"type": "control_address", "asset_alias": "%s", "amount": %s,"address": "%s"} ]}` var buildTransactionCmd = &cobra.Command{ Use: "build-transaction [outputID]", Short: "Build one transaction template,default use account id and asset id", Args: cobra.RangeArgs(3, 20), PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("type") if buildType == "spend" { cmd.MarkFlagRequired("receiver") } }, Run: func(cmd *cobra.Command, args []string) { var buildReqStr string accountInfo := args[0] assetInfo := args[1] amount := args[2] switch buildType { case "issue": if alias { buildReqStr = fmt.Sprintf(buildIssueReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, assetInfo, amount, address) break } buildReqStr = fmt.Sprintf(buildIssueReqFmt, btmGas, accountInfo, assetInfo, amount, assetInfo, amount, address) case "spend": if alias { buildReqStr = fmt.Sprintf(buildSpendReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram) break } buildReqStr = fmt.Sprintf(buildSpendReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, receiverProgram) case "retire": if alias { buildReqStr = fmt.Sprintf(buildRetireReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, arbitrary) break } buildReqStr = fmt.Sprintf(buildRetireReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, arbitrary) case "address": if alias { buildReqStr = fmt.Sprintf(buildControlAddressReqFmtByAlias, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, address) break } buildReqStr = fmt.Sprintf(buildControlAddressReqFmt, btmGas, accountInfo, assetInfo, amount, accountInfo, assetInfo, amount, address) case "unlock": var err error usage := "Usage:\n bytomcli build-transaction -c " baseCount := 4 if len(args) < baseCount { jww.ERROR.Printf("%s ... [flags]\n\n", usage) os.Exit(util.ErrLocalExe) } baseArg := baseContractArg{ accountInfo: accountInfo, assetInfo: assetInfo, amount: amount, alias: alias, program: program, btmGas: btmGas, outputID: args[baseCount-1], } specArgs := args[baseCount:] if buildReqStr, err = addContractArgs(contractName, baseArg, specArgs, usage); err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } default: jww.ERROR.Println("Invalid transaction template type") os.Exit(util.ErrLocalExe) } var buildReq api.BuildRequest if err := json.Unmarshal([]byte(buildReqStr), &buildReq); err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } data, exitCode := util.ClientCall("/build-transaction", &buildReq) if exitCode != util.Success { os.Exit(exitCode) } if pretty { printJSON(data) return } dataMap, ok := data.(map[string]interface{}) if ok != true { jww.ERROR.Println("invalid type assertion") os.Exit(util.ErrLocalParse) } rawTemplate, err := json.Marshal(dataMap) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalParse) } jww.FEEDBACK.Printf("Template Type: %s\n%s\n", buildType, string(rawTemplate)) }, } var signTransactionCmd = &cobra.Command{ Use: "sign-transaction ", Short: "Sign transaction templates with account password", Args: cobra.ExactArgs(1), PreRun: func(cmd *cobra.Command, args []string) { cmd.MarkFlagRequired("password") }, Run: func(cmd *cobra.Command, args []string) { template := txbuilder.Template{} err := json.Unmarshal([]byte(args[0]), &template) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } var req = struct { Password string `json:"password"` Txs txbuilder.Template `json:"transaction"` }{Password: password, Txs: template} jww.FEEDBACK.Printf("\n\n") data, exitCode := util.ClientCall("/sign-transaction", &req) if exitCode != util.Success { os.Exit(exitCode) } if pretty { printJSON(data) return } dataMap, ok := data.(map[string]interface{}) if ok != true { jww.ERROR.Println("invalid type assertion") os.Exit(util.ErrLocalParse) } rawSign, err := json.Marshal(dataMap) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalParse) } jww.FEEDBACK.Printf("\nSign Template:\n%s\n", string(rawSign)) }, } var submitTransactionCmd = &cobra.Command{ Use: "submit-transaction ", Short: "Submit signed transaction", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { var ins = struct { Tx types.Tx `json:"raw_transaction"` }{} err := json.Unmarshal([]byte(args[0]), &ins) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } data, exitCode := util.ClientCall("/submit-transaction", &ins) if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var estimateTransactionGasCmd = &cobra.Command{ Use: "estimate-transaction-gas ", Short: "estimate gas for build transaction", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { template := txbuilder.Template{} err := json.Unmarshal([]byte(args[0]), &template) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } var req = struct { TxTemplate txbuilder.Template `json:"transaction_template"` }{TxTemplate: template} data, exitCode := util.ClientCall("/estimate-transaction-gas", &req) if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var decodeRawTransactionCmd = &cobra.Command{ Use: "decode-raw-transaction ", Short: "decode the raw transaction", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { var ins = struct { Tx types.Tx `json:"raw_transaction"` }{} err := ins.Tx.UnmarshalText([]byte(args[0])) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } data, exitCode := util.ClientCall("/decode-raw-transaction", &ins) if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var getTransactionCmd = &cobra.Command{ Use: "get-transaction ", Short: "get the transaction by matching the given transaction hash", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { txInfo := &struct { TxID string `json:"tx_id"` }{TxID: args[0]} data, exitCode := util.ClientCall("/get-transaction", txInfo) if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var listTransactionsCmd = &cobra.Command{ Use: "list-transactions", Short: "List the transactions", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { filter := struct { ID string `json:"id"` AccountID string `json:"account_id"` Detail bool `json:"detail"` Unconfirmed bool `json:"unconfirmed"` }{ID: txID, AccountID: account, Detail: detail, Unconfirmed: unconfirmed} data, exitCode := util.ClientCall("/list-transactions", &filter) if exitCode != util.Success { os.Exit(exitCode) } printJSONList(data) }, } var getUnconfirmedTransactionCmd = &cobra.Command{ Use: "get-unconfirmed-transaction ", Short: "get unconfirmed transaction by matching the given transaction hash", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { txID, err := hex.DecodeString(args[0]) if err != nil { jww.ERROR.Println(err) os.Exit(util.ErrLocalExe) } txInfo := &struct { TxID chainjson.HexBytes `json:"tx_id"` }{TxID: txID} data, exitCode := util.ClientCall("/get-unconfirmed-transaction", txInfo) if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var listUnconfirmedTransactionsCmd = &cobra.Command{ Use: "list-unconfirmed-transactions", Short: "list unconfirmed transactions hashes", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { data, exitCode := util.ClientCall("/list-unconfirmed-transactions") if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, } var gasRateCmd = &cobra.Command{ Use: "gas-rate", Short: "Print the current gas rate", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { data, exitCode := util.ClientCall("/gas-rate") if exitCode != util.Success { os.Exit(exitCode) } printJSON(data) }, }