Derek解讀Bytom源碼-孤塊管理

來源:互聯網
上載者:User

作者:Derek

簡介

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockc...

本章介紹bytom代碼孤塊管理

作者使用MacOS作業系統,其他平台也大同小異

Golang Version: 1.8

孤塊介紹

什麼是孤塊

當節點收到了一個有效區塊,而在現有的主鏈中卻未找到它的父區塊,那麼這個區塊被認為是“孤塊”。父區塊是指當前區塊的PreviousBlockHash欄位指向上一區塊的hash值。

接收到的孤塊會被儲存在孤塊池中,直到它們的父區塊被節點收到。一旦收到了父區塊,節點就會將孤塊從孤塊池中取出,並且串連到它的父區塊,讓它作為區塊鏈的一部分。

孤塊出現的原因

當兩個或多個區塊在很短的時間間隔內被挖出來,節點有可能會以不同的順序接收到它們,這個時候孤塊現象就會出現。

我們假設有三個高度分別為100、101、102的塊,分別以102、101、100的顛倒順序被節點接收。此時節點將102、101放入到孤塊管理緩衝池中,等待彼此的父塊。當高度為100的區塊被同步進來時,會被驗證區塊和交易,然後儲存到區塊鏈上。這時會對孤塊緩衝池進行遞迴查詢,根據高度為100的區塊找到101的區塊並儲存到區塊鏈上,再根據高度為101的區塊找到102的區塊並儲存到區塊鏈上。

孤塊源碼分析

孤塊管理緩衝池結構體

protocol/orphan_manage.go

type OrphanManage struct {    orphan      map[bc.Hash]*types.Block    prevOrphans map[bc.Hash][]*bc.Hash    mtx         sync.RWMutex}func NewOrphanManage() *OrphanManage {    return &OrphanManage{        orphan:      make(map[bc.Hash]*types.Block),        prevOrphans: make(map[bc.Hash][]*bc.Hash),    }}
  • orphan 儲存孤塊,key為block hash,value為block結構體
  • prevOrphans 儲存孤塊的父塊
  • mtx 互斥鎖,保護map結構在多並發讀寫狀態下保持資料一致

添加孤塊到緩衝池

func (o *OrphanManage) Add(block *types.Block) {    blockHash := block.Hash()    o.mtx.Lock()    defer o.mtx.Unlock()    if _, ok := o.orphan[blockHash]; ok {        return    }    o.orphan[blockHash] = block    o.prevOrphans[block.PreviousBlockHash] = append(o.prevOrphans[block.PreviousBlockHash], &blockHash)    log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("add block to orphan")}

當一個孤塊被添加到緩衝池中,還需要記錄該孤塊的父塊hash。用於父塊hash的查詢

查詢孤塊和父孤塊

func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) {    o.mtx.RLock()    block, ok := o.orphan[*hash]    o.mtx.RUnlock()    return block, ok}func (o *OrphanManage) GetPrevOrphans(hash *bc.Hash) ([]*bc.Hash, bool) {    o.mtx.RLock()    prevOrphans, ok := o.prevOrphans[*hash]    o.mtx.RUnlock()    return prevOrphans, ok}

刪除孤塊

func (o *OrphanManage) Delete(hash *bc.Hash) {    o.mtx.Lock()    defer o.mtx.Unlock()    block, ok := o.orphan[*hash]    if !ok {        return    }    delete(o.orphan, *hash)    prevOrphans, ok := o.prevOrphans[block.PreviousBlockHash]    if !ok || len(prevOrphans) == 1 {        delete(o.prevOrphans, block.PreviousBlockHash)        return    }    for i, preOrphan := range prevOrphans {        if preOrphan == hash {            o.prevOrphans[block.PreviousBlockHash] = append(prevOrphans[:i], prevOrphans[i+1:]...)            return        }    }}

刪除孤塊的過程中,同時刪除父塊

孤塊處理邏輯

protocol/block.go

func (c *Chain) processBlock(block *types.Block) (bool, error) {blockHash := block.Hash()    if c.BlockExist(&blockHash) {        log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("block has been processed")        return c.orphanManage.BlockExist(&blockHash), nil    }    if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {        c.orphanManage.Add(block)        return true, nil    }    if err := c.saveBlock(block); err != nil {        return false, err    }    bestBlock := c.saveSubBlock(block)    // ...}    

processBlock函數處理block塊加入區塊鏈上之前的過程。

c.BlockExist判斷當前block塊是否存在於區塊鏈上或是否存在孤塊緩衝池中,如果存在則返回。

c.index.GetNode判斷block塊的父節點是否存在。如果在現有的主鏈中卻未找到它的父區塊則將block塊添加到孤塊緩衝池。

c.saveBlock走到了這一步說明,block父節點是存在於區塊鏈,則將blockBlock Storage到區塊鏈。該函數會驗證區塊和交易有效性。

saveSubBlock 代碼如下:

func (c *Chain) saveSubBlock(block *types.Block) *types.Block {    blockHash := block.Hash()    prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)    if !ok {        return block    }    bestBlock := block    for _, prevOrphan := range prevOrphans {        orphanBlock, ok := c.orphanManage.Get(prevOrphan)        if !ok {            log.WithFields(log.Fields{"hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")            continue        }        if err := c.saveBlock(orphanBlock); err != nil {            log.WithFields(log.Fields{"hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")            continue        }        if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height {            bestBlock = subBestBlock        }    }    return bestBlock}

saveSubBlock 在孤塊緩衝池中查詢是否存在當前區塊的下一個區塊。比如當前區塊高度為100,則在孤塊緩衝池中查詢是否有區塊高度為101的區塊。如果存在則將101區Block Storage到區塊鏈並從孤塊緩衝池中刪除該區塊。

saveSubBlock是一個遞迴函式的實現。目的是為了尋找最深葉子節點的遞迴方式。比如當前區塊高度為100的,遞迴查詢出高度為99、98、97等高度的區塊。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.