github.com/decred/dcrd/blockchain@v1.2.1/checkpoints.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Copyright (c) 2015-2016 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 "fmt" 10 "time" 11 12 "github.com/decred/dcrd/chaincfg" 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/dcrutil" 15 "github.com/decred/dcrd/txscript" 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 = 4096 21 22 // DisableCheckpoints provides a mechanism to disable validation against 23 // checkpoints which you DO NOT want to do in production. It is provided only 24 // for debug purposes. 25 // 26 // This function is safe for concurrent access. 27 func (b *BlockChain) DisableCheckpoints(disable bool) { 28 b.chainLock.Lock() 29 b.noCheckpoints = disable 30 b.chainLock.Unlock() 31 } 32 33 // Checkpoints returns a slice of checkpoints (regardless of whether they are 34 // already known). When checkpoints are disabled or there are no checkpoints 35 // for the active network, it will return nil. 36 // 37 // This function is safe for concurrent access. 38 func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint { 39 b.chainLock.RLock() 40 defer b.chainLock.RUnlock() 41 42 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 43 return nil 44 } 45 46 return b.chainParams.Checkpoints 47 } 48 49 // latestCheckpoint returns the most recent checkpoint (regardless of whether it 50 // is already known). When checkpoints are disabled or there are no checkpoints 51 // for the active network, it will return nil. 52 // 53 // This function MUST be called with the chain state lock held (for reads). 54 func (b *BlockChain) latestCheckpoint() *chaincfg.Checkpoint { 55 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 56 return nil 57 } 58 59 checkpoints := b.chainParams.Checkpoints 60 return &checkpoints[len(checkpoints)-1] 61 } 62 63 // LatestCheckpoint returns the most recent checkpoint (regardless of whether it 64 // is already known). When checkpoints are disabled or there are no checkpoints 65 // for the active network, it will return nil. 66 // 67 // This function is safe for concurrent access. 68 func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint { 69 b.chainLock.RLock() 70 checkpoint := b.latestCheckpoint() 71 b.chainLock.RUnlock() 72 return checkpoint 73 } 74 75 // verifyCheckpoint returns whether the passed block height and hash combination 76 // match the hard-coded checkpoint data. It also returns true if there is no 77 // checkpoint data for the passed block height. 78 // 79 // This function MUST be called with the chain lock held (for reads). 80 func (b *BlockChain) verifyCheckpoint(height int64, hash *chainhash.Hash) bool { 81 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 82 return true 83 } 84 85 // Nothing to check if there is no checkpoint data for the block height. 86 checkpoint, exists := b.checkpointsByHeight[height] 87 if !exists { 88 return true 89 } 90 91 if !checkpoint.Hash.IsEqual(hash) { 92 return false 93 } 94 95 log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height, 96 checkpoint.Hash) 97 return true 98 } 99 100 // findPreviousCheckpoint finds the most recent checkpoint that is already 101 // available in the downloaded portion of the block chain and returns the 102 // associated block node. It returns nil if a checkpoint can't be found (this 103 // should really only happen for blocks before the first checkpoint). 104 // 105 // This function MUST be called with the chain lock held (for reads). 106 func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) { 107 if b.noCheckpoints || len(b.chainParams.Checkpoints) == 0 { 108 return nil, nil 109 } 110 111 // Perform the initial search to find and cache the latest known 112 // checkpoint if the best chain is not known yet or we haven't already 113 // previously searched. 114 checkpoints := b.chainParams.Checkpoints 115 numCheckpoints := len(checkpoints) 116 if b.checkpointNode == nil && b.nextCheckpoint == nil { 117 // Loop backwards through the available checkpoints to find one 118 // that is already available. 119 for i := numCheckpoints - 1; i >= 0; i-- { 120 node := b.index.LookupNode(checkpoints[i].Hash) 121 if node == nil || !b.bestChain.Contains(node) { 122 continue 123 } 124 125 // Checkpoint found. Cache it for future lookups and 126 // set the next expected checkpoint accordingly. 127 b.checkpointNode = node 128 if i < numCheckpoints-1 { 129 b.nextCheckpoint = &checkpoints[i+1] 130 } 131 return b.checkpointNode, nil 132 } 133 134 // No known latest checkpoint. This will only happen on blocks 135 // before the first known checkpoint. So, set the next expected 136 // checkpoint to the first checkpoint and return the fact there 137 // is no latest known checkpoint block. 138 b.nextCheckpoint = &checkpoints[0] 139 return nil, nil 140 } 141 142 // At this point we've already searched for the latest known checkpoint, 143 // so when there is no next checkpoint, the current checkpoint lockin 144 // will always be the latest known checkpoint. 145 if b.nextCheckpoint == nil { 146 return b.checkpointNode, nil 147 } 148 149 // When there is a next checkpoint and the height of the current best 150 // chain does not exceed it, the current checkpoint lockin is still 151 // the latest known checkpoint. 152 if b.bestChain.Tip().height < b.nextCheckpoint.Height { 153 return b.checkpointNode, nil 154 } 155 156 // We've reached or exceeded the next checkpoint height. Note that 157 // once a checkpoint lockin has been reached, forks are prevented from 158 // any blocks before the checkpoint, so we don't have to worry about the 159 // checkpoint going away out from under us due to a chain reorganize. 160 161 // Cache the latest known checkpoint for future lookups. Note that if 162 // this lookup fails something is very wrong since the chain has already 163 // passed the checkpoint which was verified as accurate before inserting 164 // it. 165 checkpointNode := b.index.LookupNode(b.nextCheckpoint.Hash) 166 if checkpointNode == nil { 167 return nil, AssertError(fmt.Sprintf("findPreviousCheckpoint "+ 168 "failed lookup of known good block node %s", 169 b.nextCheckpoint.Hash)) 170 } 171 b.checkpointNode = checkpointNode 172 173 // Set the next expected checkpoint. 174 checkpointIndex := -1 175 for i := numCheckpoints - 1; i >= 0; i-- { 176 if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) { 177 checkpointIndex = i 178 break 179 } 180 } 181 b.nextCheckpoint = nil 182 if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 { 183 b.nextCheckpoint = &checkpoints[checkpointIndex+1] 184 } 185 186 return b.checkpointNode, nil 187 } 188 189 // isNonstandardTransaction determines whether a transaction contains any 190 // scripts which are not one of the standard types. 191 func isNonstandardTransaction(tx *dcrutil.Tx) bool { 192 // Check all of the output public key scripts for non-standard scripts. 193 for _, txOut := range tx.MsgTx().TxOut { 194 scriptClass := txscript.GetScriptClass(txOut.Version, txOut.PkScript) 195 if scriptClass == txscript.NonStandardTy { 196 return true 197 } 198 } 199 return false 200 } 201 202 // IsCheckpointCandidate returns whether or not the passed block is a good 203 // checkpoint candidate. 204 // 205 // The factors used to determine a good checkpoint are: 206 // - The block must be in the main chain 207 // - The block must be at least 'CheckpointConfirmations' blocks prior to the 208 // current end of the main chain 209 // - The timestamps for the blocks before and after the checkpoint must have 210 // timestamps which are also before and after the checkpoint, respectively 211 // (due to the median time allowance this is not always the case) 212 // - The block must not contain any strange transaction such as those with 213 // nonstandard scripts 214 // 215 // The intent is that candidates are reviewed by a developer to make the final 216 // decision and then manually added to the list of checkpoints for a network. 217 // 218 // This function is safe for concurrent access. 219 func (b *BlockChain) IsCheckpointCandidate(block *dcrutil.Block) (bool, error) { 220 b.chainLock.RLock() 221 defer b.chainLock.RUnlock() 222 223 // Checkpoints must be enabled. 224 if b.noCheckpoints { 225 return false, fmt.Errorf("checkpoints are disabled") 226 } 227 228 // A checkpoint must be in the main chain. 229 node := b.index.LookupNode(block.Hash()) 230 if node == nil || !b.bestChain.Contains(node) { 231 return false, nil 232 } 233 234 // Ensure the height of the passed block and the entry for the block in 235 // the main chain match. This should always be the case unless the 236 // caller provided an invalid block. 237 if node.height != block.Height() { 238 return false, fmt.Errorf("passed block height of %d does not "+ 239 "match the main chain height of %d", block.Height(), 240 node.height) 241 } 242 243 // A checkpoint must be at least CheckpointConfirmations blocks before 244 // the end of the main chain. 245 if node.height > (b.bestChain.Tip().height - CheckpointConfirmations) { 246 return false, nil 247 } 248 249 // A checkpoint must be have at least one block after it. 250 // 251 // This should always succeed since the check above already made sure it 252 // is CheckpointConfirmations back, but be safe in case the constant 253 // changes. 254 nextNode := b.bestChain.Next(node) 255 if nextNode == nil { 256 return false, nil 257 } 258 259 // A checkpoint must be have at least one block before it. 260 if node.parent == nil { 261 return false, nil 262 } 263 264 // A checkpoint must have timestamps for the block and the blocks on 265 // either side of it in order (due to the median time allowance this is 266 // not always the case). 267 prevTime := time.Unix(node.parent.timestamp, 0) 268 curTime := block.MsgBlock().Header.Timestamp 269 nextTime := time.Unix(nextNode.timestamp, 0) 270 if prevTime.After(curTime) || nextTime.Before(curTime) { 271 return false, nil 272 } 273 274 // A checkpoint must have transactions that only contain standard 275 // scripts. 276 for _, tx := range block.Transactions() { 277 if isNonstandardTransaction(tx) { 278 return false, nil 279 } 280 } 281 282 // All of the checks passed, so the block is a candidate. 283 return true, nil 284 }