github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/radar/mainchain/league_consume.go (about) 1 package mainchain 2 3 import ( 4 "math/big" 5 "sync" 6 "time" 7 8 "fmt" 9 10 "log" 11 12 "github.com/ahmetb/go-linq" 13 //"github.com/vechain/thor/block" 14 "github.com/sixexorg/magnetic-ring/common" 15 maintypes "github.com/sixexorg/magnetic-ring/core/mainchain/types" 16 orgtypes "github.com/sixexorg/magnetic-ring/core/orgchain/types" 17 "github.com/sixexorg/magnetic-ring/crypto" 18 "github.com/sixexorg/magnetic-ring/errors" 19 "github.com/sixexorg/magnetic-ring/store/mainchain/extstates" 20 "github.com/sixexorg/magnetic-ring/store/orgchain/genesis" 21 "github.com/sixexorg/magnetic-ring/store/storelaw" 22 ) 23 24 type LeagueConsumer struct { 25 m sync.RWMutex 26 pubkey crypto.PublicKey //init from genesis block 27 leagueId common.Address 28 //currentBlock *orgtypes.Block //the block in analysis 29 currBlockHash common.Hash //1 30 curHeight uint64 //1 31 lockerr error // for league lock and this signal signal indicates that the consumer will not validate new blocks 32 lastHeight uint64 //lastheight change when determine how much height to cut 33 blockPool map[uint64]*orgtypes.Block //the receive block into here and remove the map key which less than lastHeight 34 mainTxUsed common.HashArray //Cache the main chain block reference of the current compute blockοΌit's persisted to the database at the end 35 mainTxsNew map[uint64]maintypes.Transactions //newly produced main chain txοΌDelete after generateMainTx(func) or checkLeagueResult(func) 36 receiveSign chan uint64 //notice end 37 adapter *ConsumerAdapter 38 willdo uint64 39 //needRemove []uint64 40 executing bool 41 } 42 43 //receiveBlock is shunt the block to its corresponding location, which requires special handling if it is a genesis block 44 //func (this *LeagueConsumer) receiveBlock(block *orgtypes.Block, teller Teller, initTeller InitTellerfunc) { 45 func (this *LeagueConsumer) receiveBlock(block *orgtypes.Block) { 46 if block.Header.Height <= this.currentHeight() { 47 return 48 } 49 if block.Header.Height == 1 { 50 if this.pubkey != nil { 51 return 52 } 53 //todo Verify the circle tx of the public chain and initialize the creation data 54 } else { 55 if !this.authenticate(block) { 56 return 57 } 58 } 59 fmt.Println("π‘ receiveBlock") 60 this.fillBlockPool(block) 61 } 62 func (this *LeagueConsumer) commitLastHeight() { 63 if this.willdo != 0 { 64 willdo := this.willdo 65 last := this.getLastHeight() 66 this.setLastHeight(willdo) 67 this.removeMainNewBefore(last, willdo) 68 fmt.Println("π‘ commitLastHeight ", last, willdo) 69 } 70 } 71 func (this *LeagueConsumer) rollbackWillDo() { 72 this.m.Lock() 73 defer this.m.Unlock() 74 this.willdo = 0 75 } 76 77 func (this *LeagueConsumer) generateMainTx(right uint64) (mtc *MainTxCradle, err error) { 78 if this.getLastHeight() >= right { 79 return nil, errors.ERR_RADAR_NOT_FOUNT 80 } 81 if this.currentHeight() >= right { 82 return this.generateMainTxPart1(this.getLastHeight()+1, right) 83 } 84 return nil, errors.ERR_RADAR_NOT_FOUNT 85 /* if this.currentHeight() < right { 86 if this.lockerr != nil { 87 //TODO Lock circle 88 return this.generateMainTxPart1(this.getLastHeight()+1, this.currentHeight(), maintypes.LockLeague) 89 90 } else { 91 return nil, errors.ERR_RADAR_M_HEIGHT_BLOCKING 92 } 93 } else { 94 return this.generateMainTxPart1(this.getLastHeight()+1, right, maintypes.ConsensusLeague) 95 }*/ 96 } 97 func (this *LeagueConsumer) generateMainTxPart1(left, right uint64) (*MainTxCradle, error) { 98 hashes, gasSum, err := this.adapter.Ledger.GetBlockHashSpan(this.leagueId, left, right) 99 if err != nil { 100 return nil, err 101 } 102 fmt.Printf("π‘ π generateMainTxPart1 left:%d right:%d last:%d hashes:%v \n", left, right, this.getLastHeight(), hashes) 103 tp := maintypes.ConsensusLeague 104 if this.lockerr != nil { 105 if this.currentHeight() == right { 106 tp = maintypes.LockLeague 107 } 108 } 109 110 mtx := &maintypes.Transaction{ 111 Version: maintypes.MainTxVersion, 112 TxType: tp, 113 TxData: &maintypes.TxData{ 114 StartHeight: this.getLastHeight() + 1, 115 EndHeight: right, 116 BlockRoot: hashes.GetHashRoot(), 117 LeagueId: this.leagueId, 118 Fee: gasSum, 119 }, 120 } 121 if tp == maintypes.LockLeague { 122 mtx.TxData.UnlockEnergy = big.NewInt(10000) 123 } 124 txCount := 0 125 for i := left; i <= right; i++ { 126 txCount += this.mainTxsNew[i].Len() 127 } 128 referenceTxs := make(maintypes.Transactions, 0, txCount) 129 //removeK := make([]uint64, 0, right-left+1) 130 for i := left; i <= right; i++ { 131 referenceTxs = append(referenceTxs, this.mainTxsNew[i]...) 132 } 133 mtc := &MainTxCradle{ 134 LeagueId: this.leagueId, 135 Check: mtx, 136 ReferenceTxs: referenceTxs, 137 } 138 //this.needRemove = removeK 139 this.willdo = right 140 //TODO update 141 //this.setLastHeight(right) 142 //this.removeMainNewBefore(removeK) 143 return mtc, nil 144 } 145 146 //RegisterConsume is controls the life cycle of a single round operation 147 func (this *LeagueConsumer) registerConsume(setStopSignal func(leagueId common.Address, tx *maintypes.Transaction), getLightHouse func() bool) { 148 if this.executing { 149 return 150 } 151 this.start() 152 go func() { 153 //TODO FOR TEST 154 //this.clearHistorical() 155 for height := range this.receiveSign { 156 if height == this.currentHeight()+1 { 157 err := this.consume() 158 if err != nil { 159 this.lockerr = err 160 return 161 } 162 } else if height == 0 { 163 tx, err := this.createMainChainTx() 164 if err != nil { 165 //todo log 166 log.Println("func registerConsume createMainChainTx err ", err.Error()) 167 } 168 setStopSignal(this.leagueId, tx) 169 this.stop() 170 return 171 } 172 } 173 }() 174 175 go func() { 176 //todo This is the first use here. If you need to optimize the stop later, you can optimize it globally. Otherwise, this poll will increase in disguise. 177 for { 178 if getLightHouse() { 179 this.receiveSign <- 0 180 } 181 time.Sleep(time.Second) 182 } 183 }() 184 } 185 186 func (this *LeagueConsumer) consume() error { 187 //fmt.Println("π π‘ consume 0 start block height") 188 block := this.findBlockPool(this.currentHeight() + 1) 189 if block != nil { 190 fmt.Println("π π‘ consume 1 start block height", block.Header.Height) 191 blkInfo, err := this.verifyBlock(block) 192 if err != nil { 193 fmt.Println("π π‘ consume 2 lock", block.Header.Height, ",err:", err) 194 //log.Println("func consume err ", err.Error()) 195 /*if block.Header.Height == 1 { 196 return 197 }*/ 198 return err 199 } 200 fmt.Println("π π‘ consume 2.5 saveall", blkInfo.Block.Header.Height) 201 //todo Confirm tx generation, necessary data storage 202 err = this.saveAll(blkInfo) 203 fmt.Println("π π‘ consume 3 saveall", err) 204 if err != nil { 205 //log.Println("func consume err ", err.Error()) 206 return err 207 } 208 //this.blockHashes = append(this.blockHashes, block.Hash()) 209 this.setCurrentBlock(block) 210 fmt.Println("π π‘ consume 4 ok") 211 } 212 return nil 213 } 214 215 //VerifyBlock is verify the block and compute all the details, which must be exactly the same as the circle validation 216 func (this *LeagueConsumer) verifyBlock(block *orgtypes.Block) (*storelaw.OrgBlockInfo, error) { 217 fmt.Println("π« verifyBlock bonus ", block.Header.Bonus) 218 if this.currentHeight()+1 != block.Header.Height { 219 return nil, errors.ERR_EXTERNAL_HEIGHT_NOT_MATCH 220 } 221 if this.currentBlockHash() != block.Header.PrevBlockHash { 222 return nil, fmt.Errorf("%s,the real prevHash is %s ,the wrong is %s", 223 errors.ERR_EXTERNAL_PREVBLOCKHASH_DIFF, 224 this.currentBlockHash().String(), 225 block.Header.PrevBlockHash.String()) 226 } 227 //todo more detailed description of the error 228 if block.Header.Height == 1 { 229 tx, height, err := this.adapter.FuncGenesis(block.Header.LeagueId) 230 if err != nil { 231 return nil, err 232 } 233 234 blockRef, err := this.adapter.FuncBlk(height) 235 if err != nil { 236 return nil, err 237 } 238 239 blockGenesis, sts := genesis.GensisBlock(tx, blockRef.Timestamp) 240 this.leagueId = block.Header.LeagueId 241 stsExt := make(storelaw.AccountStaters, 1) 242 ut := big.NewInt(0) 243 for _, v := range sts { 244 stsExt[0] = &extstates.LeagueAccountState{ 245 Address: v.Account(), 246 LeagueId: block.Header.LeagueId, 247 Height: block.Header.Height, 248 Data: &extstates.Account{ 249 Nonce: v.Nonce(), 250 Balance: v.Balance(), 251 EnergyBalance: v.Energy(), 252 BonusHeight: 0, 253 }, 254 } 255 ut.Add(ut, v.Balance()) 256 } 257 258 /*blockGenesis.Header.Coinbase = block.Header.Coinbase 259 blockGenesis.Header.Extra = block.Header.Extra*/ 260 261 blkInfo := &storelaw.OrgBlockInfo{ 262 Block: blockGenesis, 263 AccStates: stsExt, 264 UT: ut, 265 FeeSum: big.NewInt(0), 266 } 267 return blkInfo, nil 268 } else { 269 var errCheck error 270 this.initializeMainTxHeight(block.Header.Height, block.Transactions.Len()/5) 271 for _, v := range block.Transactions { 272 errCheck = this.checkReferencedTransaction(v) 273 if errCheck != nil { 274 return nil, errCheck 275 } 276 if v.TxType != orgtypes.VoteIncreaseUT { 277 this.orgTxBirthMainTx(v, block.Header.Height, block.Header.LeagueId, nil) 278 } 279 } 280 blkInfo, err := this.adapter.FuncValidate(block, this.adapter.Ledger) 281 282 fmt.Printf("π« cmp β \n"+ 283 "π«β version %v %v \n"+ 284 "π«β prevHash %s %s \n"+ 285 "π«β leagueId %s %s \n"+ 286 "π«β txRoot %s %s \n"+ 287 "π«β stateRoot %s %s \n"+ 288 "π«β receipt %s %s \n"+ 289 "π«β timestamp %d %d \n"+ 290 "π«β height %d %d \n"+ 291 "π«β difficulty %d %d \n"+ 292 "π«β coinbase %s %s \n"+ 293 "π«β extra %s %s\n"+ 294 "π«β hash %s %s \n", 295 blkInfo.Block.Header.Version, block.Header.Version, 296 blkInfo.Block.Header.PrevBlockHash.String(), block.Header.PrevBlockHash.String(), 297 blkInfo.Block.Header.LeagueId.ToString(), block.Header.LeagueId.ToString(), 298 blkInfo.Block.Header.TxRoot.String(), block.Header.TxRoot.String(), 299 blkInfo.Block.Header.StateRoot.String(), block.Header.StateRoot.String(), 300 blkInfo.Block.Header.ReceiptsRoot.String(), block.Header.ReceiptsRoot.String(), 301 blkInfo.Block.Header.Timestamp, block.Header.Timestamp, 302 blkInfo.Block.Header.Height, block.Header.Height, 303 blkInfo.Block.Header.Difficulty.Uint64(), block.Header.Difficulty.Uint64(), 304 blkInfo.Block.Header.Coinbase.ToString(), block.Header.Coinbase.ToString(), 305 string(blkInfo.Block.Header.Extra), string(block.Header.Extra), 306 blkInfo.Block.Hash().String(), block.Hash().String(), 307 ) 308 if err != nil { 309 return nil, err 310 } 311 fmt.Println("π« voteFirstPass num ", blkInfo.VoteFirstPass.Len()) 312 for _, v := range blkInfo.VoteFirstPass { 313 ff := func(leagueId common.Address) (rate uint32) { 314 tx, _, _ := this.adapter.Ledger.LightLedger.GetTxByLeagueId(leagueId) 315 return tx.TxData.Rate 316 } 317 this.orgTxBirthMainTx(v, block.Header.Height, block.Header.LeagueId, ff) 318 319 } 320 return blkInfo, nil 321 } 322 } 323 func (this *LeagueConsumer) setBlockCurrentBySync(hash common.Hash, height uint64) { 324 this.m.Lock() 325 defer this.m.Unlock() 326 if this.curHeight+1 == height { 327 this.curHeight = height 328 this.currBlockHash = hash 329 } 330 } 331 332 func (this *LeagueConsumer) saveAll(blkInfo *storelaw.OrgBlockInfo) error { 333 err := this.adapter.Ledger.SaveAll(blkInfo, this.mainTxUsed) 334 if err != nil { 335 return err 336 } 337 this.mainTxUsed = make(common.HashArray, 0, 50) 338 this.clearBlockPool(blkInfo.Block.Header.Height) 339 return nil 340 } 341 342 func (this *LeagueConsumer) createMainChainTx() (*maintypes.Transaction, error) { 343 var ( 344 tx *maintypes.Transaction 345 err error = nil 346 ) 347 left := this.getLastHeight() 348 right := this.currentHeight() 349 if right > left { 350 hashes, gasSum, err := this.adapter.Ledger.GetBlockHashSpan(this.leagueId, left+1, right) 351 if err != nil { 352 return nil, err 353 } 354 //log.Println("createMainChainTx func: offset=>", l) 355 txdata := &maintypes.TxData{ 356 LeagueId: this.leagueId, 357 StartHeight: left + 1, 358 EndHeight: right, 359 BlockRoot: hashes.GetHashRoot(), 360 Energy: gasSum, 361 } 362 if this.lockerr != nil { 363 txdata.UnlockEnergy = unlockEnergy 364 txdata.Msg = common.Hash{} //TODO 365 tx, err = maintypes.NewTransaction(maintypes.LockLeague, 0x01, txdata) 366 367 } else { 368 tx, err = maintypes.NewTransaction(maintypes.ConsensusLeague, 0x01, txdata) 369 } 370 } 371 return tx, err 372 } 373 374 func (this *LeagueConsumer) checkGenesisBlock(block *orgtypes.Block) { 375 if this.currentHeight() != 1 { 376 return 377 } 378 //this.pubkey = &ecdsa.PrivateKey{} 379 //TODO get tx from mainchain 380 381 } 382 383 //checkReferencedTransaction is only check the transaction from the main chain to the league 384 func (this *LeagueConsumer) checkReferencedTransaction(tx *orgtypes.Transaction) error { 385 if containMainType(tx.TxType) { 386 387 if this.contains(tx.TxData.TxHash) { 388 return errors.ERR_EXTERNAL_MAIN_TX_USED 389 } 390 bl := this.adapter.Ledger.MainTxUsedExist(tx.Hash()) 391 if bl { 392 return errors.ERR_EXTERNAL_MAIN_TX_USED 393 } 394 this.mainTxUsed = append(this.mainTxUsed, tx.TxData.TxHash) 395 txMain, _, err := this.adapter.FuncTx(tx.TxData.TxHash) 396 if err != nil { 397 return err 398 } 399 400 err = checkOrgTx(txMain, tx) 401 return err 402 /*//todo need receipt 403 if tx.TxType == orgtypes.Join && 404 txMain.TxType == maintypes.JoinLeague && 405 txMain.TxData.From == tx.TxData.From { 406 return nil 407 408 } else if tx.TxType == orgtypes.EnergyFromMain && 409 txMain.TxType == maintypes.EnergyToLeague && 410 txMain.TxData.From == tx.TxData.From && 411 txMain.TxData.Energy.Cmp(tx.TxData.Energy) == 0 { 412 return nil 413 } 414 return errors.ERR_EXTERNAL_TX_REFERENCE_WRONG*/ 415 } 416 return nil 417 } 418 func (this *LeagueConsumer) contains(txHash common.Hash) bool { 419 return linq.From(this.mainTxUsed).Contains(txHash) 420 } 421 422 //TODO Verify that the block is the one that the circle itself gave. 423 func (this *LeagueConsumer) authenticate(block *orgtypes.Block) bool { 424 return true 425 } 426 427 func (this *LeagueConsumer) setCurrentBlock(block *orgtypes.Block) { 428 this.m.Lock() 429 defer this.m.Unlock() 430 this.currBlockHash = block.Hash() 431 this.curHeight = block.Header.Height 432 fmt.Println("π« setCurrentBlock height ", block.Header.Height) 433 /*this.adapter.initTeller() 434 this.adapter.P2pTlr.Tell(&common.NodeLH{ 435 NodeId: this.adapter.NodeId, 436 LeagueId: this.leagueId, 437 Height: block.Header.Height, 438 })*/ 439 //fmt.Printf("π« π« send nodeLH nodeId:%s leagueId:%s height:%d\n", this.adapter.NodeId, this.leagueId.ToString(), block.Header.Height) 440 } 441 func (this *LeagueConsumer) currentHeight() uint64 { 442 this.m.RLock() 443 defer this.m.RUnlock() 444 return this.curHeight 445 } 446 func (this *LeagueConsumer) setLastHeight(height uint64) { 447 this.m.Lock() 448 defer this.m.Unlock() 449 this.lastHeight = height 450 this.willdo = 0 451 } 452 func (this *LeagueConsumer) getLastHeight() uint64 { 453 this.m.RLock() 454 defer this.m.RUnlock() 455 return this.lastHeight 456 } 457 458 func (this *LeagueConsumer) currentBlockHash() common.Hash { 459 this.m.RLock() 460 defer this.m.RUnlock() 461 return this.currBlockHash 462 } 463 func (this *LeagueConsumer) UnlockLeague() { 464 if this.lockerr != nil { 465 this.lockerr = nil 466 } 467 } 468 469 func (this *LeagueConsumer) start() { 470 this.executing = true 471 } 472 func (this *LeagueConsumer) stop() { 473 this.executing = false 474 } 475 func containMainType(key orgtypes.TransactionType) bool { 476 _, ok := txTypeForReference[key] 477 return ok 478 } 479 480 func containTransferToMainTx(key orgtypes.TransactionType) bool { 481 _, ok := txTypeForTransfer[key] 482 return ok 483 } 484 func (this *LeagueConsumer) initializeMainTxHeight(height uint64, cap int) { 485 this.mainTxsNew[height] = make(maintypes.Transactions, 0, cap) 486 } 487 func (this *LeagueConsumer) orgTxBirthMainTx(tx *orgtypes.Transaction, height uint64, leagueId common.Address, f func(leagueId common.Address) (rate uint32)) { 488 mainTx := orgTxBirthMainTx(tx, height, leagueId, f) 489 if mainTx != nil { 490 fmt.Println("π« orgTxBirthMainTx append txType ", mainTx.TxType, "height ", height) 491 this.appendMainNew(mainTx, height) 492 } 493 } 494 func (this *LeagueConsumer) appendMainNew(mainTx *maintypes.Transaction, height uint64) { 495 this.m.Lock() 496 defer this.m.Unlock() 497 this.mainTxsNew[height] = append(this.mainTxsNew[height], mainTx) 498 } 499 func (this *LeagueConsumer) removeMainNewBefore(start, end uint64) { 500 this.m.Lock() 501 defer this.m.Unlock() 502 for i := start; i <= end; i++ { 503 delete(this.mainTxsNew, i) 504 } 505 } 506 func (this *LeagueConsumer) fillBlockPool(block *orgtypes.Block) { 507 this.m.Lock() 508 defer this.m.Unlock() 509 if this.blockPool[block.Header.Height] == nil { 510 this.blockPool[block.Header.Height] = block 511 /*opd := &p2pcommon.OrgPendingData{ 512 BANodeSrc: false, 513 OrgId: block.Header.LeagueId, 514 Block: block, 515 }*/ 516 this.receiveSign <- block.Header.Height 517 /* this.adapter.initTeller() 518 this.adapter.P2pTlr.Tell(opd)*/ 519 520 /* actor, err := bactor.GetActorPid(bactor.P2PACTOR) 521 if err != nil { 522 panic(err) 523 } 524 actor.Tell(opd)*/ 525 } 526 } 527 func (this *LeagueConsumer) findBlockPool(height uint64) *orgtypes.Block { 528 this.m.RLock() 529 defer this.m.RUnlock() 530 return this.blockPool[height] 531 } 532 func (this *LeagueConsumer) clearBlockPool(targetHeight uint64) { 533 this.m.Lock() 534 defer this.m.Unlock() 535 for k, _ := range this.blockPool { 536 if k <= targetHeight { 537 delete(this.blockPool, k) 538 } 539 } 540 }