github.com/palcoin-project/palcd@v1.0.0/blockchain/chainview.go (about) 1 // Copyright (c) 2017 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package blockchain 6 7 import ( 8 "sync" 9 ) 10 11 // approxNodesPerWeek is an approximation of the number of new blocks there are 12 // in a week on average. 13 const approxNodesPerWeek = 6 * 24 * 7 14 15 // log2FloorMasks defines the masks to use when quickly calculating 16 // floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using 17 // shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0. 18 var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2} 19 20 // fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps. 21 func fastLog2Floor(n uint32) uint8 { 22 rv := uint8(0) 23 exponent := uint8(16) 24 for i := 0; i < 5; i++ { 25 if n&log2FloorMasks[i] != 0 { 26 rv += exponent 27 n >>= exponent 28 } 29 exponent >>= 1 30 } 31 return rv 32 } 33 34 // chainView provides a flat view of a specific branch of the block chain from 35 // its tip back to the genesis block and provides various convenience functions 36 // for comparing chains. 37 // 38 // For example, assume a block chain with a side chain as depicted below: 39 // genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 40 // \-> 4a -> 5a -> 6a 41 // 42 // The chain view for the branch ending in 6a consists of: 43 // genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a 44 type chainView struct { 45 mtx sync.Mutex 46 nodes []*blockNode 47 } 48 49 // newChainView returns a new chain view for the given tip block node. Passing 50 // nil as the tip will result in a chain view that is not initialized. The tip 51 // can be updated at any time via the setTip function. 52 func newChainView(tip *blockNode) *chainView { 53 // The mutex is intentionally not held since this is a constructor. 54 var c chainView 55 c.setTip(tip) 56 return &c 57 } 58 59 // genesis returns the genesis block for the chain view. This only differs from 60 // the exported version in that it is up to the caller to ensure the lock is 61 // held. 62 // 63 // This function MUST be called with the view mutex locked (for reads). 64 func (c *chainView) genesis() *blockNode { 65 if len(c.nodes) == 0 { 66 return nil 67 } 68 69 return c.nodes[0] 70 } 71 72 // Genesis returns the genesis block for the chain view. 73 // 74 // This function is safe for concurrent access. 75 func (c *chainView) Genesis() *blockNode { 76 c.mtx.Lock() 77 genesis := c.genesis() 78 c.mtx.Unlock() 79 return genesis 80 } 81 82 // tip returns the current tip block node for the chain view. It will return 83 // nil if there is no tip. This only differs from the exported version in that 84 // it is up to the caller to ensure the lock is held. 85 // 86 // This function MUST be called with the view mutex locked (for reads). 87 func (c *chainView) tip() *blockNode { 88 if len(c.nodes) == 0 { 89 return nil 90 } 91 92 return c.nodes[len(c.nodes)-1] 93 } 94 95 // Tip returns the current tip block node for the chain view. It will return 96 // nil if there is no tip. 97 // 98 // This function is safe for concurrent access. 99 func (c *chainView) Tip() *blockNode { 100 c.mtx.Lock() 101 tip := c.tip() 102 c.mtx.Unlock() 103 return tip 104 } 105 106 // setTip sets the chain view to use the provided block node as the current tip 107 // and ensures the view is consistent by populating it with the nodes obtained 108 // by walking backwards all the way to genesis block as necessary. Further 109 // calls will only perform the minimum work needed, so switching between chain 110 // tips is efficient. This only differs from the exported version in that it is 111 // up to the caller to ensure the lock is held. 112 // 113 // This function MUST be called with the view mutex locked (for writes). 114 func (c *chainView) setTip(node *blockNode) { 115 if node == nil { 116 // Keep the backing array around for potential future use. 117 c.nodes = c.nodes[:0] 118 return 119 } 120 121 // Create or resize the slice that will hold the block nodes to the 122 // provided tip height. When creating the slice, it is created with 123 // some additional capacity for the underlying array as append would do 124 // in order to reduce overhead when extending the chain later. As long 125 // as the underlying array already has enough capacity, simply expand or 126 // contract the slice accordingly. The additional capacity is chosen 127 // such that the array should only have to be extended about once a 128 // week. 129 needed := node.height + 1 130 if int32(cap(c.nodes)) < needed { 131 nodes := make([]*blockNode, needed, needed+approxNodesPerWeek) 132 copy(nodes, c.nodes) 133 c.nodes = nodes 134 } else { 135 prevLen := int32(len(c.nodes)) 136 c.nodes = c.nodes[0:needed] 137 for i := prevLen; i < needed; i++ { 138 c.nodes[i] = nil 139 } 140 } 141 142 for node != nil && c.nodes[node.height] != node { 143 c.nodes[node.height] = node 144 node = node.parent 145 } 146 } 147 148 // SetTip sets the chain view to use the provided block node as the current tip 149 // and ensures the view is consistent by populating it with the nodes obtained 150 // by walking backwards all the way to genesis block as necessary. Further 151 // calls will only perform the minimum work needed, so switching between chain 152 // tips is efficient. 153 // 154 // This function is safe for concurrent access. 155 func (c *chainView) SetTip(node *blockNode) { 156 c.mtx.Lock() 157 c.setTip(node) 158 c.mtx.Unlock() 159 } 160 161 // height returns the height of the tip of the chain view. It will return -1 if 162 // there is no tip (which only happens if the chain view has not been 163 // initialized). This only differs from the exported version in that it is up 164 // to the caller to ensure the lock is held. 165 // 166 // This function MUST be called with the view mutex locked (for reads). 167 func (c *chainView) height() int32 { 168 return int32(len(c.nodes) - 1) 169 } 170 171 // Height returns the height of the tip of the chain view. It will return -1 if 172 // there is no tip (which only happens if the chain view has not been 173 // initialized). 174 // 175 // This function is safe for concurrent access. 176 func (c *chainView) Height() int32 { 177 c.mtx.Lock() 178 height := c.height() 179 c.mtx.Unlock() 180 return height 181 } 182 183 // nodeByHeight returns the block node at the specified height. Nil will be 184 // returned if the height does not exist. This only differs from the exported 185 // version in that it is up to the caller to ensure the lock is held. 186 // 187 // This function MUST be called with the view mutex locked (for reads). 188 func (c *chainView) nodeByHeight(height int32) *blockNode { 189 if height < 0 || height >= int32(len(c.nodes)) { 190 return nil 191 } 192 193 return c.nodes[height] 194 } 195 196 // NodeByHeight returns the block node at the specified height. Nil will be 197 // returned if the height does not exist. 198 // 199 // This function is safe for concurrent access. 200 func (c *chainView) NodeByHeight(height int32) *blockNode { 201 c.mtx.Lock() 202 node := c.nodeByHeight(height) 203 c.mtx.Unlock() 204 return node 205 } 206 207 // Equals returns whether or not two chain views are the same. Uninitialized 208 // views (tip set to nil) are considered equal. 209 // 210 // This function is safe for concurrent access. 211 func (c *chainView) Equals(other *chainView) bool { 212 c.mtx.Lock() 213 other.mtx.Lock() 214 equals := len(c.nodes) == len(other.nodes) && c.tip() == other.tip() 215 other.mtx.Unlock() 216 c.mtx.Unlock() 217 return equals 218 } 219 220 // contains returns whether or not the chain view contains the passed block 221 // node. This only differs from the exported version in that it is up to the 222 // caller to ensure the lock is held. 223 // 224 // This function MUST be called with the view mutex locked (for reads). 225 func (c *chainView) contains(node *blockNode) bool { 226 return c.nodeByHeight(node.height) == node 227 } 228 229 // Contains returns whether or not the chain view contains the passed block 230 // node. 231 // 232 // This function is safe for concurrent access. 233 func (c *chainView) Contains(node *blockNode) bool { 234 c.mtx.Lock() 235 contains := c.contains(node) 236 c.mtx.Unlock() 237 return contains 238 } 239 240 // next returns the successor to the provided node for the chain view. It will 241 // return nil if there is no successor or the provided node is not part of the 242 // view. This only differs from the exported version in that it is up to the 243 // caller to ensure the lock is held. 244 // 245 // See the comment on the exported function for more details. 246 // 247 // This function MUST be called with the view mutex locked (for reads). 248 func (c *chainView) next(node *blockNode) *blockNode { 249 if node == nil || !c.contains(node) { 250 return nil 251 } 252 253 return c.nodeByHeight(node.height + 1) 254 } 255 256 // Next returns the successor to the provided node for the chain view. It will 257 // return nil if there is no successfor or the provided node is not part of the 258 // view. 259 // 260 // For example, assume a block chain with a side chain as depicted below: 261 // genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 262 // \-> 4a -> 5a -> 6a 263 // 264 // Further, assume the view is for the longer chain depicted above. That is to 265 // say it consists of: 266 // genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 267 // 268 // Invoking this function with block node 5 would return block node 6 while 269 // invoking it with block node 5a would return nil since that node is not part 270 // of the view. 271 // 272 // This function is safe for concurrent access. 273 func (c *chainView) Next(node *blockNode) *blockNode { 274 c.mtx.Lock() 275 next := c.next(node) 276 c.mtx.Unlock() 277 return next 278 } 279 280 // findFork returns the final common block between the provided node and the 281 // the chain view. It will return nil if there is no common block. This only 282 // differs from the exported version in that it is up to the caller to ensure 283 // the lock is held. 284 // 285 // See the exported FindFork comments for more details. 286 // 287 // This function MUST be called with the view mutex locked (for reads). 288 func (c *chainView) findFork(node *blockNode) *blockNode { 289 // No fork point for node that doesn't exist. 290 if node == nil { 291 return nil 292 } 293 294 // When the height of the passed node is higher than the height of the 295 // tip of the current chain view, walk backwards through the nodes of 296 // the other chain until the heights match (or there or no more nodes in 297 // which case there is no common node between the two). 298 // 299 // NOTE: This isn't strictly necessary as the following section will 300 // find the node as well, however, it is more efficient to avoid the 301 // contains check since it is already known that the common node can't 302 // possibly be past the end of the current chain view. It also allows 303 // this code to take advantage of any potential future optimizations to 304 // the Ancestor function such as using an O(log n) skip list. 305 chainHeight := c.height() 306 if node.height > chainHeight { 307 node = node.Ancestor(chainHeight) 308 } 309 310 // Walk the other chain backwards as long as the current one does not 311 // contain the node or there are no more nodes in which case there is no 312 // common node between the two. 313 for node != nil && !c.contains(node) { 314 node = node.parent 315 } 316 317 return node 318 } 319 320 // FindFork returns the final common block between the provided node and the 321 // the chain view. It will return nil if there is no common block. 322 // 323 // For example, assume a block chain with a side chain as depicted below: 324 // genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8 325 // \-> 6a -> 7a 326 // 327 // Further, assume the view is for the longer chain depicted above. That is to 328 // say it consists of: 329 // genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8. 330 // 331 // Invoking this function with block node 7a would return block node 5 while 332 // invoking it with block node 7 would return itself since it is already part of 333 // the branch formed by the view. 334 // 335 // This function is safe for concurrent access. 336 func (c *chainView) FindFork(node *blockNode) *blockNode { 337 c.mtx.Lock() 338 fork := c.findFork(node) 339 c.mtx.Unlock() 340 return fork 341 } 342 343 // blockLocator returns a block locator for the passed block node. The passed 344 // node can be nil in which case the block locator for the current tip 345 // associated with the view will be returned. This only differs from the 346 // exported version in that it is up to the caller to ensure the lock is held. 347 // 348 // See the exported BlockLocator function comments for more details. 349 // 350 // This function MUST be called with the view mutex locked (for reads). 351 func (c *chainView) blockLocator(node *blockNode) BlockLocator { 352 // Use the current tip if requested. 353 if node == nil { 354 node = c.tip() 355 } 356 if node == nil { 357 return nil 358 } 359 360 // Calculate the max number of entries that will ultimately be in the 361 // block locator. See the description of the algorithm for how these 362 // numbers are derived. 363 var maxEntries uint8 364 if node.height <= 12 { 365 maxEntries = uint8(node.height) + 1 366 } else { 367 // Requested hash itself + previous 10 entries + genesis block. 368 // Then floor(log2(height-10)) entries for the skip portion. 369 adjustedHeight := uint32(node.height) - 10 370 maxEntries = 12 + fastLog2Floor(adjustedHeight) 371 } 372 locator := make(BlockLocator, 0, maxEntries) 373 374 step := int32(1) 375 for node != nil { 376 locator = append(locator, &node.hash) 377 378 // Nothing more to add once the genesis block has been added. 379 if node.height == 0 { 380 break 381 } 382 383 // Calculate height of previous node to include ensuring the 384 // final node is the genesis block. 385 height := node.height - step 386 if height < 0 { 387 height = 0 388 } 389 390 // When the node is in the current chain view, all of its 391 // ancestors must be too, so use a much faster O(1) lookup in 392 // that case. Otherwise, fall back to walking backwards through 393 // the nodes of the other chain to the correct ancestor. 394 if c.contains(node) { 395 node = c.nodes[height] 396 } else { 397 node = node.Ancestor(height) 398 } 399 400 // Once 11 entries have been included, start doubling the 401 // distance between included hashes. 402 if len(locator) > 10 { 403 step *= 2 404 } 405 } 406 407 return locator 408 } 409 410 // BlockLocator returns a block locator for the passed block node. The passed 411 // node can be nil in which case the block locator for the current tip 412 // associated with the view will be returned. 413 // 414 // See the BlockLocator type for details on the algorithm used to create a block 415 // locator. 416 // 417 // This function is safe for concurrent access. 418 func (c *chainView) BlockLocator(node *blockNode) BlockLocator { 419 c.mtx.Lock() 420 locator := c.blockLocator(node) 421 c.mtx.Unlock() 422 return locator 423 }