github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/core/ticketcache/ticketids_cache.go (about)

     1  package ticketcache
     2  
     3  import (
     4  	"errors"
     5  	"github.com/PlatONnetwork/PlatON-Go/common"
     6  	"github.com/PlatONnetwork/PlatON-Go/crypto"
     7  	"github.com/PlatONnetwork/PlatON-Go/ethdb"
     8  	"github.com/PlatONnetwork/PlatON-Go/log"
     9  	"github.com/PlatONnetwork/PlatON-Go/p2p/discover"
    10  	"github.com/golang/protobuf/proto"
    11  	"math/big"
    12  	"sort"
    13  	"sync"
    14  	"time"
    15  )
    16  
    17  type TicketCache map[discover.NodeID][]common.Hash
    18  
    19  type Timer struct {
    20  	start time.Time
    21  }
    22  
    23  func (t *Timer) Begin() {
    24  	t.start = time.Now()
    25  	//fmt.Println("Begin=> ", "now: ", time.Now().Nanosecond(), " tCalc: ", t.tCalc)
    26  }
    27  
    28  func (t *Timer) End() float64 {
    29  	//fmt.Println("End=> ", "now: ", time.Now().Nanosecond(), " tCalc: ", t.tCalc)
    30  	tns := time.Since(t.start).Nanoseconds()
    31  	tms := float64(tns) / float64(1e6)
    32  	return tms
    33  
    34  }
    35  
    36  var (
    37  	//error def
    38  	ErrNotfindFromNodeId = errors.New("Not find tickets from node id")
    39  	ErrProbufMarshal     = errors.New("protocol buffer Marshal faile")
    40  	ErrLeveldbPut        = errors.New("level db put faile")
    41  
    42  	//const def
    43  	ticketPoolCacheKey = []byte("ticketPoolCache")
    44  )
    45  
    46  //var ticketidsCache *NumBlocks
    47  
    48  type TicketTempCache struct {
    49  	Cache *NumBlocks
    50  	lock  *sync.Mutex
    51  }
    52  
    53  // global obj of ticket related
    54  var ticketTemp *TicketTempCache
    55  
    56  //func NewTicketIdsCache(db ethdb.Database) *NumBlocks {
    57  func NewTicketIdsCache(db ethdb.Database) *TicketTempCache {
    58  	/*
    59  		append: New votes for ticket purchases
    60  		Del: Node elimination,ticket expired,ticket release
    61  	*/
    62  	//logInfo("NewTicketIdsCache==> Init ticketidsCache call NewTicketIdsCache func")
    63  	timer := Timer{}
    64  	timer.Begin()
    65  	if nil != ticketTemp {
    66  		return ticketTemp
    67  	}
    68  	ticketTemp = &TicketTempCache{
    69  		Cache: &NumBlocks{
    70  			NBlocks: make(map[string]*BlockNodes),
    71  		},
    72  
    73  		lock: &sync.Mutex{},
    74  	}
    75  
    76  	if cache, err := db.Get(ticketPoolCacheKey); nil != err {
    77  		log.Warn("Warn call ticketcache NewTicketIdsCache to get Global Cache by levelDB", "err", err)
    78  	} else {
    79  		log.Info("Call ticketcache NewTicketIdsCache to Unmarshal Global Cache", "Cachelen", len(cache))
    80  		//if err := proto.Unmarshal(cache, ticketidsCache); err != nil {
    81  		if err := proto.Unmarshal(cache, ticketTemp.Cache); err != nil {
    82  			log.Error("Failed call NewTicketIdsCache to Unmarshal Global Cache", "err", err)
    83  			return ticketTemp
    84  		}
    85  	}
    86  	log.Debug("Call ticketcache NewTicketIdsCache finish ...", "ms: ", timer.End())
    87  	return ticketTemp
    88  }
    89  
    90  // Create a ticket cache by blocknumber and blockHash from global temp
    91  func GetNodeTicketsCacheMap(blocknumber *big.Int, blockhash common.Hash) (ret TicketCache) {
    92  	log.Debug("Call ticketcache GetNodeTicketsCacheMap", "blocknumber", blocknumber, "blockhash", blockhash.Hex())
    93  	if ticketTemp != nil {
    94  
    95  		// getting a ticket cache by blocknumber and blockHash from global temp
    96  		ret = ticketTemp.GetNodeTicketsMap(blocknumber, blockhash)
    97  	} else {
    98  		if blocknumber.Cmp(big.NewInt(0)) > 0 {
    99  			log.Warn("Warn call ticketcache GetNodeTicketsCacheMap, the Global ticketTemp instance is nil !!!!!!!!!!!!!!!", "blocknumber", blocknumber.Uint64(), "blockHash", blockhash.Hex())
   100  		}
   101  	}
   102  	return
   103  }
   104  
   105  func GetTicketidsCachePtr() *TicketTempCache {
   106  	return ticketTemp
   107  }
   108  
   109  func Hash(cache TicketCache) (common.Hash, error) {
   110  
   111  	if len(cache) == 0 {
   112  		return common.Hash{}, nil
   113  	}
   114  
   115  	timer := Timer{}
   116  	timer.Begin()
   117  	out, err := proto.Marshal(cache.GetSortStruct())
   118  	if err != nil {
   119  		log.Error("Faile to call ticketcache Hash", ErrProbufMarshal.Error()+":err", err)
   120  		return common.Hash{}, err
   121  	}
   122  	ret := crypto.Keccak256Hash(out)
   123  	log.Debug("Call ticketcache Hash finish...", "proto out len", len(out), "run time  ms: ", timer.End())
   124  	return ret, nil
   125  }
   126  
   127  func (t *TicketTempCache) GetNodeTicketsMap(blocknumber *big.Int, blockhash common.Hash) TicketCache {
   128  	t.lock.Lock()
   129  
   130  	log.Info("Call TicketTempCache GetNodeTicketsMap ...", "blocknumber", blocknumber, "blockhash", blockhash.Hex())
   131  
   132  	notGenesisBlock := blocknumber.Cmp(big.NewInt(0)) > 0
   133  
   134  	/**
   135  	Build a new TicketCache
   136  	This TicketCache will be used in StateDB
   137  	*/
   138  	out := NewTicketCache()
   139  
   140  
   141  	// a map (blocknumber => map[blockHash]map[nodeId][]ticketId)
   142  	// Direct short-circuit if empty
   143  	blockNodes, ok := t.Cache.NBlocks[blocknumber.String()]
   144  	if !ok {
   145  		/*blockNodes = &BlockNodes{}
   146  		// create a new map (map[blockHash]map[nodeId][]ticketId)
   147  		blockNodes.BNodes = make(map[string]*NodeTicketIds)
   148  		// set to cache by current map (map[blockHash]map[nodeId][]ticketId)
   149  		t.Cache.NBlocks[blocknumber.String()] = blockNodes*/
   150  		if notGenesisBlock {
   151  			log.Warn("Warn to GetNodeTicketsMap, TicketCache is empty by blocknumber !!!!! Direct short-circuit", "blocknumber", blocknumber.String(), "blockHash", blockhash.String())
   152  		}
   153  
   154  		t.lock.Unlock()
   155  		return out
   156  	}
   157  
   158  	// a map (blockHash => map[nodeId][]ticketId)
   159  	// Direct short-circuit if empty
   160  	nodeTicketIds, ok := blockNodes.BNodes[blockhash.String()]
   161  	if !ok {
   162  		/*nodeTicketIds = &NodeTicketIds{}
   163  		// create a new map (map[nodeId][]ticketId)
   164  		nodeTicketIds.NTickets = make(map[string]*TicketIds)
   165  		// set to cache by current map (map[nodeId][]ticketId)
   166  		blockNodes.BNodes[blockhash.String()] = nodeTicketIds*/
   167  
   168  		if notGenesisBlock {
   169  			log.Warn("Warn to GetNodeTicketsMap, TicketCache is empty by blockHash !!!!! Direct short-circuit", "blocknumber", blocknumber.String(), "blockHash", blockhash.String())
   170  		}
   171  
   172  		t.lock.Unlock()
   173  		return out
   174  	}
   175  
   176  	// Direct short-circuit if empty
   177  	if nil == nodeTicketIds.NTickets || len(nodeTicketIds.NTickets) == 0 {
   178  
   179  		if notGenesisBlock {
   180  			log.Warn("Warn to GetNodeTicketsMap, TicketCache'NTickets is empty !!!!! Direct short-circuit", "blocknumber", blocknumber.String(), "blockHash", blockhash.String())
   181  		}
   182  
   183  		t.lock.Unlock()
   184  		return out
   185  	}
   186  
   187  	/**
   188  	goroutine task
   189  	build data by global cache (map[nodeId][]ticketId)
   190  	*/
   191  	type result struct {
   192  		key  discover.NodeID
   193  		tids []common.Hash
   194  	}
   195  	resCh := make(chan *result, len(nodeTicketIds.NTickets))
   196  	var wg sync.WaitGroup
   197  	wg.Add(len(nodeTicketIds.NTickets))
   198  
   199  	// key == nodeId
   200  	// value == []ticketId
   201  	for k, v := range nodeTicketIds.NTickets {
   202  		nid, err := discover.HexID(k)
   203  		if err == nil {
   204  			// copy nodeId => []tickId by routine task
   205  			go func(nodeid discover.NodeID, tidslice *TicketIds) {
   206  				// create a new []ticketId
   207  				tids := make([]common.Hash, 0, len(tidslice.TicketId))
   208  
   209  				// recursive to build ticketId  from global slice of ticketId
   210  				for _, tid := range tidslice.TicketId {
   211  					tids = append(tids, common.BytesToHash(tid))
   212  				}
   213  				res := new(result)
   214  				res.key = nodeid
   215  				res.tids = tids
   216  				resCh <- res
   217  				wg.Done()
   218  			}(nid, v)
   219  		} else {
   220  			wg.Done()
   221  			log.Error("Failed to TicketTempCache GetNodeTicketsMap: nodeId to discover.HexID error ", "NodeId", k, "blocknumber", blocknumber.String(), "blockHash", blockhash.String())
   222  		}
   223  	}
   224  	wg.Wait()
   225  	close(resCh)
   226  
   227  	t.lock.Unlock()
   228  
   229  
   230  	for res := range resCh {
   231  		// a map type as nodeId => []ticketId
   232  		out[res.key] = res.tids
   233  	}
   234  	return out
   235  }
   236  
   237  func (t *TicketTempCache) Submit2Cache(blocknumber, blockInterval *big.Int, blockhash common.Hash, in TicketCache) {
   238  	t.lock.Lock()
   239  
   240  	log.Info("Call TicketTempCache Submit2Cache ", "blocknumber", blocknumber.String(), "blockhash", blockhash.Hex(),  "blockInterval", blockInterval, "Before Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   241  
   242  	// first condition blockNumber
   243  	// There are four kinds.
   244  	// 1、data is empty by blockNumber AND indata is not empty; then we will incr indata into global temp
   245  	// 2、data is empty by blockNumber AND indata is empty;     then we direct short-circuit
   246  	// 3、data is not empty by blockNumber AND indata is not empty; then we will update global temp by indata
   247  	// 4、data is not empty by blockNumber AND indata is empty; then we will delete global temp data
   248  	blockNodes, ok := t.Cache.NBlocks[blocknumber.String()]
   249  	if !ok && len(in) != 0{
   250  		blockNodes = &BlockNodes{}
   251  		blockNodes.BNodes = make(map[string]*NodeTicketIds)
   252  	}else if !ok && len(in) == 0 {
   253  		log.Debug("Call TicketTempCache Submit2Cache, origin blockNodes and in map[nodeId][]ticketId is empty !!!! Direct short-circuit", "blockNumber", blocknumber.Uint64(), "blockHash", blockhash.Hex(), "blockInterval", blockInterval, " Before Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   254  
   255  		t.lock.Unlock()
   256  		return
   257  	}
   258  
   259  	// second condition blockHash
   260  	// There are four kinds, too.
   261  	// 1、data is empty by blockHash AND indata is not empty; then we will incr indata into global temp
   262  	// 2、data is empty by blockHash AND indata is empty;     then we direct short-circuit
   263  	// 3、data is not empty by blockHash AND indata is not empty; then we will update global temp by indata
   264  	// 4、data is not empty by blockHash AND indata is empty; then we will delete global temp data
   265  	var exist bool
   266  	var originNodeTicketIds *NodeTicketIds
   267  	if origin, ok := blockNodes.BNodes[blockhash.String()]; ok {
   268  		exist = true
   269  		originNodeTicketIds = origin
   270  	}else if !ok && len(in) == 0 {
   271  
   272  		log.Debug("Call TicketTempCache Submit2Cache,origin nodeTicketIds and in map[nodeId][]ticketId is empty by blockHash !!!! Direct short-circuit", "blockNumber", blocknumber.Uint64(), "blockHash", blockhash.Hex(), "blockInterval", blockInterval, " Before Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   273  
   274  		t.lock.Unlock()
   275  		return
   276  	}
   277  
   278  	// third condition
   279  	// There are three kinds, too.
   280  	// 1、indata is not empty; 	then we will write global temp by indata
   281  	// 2、originNodeTicketIds is empty  AND indata is empty;     		then we direct short-circuit
   282  	// 3、originNodeTicketIds is not empty  AND indata is empty; 		then we will delete global temp data
   283  
   284  	switch  {
   285  	case len(in) != 0:
   286  		// write
   287  
   288  		/** The same block hash data will be overwritten */
   289  		nodeTicketIds := &NodeTicketIds{}
   290  		nodeTicketIds.NTickets = make(map[string]*TicketIds)
   291  		//goroutine task
   292  		type result struct {
   293  			key   discover.NodeID
   294  			value *TicketIds
   295  		}
   296  		resCh := make(chan *result, len(in))
   297  		var wg sync.WaitGroup
   298  		wg.Add(len(in))
   299  		for k, v := range in {
   300  			go func(key discover.NodeID, val []common.Hash) {
   301  				tIds := &TicketIds{}
   302  				for _, va := range val {
   303  					tIds.TicketId = append(tIds.TicketId, va.Bytes())
   304  				}
   305  				res := new(result)
   306  				res.key = key
   307  				res.value = tIds
   308  				resCh <- res
   309  				wg.Done()
   310  			}(k, v)
   311  		}
   312  		wg.Wait()
   313  		close(resCh)
   314  		for res := range resCh {
   315  			nodeTicketIds.NTickets[res.key.String()] = res.value
   316  		}
   317  
   318  		blockNodes.BNodes[blockhash.String()] = nodeTicketIds
   319  		t.Cache.NBlocks[blocknumber.String()] = blockNodes
   320  
   321  		// incr block count
   322  		if !exist {
   323  			t.Cache.BlockCount += 1
   324  		}
   325  
   326  	case (nil == originNodeTicketIds || len(originNodeTicketIds.NTickets) == 0) && len(in) == 0:
   327  		// direct short-circuit
   328  		log.Debug("Call TicketTempCache Submit2Cache,origin nodeTicketIds and in map[nodeId][]ticketId is empty !!!! Direct short-circuit", "blockNumber", blocknumber.Uint64(), "blockHash", blockhash.Hex(), "blockInterval", blockInterval, " Before Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   329  
   330  		t.lock.Unlock()
   331  		return
   332  	case len(originNodeTicketIds.NTickets) != 0 && len(in) == 0:
   333  		// delete
   334  		delete(blockNodes.BNodes, blockhash.String())
   335  		t.Cache.BlockCount -= 1
   336  		if len(blockNodes.BNodes) == 0 {
   337  			delete(t.Cache.NBlocks, blocknumber.String())
   338  		}
   339  	}
   340  
   341  
   342  	// tmp fix TODO
   343  	if big.NewInt(0).Cmp(blockInterval) > 0 {
   344  		log.Error("WARN WARN WARN !!! Call TicketTempCache Submit2Cache FINISH !!!!!! blockInterval is NEGATIVE NUMBER", "blocknumber", blocknumber.String(), "blockhash", blockhash.Hex(), "blockInterval", blockInterval, "After Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   345  		t.lock.Unlock()
   346  		return
   347  	}
   348  
   349  	// blockInterval is the difference of block height between
   350  	// the highest block in memory and the highest block in the chain
   351  	interval := new(big.Int).Add(blockInterval, big.NewInt(30))
   352  
   353  	// del old cache
   354  	// blocknumber: current memory block
   355  	number := new(big.Int).Sub(blocknumber, interval)
   356  	for k := range t.Cache.NBlocks {
   357  		if n, b := new(big.Int).SetString(k, 0); b {
   358  			if n.Cmp(number) < 0 {
   359  
   360  				hashMap, ok := t.Cache.NBlocks[number.String()]
   361  
   362  				delete(t.Cache.NBlocks, k)
   363  				// decr block count
   364  				if ok {
   365  					t.Cache.BlockCount -= uint32(len(hashMap.BNodes))
   366  				}
   367  			}
   368  		}
   369  	}
   370  
   371  
   372  	log.Info("Call TicketTempCache Submit2Cache FINISH !!!!!! ", "blocknumber", blocknumber.String(), "blockhash", blockhash.Hex(), "blockInterval", blockInterval, "After Submit2Cache, then cachelen", len(t.Cache.NBlocks), "block Count", t.Cache.BlockCount)
   373  
   374  	t.lock.Unlock()
   375  }
   376  
   377  func (t *TicketTempCache) Commit(db ethdb.Database, currentBlockNumber *big.Int, currentBlockHash common.Hash) error {
   378  	t.lock.Lock()
   379  
   380  	timer := Timer{}
   381  	timer.Begin()
   382  
   383  	// TODO tmp fix
   384  	/*interval := new(big.Int).Sub(currentBlockNumber, big.NewInt(30))
   385  	log.Info("Call TicketTempCache Commit, Delete Global TicketIdsTemp key by", "currentBlockNumber", currentBlockNumber, "after calc interval", interval)
   386  	for k := range t.Cache.NBlocks {
   387  		if n, b := new(big.Int).SetString(k, 0); b {
   388  			if n.Cmp(interval) < 0 {
   389  				delete(t.Cache.NBlocks, k)
   390  			}
   391  		}
   392  	}*/
   393  
   394  	log.Info("Call TicketTempCache Commit, Delete Global TicketIdsTemp key FINISH !!!!", "currentBlockNumber", currentBlockNumber, "currentBlockHash", currentBlockHash.Hex(), "remian size after delete, then cachelen", len(t.Cache.NBlocks))
   395  
   396  	out, err := proto.Marshal(t.Cache)
   397  
   398  	if err != nil {
   399  		log.Error("Failted to TicketPoolCache Commit", "ErrProbufMarshal: err", err.Error())
   400  		t.lock.Unlock()
   401  		return ErrProbufMarshal
   402  	}
   403  	log.Info("Call TicketPoolCache Commit", "cachelen", len(t.Cache.NBlocks), "outlen", len(out))
   404  	t.lock.Unlock()
   405  
   406  	if err := db.Put(ticketPoolCacheKey, out); err != nil {
   407  		log.Error("Failed to call TicketPoolCache Commit: level db put faile", "err", err.Error())
   408  		return ErrLeveldbPut
   409  	}
   410  	log.Info("Call TicketPoolCache Commit run time ", "ms: ", timer.End())
   411  	return nil
   412  }
   413  
   414  func NewTicketCache() TicketCache {
   415  	return make(TicketCache)
   416  }
   417  
   418  func (tc TicketCache) AppendTicketCache(nodeid discover.NodeID, tids []common.Hash) {
   419  	value, ok := tc[nodeid]
   420  	if !ok {
   421  		log.Warn("Warn to AppendTicketCache, the ticketIds is empty !!!!", "nodeId", nodeid.String())
   422  		value = make([]common.Hash, 0)
   423  	}
   424  	value = append(value, tids...)
   425  	tc[nodeid] = value
   426  }
   427  
   428  func (tc TicketCache) GetTicketCache(nodeid discover.NodeID) ([]common.Hash, error) {
   429  	tids, ok := tc[nodeid]
   430  	if !ok {
   431  		log.Warn("Warn to GetTicketCache, the ticketIds is empty !!!!", "nodeId", nodeid.String())
   432  		return nil, ErrNotfindFromNodeId
   433  	}
   434  	ret := make([]common.Hash, len(tids))
   435  	copy(ret, tids)
   436  	return ret, nil
   437  }
   438  
   439  func (tc TicketCache) RemoveTicketCache(nodeid discover.NodeID, tids []common.Hash) error {
   440  	cache, ok := tc[nodeid]
   441  	if !ok {
   442  		log.Warn("Warn to RemoveTicketCache, the ticketIds is empty !!!!", "nodeId", nodeid.String())
   443  		return ErrNotfindFromNodeId
   444  	}
   445  	mapTIds := make(map[common.Hash]common.Hash)
   446  	for _, id := range tids {
   447  		mapTIds[id] = id
   448  	}
   449  	for i := 0; i < len(cache); i++ {
   450  		if _, ok := mapTIds[cache[i]]; ok {
   451  			cache = append(cache[:i], cache[i+1:]...)
   452  			i--
   453  		}
   454  	}
   455  	if len(cache) > 0 {
   456  		tc[nodeid] = cache
   457  	} else {
   458  		delete(tc, nodeid)
   459  	}
   460  	return nil
   461  }
   462  
   463  func (tc TicketCache) TCount(nodeid discover.NodeID) uint64 {
   464  	return uint64(len(tc[nodeid]))
   465  }
   466  
   467  // copy a cache as (nodeId => []ticketId)
   468  func (tc TicketCache) TicketCaceheSnapshot() TicketCache {
   469  
   470  	// create a new cache
   471  	ret := NewTicketCache()
   472  
   473  	// copy data from origin cache
   474  	for nodeid, tids := range tc {
   475  
   476  		// []ticketId
   477  		arr := make([]common.Hash, len(tids))
   478  		copy(arr, tids)
   479  		ret[nodeid] = arr
   480  	}
   481  	return ret
   482  }
   483  
   484  func (tc TicketCache) GetSortStruct() *SortCalcHash {
   485  	sc := &SortCalcHash{}
   486  	sc.Nodeids = make([]string, 0, len(tc))
   487  	sc.Tids = make([]*TicketIds, 0, len(tc))
   488  	for k := range tc {
   489  		sc.Nodeids = append(sc.Nodeids, k.String())
   490  	}
   491  	sort.Strings(sc.Nodeids)
   492  	for _, k := range sc.Nodeids {
   493  		nodeid, err := discover.HexID(k)
   494  		if err == nil {
   495  			tids := &TicketIds{}
   496  			tids.TicketId = make([][]byte, 0, len(tc[nodeid]))
   497  			for _, tid := range tc[nodeid] {
   498  				tids.TicketId = append(tids.TicketId, tid.Bytes())
   499  			}
   500  			sc.Tids = append(sc.Tids, tids)
   501  		} else {
   502  			log.Error("Failed to TicketCache GetSortStruct: discover.HexID error ", "err", err, "hex", k)
   503  		}
   504  	}
   505  	return sc
   506  }