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