github.com/decred/dcrd/blockchain@v1.2.1/stakeversion.go (about) 1 // Copyright (c) 2016-2018 The Decred 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 "encoding/binary" 9 "errors" 10 "fmt" 11 12 "github.com/decred/dcrd/chaincfg/chainhash" 13 ) 14 15 var ( 16 errVoterVersionMajorityNotFound = errors.New("voter version majority " + 17 "not found") 18 errStakeVersionMajorityNotFound = errors.New("stake version majority " + 19 "not found") 20 ) 21 22 // stakeMajorityCacheVersionKey creates a map key that is comprised of a stake 23 // version and a hash. This is used for caches that require a version in 24 // addition to a simple hash. 25 func stakeMajorityCacheVersionKey(version uint32, hash *chainhash.Hash) [stakeMajorityCacheKeySize]byte { 26 var key [stakeMajorityCacheKeySize]byte 27 binary.LittleEndian.PutUint32(key[0:], version) 28 copy(key[4:], hash[:]) 29 return key 30 } 31 32 // calcWantHeight calculates the height of the final block of the previous 33 // interval given a stake validation height, stake validation interval, and 34 // block height. 35 func calcWantHeight(stakeValidationHeight, interval, height int64) int64 { 36 // The adjusted height accounts for the fact the starting validation 37 // height does not necessarily start on an interval and thus the 38 // intervals might not be zero-based. 39 intervalOffset := stakeValidationHeight % interval 40 adjustedHeight := height - intervalOffset - 1 41 return (adjustedHeight - ((adjustedHeight + 1) % interval)) + 42 intervalOffset 43 } 44 45 // CalcWantHeight calculates the height of the final block of the previous 46 // interval given a block height. 47 func (b *BlockChain) CalcWantHeight(interval, height int64) int64 { 48 return calcWantHeight(b.chainParams.StakeValidationHeight, interval, 49 height) 50 } 51 52 // findStakeVersionPriorNode walks the chain backwards from prevNode until it 53 // reaches the final block of the previous stake version interval and returns 54 // that node. The returned node will be nil when the provided prevNode is too 55 // low such that there is no previous stake version interval due to falling 56 // prior to the stake validation interval. 57 // 58 // This function MUST be called with the chain state lock held (for writes). 59 func (b *BlockChain) findStakeVersionPriorNode(prevNode *blockNode) *blockNode { 60 // Check to see if the blockchain is high enough to begin accounting 61 // stake versions. 62 svh := b.chainParams.StakeValidationHeight 63 svi := b.chainParams.StakeVersionInterval 64 nextHeight := prevNode.height + 1 65 if nextHeight < svh+svi { 66 return nil 67 } 68 69 wantHeight := calcWantHeight(svh, svi, nextHeight) 70 return prevNode.Ancestor(wantHeight) 71 } 72 73 // isStakeMajorityVersion determines if minVer requirement is met based on 74 // prevNode. The function always uses the stake versions of the prior window. 75 // For example, if StakeVersionInterval = 11 and StakeValidationHeight = 13 the 76 // windows start at 13 + (11 * 2) 25 and are as follows: 24-34, 35-45, 46-56 ... 77 // If height comes in at 35 we use the 24-34 window, up to height 45. 78 // If height comes in at 46 we use the 35-45 window, up to height 56 etc. 79 // 80 // This function MUST be called with the chain state lock held (for writes). 81 func (b *BlockChain) isStakeMajorityVersion(minVer uint32, prevNode *blockNode) bool { 82 // Walk blockchain backwards to calculate version. 83 node := b.findStakeVersionPriorNode(prevNode) 84 if node == nil { 85 return 0 >= minVer 86 } 87 88 // Generate map key and look up cached result. 89 key := stakeMajorityCacheVersionKey(minVer, &node.hash) 90 if result, ok := b.isStakeMajorityVersionCache[key]; ok { 91 return result 92 } 93 94 // Tally how many of the block headers in the previous stake version 95 // validation interval have their stake version set to at least the 96 // requested minimum version. 97 versionCount := int32(0) 98 iterNode := node 99 for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ { 100 if iterNode.stakeVersion >= minVer { 101 versionCount++ 102 } 103 104 iterNode = iterNode.parent 105 } 106 107 // Determine the required amount of votes to reach supermajority. 108 numRequired := int32(b.chainParams.StakeVersionInterval) * 109 b.chainParams.StakeMajorityMultiplier / 110 b.chainParams.StakeMajorityDivisor 111 112 // Cache result. 113 result := versionCount >= numRequired 114 b.isStakeMajorityVersionCache[key] = result 115 116 return result 117 } 118 119 // calcPriorStakeVersion calculates the header stake version of the prior 120 // interval. The function walks the chain backwards by one interval and then 121 // it performs a standard majority calculation. 122 // 123 // This function MUST be called with the chain state lock held (for writes). 124 func (b *BlockChain) calcPriorStakeVersion(prevNode *blockNode) (uint32, error) { 125 // Walk blockchain backwards to calculate version. 126 node := b.findStakeVersionPriorNode(prevNode) 127 if node == nil { 128 return 0, nil 129 } 130 131 // Check cache. 132 if result, ok := b.calcPriorStakeVersionCache[node.hash]; ok { 133 return result, nil 134 } 135 136 // Tally how many of each stake version the block headers in the previous stake 137 // version validation interval have. 138 versions := make(map[uint32]int32) // [version][count] 139 iterNode := node 140 for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ { 141 versions[iterNode.stakeVersion]++ 142 143 iterNode = iterNode.parent 144 } 145 146 // Determine the required amount of votes to reach supermajority. 147 numRequired := int32(b.chainParams.StakeVersionInterval) * 148 b.chainParams.StakeMajorityMultiplier / 149 b.chainParams.StakeMajorityDivisor 150 151 for version, count := range versions { 152 if count >= numRequired { 153 b.calcPriorStakeVersionCache[node.hash] = version 154 return version, nil 155 } 156 } 157 158 return 0, errStakeVersionMajorityNotFound 159 } 160 161 // calcVoterVersionInterval tallies all voter versions in an interval and 162 // returns a version that has reached 75% majority. This function MUST be 163 // called with a node that is the final node in a valid stake version interval 164 // and greater than or equal to the stake validation height or it will result in 165 // an assertion error. 166 // 167 // This function is really meant to be called internally only from this file. 168 // 169 // This function MUST be called with the chain state lock held (for writes). 170 func (b *BlockChain) calcVoterVersionInterval(prevNode *blockNode) (uint32, error) { 171 // Ensure the provided node is the final node in a valid stake version 172 // interval and is greater than or equal to the stake validation height 173 // since the logic below relies on these assumptions. 174 svh := b.chainParams.StakeValidationHeight 175 svi := b.chainParams.StakeVersionInterval 176 expectedHeight := calcWantHeight(svh, svi, prevNode.height+1) 177 if prevNode.height != expectedHeight || expectedHeight < svh { 178 return 0, AssertError(fmt.Sprintf("calcVoterVersionInterval "+ 179 "must be called with a node that is the final node "+ 180 "in a stake version interval -- called with node %s "+ 181 "(height %d)", prevNode.hash, prevNode.height)) 182 } 183 184 // See if we have cached results. 185 if result, ok := b.calcVoterVersionIntervalCache[prevNode.hash]; ok { 186 return result, nil 187 } 188 189 // Tally both the total number of votes in the previous stake version validation 190 // interval and how many of each version those votes have. 191 versions := make(map[uint32]int32) // [version][count] 192 totalVotesFound := int32(0) 193 iterNode := prevNode 194 for i := int64(0); i < svi && iterNode != nil; i++ { 195 totalVotesFound += int32(len(iterNode.votes)) 196 for _, v := range iterNode.votes { 197 versions[v.Version]++ 198 } 199 200 iterNode = iterNode.parent 201 } 202 203 // Determine the required amount of votes to reach supermajority. 204 numRequired := totalVotesFound * b.chainParams.StakeMajorityMultiplier / 205 b.chainParams.StakeMajorityDivisor 206 207 for version, count := range versions { 208 if count >= numRequired { 209 b.calcVoterVersionIntervalCache[prevNode.hash] = version 210 return version, nil 211 } 212 } 213 214 return 0, errVoterVersionMajorityNotFound 215 } 216 217 // calcVoterVersion calculates the last prior valid majority stake version. If 218 // the current interval does not have a majority stake version it'll go back to 219 // the prior interval. It'll keep going back up to the minimum height at which 220 // point we know the version was 0. 221 // 222 // This function MUST be called with the chain state lock held (for writes). 223 func (b *BlockChain) calcVoterVersion(prevNode *blockNode) (uint32, *blockNode) { 224 // Walk blockchain backwards to find interval. 225 node := b.findStakeVersionPriorNode(prevNode) 226 227 // Iterate over versions until a majority is found. Don't try to count 228 // votes before the stake validation height since there could not 229 // possibly have been any. 230 for node != nil && node.height >= b.chainParams.StakeValidationHeight { 231 version, err := b.calcVoterVersionInterval(node) 232 if err == nil { 233 return version, node 234 } 235 if err != errVoterVersionMajorityNotFound { 236 break 237 } 238 239 node = node.RelativeAncestor(b.chainParams.StakeVersionInterval) 240 } 241 242 // No majority version found. 243 return 0, nil 244 } 245 246 // calcStakeVersion calculates the header stake version based on voter versions. 247 // If there is a majority of voter versions it uses the header stake version to 248 // prevent reverting to a prior version. 249 // 250 // This function MUST be called with the chain state lock held (for writes). 251 func (b *BlockChain) calcStakeVersion(prevNode *blockNode) uint32 { 252 version, node := b.calcVoterVersion(prevNode) 253 if version == 0 || node == nil { 254 // Short circuit. 255 return 0 256 } 257 258 // Check cache. 259 if result, ok := b.calcStakeVersionCache[node.hash]; ok { 260 return result 261 } 262 263 // Walk chain backwards to start of node interval (start of current 264 // period) Note that calcWantHeight returns the LAST height of the 265 // prior interval; hence the + 1. 266 startIntervalHeight := calcWantHeight(b.chainParams.StakeValidationHeight, 267 b.chainParams.StakeVersionInterval, node.height) + 1 268 startNode := node.Ancestor(startIntervalHeight) 269 if startNode == nil { 270 // Note that should this not be possible to hit because a 271 // majority voter version was obtained above, which means there 272 // is at least an interval of nodes. However, be paranoid. 273 b.calcStakeVersionCache[node.hash] = 0 274 } 275 276 // See if we are enforcing V3 blocks yet. Just return V0 since it 277 // wasn't enforced and therefore irrelevant. 278 if !b.isMajorityVersion(3, startNode, 279 b.chainParams.BlockRejectNumRequired) { 280 b.calcStakeVersionCache[node.hash] = 0 281 return 0 282 } 283 284 // Don't allow the stake version to go backwards once it has been locked 285 // in by a previous majority, even if the majority of votes are now a 286 // lower version. 287 if b.isStakeMajorityVersion(version, node) { 288 priorVersion, _ := b.calcPriorStakeVersion(node) 289 if priorVersion > version { 290 version = priorVersion 291 } 292 } 293 294 b.calcStakeVersionCache[node.hash] = version 295 return version 296 } 297 298 // calcStakeVersionByHash calculates the last prior valid majority stake 299 // version. If the current interval does not have a majority stake version 300 // it'll go back to the prior interval. It'll keep going back up to the 301 // minimum height at which point we know the version was 0. 302 // 303 // This function MUST be called with the chain state lock held (for writes). 304 func (b *BlockChain) calcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) { 305 prevNode := b.index.LookupNode(hash) 306 if prevNode == nil { 307 return 0, fmt.Errorf("block %s is not known", hash) 308 } 309 310 return b.calcStakeVersion(prevNode), nil 311 } 312 313 // CalcStakeVersionByHash calculates the expected stake version for the block 314 // AFTER provided block hash. 315 // 316 // This function is safe for concurrent access. 317 func (b *BlockChain) CalcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) { 318 b.chainLock.Lock() 319 version, err := b.calcStakeVersionByHash(hash) 320 b.chainLock.Unlock() 321 return version, err 322 }