github.com/turingchain2020/turingchain@v1.1.21/system/dapp/commands/stat.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package commands
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"fmt"
    11  	"os"
    12  	"strconv"
    13  
    14  	"github.com/turingchain2020/turingchain/common"
    15  	"github.com/turingchain2020/turingchain/rpc/jsonclient"
    16  	rpctypes "github.com/turingchain2020/turingchain/rpc/types"
    17  	commandtypes "github.com/turingchain2020/turingchain/system/dapp/commands/types"
    18  	"github.com/turingchain2020/turingchain/types"
    19  	"github.com/spf13/cobra"
    20  )
    21  
    22  // StatCmd stat command
    23  func StatCmd() *cobra.Command {
    24  	cmd := &cobra.Command{
    25  		Use:   "stat",
    26  		Short: "Coin statistic",
    27  		Args:  cobra.MinimumNArgs(1),
    28  	}
    29  
    30  	cmd.AddCommand(
    31  		getTotalCoinsCmd(),
    32  		getExecBalanceCmd(),
    33  		totalFeeCmd(),
    34  	)
    35  
    36  	return cmd
    37  }
    38  
    39  // getTotalCoinsCmd get total coins
    40  func getTotalCoinsCmd() *cobra.Command {
    41  	cmd := &cobra.Command{
    42  		Use:   "total_coins",
    43  		Short: "Get total amount of a token (default: trc of current height)",
    44  		Run:   totalCoins,
    45  	}
    46  	addTotalCoinsCmdFlags(cmd)
    47  	return cmd
    48  }
    49  
    50  func addTotalCoinsCmdFlags(cmd *cobra.Command) {
    51  	cmd.Flags().StringP("symbol", "s", "trc", "token symbol")
    52  	cmd.Flags().StringP("actual", "a", "", "actual statistics, any string")
    53  	cmd.Flags().Int64P("height", "t", -1, `block height, "-1" stands for current height`)
    54  }
    55  
    56  func totalCoins(cmd *cobra.Command, args []string) {
    57  	rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
    58  	symbol, _ := cmd.Flags().GetString("symbol")
    59  	height, _ := cmd.Flags().GetInt64("height")
    60  	actual, _ := cmd.Flags().GetString("actual")
    61  
    62  	rpc, err := jsonclient.NewJSONClient(rpcAddr)
    63  	if err != nil {
    64  		fmt.Fprintln(os.Stderr, err)
    65  		return
    66  	}
    67  
    68  	var stateHashHex string
    69  	if height < 0 {
    70  
    71  		header, err := getLastBlock(rpc)
    72  		if err != nil {
    73  			fmt.Fprintln(os.Stderr, err)
    74  			return
    75  		}
    76  		height = header.Height
    77  		stateHashHex = header.StateHash
    78  	} else {
    79  		blocks, err := getBlocks(height, height, rpc)
    80  		if err != nil {
    81  			fmt.Fprintf(os.Stderr, "GetBlocksErr:%s", err.Error())
    82  			return
    83  		}
    84  		stateHashHex = blocks.Items[0].Block.StateHash
    85  	}
    86  
    87  	stateHash, err := common.FromHex(stateHashHex)
    88  	if err != nil {
    89  		fmt.Fprintln(os.Stderr, err)
    90  		return
    91  	}
    92  
    93  	// 查询高度哈希对应数据
    94  	var totalAmount int64
    95  	resp := commandtypes.GetTotalCoinsResult{}
    96  
    97  	if symbol == "trc" {
    98  		//查询历史总手续费
    99  		fee, err := queryTotalFeeWithHeight(height, rpc)
   100  		if err != nil {
   101  			fmt.Fprintln(os.Stderr, err)
   102  			return
   103  		}
   104  
   105  		resp.TxCount = fee.TxCount
   106  		var issueCoins int64
   107  		//只适用trc主网计算
   108  		if height < 2270000 {
   109  			issueCoins = 30 * height
   110  		} else { //挖矿产量降低30->8
   111  			issueCoins = 22*2269999 + height*8
   112  		}
   113  		totalAmount = (317430000+issueCoins)*types.Coin - fee.Fee
   114  		resp.TotalAmount = strconv.FormatFloat(float64(totalAmount)/float64(types.Coin), 'f', 4, 64)
   115  	} else {
   116  		var req types.ReqString
   117  		req.Data = symbol
   118  		var params rpctypes.Query4Jrpc
   119  		params.Execer = "token"
   120  		// 查询Token的总量
   121  		params.FuncName = "GetTotalAmount"
   122  		params.Payload = types.MustPBToJSON(&req)
   123  		var res types.TotalAmount
   124  
   125  		//查询Token总量
   126  		//params.FuncName = "GetTokenInfo"
   127  		//params.Payload = req
   128  		//var res tokenty.Token
   129  		err = rpc.Call("Turingchain.Query", params, &res)
   130  		if err != nil {
   131  			fmt.Fprintln(os.Stderr, err)
   132  			return
   133  		}
   134  
   135  		totalAmount = res.Total
   136  		resp.TotalAmount = strconv.FormatFloat(float64(totalAmount)/float64(types.TokenPrecision), 'f', 4, 64)
   137  	}
   138  
   139  	if actual != "" {
   140  		var actualAmount int64
   141  		var startKey []byte
   142  		var count int64
   143  		for count = 100; count == 100; {
   144  			params := types.ReqGetTotalCoins{
   145  				Symbol:    symbol,
   146  				StateHash: stateHash,
   147  				StartKey:  startKey,
   148  				Count:     count,
   149  			}
   150  			if symbol == "trc" {
   151  				params.Execer = "coins"
   152  			} else {
   153  				params.Execer = "token"
   154  			}
   155  			var res types.ReplyGetTotalCoins
   156  			err = rpc.Call("Turingchain.GetTotalCoins", params, &res)
   157  			if err != nil {
   158  				fmt.Fprintln(os.Stderr, err)
   159  				return
   160  			}
   161  			count = res.Num
   162  			resp.AccountCount += res.Num
   163  			actualAmount += res.Amount
   164  			startKey = res.NextKey
   165  		}
   166  
   167  		if symbol == "trc" {
   168  			resp.ActualAmount = strconv.FormatFloat(float64(actualAmount)/float64(types.Coin), 'f', 4, 64)
   169  			resp.DifferenceAmount = strconv.FormatFloat(float64(totalAmount-actualAmount)/float64(types.Coin), 'f', 4, 64)
   170  		} else {
   171  			resp.ActualAmount = strconv.FormatFloat(float64(actualAmount)/float64(types.TokenPrecision), 'f', 4, 64)
   172  			resp.DifferenceAmount = strconv.FormatFloat(float64(totalAmount-actualAmount)/float64(types.TokenPrecision), 'f', 4, 64)
   173  		}
   174  
   175  	}
   176  
   177  	data, err := json.MarshalIndent(resp, "", "    ")
   178  	if err != nil {
   179  		fmt.Fprintln(os.Stderr, err)
   180  		return
   181  	}
   182  
   183  	fmt.Println(string(data))
   184  }
   185  
   186  // getExecBalanceCmd get exec-addr balance of specific addr
   187  func getExecBalanceCmd() *cobra.Command {
   188  	cmd := &cobra.Command{
   189  		Use:   "exec_balance",
   190  		Short: "Get the exec amount of a token of one address (default: all exec-addr trc of current height of one addr)",
   191  		Run:   execBalance,
   192  	}
   193  	addExecBalanceCmdFlags(cmd)
   194  	return cmd
   195  }
   196  
   197  func addExecBalanceCmdFlags(cmd *cobra.Command) {
   198  	cmd.Flags().StringP("symbol", "s", "trc", "token symbol")
   199  	cmd.Flags().StringP("exec", "e", "coins", "excutor name")
   200  	cmd.Flags().StringP("addr", "a", "", "address")
   201  	cmd.MarkFlagRequired("addr")
   202  	cmd.Flags().StringP("exec_addr", "x", "", "exec address")
   203  	cmd.Flags().Int64P("height", "t", -1, `block height, "-1" stands for current height`)
   204  }
   205  
   206  func execBalance(cmd *cobra.Command, args []string) {
   207  	rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
   208  	symbol, _ := cmd.Flags().GetString("symbol")
   209  	exec, _ := cmd.Flags().GetString("exec")
   210  	addr, _ := cmd.Flags().GetString("addr")
   211  	execAddr, _ := cmd.Flags().GetString("exec_addr")
   212  	height, _ := cmd.Flags().GetInt64("height")
   213  
   214  	rpc, err := jsonclient.NewJSONClient(rpcAddr)
   215  	if err != nil {
   216  		fmt.Fprintln(os.Stderr, err)
   217  		return
   218  	}
   219  	var stateHashHex string
   220  	if height < 0 {
   221  
   222  		header, err := getLastBlock(rpc)
   223  		if err != nil {
   224  			fmt.Fprintln(os.Stderr, err)
   225  			return
   226  		}
   227  		stateHashHex = header.StateHash
   228  	} else {
   229  		blocks, err := getBlocks(height, height, rpc)
   230  		if err != nil {
   231  			fmt.Fprintf(os.Stderr, "GetBlocksErr:%s", err.Error())
   232  			return
   233  		}
   234  		stateHashHex = blocks.Items[0].Block.StateHash
   235  	}
   236  
   237  	stateHash, err := common.FromHex(stateHashHex)
   238  	if err != nil {
   239  		fmt.Fprintln(os.Stderr, err)
   240  		return
   241  	}
   242  
   243  	resp := commandtypes.GetExecBalanceResult{}
   244  
   245  	if symbol == "trc" {
   246  		exec = "coins"
   247  	}
   248  
   249  	reqParam := types.ReqGetExecBalance{
   250  		Symbol:    symbol,
   251  		StateHash: stateHash,
   252  		Addr:      []byte(addr),
   253  		ExecAddr:  []byte(execAddr),
   254  		Execer:    exec,
   255  	}
   256  
   257  	if len(execAddr) > 0 {
   258  		reqParam.Count = 1 //由于精确匹配一条记录,所以这里设定为1
   259  	} else {
   260  		reqParam.Count = 100 //每次最多读取100条
   261  	}
   262  
   263  	var replys types.ReplyGetExecBalance
   264  	for {
   265  		var reply types.ReplyGetExecBalance
   266  		var str string
   267  		err = rpc.Call("Turingchain.GetExecBalance", reqParam, &str)
   268  		if err != nil {
   269  			fmt.Fprintln(os.Stderr, err)
   270  			return
   271  		}
   272  
   273  		data, err := common.FromHex(str)
   274  		if err != nil {
   275  			fmt.Fprintln(os.Stderr, err)
   276  			return
   277  		}
   278  		err = types.Decode(data, &reply)
   279  		if err != nil {
   280  			fmt.Fprintln(os.Stderr, err)
   281  			return
   282  		}
   283  
   284  		replys.Amount += reply.Amount
   285  		replys.AmountActive += reply.AmountActive
   286  		replys.AmountFrozen += reply.AmountFrozen
   287  		replys.Items = append(replys.Items, reply.Items...)
   288  
   289  		if reqParam.Count == 1 {
   290  			break
   291  		}
   292  
   293  		if len(reply.NextKey) > 0 {
   294  			reqParam.NextKey = reply.NextKey
   295  		} else {
   296  			break
   297  		}
   298  	}
   299  
   300  	if symbol == "trc" {
   301  		convertReplyToResult(&replys, &resp, types.Coin)
   302  	} else {
   303  		convertReplyToResult(&replys, &resp, types.TokenPrecision)
   304  	}
   305  
   306  	data, err := json.MarshalIndent(resp, "", "    ")
   307  	if err != nil {
   308  		fmt.Fprintln(os.Stderr, err)
   309  		return
   310  	}
   311  
   312  	fmt.Println(string(data))
   313  }
   314  
   315  func convertReplyToResult(reply *types.ReplyGetExecBalance, result *commandtypes.GetExecBalanceResult, precision int64) {
   316  	result.Amount = strconv.FormatFloat(float64(reply.Amount)/float64(precision), 'f', 4, 64)
   317  	result.AmountFrozen = strconv.FormatFloat(float64(reply.AmountFrozen)/float64(precision), 'f', 4, 64)
   318  	result.AmountActive = strconv.FormatFloat(float64(reply.AmountActive)/float64(precision), 'f', 4, 64)
   319  
   320  	for i := 0; i < len(reply.Items); i++ {
   321  		item := &commandtypes.ExecBalance{}
   322  		item.ExecAddr = string(reply.Items[i].ExecAddr)
   323  		item.Frozen = strconv.FormatFloat(float64(reply.Items[i].Frozen)/float64(precision), 'f', 4, 64)
   324  		item.Active = strconv.FormatFloat(float64(reply.Items[i].Active)/float64(precision), 'f', 4, 64)
   325  		result.ExecBalances = append(result.ExecBalances, item)
   326  	}
   327  }
   328  
   329  // totalFeeCmd query total fee command
   330  func totalFeeCmd() *cobra.Command {
   331  	cmd := &cobra.Command{
   332  		Use:   "total_fee",
   333  		Short: "query total transaction fee, support specific block height interval [start, end]",
   334  		Run:   totalFee,
   335  	}
   336  	cmd.Flags().Int64P("start_height", "s", 0, "start block height, default 0")
   337  	cmd.Flags().Int64P("end_height", "e", -1, "end block height, default current block height")
   338  	return cmd
   339  }
   340  
   341  func totalFee(cmd *cobra.Command, args []string) {
   342  	rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
   343  	start, _ := cmd.Flags().GetInt64("start_height")
   344  	end, _ := cmd.Flags().GetInt64("end_height")
   345  
   346  	var startFeeAmount, endFeeAmount int64
   347  	rpc, err := jsonclient.NewJSONClient(rpcAddr)
   348  	if err != nil {
   349  		fmt.Fprintf(os.Stderr, "NewJsonClientErr:%s\n", err.Error())
   350  		return
   351  	}
   352  	if start < 0 {
   353  		start = 0
   354  	}
   355  
   356  	if start > 0 {
   357  		totalFee, err := queryTotalFeeWithHeight(start-1, rpc)
   358  		if err != nil {
   359  			fmt.Fprintf(os.Stderr, "QueryStartFeeErr:%s\n", err.Error())
   360  			return
   361  		}
   362  		startFeeAmount = totalFee.Fee
   363  	}
   364  
   365  	if end < 0 {
   366  		//last block fee
   367  		currentHeight, totalFee, err := queryCurrentTotalFee(rpc)
   368  		if err != nil {
   369  			fmt.Fprintf(os.Stderr, "QueryCurrentTotalFeeErr:%s\n", err.Error())
   370  			return
   371  		}
   372  		endFeeAmount = totalFee.Fee
   373  		end = currentHeight
   374  	} else {
   375  		totalFee, err := queryTotalFeeWithHeight(end, rpc)
   376  		if err != nil {
   377  			fmt.Fprintf(os.Stderr, "QueryEndFeeErr:%s\n", err.Error())
   378  			return
   379  		}
   380  		endFeeAmount = totalFee.Fee
   381  	}
   382  
   383  	fee := endFeeAmount - startFeeAmount
   384  	if fee < 0 {
   385  		fee = 0
   386  	}
   387  	resp := fmt.Sprintf(`{"startHeight":%d,"endHeight":%d, "totalFee":%s}`, start, end, commandtypes.FormatAmountValue2Display(fee))
   388  	buf := &bytes.Buffer{}
   389  	err = json.Indent(buf, []byte(resp), "", "    ")
   390  	if err != nil {
   391  		fmt.Fprintf(os.Stderr, "JsonIndentResultErr:%s\n", err.Error())
   392  		return
   393  	}
   394  
   395  	fmt.Println(buf.String())
   396  }
   397  
   398  //get last block header
   399  func getLastBlock(rpc *jsonclient.JSONClient) (*rpctypes.Header, error) {
   400  
   401  	res := &rpctypes.Header{}
   402  	err := rpc.Call("Turingchain.GetLastHeader", nil, &res)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  	return res, nil
   407  }
   408  
   409  //get block hash with height
   410  func getBlockHash(height int64, rpc *jsonclient.JSONClient) (string, error) {
   411  
   412  	params := types.ReqInt{Height: height}
   413  	var res rpctypes.ReplyHash
   414  	err := rpc.Call("Turingchain.GetBlockHash", params, &res)
   415  	if err != nil {
   416  		return "", err
   417  	}
   418  	return res.Hash, nil
   419  }
   420  
   421  func queryCurrentTotalFee(rpc *jsonclient.JSONClient) (int64, *types.TotalFee, error) {
   422  	header, err := getLastBlock(rpc)
   423  	if err != nil {
   424  		return 0, nil, err
   425  	}
   426  	fee, err := queryTotalFeeWithHash(header.Hash, rpc)
   427  	if err != nil {
   428  		return 0, nil, err
   429  	}
   430  	return header.Height, fee, nil
   431  }
   432  
   433  func queryTotalFeeWithHeight(height int64, rpc *jsonclient.JSONClient) (*types.TotalFee, error) {
   434  	hash, err := getBlockHash(height, rpc)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  	fee, err := queryTotalFeeWithHash(hash, rpc)
   439  	if err != nil {
   440  		return nil, err
   441  	}
   442  	return fee, nil
   443  }
   444  
   445  func queryTotalFeeWithHash(blockHash string, rpc *jsonclient.JSONClient) (*types.TotalFee, error) {
   446  
   447  	hash, err := common.FromHex(blockHash)
   448  	if err != nil {
   449  		return nil, err
   450  	}
   451  
   452  	//查询手续费
   453  	hash = append([]byte("TotalFeeKey:"), hash...)
   454  	params := types.LocalDBGet{Keys: [][]byte{hash[:]}}
   455  	res := &types.TotalFee{}
   456  	err = rpc.Call("Turingchain.QueryTotalFee", params, &res)
   457  	if err != nil {
   458  		return nil, err
   459  	}
   460  	return res, nil
   461  }
   462  
   463  func getBlocks(start, end int64, rpc *jsonclient.JSONClient) (*rpctypes.BlockDetails, error) {
   464  	// 获取blocks
   465  	params := rpctypes.BlockParam{
   466  		Start: start,
   467  		End:   end,
   468  		//Isdetail: false,
   469  		Isdetail: true,
   470  	}
   471  	res := &rpctypes.BlockDetails{}
   472  	err := rpc.Call("Turingchain.GetBlocks", params, &res)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  	return res, nil
   477  }