github.com/decred/dcrd/blockchain@v1.2.1/fullblocksstakeversion_test.go (about) 1 // Copyright (c) 2016-2019 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 "fmt" 9 "testing" 10 11 "github.com/decred/dcrd/blockchain/chaingen" 12 "github.com/decred/dcrd/chaincfg" 13 ) 14 15 // TestStakeVersion ensures that the stake version field in the block header is 16 // enforced properly. 17 func TestStakeVersion(t *testing.T) { 18 // Create a test harness initialized with the genesis block as the tip. 19 params := &chaincfg.RegNetParams 20 g, teardownFunc := newChaingenHarness(t, params, "stakeversiontest") 21 defer teardownFunc() 22 23 // Shorter versions of useful params for convenience. 24 ticketsPerBlock := params.TicketsPerBlock 25 stakeValidationHeight := params.StakeValidationHeight 26 stakeVerInterval := params.StakeVersionInterval 27 stakeMajorityMul := int64(params.StakeMajorityMultiplier) 28 stakeMajorityDiv := int64(params.StakeMajorityDivisor) 29 30 // --------------------------------------------------------------------- 31 // Generate and accept enough blocks to reach stake validation height. 32 // --------------------------------------------------------------------- 33 34 g.AdvanceToStakeValidationHeight() 35 36 // --------------------------------------------------------------------- 37 // Generate enough blocks to reach one block before the next stake 38 // version interval with block version 2, stake version 0, and vote 39 // version 3. 40 // 41 // This will result in a majority of blocks with a version prior to 42 // version 3 where stake version enforcement begins and thus it must not 43 // be enforced. 44 // --------------------------------------------------------------------- 45 46 for i := int64(0); i < stakeVerInterval-1; i++ { 47 outs := g.OldestCoinbaseOuts() 48 blockName := fmt.Sprintf("bsvtA%d", i) 49 g.NextBlock(blockName, nil, outs[1:], 50 chaingen.ReplaceBlockVersion(2), 51 chaingen.ReplaceStakeVersion(0), 52 chaingen.ReplaceVoteVersions(3)) 53 g.SaveTipCoinbaseOuts() 54 g.AcceptTipBlock() 55 } 56 g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval - 1)) 57 g.AssertBlockVersion(2) 58 g.AssertStakeVersion(0) 59 60 // --------------------------------------------------------------------- 61 // Generate a single block with block version 3, stake version 42, and 62 // vote version 41. 63 // 64 // This block must be accepted because even though it is a version 3 65 // block with an invalid stake version, there have not yet been a 66 // majority of version 3 blocks which is required to trigger stake 67 // version enforcement. 68 // --------------------------------------------------------------------- 69 70 outs := g.OldestCoinbaseOuts() 71 g.NextBlock("bsvtB0", nil, outs[1:], 72 chaingen.ReplaceBlockVersion(3), 73 chaingen.ReplaceStakeVersion(42), 74 chaingen.ReplaceVoteVersions(41)) 75 g.SaveTipCoinbaseOuts() 76 g.AcceptTipBlock() 77 g.AssertTipHeight(uint32(stakeValidationHeight + stakeVerInterval)) 78 g.AssertBlockVersion(3) 79 g.AssertStakeVersion(42) // expected bogus 80 81 // --------------------------------------------------------------------- 82 // Generate enough blocks to reach one block before the next stake 83 // version interval with block version 3, stake version 0, and vote 84 // version 2. 85 // 86 // This will result in a majority of version 3 blocks which will trigger 87 // enforcement of the stake version. It also results in a majority of 88 // version 2 votes, however, since enforcement is not yet active in this 89 // interval, they will not actually count toward establishing a 90 // majority. 91 // --------------------------------------------------------------------- 92 93 for i := int64(0); i < stakeVerInterval-1; i++ { 94 outs := g.OldestCoinbaseOuts() 95 blockName := fmt.Sprintf("bsvtB%d", i+1) 96 g.NextBlock(blockName, nil, outs[1:], 97 chaingen.ReplaceBlockVersion(3), 98 chaingen.ReplaceStakeVersion(0), 99 chaingen.ReplaceVoteVersions(2)) 100 g.SaveTipCoinbaseOuts() 101 g.AcceptTipBlock() 102 } 103 g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval - 1)) 104 g.AssertBlockVersion(3) 105 g.AssertStakeVersion(0) 106 107 // --------------------------------------------------------------------- 108 // Generate a single block with block version 3, stake version 2, and 109 // vote version 3. 110 // 111 // This block must be rejected because even though the majority stake 112 // version per voters was 2 in the previous period, stake version 113 // enforcement had not yet been achieved and thus the required stake 114 // version is still 0. 115 // --------------------------------------------------------------------- 116 117 g.NextBlock("bsvtCbad0", nil, nil, 118 chaingen.ReplaceBlockVersion(3), 119 chaingen.ReplaceStakeVersion(2), 120 chaingen.ReplaceVoteVersions(3)) 121 g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval)) 122 g.AssertBlockVersion(3) 123 g.AssertStakeVersion(2) 124 g.RejectTipBlock(ErrBadStakeVersion) 125 126 // --------------------------------------------------------------------- 127 // Generate a single block with block version 3, stake version 1, and 128 // vote version 3. 129 // 130 // This block must be rejected because even though the majority stake 131 // version per voters was 2 in the previous period, stake version 132 // enforcement had not yet been achieved and thus the required stake 133 // version is still 0. 134 // --------------------------------------------------------------------- 135 136 g.SetTip(fmt.Sprintf("bsvtB%d", stakeVerInterval-1)) 137 g.NextBlock("bsvtCbad1", nil, nil, 138 chaingen.ReplaceBlockVersion(3), 139 chaingen.ReplaceStakeVersion(1), 140 chaingen.ReplaceVoteVersions(3)) 141 g.AssertTipHeight(uint32(stakeValidationHeight + 2*stakeVerInterval)) 142 g.AssertBlockVersion(3) 143 g.AssertStakeVersion(1) 144 g.RejectTipBlock(ErrBadStakeVersion) 145 146 // --------------------------------------------------------------------- 147 // Generate enough blocks to reach one block before the next stake 148 // version interval with block version 3, stake version 0, and vote 149 // version 3. 150 // 151 // This will result in a majority of version 3 votes which will trigger 152 // enforcement of a bump in the stake version to 3. 153 // --------------------------------------------------------------------- 154 155 g.SetTip(fmt.Sprintf("bsvtB%d", stakeVerInterval-1)) 156 for i := int64(0); i < stakeVerInterval; i++ { 157 outs := g.OldestCoinbaseOuts() 158 blockName := fmt.Sprintf("bsvtC%d", i) 159 g.NextBlock(blockName, nil, outs[1:], 160 chaingen.ReplaceBlockVersion(3), 161 chaingen.ReplaceStakeVersion(0), 162 chaingen.ReplaceVoteVersions(3)) 163 g.SaveTipCoinbaseOuts() 164 g.AcceptTipBlock() 165 } 166 g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval - 1)) 167 g.AssertBlockVersion(3) 168 g.AssertStakeVersion(0) 169 170 // --------------------------------------------------------------------- 171 // Generate a single block with block version 3, stake version 2, and 172 // vote version 2. 173 // 174 // This block must be rejected because the majority stake version per 175 // voters is now 3 and stake version enforcement has been achieved. 176 // --------------------------------------------------------------------- 177 178 g.NextBlock("bsvtDbad0", nil, nil, 179 chaingen.ReplaceBlockVersion(3), 180 chaingen.ReplaceStakeVersion(2), 181 chaingen.ReplaceVoteVersions(2)) 182 g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval)) 183 g.AssertBlockVersion(3) 184 g.AssertStakeVersion(2) 185 g.RejectTipBlock(ErrBadStakeVersion) 186 187 // --------------------------------------------------------------------- 188 // Generate a single block with block version 3, stake version 4, and 189 // vote version 2. 190 // 191 // This block must be rejected because the majority stake version per 192 // voters is now 3 and stake version enforcement has been achieved. 193 // --------------------------------------------------------------------- 194 195 g.SetTip(fmt.Sprintf("bsvtC%d", stakeVerInterval-1)) 196 g.NextBlock("bsvtDbad1", nil, nil, 197 chaingen.ReplaceBlockVersion(3), 198 chaingen.ReplaceStakeVersion(4), 199 chaingen.ReplaceVoteVersions(2)) 200 g.AssertTipHeight(uint32(stakeValidationHeight + 3*stakeVerInterval)) 201 g.AssertBlockVersion(3) 202 g.AssertStakeVersion(4) 203 g.RejectTipBlock(ErrBadStakeVersion) 204 205 // --------------------------------------------------------------------- 206 // Generate enough blocks to reach one block before the next stake 207 // version interval with block version 3, stake version 3, and vote 208 // version 2. 209 // 210 // This will result in a majority of version 2 votes, but since version 211 // 3 has already been achieved, the stake version must not regress. 212 // --------------------------------------------------------------------- 213 214 g.SetTip(fmt.Sprintf("bsvtC%d", stakeVerInterval-1)) 215 for i := int64(0); i < stakeVerInterval; i++ { 216 outs := g.OldestCoinbaseOuts() 217 blockName := fmt.Sprintf("bsvtD%d", i) 218 g.NextBlock(blockName, nil, outs[1:], 219 chaingen.ReplaceBlockVersion(3), 220 chaingen.ReplaceStakeVersion(3), 221 chaingen.ReplaceVoteVersions(2)) 222 g.SaveTipCoinbaseOuts() 223 g.AcceptTipBlock() 224 } 225 g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval - 1)) 226 g.AssertBlockVersion(3) 227 g.AssertStakeVersion(3) 228 229 // --------------------------------------------------------------------- 230 // Generate a single block with block version 3, stake version 2, and 231 // vote version 2. 232 // 233 // This block must be rejected because even though the majority stake 234 // version per voters in the previous interval was 2, the majority stake 235 // version is not allowed to regress. 236 // --------------------------------------------------------------------- 237 238 g.NextBlock("bsvtEbad0", nil, nil, 239 chaingen.ReplaceBlockVersion(3), 240 chaingen.ReplaceStakeVersion(2), 241 chaingen.ReplaceVoteVersions(2)) 242 g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval)) 243 g.AssertBlockVersion(3) 244 g.AssertStakeVersion(2) 245 g.RejectTipBlock(ErrBadStakeVersion) 246 247 // --------------------------------------------------------------------- 248 // Generate a single block with block version 3, stake version 4, and 249 // vote version 3. 250 // 251 // This block must be rejected because the majority stake version is 252 // still 3. 253 // --------------------------------------------------------------------- 254 255 g.SetTip(fmt.Sprintf("bsvtD%d", stakeVerInterval-1)) 256 g.NextBlock("bsvtEbad1", nil, nil, 257 chaingen.ReplaceBlockVersion(3), 258 chaingen.ReplaceStakeVersion(4), 259 chaingen.ReplaceVoteVersions(3)) 260 g.AssertTipHeight(uint32(stakeValidationHeight + 4*stakeVerInterval)) 261 g.AssertBlockVersion(3) 262 g.AssertStakeVersion(4) 263 g.RejectTipBlock(ErrBadStakeVersion) 264 265 // --------------------------------------------------------------------- 266 // Generate enough blocks to reach one block before the next stake 267 // version interval with block version 3, stake version 3, and a mix of 268 // 3 and 4 for the vote version such that a super majority is *NOT* 269 // achieved. 270 // 271 // This will result in an unchanged required stake version. 272 // --------------------------------------------------------------------- 273 274 g.SetTip(fmt.Sprintf("bsvtD%d", stakeVerInterval-1)) 275 votesPerInterval := stakeVerInterval * int64(ticketsPerBlock) 276 targetVotes := (votesPerInterval * stakeMajorityMul) / stakeMajorityDiv 277 targetBlocks := targetVotes / int64(ticketsPerBlock) 278 for i := int64(0); i < targetBlocks-1; i++ { 279 outs := g.OldestCoinbaseOuts() 280 blockName := fmt.Sprintf("bsvtE%da", i) 281 g.NextBlock(blockName, nil, outs[1:], 282 chaingen.ReplaceBlockVersion(3), 283 chaingen.ReplaceStakeVersion(3), 284 chaingen.ReplaceVoteVersions(4)) 285 g.SaveTipCoinbaseOuts() 286 g.AssertBlockVersion(3) 287 g.AssertStakeVersion(3) 288 g.AcceptTipBlock() 289 } 290 for i := int64(0); i < stakeVerInterval-(targetBlocks-1); i++ { 291 outs := g.OldestCoinbaseOuts() 292 blockName := fmt.Sprintf("bsvtE%db", targetBlocks-1+i) 293 g.NextBlock(blockName, nil, outs[1:], 294 chaingen.ReplaceBlockVersion(3), 295 chaingen.ReplaceStakeVersion(3), 296 chaingen.ReplaceVoteVersions(3)) 297 g.SaveTipCoinbaseOuts() 298 g.AssertBlockVersion(3) 299 g.AssertStakeVersion(3) 300 g.AcceptTipBlock() 301 } 302 g.AssertTipHeight(uint32(stakeValidationHeight + 5*stakeVerInterval - 1)) 303 304 // --------------------------------------------------------------------- 305 // Generate a single block with block version 3, stake version 4, and 306 // vote version 3. 307 // 308 // This block must be rejected because the majority stake version is 309 // still 3 due to failing to achieve enough votes in the previous 310 // period. 311 // --------------------------------------------------------------------- 312 313 g.NextBlock("bsvtFbad0", nil, nil, 314 chaingen.ReplaceBlockVersion(3), 315 chaingen.ReplaceStakeVersion(4), 316 chaingen.ReplaceVoteVersions(3)) 317 g.AssertTipHeight(uint32(stakeValidationHeight + 5*stakeVerInterval)) 318 g.AssertBlockVersion(3) 319 g.AssertStakeVersion(4) 320 g.RejectTipBlock(ErrBadStakeVersion) 321 322 // --------------------------------------------------------------------- 323 // Generate enough blocks to reach one block before the next stake 324 // version interval with block version 3, stake version 3, and a mix of 325 // 3 and 4 for the vote version such that a super majority of version 4 326 // is achieved. 327 // 328 // This will result in a majority stake version of 4. 329 // --------------------------------------------------------------------- 330 331 g.SetTip(fmt.Sprintf("bsvtE%db", stakeVerInterval-1)) 332 for i := int64(0); i < targetBlocks; i++ { 333 outs := g.OldestCoinbaseOuts() 334 blockName := fmt.Sprintf("bsvtF%da", i) 335 g.NextBlock(blockName, nil, outs[1:], 336 chaingen.ReplaceBlockVersion(3), 337 chaingen.ReplaceStakeVersion(3), 338 chaingen.ReplaceVoteVersions(4)) 339 g.SaveTipCoinbaseOuts() 340 g.AssertBlockVersion(3) 341 g.AssertStakeVersion(3) 342 g.AcceptTipBlock() 343 } 344 for i := int64(0); i < stakeVerInterval-targetBlocks; i++ { 345 outs := g.OldestCoinbaseOuts() 346 blockName := fmt.Sprintf("bsvtF%db", targetBlocks+i) 347 g.NextBlock(blockName, nil, outs[1:], 348 chaingen.ReplaceBlockVersion(3), 349 chaingen.ReplaceStakeVersion(3), 350 chaingen.ReplaceVoteVersions(3)) 351 g.SaveTipCoinbaseOuts() 352 g.AssertBlockVersion(3) 353 g.AssertStakeVersion(3) 354 g.AcceptTipBlock() 355 } 356 g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval - 1)) 357 358 // --------------------------------------------------------------------- 359 // Generate a single block with block version 3, stake version 3, and 360 // vote version 3. 361 // 362 // This block must be rejected because the majority stake version is 363 // now 4 due to achieving a majority of votes in the previous period. 364 // --------------------------------------------------------------------- 365 366 g.NextBlock("bsvtGbad0", nil, nil, 367 chaingen.ReplaceBlockVersion(3), 368 chaingen.ReplaceStakeVersion(3), 369 chaingen.ReplaceVoteVersions(3)) 370 g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval)) 371 g.AssertBlockVersion(3) 372 g.AssertStakeVersion(3) 373 g.RejectTipBlock(ErrBadStakeVersion) 374 375 // --------------------------------------------------------------------- 376 // Generate a single block with block version 3, stake version 4, and 377 // vote version 3. 378 // 379 // This block must be accepted because the majority stake version is 380 // now 4 due to achieving a majority of votes in the previous period. 381 // --------------------------------------------------------------------- 382 383 g.SetTip(fmt.Sprintf("bsvtF%db", stakeVerInterval-1)) 384 g.NextBlock("bsvtG0", nil, nil, 385 chaingen.ReplaceBlockVersion(3), 386 chaingen.ReplaceStakeVersion(4), 387 chaingen.ReplaceVoteVersions(3)) 388 g.AssertTipHeight(uint32(stakeValidationHeight + 6*stakeVerInterval)) 389 g.AssertBlockVersion(3) 390 g.AssertStakeVersion(4) 391 g.AcceptTipBlock() 392 }