github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/blockchain/blocklocator.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2016 The Dash 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 "github.com/BlockABC/godash/database" 10 "github.com/BlockABC/godash/wire" 11 ) 12 13 // BlockLocator is used to help locate a specific block. The algorithm for 14 // building the block locator is to add the hashes in reverse order until 15 // the genesis block is reached. In order to keep the list of locator hashes 16 // to a reasonable number of entries, first the most recent previous 10 block 17 // hashes are added, then the step is doubled each loop iteration to 18 // exponentially decrease the number of hashes as a function of the distance 19 // from the block being located. 20 // 21 // For example, assume you have a block chain with a side chain as depicted 22 // below: 23 // genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 24 // \-> 16a -> 17a 25 // 26 // The block locator for block 17a would be the hashes of blocks: 27 // [17a 16a 15 14 13 12 11 10 9 8 6 2 genesis] 28 type BlockLocator []*wire.ShaHash 29 30 // blockLocatorFromHash returns a block locator for the passed block hash. 31 // See BlockLocator for details on the algotirhm used to create a block locator. 32 // 33 // In addition to the general algorithm referenced above, there are a couple of 34 // special cases which are handled: 35 // 36 // - If the genesis hash is passed, there are no previous hashes to add and 37 // therefore the block locator will only consist of the genesis hash 38 // - If the passed hash is not currently known, the block locator will only 39 // consist of the passed hash 40 // 41 // This function MUST be called with the chain state lock held (for reads). 42 func (b *BlockChain) blockLocatorFromHash(hash *wire.ShaHash) BlockLocator { 43 // The locator contains the requested hash at the very least. 44 locator := make(BlockLocator, 0, wire.MaxBlockLocatorsPerMsg) 45 locator = append(locator, hash) 46 47 // Nothing more to do if a locator for the genesis hash was requested. 48 if hash.IsEqual(b.chainParams.GenesisHash) { 49 return locator 50 } 51 52 // Attempt to find the height of the block that corresponds to the 53 // passed hash, and if it's on a side chain, also find the height at 54 // which it forks from the main chain. 55 blockHeight := int32(-1) 56 forkHeight := int32(-1) 57 node, exists := b.index[*hash] 58 if !exists { 59 // Try to look up the height for passed block hash. Assume an 60 // error means it doesn't exist and just return the locator for 61 // the block itself. 62 var height int32 63 err := b.db.View(func(dbTx database.Tx) error { 64 var err error 65 height, err = dbFetchHeightByHash(dbTx, hash) 66 return err 67 }) 68 if err != nil { 69 return locator 70 } 71 72 blockHeight = height 73 } else { 74 blockHeight = node.height 75 76 // Find the height at which this node forks from the main chain 77 // if the node is on a side chain. 78 if !node.inMainChain { 79 for n := node; n.parent != nil; n = n.parent { 80 if n.inMainChain { 81 forkHeight = n.height 82 break 83 } 84 } 85 } 86 } 87 88 // Generate the block locators according to the algorithm described in 89 // in the BlockLocator comment and make sure to leave room for the final 90 // genesis hash. 91 // 92 // The error is intentionally ignored here since the only way the code 93 // could fail is if there is something wrong with the database which 94 // will be caught in short order anyways and it's also safe to ignore 95 // block locators. 96 _ = b.db.View(func(dbTx database.Tx) error { 97 iterNode := node 98 increment := int32(1) 99 for len(locator) < wire.MaxBlockLocatorsPerMsg-1 { 100 // Once there are 10 locators, exponentially increase 101 // the distance between each block locator. 102 if len(locator) > 10 { 103 increment *= 2 104 } 105 blockHeight -= increment 106 if blockHeight < 1 { 107 break 108 } 109 110 // As long as this is still on the side chain, walk 111 // backwards along the side chain nodes to each block 112 // height. 113 if forkHeight != -1 && blockHeight > forkHeight { 114 // Intentionally use parent field instead of the 115 // getPrevNodeFromNode function since we don't 116 // want to dynamically load nodes when building 117 // block locators. Side chain blocks should 118 // always be in memory already, and if they 119 // aren't for some reason it's ok to skip them. 120 for iterNode != nil && blockHeight > iterNode.height { 121 iterNode = iterNode.parent 122 } 123 if iterNode != nil && iterNode.height == blockHeight { 124 locator = append(locator, iterNode.hash) 125 } 126 continue 127 } 128 129 // The desired block height is in the main chain, so 130 // look it up from the main chain database. 131 h, err := dbFetchHashByHeight(dbTx, blockHeight) 132 if err != nil { 133 // This shouldn't happen and it's ok to ignore 134 // block locators, so just continue to the next 135 // one. 136 log.Warnf("Lookup of known valid height failed %v", 137 blockHeight) 138 continue 139 } 140 locator = append(locator, h) 141 } 142 143 return nil 144 }) 145 146 // Append the appropriate genesis block. 147 locator = append(locator, b.chainParams.GenesisHash) 148 return locator 149 } 150 151 // BlockLocatorFromHash returns a block locator for the passed block hash. 152 // See BlockLocator for details on the algorithm used to create a block locator. 153 // 154 // In addition to the general algorithm referenced above, there are a couple of 155 // special cases which are handled: 156 // 157 // - If the genesis hash is passed, there are no previous hashes to add and 158 // therefore the block locator will only consist of the genesis hash 159 // - If the passed hash is not currently known, the block locator will only 160 // consist of the passed hash 161 // 162 // This function is safe for concurrent access. 163 func (b *BlockChain) BlockLocatorFromHash(hash *wire.ShaHash) BlockLocator { 164 b.chainLock.RLock() 165 locator := b.blockLocatorFromHash(hash) 166 b.chainLock.RUnlock() 167 return locator 168 } 169 170 // LatestBlockLocator returns a block locator for the latest known tip of the 171 // main (best) chain. 172 // 173 // This function is safe for concurrent access. 174 func (b *BlockChain) LatestBlockLocator() (BlockLocator, error) { 175 b.chainLock.RLock() 176 locator := b.blockLocatorFromHash(b.bestNode.hash) 177 b.chainLock.RUnlock() 178 return locator, nil 179 }