github.com/turingchain2020/turingchain@v1.1.21/cmd/miner_accounts/accounts/show.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 accounts
     6  
     7  import (
     8  	"time"
     9  
    10  	l "github.com/turingchain2020/turingchain/common/log/log15"
    11  
    12  	"github.com/turingchain2020/turingchain/types"
    13  
    14  	//"encoding/json"
    15  	//"io/ioutil"
    16  	"fmt"
    17  	"strconv"
    18  
    19  	rpctypes "github.com/turingchain2020/turingchain/rpc/types"
    20  )
    21  
    22  const secondsPerBlock = 5
    23  const trcPreBlock = 5
    24  const baseInterval = 3600
    25  const maxInterval = 15 * 24 * 3600
    26  const monitorTrcLowLimit = 3 * 1e7 * types.Coin
    27  
    28  var log = l.New("module", "accounts")
    29  
    30  //ShowMinerAccount 挖矿账户
    31  type ShowMinerAccount struct {
    32  	DataDir string
    33  	Addrs   []string
    34  }
    35  
    36  //Echo 打印
    37  func (*ShowMinerAccount) Echo(in *string, out *interface{}) error {
    38  	if in == nil {
    39  		return types.ErrInvalidParam
    40  	}
    41  	*out = *in
    42  	return nil
    43  }
    44  
    45  //TimeAt time
    46  type TimeAt struct {
    47  	// YYYY-mm-dd-HH
    48  	TimeAt string   `json:"timeAt,omitempty"`
    49  	Addrs  []string `json:"addrs,omitempty"`
    50  }
    51  
    52  //Get get
    53  func (show *ShowMinerAccount) Get(in *TimeAt, out *interface{}) error {
    54  	if in == nil {
    55  		log.Error("show", "in", "nil")
    56  		return types.ErrInvalidParam
    57  	}
    58  	addrs := show.Addrs
    59  	if in.Addrs != nil && len(in.Addrs) > 0 {
    60  		addrs = in.Addrs
    61  	}
    62  	log.Info("show", "miners", addrs)
    63  
    64  	height, err := toBlockHeight(in.TimeAt)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	log.Info("show", "header", height)
    69  
    70  	header, curAcc, err := cache.getBalance(addrs, "ticket", height)
    71  	if err != nil {
    72  		log.Error("show", "getBalance failed", err, "height", height)
    73  		return nil
    74  	}
    75  
    76  	totalTrc := int64(0)
    77  	for _, acc := range curAcc {
    78  		totalTrc += acc.Frozen
    79  	}
    80  	log.Info("show 1st balance", "utc", header.BlockTime, "total", totalTrc)
    81  
    82  	monitorInterval := calcMoniterInterval(totalTrc)
    83  	log.Info("show", "monitor Interval", monitorInterval)
    84  
    85  	lastHourHeader, lastAcc, err := cache.getBalance(addrs, "ticket", header.Height-monitorInterval)
    86  	if err != nil {
    87  		log.Error("show", "getBalance failed", err, "ts", header.BlockTime-monitorInterval)
    88  		return nil
    89  	}
    90  	fmt.Print(curAcc, lastAcc)
    91  	log.Info("show 2nd balance", "utc", *lastHourHeader)
    92  
    93  	miner := &MinerAccounts{}
    94  	miner.Seconds = header.BlockTime - lastHourHeader.BlockTime
    95  	miner.Blocks = header.Height - lastHourHeader.Height
    96  	miner.ExpectBlocks = miner.Seconds / secondsPerBlock
    97  
    98  	miner = calcIncrease(miner, curAcc, lastAcc, header)
    99  	*out = &miner
   100  
   101  	return nil
   102  }
   103  
   104  // 找指定时间最接近的区块, 默认是当前时间
   105  func toBlockHeight(timeAt string) (int64, error) {
   106  	seconds := time.Now().Unix()
   107  	if len(timeAt) != 0 {
   108  		tm, err := time.Parse("2006-01-02-15", timeAt)
   109  		if err != nil {
   110  			log.Error("show", "in.TimeAt Parse", err)
   111  			return 0, types.ErrInvalidParam
   112  		}
   113  		seconds = tm.Unix()
   114  	}
   115  	log.Info("show", "utc-init", seconds)
   116  
   117  	realTs, header := cache.findBlock(seconds)
   118  	if realTs == 0 || header == nil {
   119  		log.Error("show", "findBlock", "nil")
   120  		return 0, types.ErrNotFound
   121  	}
   122  	return header.Height, nil
   123  }
   124  
   125  // 计算监控区块的范围
   126  // 做对小额帐号限制,不然监控范围过大, 如9000个币需要138天
   127  func calcMoniterInterval(totalTrc int64) int64 {
   128  	monitorInterval := int64(baseInterval)
   129  	if totalTrc < monitorTrcLowLimit && totalTrc > 0 {
   130  		monitorInterval = int64(float64(monitorTrcLowLimit) / float64(totalTrc) * float64(baseInterval))
   131  	}
   132  	if monitorInterval > maxInterval {
   133  		monitorInterval = maxInterval
   134  	}
   135  	log.Info("show", "monitor Interval", monitorInterval)
   136  	return monitorInterval / secondsPerBlock
   137  }
   138  
   139  func calcIncrease(miner *MinerAccounts, acc1, acc2 []*rpctypes.Account, header *rpctypes.Header) *MinerAccounts {
   140  	type minerAt struct {
   141  		addr    string
   142  		curAcc  *rpctypes.Account
   143  		lastAcc *rpctypes.Account
   144  	}
   145  	miners := map[string]*minerAt{}
   146  	for _, a := range acc1 {
   147  		miners[a.Addr] = &minerAt{a.Addr, a, nil}
   148  	}
   149  	for _, a := range acc2 {
   150  		if _, ok := miners[a.Addr]; ok {
   151  			miners[a.Addr].lastAcc = a
   152  		}
   153  	}
   154  
   155  	totalIncrease := float64(0)
   156  	expectTotalIncrease := float64(0)
   157  	totalFrozen := float64(0)
   158  	for _, v := range miners {
   159  		if v.lastAcc != nil && v.curAcc != nil {
   160  			totalFrozen += float64(v.curAcc.Frozen) / float64(types.Coin)
   161  		}
   162  	}
   163  	ticketTotal := float64(30000 * 10000)
   164  	_, ticketAcc, err := cache.getBalance([]string{"16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp"}, "coins", header.Height)
   165  	if err == nil && len(ticketAcc) == 1 {
   166  		ticketTotal = float64(ticketAcc[0].Balance+ticketAcc[0].Frozen) / float64(types.Coin)
   167  	}
   168  	for k, v := range miners {
   169  		if v.lastAcc != nil && v.curAcc != nil {
   170  			total := v.curAcc.Balance + v.curAcc.Frozen
   171  			increase := total - v.lastAcc.Balance - v.lastAcc.Frozen
   172  			expectIncrease := float64(miner.Blocks) * float64(trcPreBlock) * (float64(v.curAcc.Frozen) / float64(types.Coin)) / ticketTotal
   173  
   174  			m := &MinerAccount{
   175  				Addr:           k,
   176  				Total:          strconv.FormatFloat(float64(total)/float64(types.Coin), 'f', 4, 64),
   177  				Increase:       strconv.FormatFloat(float64(increase)/float64(types.Coin), 'f', 4, 64),
   178  				Frozen:         strconv.FormatFloat(float64(v.curAcc.Frozen)/float64(types.Coin), 'f', 4, 64),
   179  				ExpectIncrease: strconv.FormatFloat(expectIncrease, 'f', 4, 64),
   180  			}
   181  
   182  			//if m.Addr == "1Lw6QLShKVbKM6QvMaCQwTh5Uhmy4644CG" {
   183  			//	log.Info("acount", "Increase", m.Increase, "expectIncrease", m.ExpectIncrease)
   184  			//	fmt.Println(os.Stderr, "Increase", m.Increase, "expectIncrease", m.ExpectIncrease)
   185  			//}
   186  
   187  			// 由于取不到挖矿的交易, 通过预期挖矿数, 推断间隔多少个区块能挖到。
   188  			// 由于挖矿分布的波动, 用双倍的预期能挖到区块的时间间隔来预警
   189  			expectBlocks := (expectIncrease / trcPreBlock)                               // 一个小时预期挖多少个块
   190  			expectMinerInterval := float64(miner.Seconds/secondsPerBlock) / expectBlocks // 预期多少秒可以挖一个块
   191  			moniterInterval := int64(2*expectMinerInterval) + 1
   192  
   193  			m.ExpectMinerBlocks = strconv.FormatFloat(expectBlocks, 'f', 4, 64)
   194  			_, acc, err := cache.getBalance([]string{m.Addr}, "ticket", header.Height-moniterInterval)
   195  			if err != nil || len(acc) == 0 {
   196  				m.MinerTrcDuring = "0.0000"
   197  			} else {
   198  				minerDelta := total - acc[0].Balance - acc[0].Frozen
   199  				m.MinerTrcDuring = strconv.FormatFloat(float64(minerDelta)/float64(types.Coin), 'f', 4, 64)
   200  			}
   201  
   202  			miner.MinerAccounts = append(miner.MinerAccounts, m)
   203  			totalIncrease += float64(increase) / float64(types.Coin)
   204  			expectTotalIncrease += expectIncrease
   205  		}
   206  	}
   207  	miner.TotalIncrease = strconv.FormatFloat(totalIncrease, 'f', 4, 64)
   208  	miner.ExpectTotalIncrease = strconv.FormatFloat(expectTotalIncrease, 'f', 4, 64)
   209  
   210  	return miner
   211  
   212  }