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 }