github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/cmd/bytomcli/commands/transaction.go (about)

     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/bytom/api"
    13  	"github.com/bytom/bytom/blockchain/txbuilder"
    14  	chainjson "github.com/bytom/bytom/encoding/json"
    15  	"github.com/bytom/bytom/protocol/bc/types"
    16  	"github.com/bytom/bytom/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  }