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 }