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