github.com/turingchain2020/turingchain@v1.1.21/blockchain/chain.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 /* 6 Package blockchain 实现区块链模块,包含区块链存储 7 */ 8 package blockchain 9 10 import ( 11 "fmt" 12 "sync" 13 "sync/atomic" 14 "time" 15 16 "github.com/turingchain2020/turingchain/common" 17 dbm "github.com/turingchain2020/turingchain/common/db" 18 log "github.com/turingchain2020/turingchain/common/log/log15" 19 "github.com/turingchain2020/turingchain/queue" 20 "github.com/turingchain2020/turingchain/types" 21 lru "github.com/hashicorp/golang-lru" 22 ) 23 24 //var 25 var ( 26 //cache 存贮的block个数 27 zeroHash [32]byte 28 InitBlockNum int64 = 10240 //节点刚启动时从db向index和bestchain缓存中添加的blocknode数,和blockNodeCacheLimit保持一致 29 chainlog = log.New("module", "blockchain") 30 FutureBlockDelayTime int64 = 1 31 ) 32 33 const ( 34 maxFutureBlocks = 256 35 36 maxActiveBlocks = 1024 37 38 // 默认轻广播组装临时区块缓存, 100M 39 maxActiveBlocksCacheSize = 100 40 41 defaultChunkBlockNum = 1 42 ) 43 44 //BlockChain 区块链结构体 45 type BlockChain struct { 46 client queue.Client 47 blockCache *BlockCache 48 txHeightCache txHeightCacheType 49 50 // 永久存储数据到db中 51 blockStore *BlockStore 52 push *Push 53 //cache 缓存block方便快速查询 54 cfg *types.BlockChain 55 syncTask *Task 56 downLoadTask *Task 57 chunkRecordTask *Task 58 59 query *Query 60 61 //记录收到的最新广播的block高度,用于节点追赶active链 62 rcvLastBlockHeight int64 63 64 //记录本节点已经同步的block高度,用于节点追赶active链,处理节点分叉不同步的场景 65 synBlockHeight int64 66 67 //记录peer的最新block高度,用于节点追赶active链 68 peerList PeerInfoList 69 recvwg *sync.WaitGroup 70 tickerwg *sync.WaitGroup 71 reducewg *sync.WaitGroup 72 73 synblock chan struct{} 74 quit chan struct{} 75 isclosed int32 76 runcount int32 77 isbatchsync int32 78 firstcheckbestchain int32 //节点启动之后首次检测最优链的标志 79 80 // 孤儿链 81 orphanPool *OrphanPool 82 // 主链或者侧链的blocknode信息 83 index *blockIndex 84 //当前主链 85 bestChain *chainView 86 87 chainLock sync.RWMutex 88 //blockchain的启动时间 89 startTime time.Time 90 91 //标记本节点是否已经追赶上主链 92 isCaughtUp bool 93 94 //同步block批量写数据库时,是否需要刷盘的标志。 95 //非固态硬盘的电脑可以关闭刷盘,提高同步性能. 96 cfgBatchSync bool 97 98 //记录可疑故障节点peer信息 99 //在ExecBlock执行失败时记录对应的peerid以及故障区块的高度和hash 100 faultPeerList map[string]*FaultPeerInfo 101 102 bestChainPeerList map[string]*BestPeerInfo 103 104 //记录futureblocks 105 futureBlocks *lru.Cache // future blocks are broadcast later processing 106 107 //downLoad block info 108 downLoadInfo *DownLoadInfo 109 downloadMode int //当本节点落后很多时,可以先下载区块到db,启动单独的goroutine去执行block 110 111 isRecordBlockSequence bool //是否记录add或者del block的序列,方便blcokchain的恢复通过记录的序列表 112 enablePushSubscribe bool //是否允许推送订阅 113 isParaChain bool //是否是平行链。平行链需要记录Sequence信息 114 isStrongConsistency bool 115 //lock 116 synBlocklock sync.Mutex 117 peerMaxBlklock sync.Mutex 118 castlock sync.Mutex 119 ntpClockSynclock sync.Mutex 120 faultpeerlock sync.Mutex 121 bestpeerlock sync.Mutex 122 downLoadlock sync.Mutex 123 downLoadModeLock sync.Mutex 124 isNtpClockSync bool //ntp时间是否同步 125 126 //cfg 127 MaxFetchBlockNum int64 //一次最多申请获取block个数 128 TimeoutSeconds int64 129 blockSynInterVal time.Duration 130 131 failed int32 132 133 blockOnChain *BlockOnChain 134 onChainTimeout int64 135 136 //记录当前已经连续的最高高度 137 maxSerialChunkNum int64 138 processingGenChunk int32 139 processingDeleteChunk int32 140 deleteChunkCount int64 141 } 142 143 //New new 144 func New(cfg *types.TuringchainConfig) *BlockChain { 145 mcfg := cfg.GetModuleConfig().BlockChain 146 futureBlocks, err := lru.New(maxFutureBlocks) 147 if err != nil { 148 panic("when New BlockChain lru.New return err") 149 } 150 151 if atomic.LoadInt64(&mcfg.ChunkblockNum) == 0 { 152 atomic.StoreInt64(&mcfg.ChunkblockNum, defaultChunkBlockNum) 153 } 154 155 blockchain := &BlockChain{ 156 rcvLastBlockHeight: -1, 157 synBlockHeight: -1, 158 maxSerialChunkNum: -1, 159 peerList: nil, 160 cfg: mcfg, 161 recvwg: &sync.WaitGroup{}, 162 tickerwg: &sync.WaitGroup{}, 163 reducewg: &sync.WaitGroup{}, 164 165 syncTask: newTask(300 * time.Second), //考虑到区块交易多时执行耗时,需要延长task任务的超时时间 166 downLoadTask: newTask(300 * time.Second), 167 chunkRecordTask: newTask(120 * time.Second), 168 169 quit: make(chan struct{}), 170 synblock: make(chan struct{}, 1), 171 orphanPool: NewOrphanPool(cfg), 172 index: newBlockIndex(), 173 isCaughtUp: false, 174 isbatchsync: 1, 175 firstcheckbestchain: 0, 176 cfgBatchSync: mcfg.Batchsync, 177 faultPeerList: make(map[string]*FaultPeerInfo), 178 bestChainPeerList: make(map[string]*BestPeerInfo), 179 futureBlocks: futureBlocks, 180 downLoadInfo: &DownLoadInfo{}, 181 isNtpClockSync: true, 182 MaxFetchBlockNum: 128 * 6, //一次最多申请获取block个数 183 TimeoutSeconds: 2, 184 downloadMode: fastDownLoadMode, 185 blockOnChain: &BlockOnChain{}, 186 onChainTimeout: 0, 187 } 188 blockchain.initConfig(cfg) 189 blockchain.blockCache = newBlockCache(cfg, defaultBlockHashCacheSize) 190 return blockchain 191 } 192 193 func (chain *BlockChain) initConfig(cfg *types.TuringchainConfig) { 194 mcfg := cfg.GetModuleConfig().BlockChain 195 196 if mcfg.MaxFetchBlockNum > 0 { 197 chain.MaxFetchBlockNum = mcfg.MaxFetchBlockNum 198 } 199 200 if mcfg.TimeoutSeconds > 0 { 201 chain.TimeoutSeconds = mcfg.TimeoutSeconds 202 } 203 204 if mcfg.DefCacheSize <= 0 { 205 mcfg.DefCacheSize = 128 206 } 207 208 chain.blockSynInterVal = time.Duration(chain.TimeoutSeconds) 209 chain.isStrongConsistency = mcfg.IsStrongConsistency 210 chain.isRecordBlockSequence = mcfg.IsRecordBlockSequence 211 chain.enablePushSubscribe = mcfg.EnablePushSubscribe 212 chain.isParaChain = mcfg.IsParaChain 213 cfg.S("quickIndex", mcfg.EnableTxQuickIndex) 214 cfg.S("reduceLocaldb", mcfg.EnableReduceLocaldb) 215 216 if mcfg.OnChainTimeout > 0 { 217 chain.onChainTimeout = mcfg.OnChainTimeout 218 } 219 chain.initOnChainTimeout() 220 // 初始化AllowPackHeight 221 initAllowPackHeight(chain.cfg) 222 } 223 224 //Close 关闭区块链 225 func (chain *BlockChain) Close() { 226 //等待所有的写线程退出,防止数据库写到一半被暂停 227 atomic.StoreInt32(&chain.isclosed, 1) 228 229 //退出线程 230 close(chain.quit) 231 232 //等待执行完成 233 for atomic.LoadInt32(&chain.runcount) > 0 { 234 time.Sleep(time.Microsecond) 235 } 236 chain.client.Close() 237 //wait for recvwg quit: 238 chainlog.Info("blockchain wait for recvwg quit") 239 chain.recvwg.Wait() 240 241 //wait for tickerwg quit: 242 chainlog.Info("blockchain wait for tickerwg quit") 243 chain.tickerwg.Wait() 244 245 //wait for reducewg quit: 246 chainlog.Info("blockchain wait for reducewg quit") 247 chain.reducewg.Wait() 248 249 if chain.push != nil { 250 chainlog.Info("blockchain wait for push quit") 251 chain.push.Close() 252 } 253 254 //关闭数据库 255 chain.blockStore.db.Close() 256 chainlog.Info("blockchain module closed") 257 } 258 259 //SetQueueClient 设置队列 260 func (chain *BlockChain) SetQueueClient(client queue.Client) { 261 chain.client = client 262 chain.client.Sub("blockchain") 263 264 blockStoreDB := dbm.NewDB("blockchain", chain.cfg.Driver, chain.cfg.DbPath, chain.cfg.DbCache) 265 chain.blockStore = NewBlockStore(chain, blockStoreDB, client) 266 stateHash := chain.getStateHash() 267 chain.query = NewQuery(blockStoreDB, chain.client, stateHash) 268 if chain.enablePushSubscribe && chain.isRecordBlockSequence { 269 chain.push = newpush(chain.blockStore, chain.blockStore, chain.client.GetConfig()) 270 chainlog.Info("chain push is setup") 271 } 272 273 //startTime 274 chain.startTime = types.Now() 275 // 获取当前最大chunk连续高度 276 chain.maxSerialChunkNum = chain.blockStore.GetMaxSerialChunkNum() 277 278 //recv 消息的处理,共识模块需要获取lastblock从数据库中 279 chain.recvwg.Add(1) 280 //初始化blockchian模块 281 chain.InitBlockChain() 282 go chain.ProcRecvMsg() 283 } 284 285 //Wait for ready 286 func (chain *BlockChain) Wait() {} 287 288 //GetStore only used for test 289 func (chain *BlockChain) GetStore() *BlockStore { 290 return chain.blockStore 291 } 292 293 //GetOrphanPool 获取孤儿链 294 func (chain *BlockChain) GetOrphanPool() *OrphanPool { 295 return chain.orphanPool 296 } 297 298 //InitBlockChain 区块链初始化 299 func (chain *BlockChain) InitBlockChain() { 300 //isRecordBlockSequence配置的合法性检测 301 seqStatus := chain.blockStore.CheckSequenceStatus(chain.isRecordBlockSequence) 302 if seqStatus == seqStatusNeedCreate { 303 chain.blockStore.CreateSequences(100000) 304 } 305 306 //先缓存最新的128个block信息到cache中 307 curheight := chain.GetBlockHeight() 308 chain.InitCache(curheight) 309 310 //获取数据库中最新的10240个区块加载到index和bestview链中 311 beg := types.Now() 312 chain.InitIndexAndBestView() 313 chainlog.Info("InitIndexAndBestView", "cost", types.Since(beg)) 314 315 //获取数据库中最新的区块高度,以及blockchain的数据库版本号 316 curdbver := chain.blockStore.GetDbVersion() 317 if curdbver == 0 && curheight == -1 { 318 curdbver = 1 319 err := chain.blockStore.SetDbVersion(curdbver) 320 //设置失败后恢复成原来的值保持和types.S("dbversion", curdbver)设置的版本一致 321 if err != nil { 322 curdbver = 0 323 chainlog.Error("InitIndexAndBestView SetDbVersion ", "err", err) 324 } 325 } 326 cfg := chain.client.GetConfig() 327 cfg.S("dbversion", curdbver) 328 if !chain.cfg.IsParaChain && chain.cfg.RollbackBlock <= 0 { 329 // 定时检测/同步block 330 go chain.SynRoutine() 331 332 // 定时处理futureblock 333 go chain.UpdateRoutine() 334 } 335 336 if !chain.cfg.DisableShard { 337 chain.tickerwg.Add(1) 338 go chain.chunkProcessRoutine() 339 } 340 341 //初始化默认DownLoadInfo 342 chain.DefaultDownLoadInfo() 343 } 344 345 func (chain *BlockChain) getStateHash() []byte { 346 blockhight := chain.GetBlockHeight() 347 blockdetail, err := chain.GetBlock(blockhight) 348 if err != nil { 349 return zeroHash[:] 350 } 351 if blockdetail != nil { 352 return blockdetail.GetBlock().GetStateHash() 353 } 354 return zeroHash[:] 355 } 356 357 //SendAddBlockEvent blockchain 模块add block到db之后通知mempool 和consense模块做相应的更新 358 func (chain *BlockChain) SendAddBlockEvent(block *types.BlockDetail) (err error) { 359 if chain.client == nil { 360 chainlog.Error("SendAddBlockEvent: chain client not bind message queue.") 361 return types.ErrClientNotBindQueue 362 } 363 if block == nil { 364 chainlog.Error("SendAddBlockEvent block is null") 365 return types.ErrInvalidParam 366 } 367 chainlog.Debug("SendAddBlockEvent", "Height", block.Block.Height) 368 369 chainlog.Debug("SendAddBlockEvent -->>mempool") 370 msg := chain.client.NewMessage("mempool", types.EventAddBlock, block) 371 //此处采用同步发送模式,主要是为了消息在消息队列内部走高速通道,尽快被mempool模块处理 372 Err := chain.client.Send(msg, true) 373 if Err != nil { 374 chainlog.Error("SendAddBlockEvent -->>mempool", "err", Err) 375 } 376 chainlog.Debug("SendAddBlockEvent -->>consensus") 377 378 msg = chain.client.NewMessage("consensus", types.EventAddBlock, block) 379 Err = chain.client.Send(msg, false) 380 if Err != nil { 381 chainlog.Error("SendAddBlockEvent -->>consensus", "err", Err) 382 } 383 chainlog.Debug("SendAddBlockEvent -->>wallet", "height", block.GetBlock().GetHeight()) 384 msg = chain.client.NewMessage("wallet", types.EventAddBlock, block) 385 Err = chain.client.Send(msg, false) 386 if Err != nil { 387 chainlog.Error("SendAddBlockEvent -->>wallet", "err", Err) 388 } 389 return nil 390 } 391 392 //SendBlockBroadcast blockchain模块广播此block到网络中 393 func (chain *BlockChain) SendBlockBroadcast(block *types.BlockDetail) { 394 cfg := chain.client.GetConfig() 395 if chain.client == nil { 396 chainlog.Error("SendBlockBroadcast: chain client not bind message queue.") 397 return 398 } 399 if block == nil { 400 chainlog.Error("SendBlockBroadcast block is null") 401 return 402 } 403 chainlog.Debug("SendBlockBroadcast", "Height", block.Block.Height, "hash", common.ToHex(block.Block.Hash(cfg))) 404 405 msg := chain.client.NewMessage("p2p", types.EventBlockBroadcast, block.Block) 406 err := chain.client.Send(msg, false) 407 if err != nil { 408 chainlog.Error("SendBlockBroadcast", "Height", block.Block.Height, "hash", common.ToHex(block.Block.Hash(cfg)), "err", err) 409 } 410 } 411 412 //GetBlockHeight 获取区块高度 413 func (chain *BlockChain) GetBlockHeight() int64 { 414 return chain.blockStore.Height() 415 } 416 417 //GetBlock 用于获取指定高度的block,首先在缓存中获取,如果不存在就从db中获取 418 func (chain *BlockChain) GetBlock(height int64) (detail *types.BlockDetail, err error) { 419 420 cfg := chain.client.GetConfig() 421 422 var hash []byte 423 if hash, err = chain.blockStore.GetBlockHashByHeight(height); err != nil { 424 return nil, err 425 } 426 427 //从缓存的最新区块中尝试获取,最新区块的add是在执行block流程中处理 428 if detail = chain.blockCache.GetBlockByHash(hash); detail != nil { 429 if len(detail.Receipts) == 0 && len(detail.Block.Txs) != 0 { 430 chainlog.Debug("GetBlockByHash GetBlockByHeight Receipts ==0", "height", height) 431 } 432 return detail, nil 433 } 434 435 if detail, exist := chain.blockStore.GetActiveBlock(string(hash)); exist { 436 return detail, nil 437 } 438 439 //从blockstore db中通过block height获取block 440 detail, err = chain.blockStore.LoadBlock(height, hash) 441 if err != nil { 442 chainlog.Error("GetBlock", "height", height, "LoadBlock err", err) 443 return nil, err 444 } 445 if len(detail.Receipts) == 0 && len(detail.Block.Txs) != 0 { 446 chainlog.Debug("GetBlock LoadBlock Receipts ==0", "height", height) 447 } 448 //缓存到活跃区块中 449 chain.blockStore.AddActiveBlock(string(detail.Block.Hash(cfg)), detail) 450 return detail, nil 451 452 } 453 454 //SendDelBlockEvent blockchain 模块 del block从db之后通知mempool 和consense以及wallet模块做相应的更新 455 func (chain *BlockChain) SendDelBlockEvent(block *types.BlockDetail) (err error) { 456 if chain.client == nil { 457 chainlog.Error("SendDelBlockEvent: chain client not bind message queue.") 458 err := types.ErrClientNotBindQueue 459 return err 460 } 461 if block == nil { 462 chainlog.Error("SendDelBlockEvent block is null") 463 return nil 464 } 465 466 chainlog.Debug("SendDelBlockEvent -->>mempool&consensus&wallet", "height", block.GetBlock().GetHeight()) 467 468 msg := chain.client.NewMessage("consensus", types.EventDelBlock, block) 469 Err := chain.client.Send(msg, false) 470 if Err != nil { 471 chainlog.Debug("SendDelBlockEvent -->>consensus", "err", err) 472 } 473 msg = chain.client.NewMessage("mempool", types.EventDelBlock, block) 474 Err = chain.client.Send(msg, false) 475 if Err != nil { 476 chainlog.Debug("SendDelBlockEvent -->>mempool", "err", err) 477 } 478 msg = chain.client.NewMessage("wallet", types.EventDelBlock, block) 479 Err = chain.client.Send(msg, false) 480 if Err != nil { 481 chainlog.Debug("SendDelBlockEvent -->>wallet", "err", err) 482 } 483 return nil 484 } 485 486 //GetDB 获取DB 487 func (chain *BlockChain) GetDB() dbm.DB { 488 return chain.blockStore.db 489 } 490 491 //InitCache 初始化缓存 492 func (chain *BlockChain) InitCache(currHeight int64) { 493 494 if !chain.client.GetConfig().IsEnable("TxHeight") { 495 chain.txHeightCache = &noneCache{} 496 return 497 } 498 chain.txHeightCache = newTxHashCache(chain, types.HighAllowPackHeight, types.LowAllowPackHeight) 499 // cache history block if exist 500 if currHeight < 0 { 501 return 502 } 503 504 for i := currHeight - chain.cfg.DefCacheSize; i <= currHeight; i++ { 505 if i < 0 { 506 i = 0 507 } 508 block, err := chain.GetBlock(i) 509 if err != nil { 510 panic(fmt.Sprintf("getBlock err=%s, height=%d", err.Error(), i)) 511 } 512 chain.blockCache.AddBlock(block) 513 } 514 515 for i := currHeight - types.HighAllowPackHeight - types.LowAllowPackHeight + 1; i <= currHeight; i++ { 516 if i < 0 { 517 i = 0 518 } 519 block, err := chain.GetBlock(i) 520 if err != nil { 521 panic(err) 522 } 523 chain.txHeightCache.Add(block.GetBlock()) 524 } 525 } 526 527 //InitIndexAndBestView 第一次启动之后需要将数据库中最新的128个block的node添加到index和bestchain中 528 // 主要是为了接下来分叉时的block处理,.........todo 529 func (chain *BlockChain) InitIndexAndBestView() { 530 //获取lastblocks从数据库,创建bestviewtip节点 531 var node *blockNode 532 var prevNode *blockNode 533 var height int64 534 var initflag = false 535 curheight := chain.blockStore.height 536 if curheight == -1 { 537 node = newPreGenBlockNode() 538 node.parent = nil 539 chain.bestChain = newChainView(node) 540 chain.index.AddNode(node) 541 return 542 } 543 if curheight >= InitBlockNum { 544 height = curheight - InitBlockNum 545 } else { 546 height = 0 547 } 548 for ; height <= curheight; height++ { 549 header, err := chain.blockStore.GetBlockHeaderByHeight(height) 550 if header == nil { 551 chainlog.Error("InitIndexAndBestView GetBlockHeaderByHeight", "height", height, "err", err) 552 //开始升级localdb到2.0.0版本时需要兼容旧的存储方式 553 header, err = chain.blockStore.getBlockHeaderByHeightOld(height) 554 if header == nil { 555 chainlog.Error("InitIndexAndBestView getBlockHeaderByHeightOld", "height", height, "err", err) 556 panic("InitIndexAndBestView fail!") 557 } 558 } 559 560 newNode := newBlockNodeByHeader(false, header, "self", -1) 561 newNode.parent = prevNode 562 prevNode = newNode 563 564 chain.index.AddNode(newNode) 565 if !initflag { 566 chain.bestChain = newChainView(newNode) 567 initflag = true 568 } else { 569 chain.bestChain.SetTip(newNode) 570 } 571 } 572 573 } 574 575 //UpdateRoutine 定时延时广播futureblock 576 func (chain *BlockChain) UpdateRoutine() { 577 //1秒尝试检测一次futureblock,futureblock的time小于当前系统时间就广播此block 578 futureblockTicker := time.NewTicker(1 * time.Second) 579 580 for { 581 select { 582 case <-chain.quit: 583 //chainlog.Info("UpdateRoutine quit") 584 return 585 case <-futureblockTicker.C: 586 chain.ProcFutureBlocks() 587 } 588 } 589 } 590 591 //ProcFutureBlocks 循环遍历所有futureblocks,当futureblock的block生成time小于当前系统时间就将此block广播出去 592 func (chain *BlockChain) ProcFutureBlocks() { 593 cfg := chain.client.GetConfig() 594 for _, hash := range chain.futureBlocks.Keys() { 595 if block, exist := chain.futureBlocks.Peek(hash); exist { 596 if block != nil { 597 blockdetail := block.(*types.BlockDetail) 598 //block产生的时间小于当前时间,广播此block,然后将此block从futureblocks中移除 599 if types.Now().Unix() > blockdetail.Block.BlockTime { 600 chain.SendBlockBroadcast(blockdetail) 601 chain.futureBlocks.Remove(hash) 602 chainlog.Debug("ProcFutureBlocks Remove", "height", blockdetail.Block.Height, "hash", common.ToHex(blockdetail.Block.Hash(cfg)), "blocktime", blockdetail.Block.BlockTime, "curtime", types.Now().Unix()) 603 } 604 } 605 } 606 } 607 } 608 609 //SetValueByKey 设置kv对到blockchain数据库 610 func (chain *BlockChain) SetValueByKey(kvs *types.LocalDBSet) error { 611 return chain.blockStore.SetConsensusPara(kvs) 612 } 613 614 //GetValueByKey 通过key值从blockchain数据库中获取value值 615 func (chain *BlockChain) GetValueByKey(keys *types.LocalDBGet) *types.LocalReplyValue { 616 return chain.blockStore.Get(keys) 617 } 618 619 // AddCacheBlock 添加区块相关缓存 620 func (chain *BlockChain) AddCacheBlock(detail *types.BlockDetail) { 621 //txHeight缓存先增加 622 chain.txHeightCache.Add(detail.Block) 623 chain.blockCache.AddBlock(detail) 624 } 625 626 //DelCacheBlock 删除缓存的中对应的区块 627 func (chain *BlockChain) DelCacheBlock(height int64, hash []byte) { 628 //txHeight缓存先删除 629 chain.txHeightCache.Del(height) 630 chain.blockCache.DelBlock(height) 631 chain.blockStore.RemoveActiveBlock(string(hash)) 632 } 633 634 //initAllowPackHeight 根据配置修改LowAllowPackHeight和值HighAllowPackHeight 635 func initAllowPackHeight(mcfg *types.BlockChain) { 636 if mcfg.HighAllowPackHeight > 0 && mcfg.LowAllowPackHeight > 0 { 637 if mcfg.HighAllowPackHeight+mcfg.LowAllowPackHeight > types.MaxAllowPackInterval { 638 panic("when Enable TxHeight HighAllowPackHeight + LowAllowPackHeight must less than types.MaxAllowPackInterval") 639 } 640 types.HighAllowPackHeight = mcfg.HighAllowPackHeight 641 types.LowAllowPackHeight = mcfg.LowAllowPackHeight 642 } 643 chainlog.Debug("initAllowPackHeight", "types.HighAllowPackHeight", types.HighAllowPackHeight, "types.LowAllowPackHeight", types.LowAllowPackHeight) 644 }