이 더 리 움 소스 코드 해독 (5) BlockChain 류 의 해석 및 NewBlockChain () 분석
19808 단어 이 더 리 움 소스 코드
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(¤tBlock); 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 을 설정 합 니 다.