github.com/turingchain2020/turingchain@v1.1.21/blockchain/chainview.go (about)

     1  // Copyright Turing Corp. 2018 All Rights Reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"bytes"
     9  	"container/list"
    10  	"sync"
    11  
    12  	"github.com/turingchain2020/turingchain/common"
    13  )
    14  
    15  const blockNodeCacheLimit = 10240 //目前best主链保存最新的10240个blocknode
    16  
    17  // chainView provides a flat view of a specific branch of the block chain from
    18  // its tip back to the genesis block and provides various convenience functions
    19  // for comparing chains.
    20  //
    21  // For example, assume a block chain with a side chain as depicted below:
    22  //   genesis -> 1 -> 2 -> 3 -> 4  -> 5 ->  6  -> 7  -> 8
    23  //                         \-> 4a -> 5a -> 6a
    24  //
    25  // The chain view for the branch ending in 6a consists of:
    26  //   genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a
    27  type chainView struct {
    28  	mtx        sync.Mutex
    29  	nodes      map[int64]*list.Element
    30  	cacheQueue *list.List
    31  }
    32  
    33  //需要从数据库中获取lastblock然后组装成node节点开始创建ChainView
    34  func newChainView(tip *blockNode) *chainView {
    35  
    36  	chainview := &chainView{
    37  		nodes:      make(map[int64]*list.Element),
    38  		cacheQueue: list.New(),
    39  	}
    40  
    41  	chainview.setTip(tip)
    42  	return chainview
    43  }
    44  
    45  func (c *chainView) tip() *blockNode {
    46  	if c.cacheQueue.Len() == 0 {
    47  		return nil
    48  	}
    49  	elem := c.cacheQueue.Front()
    50  	blocknode := elem.Value.(*blockNode)
    51  	return blocknode
    52  }
    53  
    54  func (c *chainView) Tip() *blockNode {
    55  	c.mtx.Lock()
    56  	tip := c.tip()
    57  	c.mtx.Unlock()
    58  	return tip
    59  }
    60  
    61  // 插入到list的头,节点超出blockNodeCacheLimit从back开始删除
    62  func (c *chainView) setTip(node *blockNode) {
    63  	//需要检查node节点的父节点是当前的tip节点
    64  	if c.cacheQueue.Len() != 0 {
    65  
    66  		if node.parent != c.tip() {
    67  			chainlog.Error("setTip err", "node.height", node.height, "node.hash", common.ToHex(node.hash))
    68  			chainlog.Error("setTip err", "c.tip().height", c.tip().height, "c.tip().hash", common.ToHex(c.tip().hash))
    69  			return
    70  		}
    71  	}
    72  	// Create entry in cache and append to cacheQueue.
    73  	elem := c.cacheQueue.PushFront(node)
    74  	c.nodes[node.height] = elem
    75  
    76  	// Maybe expire an item.
    77  	if int64(c.cacheQueue.Len()) > blockNodeCacheLimit {
    78  		height := c.cacheQueue.Remove(c.cacheQueue.Back()).(*blockNode).height
    79  		delete(c.nodes, height)
    80  	}
    81  	chainlog.Debug("setTip", "node.height", node.height, "node.hash", common.ToHex(node.hash))
    82  }
    83  
    84  func (c *chainView) SetTip(node *blockNode) {
    85  	c.mtx.Lock()
    86  	c.setTip(node)
    87  	c.mtx.Unlock()
    88  }
    89  
    90  // 删除tip节点,主要是节点回退时使用
    91  func (c *chainView) delTip(node *blockNode) {
    92  	if c.tip() != node {
    93  		chainlog.Error("delTip err", "node.height", node.height, "node.hash", node.hash)
    94  		chainlog.Error("delTip err", "tip.height", c.tip().height, "tip.hash", c.tip().hash)
    95  	}
    96  
    97  	elem, ok := c.nodes[node.height]
    98  	if ok {
    99  		delheight := c.cacheQueue.Remove(elem).(*blockNode).height
   100  		if delheight != node.height {
   101  			chainlog.Error("delTip height err ", "height", node.height, "delheight", delheight)
   102  		}
   103  		delete(c.nodes, delheight)
   104  	}
   105  }
   106  
   107  func (c *chainView) DelTip(node *blockNode) {
   108  	c.mtx.Lock()
   109  	c.delTip(node)
   110  	c.mtx.Unlock()
   111  }
   112  
   113  // 返回 chain view tip 的height
   114  func (c *chainView) height() int64 {
   115  	node := c.tip()
   116  	if node != nil {
   117  		return node.height
   118  	}
   119  	return -1
   120  }
   121  
   122  func (c *chainView) Height() int64 {
   123  	c.mtx.Lock()
   124  	height := c.height()
   125  	c.mtx.Unlock()
   126  	return height
   127  }
   128  
   129  //获取指定height的node
   130  func (c *chainView) nodeByHeight(height int64) *blockNode {
   131  	if height < 0 || height > c.height() {
   132  		return nil
   133  	}
   134  
   135  	elem, ok := c.nodes[height]
   136  	if ok {
   137  		return elem.Value.(*blockNode)
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  // contains returns whether or not the chain view contains the passed block node.
   144  func (c *chainView) contains(node *blockNode) bool {
   145  	return c.nodeByHeight(node.height) == node
   146  }
   147  
   148  func (c *chainView) next(node *blockNode) *blockNode {
   149  	if node == nil || !c.contains(node) {
   150  		return nil
   151  	}
   152  
   153  	return c.nodeByHeight(node.height + 1)
   154  }
   155  func (c *chainView) Next(node *blockNode) *blockNode {
   156  	c.mtx.Lock()
   157  	next := c.next(node)
   158  	c.mtx.Unlock()
   159  	return next
   160  }
   161  
   162  // findFork returns the final common block between the provided node and the
   163  // the chain view.  It will return nil if there is no common block.  This only
   164  // differs from the exported version in that it is up to the caller to ensure
   165  // the lock is held.
   166  
   167  func (c *chainView) findFork(node *blockNode) *blockNode {
   168  
   169  	if node == nil {
   170  		return nil
   171  	}
   172  
   173  	chainHeight := c.height()
   174  	if node.height > chainHeight {
   175  		node = node.Ancestor(chainHeight)
   176  	}
   177  
   178  	for node != nil && !c.contains(node) {
   179  		node = node.parent
   180  	}
   181  
   182  	return node
   183  }
   184  
   185  func (c *chainView) FindFork(node *blockNode) *blockNode {
   186  	c.mtx.Lock()
   187  	fork := c.findFork(node)
   188  	c.mtx.Unlock()
   189  	return fork
   190  }
   191  
   192  func (c *chainView) HaveBlock(hash []byte, height int64) bool {
   193  	c.mtx.Lock()
   194  	defer c.mtx.Unlock()
   195  	node := c.nodeByHeight(height)
   196  	if node != nil {
   197  		if bytes.Equal(hash, node.hash) {
   198  			return true
   199  		}
   200  	}
   201  	return false
   202  }