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 }