github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/state/blockindex.go (about)

     1  package state
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  	"sort"
     7  	"sync"
     8  
     9  	"github.com/bytom/bytom/common"
    10  	"github.com/bytom/bytom/consensus"
    11  	"github.com/bytom/bytom/consensus/difficulty"
    12  	"github.com/bytom/bytom/protocol/bc"
    13  	"github.com/bytom/bytom/protocol/bc/types"
    14  	"github.com/bytom/bytom/testutil"
    15  )
    16  
    17  // approxNodesPerDay is an approximation of the number of new blocks there are
    18  // in a day on average.
    19  const approxNodesPerDay = 24 * 24
    20  
    21  // BlockNode represents a block within the block chain and is primarily used to
    22  // aid in selecting the best chain to be the main chain.
    23  type BlockNode struct {
    24  	Parent  *BlockNode // parent is the parent block for this node.
    25  	Hash    bc.Hash    // hash of the block.
    26  	Seed    *bc.Hash   // seed hash of the block
    27  	WorkSum *big.Int   // total amount of work in the chain up to
    28  
    29  	Version                uint64
    30  	Height                 uint64
    31  	Timestamp              uint64
    32  	Nonce                  uint64
    33  	Bits                   uint64
    34  	TransactionsMerkleRoot bc.Hash
    35  	TransactionStatusHash  bc.Hash
    36  }
    37  
    38  func NewBlockNode(bh *types.BlockHeader, parent *BlockNode) (*BlockNode, error) {
    39  	if bh.Height != 0 && parent == nil {
    40  		return nil, errors.New("parent node can not be nil")
    41  	}
    42  
    43  	node := &BlockNode{
    44  		Parent:    parent,
    45  		Hash:      bh.Hash(),
    46  		WorkSum:   difficulty.CalcWork(bh.Bits),
    47  		Version:   bh.Version,
    48  		Height:    bh.Height,
    49  		Timestamp: bh.Timestamp,
    50  		Nonce:     bh.Nonce,
    51  		Bits:      bh.Bits,
    52  		TransactionsMerkleRoot: bh.TransactionsMerkleRoot,
    53  		TransactionStatusHash:  bh.TransactionStatusHash,
    54  	}
    55  
    56  	if bh.Height == 0 {
    57  		node.Seed = consensus.InitialSeed
    58  	} else {
    59  		node.Seed = parent.CalcNextSeed()
    60  		node.WorkSum = node.WorkSum.Add(parent.WorkSum, node.WorkSum)
    61  	}
    62  	return node, nil
    63  }
    64  
    65  // blockHeader convert a node to the header struct
    66  func (node *BlockNode) BlockHeader() *types.BlockHeader {
    67  	previousBlockHash := bc.Hash{}
    68  	if node.Parent != nil {
    69  		previousBlockHash = node.Parent.Hash
    70  	}
    71  	return &types.BlockHeader{
    72  		Version:           node.Version,
    73  		Height:            node.Height,
    74  		PreviousBlockHash: previousBlockHash,
    75  		Timestamp:         node.Timestamp,
    76  		Nonce:             node.Nonce,
    77  		Bits:              node.Bits,
    78  		BlockCommitment: types.BlockCommitment{
    79  			TransactionsMerkleRoot: node.TransactionsMerkleRoot,
    80  			TransactionStatusHash:  node.TransactionStatusHash,
    81  		},
    82  	}
    83  }
    84  
    85  func (node *BlockNode) CalcPastMedianTime() uint64 {
    86  	timestamps := []uint64{}
    87  	iterNode := node
    88  	for i := 0; i < consensus.MedianTimeBlocks && iterNode != nil; i++ {
    89  		timestamps = append(timestamps, iterNode.Timestamp)
    90  		iterNode = iterNode.Parent
    91  	}
    92  
    93  	sort.Sort(common.TimeSorter(timestamps))
    94  	return timestamps[len(timestamps)/2]
    95  }
    96  
    97  // CalcNextBits calculate the bits for next block
    98  func (node *BlockNode) CalcNextBits() uint64 {
    99  	if node.Height%consensus.BlocksPerRetarget != 0 || node.Height == 0 {
   100  		return node.Bits
   101  	}
   102  
   103  	compareNode := node.Parent
   104  	for compareNode.Height%consensus.BlocksPerRetarget != 0 {
   105  		compareNode = compareNode.Parent
   106  	}
   107  	return difficulty.CalcNextRequiredDifficulty(node.BlockHeader(), compareNode.BlockHeader())
   108  }
   109  
   110  // CalcNextSeed calculate the seed for next block
   111  func (node *BlockNode) CalcNextSeed() *bc.Hash {
   112  	if node.Height == 0 {
   113  		return consensus.InitialSeed
   114  	}
   115  	if node.Height%consensus.SeedPerRetarget == 0 {
   116  		return &node.Hash
   117  	}
   118  	return node.Seed
   119  }
   120  
   121  // BlockIndex is the struct for help chain trace block chain as tree
   122  type BlockIndex struct {
   123  	sync.RWMutex
   124  
   125  	index     map[bc.Hash]*BlockNode
   126  	mainChain []*BlockNode
   127  }
   128  
   129  // NewBlockIndex will create a empty BlockIndex
   130  func NewBlockIndex() *BlockIndex {
   131  	return &BlockIndex{
   132  		index:     make(map[bc.Hash]*BlockNode),
   133  		mainChain: make([]*BlockNode, 0, approxNodesPerDay),
   134  	}
   135  }
   136  
   137  func NewBlockIndexWithData(index map[bc.Hash]*BlockNode, mainChain []*BlockNode) *BlockIndex {
   138  	return &BlockIndex{index: index, mainChain: mainChain}
   139  }
   140  
   141  // AddNode will add node to the index map
   142  func (bi *BlockIndex) AddNode(node *BlockNode) {
   143  	bi.Lock()
   144  	bi.index[node.Hash] = node
   145  	bi.Unlock()
   146  }
   147  
   148  func (bi *BlockIndex) BestNode() *BlockNode {
   149  	bi.RLock()
   150  	defer bi.RUnlock()
   151  	return bi.mainChain[len(bi.mainChain)-1]
   152  }
   153  
   154  // BlockExist check does the block existed in blockIndex
   155  func (bi *BlockIndex) BlockExist(hash *bc.Hash) bool {
   156  	bi.RLock()
   157  	_, ok := bi.index[*hash]
   158  	bi.RUnlock()
   159  	return ok
   160  }
   161  
   162  func (bi *BlockIndex) Equals(bi1 *BlockIndex) bool {
   163  	if bi1 == nil {
   164  		return false
   165  	}
   166  	return testutil.DeepEqual(bi.index, bi1.index) && testutil.DeepEqual(bi.mainChain, bi1.mainChain)
   167  }
   168  
   169  // GetNode will search node from the index map
   170  func (bi *BlockIndex) GetNode(hash *bc.Hash) *BlockNode {
   171  	bi.RLock()
   172  	defer bi.RUnlock()
   173  	return bi.index[*hash]
   174  }
   175  
   176  // TODO: THIS FUNCTION MIGHT BE DELETED
   177  func (bi *BlockIndex) InMainchain(hash bc.Hash) bool {
   178  	bi.RLock()
   179  	defer bi.RUnlock()
   180  
   181  	node, ok := bi.index[hash]
   182  	if !ok {
   183  		return false
   184  	}
   185  	return bi.nodeByHeight(node.Height) == node
   186  }
   187  
   188  // NodeByHeight returns the block node at the specified height.
   189  func (bi *BlockIndex) NodeByHeight(height uint64) *BlockNode {
   190  	bi.RLock()
   191  	defer bi.RUnlock()
   192  	return bi.nodeByHeight(height)
   193  }
   194  
   195  // SetMainChain will set the the mainChain array
   196  func (bi *BlockIndex) SetMainChain(node *BlockNode) {
   197  	bi.Lock()
   198  	defer bi.Unlock()
   199  
   200  	needed := node.Height + 1
   201  	if uint64(cap(bi.mainChain)) < needed {
   202  		nodes := make([]*BlockNode, needed, needed+approxNodesPerDay)
   203  		copy(nodes, bi.mainChain)
   204  		bi.mainChain = nodes
   205  	} else {
   206  		i := uint64(len(bi.mainChain))
   207  		bi.mainChain = bi.mainChain[0:needed]
   208  		for ; i < needed; i++ {
   209  			bi.mainChain[i] = nil
   210  		}
   211  	}
   212  
   213  	for node != nil && bi.mainChain[node.Height] != node {
   214  		bi.mainChain[node.Height] = node
   215  		node = node.Parent
   216  	}
   217  }
   218  
   219  func (bi *BlockIndex) nodeByHeight(height uint64) *BlockNode {
   220  	if height >= uint64(len(bi.mainChain)) {
   221  		return nil
   222  	}
   223  	return bi.mainChain[height]
   224  }