이 더 리 움 소스 코드 해독 (5) BlockChain 류 의 해석 및 NewBlockChain () 분석

1. 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  
}

이 더 리 움 블록 체인 에는 이렇게 몇 가지 관건 적 인 요소 가 있다.
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, future Blocks: 블록 체인 의 캐 시 구 조 는 블록 체인 의 읽 기와 구축 을 가속 화 하 는 데 사 용 됩 니 다.6) engine: consensus 모듈 의 인터페이스 로 block 의 인 터 페 이 스 를 검증 합 니 다.7) processor: 블록 체인 거래 의 인 터 페 이 스 를 실행 하고 새로운 블록 을 받 을 때 블록 중의 모든 거래 를 한 번 실행 해 야 합 니 다. 한편 으로 는 검증 이 고 한편 으로 는 월 드 스테이 트 를 업데이트 하 는 것 입 니 다.8) vaidator: 데이터 의 유효성 을 검증 하 는 인터페이스 9) future Blocks: 받 은 블록 시간 은 현재 헤드 블록 시간 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() {}

2. HeaderChain
앞에서 말 한 바 와 같이 HeaderChain 은 BlockChain 이 추가 로 유지 하 는 또 다른 체인 으로 그 구 조 는 후자 와 매우 큰 유사 성 을 가진다. 이 는 데이터 베이스, genesisHeader, currentHeader, currentHeaderHash 와 캐 시 와 engine 를 연결 하 는 것 을 포함한다.메모: consensus. Engine 은 매우 유용 한 인터페이스 입 니 다. 이 더 리 움 에서 데이터 검증 이 필요 한 곳 은 모두 이 인 터 페 이 스 를 사용 해 야 합 니 다.
다른 점 은 HeaderChain 에 block 의 body 부분 이 존재 하지 않 기 때문에 processor 와 vaidator 라 는 두 인 터 페 이 스 를 사용 할 수 없다 는 점 이다.
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 모듈 규범 체인 업데이트
3. 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. 블록 과 상태 검증 초기 화: NewBlock Validator () 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 에 대응 하 는 블록 헤드 를 스크롤 합 니 다.중간 블록 헤드 의 모든 데이터 와 캐 시 를 삭제 합 니 다.헤드 를 새 current Headr 로 설정 합 니 다.2. bc. currentBlock, bc. currentFastBlock 을 다시 설정 합 니 다. 데이터베이스 에 새로운 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 을 설정 합 니 다.

좋은 웹페이지 즐겨찾기