Author: Derek
Brief introduction
GitHub Address: Https://github.com/Bytom/bytom
Gitee Address: Https://gitee.com/BytomBlockc ...
This chapter describes Bytom code orphan block Management
The author uses the MacOS operating system, and the other platforms are similar
Golang version:1.8
Orphan block Introduction
What is a solitary block?
When a node receives a valid chunk, and its parent block is not found in the existing main chain, the chunk is considered a "lone block". The parent chunk refers to the hash value of the Previousblockhash field of the current chunk that points to the previous chunk.
The orphaned blocks received will be stored in the lone pool until their parent chunk is received by the node. Once the parent chunk is received, the node takes the orphan block out of the pool and connects to its parent block, making it part of the blockchain.
The reason why the solitary block appears
When two or more chunks are dug up within a short interval of time, the nodes may receive them in a different order, and this is when the solitary block phenomenon occurs.
We assume that there are three blocks with a height of 100, 101, and 102, which are received by the nodes in reverse order of 102, 101, 100, respectively. At this point, the node puts 102, 101 into the orphan block management cache pool, waiting for each other's parent block. When a block with a height of 100 is synchronized, the chunks and trades are validated and then stored on the blockchain. In this case, the lone block cache pool will be recursive query, according to the block height of 100 to find 101 chunks and stored on the blockchain, and then according to the height of 101 blocks to find 102 chunks and stored in the blockchain.
Isolated block source code analysis
Orphan block management cache pool structure body
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 storage orphan block, key is block Hash,value for block structure
- Prevorphans the parent block of the stored orphan block
- MTX mutex to protect the map structure to maintain data consistency in multiple concurrent read and write situations
Add orphaned blocks to the cache pool
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")}
When a lone block is added to the cache pool, it is also necessary to record the parent block hash of the orphan block. Query for parent block hash
Querying for orphaned blocks and parent solitary blocks
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}
Delete Orphan blocks
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 } }}
Delete the orphaned block while deleting the parent block
Orphan block processing logic
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) // ...}
The Processblock function processes the block block before it joins the block chain.
C.blockexist determines whether the current block blocks exist on the blockchain or if there is a lone block cache pool, and returns if present.
C.index.getnode determines whether the parent node of block blocks exists. If the parent chunk is not found in the existing main chain, the block block is added to the orphaned block cache pool.
C.saveblock This step shows that the block parent node is present in the blockchain, and then the block block is stored in the block chain. This function verifies the block and the transaction validity.
The Savesubblock code is as follows:
The func (c *chain) Savesubblock (Block *types. Block) *types. Block {blockhash: = block. Hash () Prevorphans, OK: = C.orphanmanage.getprevorphans (&blockhash) if!ok {return block} Bestblo CK: = 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 (orph Anblock); Err! = Nil {log. Withfields (log. fields{"Hash": prevorphan.string (), "height": orphanblock.height}). Warning ("Savesubblock fail to save Block") continue} if Subbestblock: = C.savesubblock (Orphanblo CK); Subbestblock.height > Bestblock.height {bestblock = Subbestblock}} return Bestblock}
/pre>
Savesubblock queries for the existence of the next chunk of the current chunk in the lone block cache pool. For example, if the current chunk height is 100, then query for chunks with a block height of 101 in the lone block cache pool. If present, store 101 chunks in the blockchain and delete the chunk from the orphaned block cache pool.
Savesubblock is the implementation of a recursive function. The aim is to find the recursive way of the deepest leaf nodes. For example, the current block height of 100, recursive query out height of 99, 98, 97, such as the height of the block.