github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/blockchain/checkpoints.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 "fmt" 10 11 "github.com/BlockABC/godash/chaincfg" 12 "github.com/BlockABC/godash/database" 13 "github.com/BlockABC/godash/txscript" 14 "github.com/BlockABC/godash/wire" 15 "github.com/BlockABC/godashutil" 16 ) 17 18 // CheckpointConfirmations is the number of blocks before the end of the current 19 // best block chain that a good checkpoint candidate must be. 20 const CheckpointConfirmations = 2016 21 22 // newShaHashFromStr converts the passed big-endian hex string into a 23 // wire.ShaHash. It only differs from the one available in wire in that 24 // it ignores the error since it will only (and must only) be called with 25 // hard-coded, and therefore known good, hashes. 26 func newShaHashFromStr(hexStr string) *wire.ShaHash { 27 sha, _ := wire.NewShaHashFromStr(hexStr) 28 return sha 29 } 30 31 // DisableCheckpoints provides a mechanism to disable validation against 32 // checkpoints which you DO NOT want to do in production. It is provided only 33 // for debug purposes. 34 // 35 // This function is safe for concurrent access. 36 func (b *BlockChain) DisableCheckpoints(disable bool) { 37 b.chainLock.Lock() 38 b.noCheckpoints = disable 39 b.chainLock.Unlock() 40 } 41 42 // Checkpoints returns a slice of checkpoints (regardless of whether they are 43 // already known). When checkpoints are disabled or there are no checkpoints 44 // for the active network, it will return nil. 45 // 46 // This function is safe for concurrent access. 47 func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint { 48 b.chainLock.RLock() 49 defer b.chainLock.RUnlock() 50 51 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 52 return nil 53 } 54 55 return b.chainParams.Checkpoints 56 } 57 58 // latestCheckpoint returns the most recent checkpoint (regardless of whether it 59 // is already known). When checkpoints are disabled or there are no checkpoints 60 // for the active network, it will return nil. 61 // 62 // This function MUST be called with the chain state lock held (for reads). 63 func (b *BlockChain) latestCheckpoint() *chaincfg.Checkpoint { 64 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 65 return nil 66 } 67 68 checkpoints := b.chainParams.Checkpoints 69 return &checkpoints[len(checkpoints)-1] 70 } 71 72 // LatestCheckpoint returns the most recent checkpoint (regardless of whether it 73 // is already known). When checkpoints are disabled or there are no checkpoints 74 // for the active network, it will return nil. 75 // 76 // This function is safe for concurrent access. 77 func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint { 78 b.chainLock.RLock() 79 checkpoint := b.latestCheckpoint() 80 b.chainLock.RUnlock() 81 return checkpoint 82 } 83 84 // verifyCheckpoint returns whether the passed block height and hash combination 85 // match the hard-coded checkpoint data. It also returns true if there is no 86 // checkpoint data for the passed block height. 87 // 88 // This function MUST be called with the chain lock held (for reads). 89 func (b *BlockChain) verifyCheckpoint(height int32, hash *wire.ShaHash) bool { 90 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 91 return true 92 } 93 94 // Nothing to check if there is no checkpoint data for the block height. 95 checkpoint, exists := b.checkpointsByHeight[height] 96 if !exists { 97 return true 98 } 99 100 if !checkpoint.Hash.IsEqual(hash) { 101 return false 102 } 103 104 log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height, 105 checkpoint.Hash) 106 return true 107 } 108 109 // findPreviousCheckpoint finds the most recent checkpoint that is already 110 // available in the downloaded portion of the block chain and returns the 111 // associated block. It returns nil if a checkpoint can't be found (this should 112 // really only happen for blocks before the first checkpoint). 113 // 114 // This function MUST be called with the chain lock held (for reads). 115 func (b *BlockChain) findPreviousCheckpoint() (*godashutil.Block, error) { 116 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 117 return nil, nil 118 } 119 120 // No checkpoints. 121 checkpoints := b.chainParams.Checkpoints 122 numCheckpoints := len(checkpoints) 123 if numCheckpoints == 0 { 124 return nil, nil 125 } 126 127 // Perform the initial search to find and cache the latest known 128 // checkpoint if the best chain is not known yet or we haven't already 129 // previously searched. 130 if b.checkpointBlock == nil && b.nextCheckpoint == nil { 131 // Loop backwards through the available checkpoints to find one 132 // that is already available. 133 checkpointIndex := -1 134 err := b.db.View(func(dbTx database.Tx) error { 135 for i := numCheckpoints - 1; i >= 0; i-- { 136 if dbMainChainHasBlock(dbTx, checkpoints[i].Hash) { 137 checkpointIndex = i 138 break 139 } 140 } 141 return nil 142 }) 143 if err != nil { 144 return nil, err 145 } 146 147 // No known latest checkpoint. This will only happen on blocks 148 // before the first known checkpoint. So, set the next expected 149 // checkpoint to the first checkpoint and return the fact there 150 // is no latest known checkpoint block. 151 if checkpointIndex == -1 { 152 b.nextCheckpoint = &checkpoints[0] 153 return nil, nil 154 } 155 156 // Cache the latest known checkpoint block for future lookups. 157 checkpoint := checkpoints[checkpointIndex] 158 err = b.db.View(func(dbTx database.Tx) error { 159 block, err := dbFetchBlockByHash(dbTx, checkpoint.Hash) 160 if err != nil { 161 return err 162 } 163 b.checkpointBlock = block 164 165 // Set the next expected checkpoint block accordingly. 166 b.nextCheckpoint = nil 167 if checkpointIndex < numCheckpoints-1 { 168 b.nextCheckpoint = &checkpoints[checkpointIndex+1] 169 } 170 171 return nil 172 }) 173 if err != nil { 174 return nil, err 175 } 176 177 return b.checkpointBlock, nil 178 } 179 180 // At this point we've already searched for the latest known checkpoint, 181 // so when there is no next checkpoint, the current checkpoint lockin 182 // will always be the latest known checkpoint. 183 if b.nextCheckpoint == nil { 184 return b.checkpointBlock, nil 185 } 186 187 // When there is a next checkpoint and the height of the current best 188 // chain does not exceed it, the current checkpoint lockin is still 189 // the latest known checkpoint. 190 if b.bestNode.height < b.nextCheckpoint.Height { 191 return b.checkpointBlock, nil 192 } 193 194 // We've reached or exceeded the next checkpoint height. Note that 195 // once a checkpoint lockin has been reached, forks are prevented from 196 // any blocks before the checkpoint, so we don't have to worry about the 197 // checkpoint going away out from under us due to a chain reorganize. 198 199 // Cache the latest known checkpoint block for future lookups. Note 200 // that if this lookup fails something is very wrong since the chain 201 // has already passed the checkpoint which was verified as accurate 202 // before inserting it. 203 err := b.db.View(func(tx database.Tx) error { 204 block, err := dbFetchBlockByHash(tx, b.nextCheckpoint.Hash) 205 if err != nil { 206 return err 207 } 208 b.checkpointBlock = block 209 return nil 210 }) 211 if err != nil { 212 return nil, err 213 } 214 215 // Set the next expected checkpoint. 216 checkpointIndex := -1 217 for i := numCheckpoints - 1; i >= 0; i-- { 218 if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) { 219 checkpointIndex = i 220 break 221 } 222 } 223 b.nextCheckpoint = nil 224 if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 { 225 b.nextCheckpoint = &checkpoints[checkpointIndex+1] 226 } 227 228 return b.checkpointBlock, nil 229 } 230 231 // isNonstandardTransaction determines whether a transaction contains any 232 // scripts which are not one of the standard types. 233 func isNonstandardTransaction(tx *godashutil.Tx) bool { 234 // Check all of the output public key scripts for non-standard scripts. 235 for _, txOut := range tx.MsgTx().TxOut { 236 scriptClass := txscript.GetScriptClass(txOut.PkScript) 237 if scriptClass == txscript.NonStandardTy { 238 return true 239 } 240 } 241 return false 242 } 243 244 // IsCheckpointCandidate returns whether or not the passed block is a good 245 // checkpoint candidate. 246 // 247 // The factors used to determine a good checkpoint are: 248 // - The block must be in the main chain 249 // - The block must be at least 'CheckpointConfirmations' blocks prior to the 250 // current end of the main chain 251 // - The timestamps for the blocks before and after the checkpoint must have 252 // timestamps which are also before and after the checkpoint, respectively 253 // (due to the median time allowance this is not always the case) 254 // - The block must not contain any strange transaction such as those with 255 // nonstandard scripts 256 // 257 // The intent is that candidates are reviewed by a developer to make the final 258 // decision and then manually added to the list of checkpoints for a network. 259 // 260 // This function is safe for concurrent access. 261 func (b *BlockChain) IsCheckpointCandidate(block *godashutil.Block) (bool, error) { 262 b.chainLock.RLock() 263 defer b.chainLock.RUnlock() 264 265 // Checkpoints must be enabled. 266 if b.noCheckpoints { 267 return false, fmt.Errorf("checkpoints are disabled") 268 } 269 270 var isCandidate bool 271 err := b.db.View(func(dbTx database.Tx) error { 272 // A checkpoint must be in the main chain. 273 blockHeight, err := dbFetchHeightByHash(dbTx, block.Sha()) 274 if err != nil { 275 // Only return an error if it's not due to the block not 276 // being in the main chain. 277 if !isNotInMainChainErr(err) { 278 return err 279 } 280 return nil 281 } 282 283 // Ensure the height of the passed block and the entry for the 284 // block in the main chain match. This should always be the 285 // case unless the caller provided an invalid block. 286 if blockHeight != block.Height() { 287 return fmt.Errorf("passed block height of %d does not "+ 288 "match the main chain height of %d", 289 block.Height(), blockHeight) 290 } 291 292 // A checkpoint must be at least CheckpointConfirmations blocks 293 // before the end of the main chain. 294 mainChainHeight := b.bestNode.height 295 if blockHeight > (mainChainHeight - CheckpointConfirmations) { 296 return nil 297 } 298 299 // Get the previous block header. 300 prevHash := &block.MsgBlock().Header.PrevBlock 301 prevHeader, err := dbFetchHeaderByHash(dbTx, prevHash) 302 if err != nil { 303 return err 304 } 305 306 // Get the next block header. 307 nextHeader, err := dbFetchHeaderByHeight(dbTx, blockHeight+1) 308 if err != nil { 309 return err 310 } 311 312 // A checkpoint must have timestamps for the block and the 313 // blocks on either side of it in order (due to the median time 314 // allowance this is not always the case). 315 prevTime := prevHeader.Timestamp 316 curTime := block.MsgBlock().Header.Timestamp 317 nextTime := nextHeader.Timestamp 318 if prevTime.After(curTime) || nextTime.Before(curTime) { 319 return nil 320 } 321 322 // A checkpoint must have transactions that only contain 323 // standard scripts. 324 for _, tx := range block.Transactions() { 325 if isNonstandardTransaction(tx) { 326 return nil 327 } 328 } 329 330 // All of the checks passed, so the block is a candidate. 331 isCandidate = true 332 return nil 333 }) 334 return isCandidate, err 335 }