1. 程式人生 > >以太坊原始碼解讀(5)BlockChain類的解析及NewBlockChain()分析

以太坊原始碼解讀(5)BlockChain類的解析及NewBlockChain()分析

一、blockchain的資料結構

type BlockChain struct {
	chainConfig *params.ChainConfig // 初始化配置
	cacheConfig *CacheConfig        // 快取配置

	db     ethdb.Database // 底層的資料儲存
	triegc *prque.Prque   
	gcproc time.Duration  

	hc            *HeaderChain  // 區塊頭組成的鏈
	rmLogsFeed    event.Feed
	chainFeed     event.Feed
	chainSideFeed event.Feed
	chainHeadFeed event.Feed
	logsFeed      event.Feed
	scope         event.SubscriptionScope
	genesisBlock  *types.Block  // 創始區塊

	mu      sync.RWMutex  // 鎖
	chainmu sync.RWMutex 
	procmu  sync.RWMutex 

	checkpoint       int          // checkpoint counts towards the new checkpoint
	currentBlock     atomic.Value // 主鏈的頭區塊
	currentFastBlock atomic.Value // 快速同步模式下鏈的頭區塊,這種情況下可能比主鏈長

	stateCache   state.Database // State database to reuse between imports (contains state cache)
	bodyCache    *lru.Cache     // 最近區塊體的快取
	bodyRLPCache *lru.Cache     // 最近區塊體RLP格式的快取
	blockCache   *lru.Cache     // 最近完整區塊的快取
	futureBlocks *lru.Cache     // 待上鍊的區塊的快取

	quit    chan struct{} 
	running int32         // running must be called atomically
	// procInterrupt must be atomically called
	procInterrupt int32          // interrupt signaler for block processing
	wg            sync.WaitGroup // chain processing wait group for shutting down

	engine    consensus.Engine // 用來驗證區塊的介面
	processor Processor // 執行區塊交易的介面
	validator Validator // 驗證資料有效性的介面
	vmConfig  vm.Config

	badBlocks *lru.Cache // Bad block cache,來自DAO事件
}

以太坊blockchain有這麼幾個關鍵的元素:

1)db:連線到底層資料儲存,即leveldb;
2)hc:headerchain區塊頭鏈,由blockchain額外維護的另一條鏈,由於Header和Block的儲存空間是有很大差別的,但同時Block的Hash值就是Header(RLP)的Hash值,所以維護一個headerchain可以用於快速延長鏈,驗證通過後再下載blockchain,或者可以與blockchain進行相互驗證;
3)genesisBlock:創始區塊;
4)currentBlock:當前區塊,blockchain中並不是儲存鏈所有的block,而是通過currentBlock向前回溯直到genesisBlock,這樣就構成了區塊鏈。
5)bodyCache、bodyRLPCache、blockCache、futureBlocks:區塊鏈中的快取結構,用於加快區塊鏈的讀取和構建;
6)engine:是consensus模組中的介面,用來驗證block的介面;
7)processor:執行區塊鏈交易的介面,收到一個新的區塊時,要對區塊中的所有交易執行一遍,一方面是驗證,一方面是更新worldState;
8)validator:驗證資料有效性的介面
9)futureBlocks:收到的區塊時間大於當前頭區塊時間15s而小於30s的區塊,可作為當前節點待處理的區塊。

blockchain.go中的部分函式和方法:

// NewBlockChain使用資料庫中可用的資訊返回完全初始化的塊鏈。 它初始化預設的以太坊Validitor和Processor。
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig,
engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {}

// BadBlocks 處理客戶端從網路上獲取的最近的bad block列表
func (bc *BlockChain) BadBlocks() []*types.Block {}

// addBadBlock 把bad block放入快取
func (bc *BlockChain) addBadBlock(block *types.Block) {}

// CurrentBlock取回主鏈的當前頭區塊,這個區塊是從blockchian的內部快取中取得
func (bc *BlockChain) CurrentBlock() *types.Block {}

// CurrentHeader檢索規範鏈的當前頭區塊header。從HeaderChain的內部快取中檢索標頭。
func (bc *BlockChain) CurrentHeader() *types.Header{}

// CurrentFastBlock取回主鏈的當前fast-sync頭區塊,這個區塊是從blockchian的內部快取中取得
func (bc *BlockChain) CurrentFastBlock() *types.Block {}

// 將活動鏈或其子集寫入給定的編寫器.
func (bc *BlockChain) Export(w io.Writer) error {}
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {}

// FastSyncCommitHead快速同步,將當前頭塊設定為特定hash的區塊。
func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {}

// GasLimit返回當前頭區塊的gas limit
func (bc *BlockChain) GasLimit() uint64 {}

// Genesis 取回genesis區塊
func (bc *BlockChain) Genesis() *types.Block {}

// 通過hash從資料庫或快取中取到一個區塊體(transactions and uncles)或RLP資料
func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {}
func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {}

// GetBlock 通過hash和number取到區塊
func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {}
// GetBlockByHash 通過hash取到區塊
func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {}
// GetBlockByNumber 通過number取到區塊
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {}

// 獲取給定hash的區塊的總難度
func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int{}

// 獲取給定hash和number區塊的header
func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header{}

// 獲取給定hash的區塊header
func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header{}

// 獲取給定number的區塊header
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header{}

// 獲取從給定hash的區塊到genesis區塊的所有hash
func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash{}

// GetReceiptsByHash 在特定的區塊中取到所有交易的收據
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {}

// GetBlocksFromHash 取到特定hash的區塊及其n-1個父區塊
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {}

// GetUnclesInChain 取回從給定區塊到向前回溯特定距離到區塊上的所有叔區塊
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {}

// HasBlock檢驗hash對應的區塊是否完全存在資料庫中
func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {}

// 檢查給定hash和number的區塊的區塊頭是否存在資料庫
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool{}

// HasState檢驗state trie是否完全存在資料庫中
func (bc *BlockChain) HasState(hash common.Hash) bool {}

// HasBlockAndState檢驗hash對應的block和state trie是否完全存在資料庫中
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {}

// insert 將新的頭塊注入當前塊鏈。 該方法假設該塊確實是真正的頭。
// 如果它們較舊或者它們位於不同的側鏈上,它還會將頭部標題和頭部快速同步塊重置為同一個塊。
func (bc *BlockChain) insert(block *types.Block) {}

// InsertChain嘗試將給定批量的block插入到規範鏈中,否則,建立一個分叉。 如果返回錯誤,它將返回失敗塊的索引號以及描述錯誤的錯誤。
//插入完成後,將觸發所有累積的事件。
func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error){}

// insertChain將執行實際的鏈插入和事件聚合。 
// 此方法作為單獨方法存在的唯一原因是使用延遲語句使鎖定更清晰。
func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error){}

// InsertHeaderChain嘗試將給定的headerchain插入到本地鏈中,可能會建立一個重組
func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error){}

// InsertReceiptChain 使用交易和收據資料來完成已經存在的headerchain
func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {}

//loadLastState從資料庫載入最後一個已知的鏈狀態。
func (bc *BlockChain) loadLastState() error {}

// Processor 返回當前current processor.
func (bc *BlockChain) Processor() Processor {}

// Reset重置清除整個區塊鏈,將其恢復到genesis state.
func (bc *BlockChain) Reset() error {}

// ResetWithGenesisBlock 清除整個區塊鏈, 用特定的genesis state重塑,被Reset所引用
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {}

// repair嘗試通過回滾當前塊來修復當前的區塊鏈,直到找到具有關聯狀態的塊。
// 用於修復由崩潰/斷電或簡單的非提交嘗試導致的不完整的資料庫寫入。
//此方法僅回滾當前塊。 當前標頭和當前快速塊保持不變。
func (bc *BlockChain) repair(head **types.Block) error {}

// reorgs需要兩個塊、一箇舊鏈以及一個新鏈,並將重新構建塊並將它們插入到新的規範鏈中,並累積潛在的缺失事務併發布有關它們的事件
func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error{}

// Rollback 旨在從資料庫中刪除不確定有效的鏈片段
func (bc *BlockChain) Rollback(chain []common.Hash) {}

// SetReceiptsData 計算收據的所有非共識欄位
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {}

// SetHead將本地鏈回滾到指定的頭部。
// 通常可用於處理分叉時重選主鏈。對於Header,新Header上方的所有內容都將被刪除,新的頭部將被設定。
// 但如果塊體丟失,則會進一步回退(快速同步後的非歸檔節點)。
func (bc *BlockChain) SetHead(head uint64) error {}

// SetProcessor設定狀態修改所需要的processor
func (bc *BlockChain) SetProcessor(processor Processor) {}

// SetValidator 設定用於驗證未來區塊的validator
func (bc *BlockChain) SetValidator(validator Validator) {}

// State 根據當前頭區塊返回一個可修改的狀態
func (bc *BlockChain) State() (*state.StateDB, error) {}

// StateAt 根據特定時間點返回新的可變狀態
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {}

// Stop 停止區塊鏈服務,如果有正在import的程序,它會使用procInterrupt來取消。
// it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() {}

// TrieNode從memory快取或storage中檢索與trie節點hash相關聯的資料。
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {}

// Validator返回當前validator.
func (bc *BlockChain) Validator() Validator {}

// WriteBlockWithoutState僅將塊及其元資料寫入資料庫,但不寫入任何狀態。 這用於構建競爭方叉,直到超過規範總難度。
func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error){}

// WriteBlockWithState將塊和所有關聯狀態寫入資料庫。
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) {}

// writeHeader將標頭寫入本地鏈,因為它的父節點已知。 如果新插入的報頭的總難度變得大於當前已知的TD,則重新路由規範鏈
func (bc *BlockChain) writeHeader(header *types.Header) error{}

// 處理未來區塊鏈
func (bc *BlockChain) update() {}

二、HeaderChain

如上所述,HeaderChain是BlockChain額外維護的另一條鏈,其結構與後者有很大的相似性,包括要連線資料庫、genesisHeader、currentHeader、currentHeaderHash以及快取和engine。注意:consensus.Engine是一個很有用的介面,以太坊中凡是需要用到資料驗證的地方都要用到這個介面。

不同之處在於,HeaderChain中由於不存在block的body部分,所以也就用不到processor和validator這兩個介面。

type HeaderChain struct {
	config *params.ChainConfig

	chainDb       ethdb.Database
	genesisHeader *types.Header

	currentHeader     atomic.Value // 當前區塊的區塊頭
	currentHeaderHash common.Hash  // 當前區塊的區塊頭hash值(禁止重計算)

	headerCache *lru.Cache 
	tdCache     *lru.Cache 
	numberCache *lru.Cache

	procInterrupt func() bool

	rand   *mrand.Rand
	engine consensus.Engine
}

headerchain的方法與blockchain類似,其中很多都是被blockchain中相應的方法呼叫的,所以我們都是通過Blockchain來管理HeaderChain的,這裡就不一一列舉了。

從這些方法我們可以看到 blockchain和headerchain的行為方式:

1、blockchain模組初始化
2、blockchain模組插入校驗分析
3、blockchain模組區塊鏈分叉處理
4、blockchian模組規範鏈更新

三、詳解blockchain模組初始化的方法和函式

區塊鏈本身是一個用來記錄資料的資料庫,那麼維護它的方法無非就是增刪查(沒有“改”,因為區塊鏈最大的特點之一就是資料不可更改)。

建立:NewBlockChain
增加、插入:InsertChain—>insertChain—>WriteBlockWithState
刪除:Reset —> ResetWithGenesisBlock
查詢:getBlockByNumber等

初始化blockchain:NewBlockChain()

func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, 
engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
  if cacheConfig == nil {
    cacheConfig = &CacheConfig{
	TrieNodeLimit: 256 * 1024 * 1024,
	TrieTimeLimit: 5 * time.Minute,
    }
  }
  bodyCache, _ := lru.New(bodyCacheLimit)
  bodyRLPCache, _ := lru.New(bodyCacheLimit)
  blockCache, _ := lru.New(blockCacheLimit)
  futureBlocks, _ := lru.New(maxFutureBlocks)
  badBlocks, _ := lru.New(badBlockLimit)

  bc := &BlockChain{
		chainConfig:  chainConfig,
		cacheConfig:  cacheConfig,
		db:           db,
		triegc:       prque.New(),
		stateCache:   state.NewDatabase(db),
		quit:         make(chan struct{}),
		bodyCache:    bodyCache,
		bodyRLPCache: bodyRLPCache,
		blockCache:   blockCache,
		futureBlocks: futureBlocks,
		engine:       engine,
		vmConfig:     vmConfig,
		badBlocks:    badBlocks,
  }
  bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
  bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))

  var err error
  bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
  if err != nil {
	return nil, err
  }
  bc.genesisBlock = bc.GetBlockByNumber(0)
  if bc.genesisBlock == nil {
	return nil, ErrNoGenesis
  }
  // 載入規範鏈的頭區塊到blockchain中
  if err := bc.loadLastState(); err != nil {
	return nil, err
  }
  // 遍歷badHash列表,如果發現規範鏈中存在badHash列表中的錯誤區塊,則將規範鏈回滾到錯誤區塊的父區塊
  for hash := range BadHashes {
	if header := bc.GetHeaderByHash(hash); header != nil {
          // get the canonical block corresponding to the offending header's number
	  headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
	  // make sure the headerByNumber (if present) is in our current canonical chain
	  if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
		log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
		bc.SetHead(header.Number.Uint64() - 1)
		log.Error("Chain rewind was successful, resuming normal operation")
	  }
        }
  }
  // 開啟處理未來區塊的go執行緒
  go bc.update()
  return bc, nil
}

NewBlockChain函式的呼叫時機:從上一篇介紹以太坊啟動的流程中可以知道,該函式的呼叫流程是

geth ——> makeFullNode ——> RegisterEthService ——> eth.New ——> core.NewBlockChain

NewBlockChain函式的執行過程:

1. 配置cacheConfig,建立各種lru快取
2. 初始化triegc(按優先順序對映排列Block number用以gc):prque.New()
3. 初始化stateDb:state.NewDatabase(db)
4. 初始化區塊和狀態驗證:NewBlockValidator()
5. 初始化狀態處理器:NewStateProcessor()
6. 初始化區塊頭部鏈:NewHeaderChain()
7. 查詢創世區塊:bc.genesisBlock = bc.GetBlockByNumber(0)
8. 載入最新的狀態資料:bc.loadLastState()
9. 檢查本地區塊鏈上是否有bad block,如果有呼叫bc.SetHead回到硬分叉之前的區塊頭
10. go bc.update() 定時處理future block

這裡細說一下第9步,這涉及到如何查詢一個區塊以及如何寫資料庫:

for hash := range BadHashes {
    if header := bc.GetHeaderByHash(hash); header != nil {
        // get the canonical block corresponding to the offending header's number
	headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
	// make sure the headerByNumber (if present) is in our current canonical chain
	if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
	    log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
	    bc.SetHead(header.Number.Uint64() - 1)
	    log.Error("Chain rewind was successful, resuming normal operation")
	}
    }
}

首先遍歷BadHash列表,通過hash來取得header:

bc.GetHeaderByHash(hash)
—> bc.hc.GetHeaderByHash(hash)
—> hc.GetBlockNumber(hash)  // 通過hash來找到這個區塊的number,即用‘H’+hash為key在資料庫中查詢
—> hc.GetHeader(hash, *number)  // 通過hash+number來找到header
—> hc.headerCache.Get(hash)  // 先從快取裡找,找不到再去資料庫找
—> rawdb.ReadHeader(hc.chainDb, hash, number)  // 在資料庫中,通過'h'+num+hash為key來找到header的RLP編碼值

然後判斷該badHash對應的區塊頭是否在規範鏈上:

bc.GetHeaderByNumber(number)
—> hc.GetHeaderByNumber(number)
—> raw.ReadCanonicalHash(hc.chainDb, number) 
// 在規範鏈上以‘h’+num+‘n’為key查詢區塊的hash,
// 如果找到了,說明區塊鏈上確實有該badblock
// 如果找不到,則說明該bad block只存在資料庫,沒有上規範鏈
—> hc.GetHeader(hash,number) // 如果規範鏈上有這個badblock,則返回該block header

如果規範鏈上存在壞區塊,則回滾到壞區塊的父區塊。

SetHead(head uint64)

func (bc *BlockChain) SetHead(head uint64) error {
	log.Warn("Rewinding blockchain", "target", head)

	bc.mu.Lock()
	defer bc.mu.Unlock()

	// Rewind the header chain, deleting all block bodies until then
	delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
		rawdb.DeleteBody(db, hash, num)
	}
	bc.hc.SetHead(head, delFn)
	currentHeader := bc.hc.CurrentHeader()

	// Clear out any stale content from the caches
	bc.bodyCache.Purge()
	bc.bodyRLPCache.Purge()
	bc.blockCache.Purge()
	bc.futureBlocks.Purge()

	// Rewind the block chain, ensuring we don't end up with a stateless head block
	if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
		bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
	}
	if currentBlock := bc.CurrentBlock(); currentBlock != nil {
		if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
			// Rewound state missing, rolled back to before pivot, reset to genesis
			bc.currentBlock.Store(bc.genesisBlock)
		}
	}
	// Rewind the fast block in a simpleton way to the target head
	if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
		bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
	}
	// If either blocks reached nil, reset to the genesis state
	if currentBlock := bc.CurrentBlock(); currentBlock == nil {
		bc.currentBlock.Store(bc.genesisBlock)
	}
	if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
		bc.currentFastBlock.Store(bc.genesisBlock)
	}
	currentBlock := bc.CurrentBlock()
	currentFastBlock := bc.CurrentFastBlock()

	rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash())
	rawdb.WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash())

	return bc.loadLastState()
}

1. 首先呼叫bc.hc.SetHead(head, delFn),回滾head對應的區塊頭。並清除中間區塊頭所有的資料和快取。設定head為新的currentHeadr。
2. 重新設定bc.currentBlock,bc.currentFastBlock
3. 在資料庫中記錄新的headBlockHash和HeadFastBlockHash
4. 呼叫bc.loadLastState(),重新載入本地的最新狀態

loadLastState()載入本地最新狀態

func (bc *BlockChain) loadLastState() error {
	// Restore the last known head block
	head := rawdb.ReadHeadBlockHash(bc.db)
	if head == (common.Hash{}) {
		// Corrupt or empty database, init from scratch
		log.Warn("Empty database, resetting chain")
		return bc.Reset()
	}
	// Make sure the entire head block is available
	currentBlock := bc.GetBlockByHash(head)
	if currentBlock == nil {
		// Corrupt or empty database, init from scratch
		log.Warn("Head block missing, resetting chain", "hash", head)
		return bc.Reset()
	}
	// Make sure the state associated with the block is available
	if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
		// Dangling block without a state associated, init from scratch
		log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
		if err := bc.repair(&currentBlock); err != nil {
			return err
		}
	}
	// Everything seems to be fine, set as the head block
	bc.currentBlock.Store(currentBlock)

	// Restore the last known head header
	currentHeader := currentBlock.Header()
	if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
		if header := bc.GetHeaderByHash(head); header != nil {
			currentHeader = header
		}
	}
	bc.hc.SetCurrentHeader(currentHeader)

	// Restore the last known head fast block
	bc.currentFastBlock.Store(currentBlock)
	if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) {
		if block := bc.GetBlockByHash(head); block != nil {
			bc.currentFastBlock.Store(block)
		}
	}

	// Issue a status log for the user
	currentFastBlock := bc.CurrentFastBlock()

	headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
	blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
	fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())

	log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
	log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
	log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)

	return nil
}

所謂載入最新狀態,就是要找到最新的區塊頭,然後設定currentBlock、currentHeader和currentFastBlock,因此:

1. 獲取到最新區塊以及它的hash
2 . 從stateDb中開啟最新區塊的狀態trie,如果開啟失敗呼叫bc.repair(&currentBlock)方法進行修復。修復方法就是從當前區塊一個個的往前面找,直到找到好的區塊,然後賦值給currentBlock。
3 . 獲取到最新的區塊頭
4 .找到最新的fast模式下的block,並設定bc.currentFastBlock