github.com/decred/dcrd/blockchain@v1.2.1/difficulty_test.go (about) 1 // Copyright (c) 2014 The btcsuite developers 2 // Copyright (c) 2015-2019 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 "math/big" 10 "runtime" 11 "testing" 12 "time" 13 14 "github.com/decred/dcrd/chaincfg" 15 "github.com/decred/dcrd/wire" 16 ) 17 18 func TestBigToCompact(t *testing.T) { 19 tests := []struct { 20 in int64 21 out uint32 22 }{ 23 {0, 0}, 24 {-1, 25231360}, 25 } 26 27 for x, test := range tests { 28 n := big.NewInt(test.in) 29 r := BigToCompact(n) 30 if r != test.out { 31 t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n", 32 x, r, test.out) 33 return 34 } 35 } 36 } 37 38 func TestCompactToBig(t *testing.T) { 39 tests := []struct { 40 in uint32 41 out int64 42 }{ 43 {10000000, 0}, 44 } 45 46 for x, test := range tests { 47 n := CompactToBig(test.in) 48 want := big.NewInt(test.out) 49 if n.Cmp(want) != 0 { 50 t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n", 51 x, n.Int64(), want.Int64()) 52 return 53 } 54 } 55 } 56 57 func TestCalcWork(t *testing.T) { 58 tests := []struct { 59 in uint32 60 out int64 61 }{ 62 {10000000, 0}, 63 } 64 65 for x, test := range tests { 66 r := CalcWork(test.in) 67 if r.Int64() != test.out { 68 t.Errorf("TestCalcWork test #%d failed: got %v want %d\n", 69 x, r.Int64(), test.out) 70 return 71 } 72 } 73 } 74 75 // TestEstimateSupply ensures the supply estimation function used in the stake 76 // difficulty algorithm defined by DCP0001 works as expected. 77 func TestEstimateSupply(t *testing.T) { 78 t.Parallel() 79 80 // The parameters used for the supply estimation. 81 params := &chaincfg.MainNetParams 82 baseSubsidy := params.BaseSubsidy 83 reduxInterval := params.SubsidyReductionInterval 84 blockOneSubsidy := params.BlockOneSubsidy() 85 86 // intervalSubsidy is a helper function to return the full block subsidy 87 // for the given reduction interval. 88 intervalSubsidy := func(interval int) int64 { 89 subsidy := baseSubsidy 90 for i := 0; i < interval; i++ { 91 subsidy *= params.MulSubsidy 92 subsidy /= params.DivSubsidy 93 } 94 return subsidy 95 } 96 97 // Useful calculations for the tests below. 98 intervalOneSubsidy := intervalSubsidy(1) 99 intervalTwoSubsidy := intervalSubsidy(2) 100 reduxIntervalMinusOneSupply := blockOneSubsidy + (baseSubsidy * (reduxInterval - 2)) 101 reduxIntervalTwoMinusOneSupply := reduxIntervalMinusOneSupply + (intervalOneSubsidy * reduxInterval) 102 103 tests := []struct { 104 height int64 105 expected int64 106 }{ 107 {height: -1, expected: 0}, 108 {height: 0, expected: 0}, 109 {height: 1, expected: blockOneSubsidy}, 110 {height: 2, expected: blockOneSubsidy + baseSubsidy}, 111 {height: 3, expected: blockOneSubsidy + baseSubsidy*2}, 112 {height: reduxInterval - 1, expected: reduxIntervalMinusOneSupply}, 113 {height: reduxInterval, expected: reduxIntervalMinusOneSupply + intervalOneSubsidy}, 114 {height: reduxInterval + 1, expected: reduxIntervalMinusOneSupply + intervalOneSubsidy*2}, 115 {height: reduxInterval*2 - 1, expected: reduxIntervalTwoMinusOneSupply}, 116 {height: reduxInterval * 2, expected: reduxIntervalTwoMinusOneSupply + intervalTwoSubsidy}, 117 {height: reduxInterval*2 + 1, expected: reduxIntervalTwoMinusOneSupply + intervalTwoSubsidy*2}, 118 } 119 120 for _, test := range tests { 121 // Ensure the function to calculate the estimated supply is 122 // working properly. 123 gotSupply := estimateSupply(params, test.height) 124 if gotSupply != test.expected { 125 t.Errorf("estimateSupply (height %d): did not get "+ 126 "expected supply - got %d, want %d", test.height, 127 gotSupply, test.expected) 128 continue 129 } 130 } 131 } 132 133 // assertStakeDiffParamsMainNet ensure the passed params have the values used in 134 // the tests related to mainnet stake difficulty calculation. 135 func assertStakeDiffParamsMainNet(t *testing.T, params *chaincfg.Params) { 136 if params.MinimumStakeDiff != 200000000 { 137 _, file, line, _ := runtime.Caller(1) 138 t.Fatalf("%s:%d -- expect params with minimum stake diff of "+ 139 "%d, got %d", file, line, 200000000, 140 params.MinimumStakeDiff) 141 } 142 if params.TicketMaturity != 256 { 143 _, file, line, _ := runtime.Caller(1) 144 t.Fatalf("%s:%d -- expect params with ticket maturity of "+ 145 "%d, got %d", file, line, 256, params.TicketMaturity) 146 } 147 if params.StakeValidationHeight != 4096 { 148 _, file, line, _ := runtime.Caller(1) 149 t.Fatalf("%s:%d -- expect params with stake val height of %d, "+ 150 "got %d", file, line, 4096, params.StakeValidationHeight) 151 } 152 if params.StakeDiffWindowSize != 144 { 153 _, file, line, _ := runtime.Caller(1) 154 t.Fatalf("%s:%d -- expect params with stake diff interval of "+ 155 "%d, got %d", file, line, 144, params.StakeDiffWindowSize) 156 } 157 if params.TicketsPerBlock != 5 { 158 _, file, line, _ := runtime.Caller(1) 159 t.Fatalf("%s:%d -- expect params with tickets per block of "+ 160 "%d, got %d", file, line, 5, params.TicketsPerBlock) 161 } 162 } 163 164 // assertStakeDiffParamsTestNet ensure the passed params have the values used in 165 // the tests related to testnet stake difficulty calculation. 166 func assertStakeDiffParamsTestNet(t *testing.T, params *chaincfg.Params) { 167 if params.MinimumStakeDiff != 20000000 { 168 _, file, line, _ := runtime.Caller(1) 169 t.Fatalf("%s:%d -- expect params with minimum stake diff of "+ 170 "%d, got %d", file, line, 20000000, 171 params.MinimumStakeDiff) 172 } 173 if params.TicketMaturity != 16 { 174 _, file, line, _ := runtime.Caller(1) 175 t.Fatalf("%s:%d -- expect params with ticket maturity of "+ 176 "%d, got %d", file, line, 16, params.TicketMaturity) 177 } 178 if params.StakeValidationHeight != 768 { 179 _, file, line, _ := runtime.Caller(1) 180 t.Fatalf("%s:%d -- expect params with stake val height of %d, "+ 181 "got %d", file, line, 768, params.StakeValidationHeight) 182 } 183 if params.StakeDiffWindowSize != 144 { 184 _, file, line, _ := runtime.Caller(1) 185 t.Fatalf("%s:%d -- expect params with stake diff interval of "+ 186 "%d, got %d", file, line, 144, params.StakeDiffWindowSize) 187 } 188 if params.TicketsPerBlock != 5 { 189 _, file, line, _ := runtime.Caller(1) 190 t.Fatalf("%s:%d -- expect params with tickets per block of "+ 191 "%d, got %d", file, line, 5, params.TicketsPerBlock) 192 } 193 } 194 195 // TestCalcNextRequiredStakeDiffV2 ensure the stake diff calculation function 196 // for the algorithm defined by DCP0001 works as expected. 197 func TestCalcNextRequiredStakeDiffV2(t *testing.T) { 198 t.Parallel() 199 200 // ticketInfo is used to control the tests by specifying the details 201 // about how many fake blocks to create with the specified number of 202 // ticket and stake difficulty. 203 type ticketInfo struct { 204 numNodes uint32 205 newTickets uint8 206 stakeDiff int64 207 } 208 209 // Specify the params used in the tests and assert the values directly 210 // used by the tests are the expected ones. All of the test values will 211 // need to be updated if these parameters change since they are manually 212 // calculated based on them. 213 params := &chaincfg.MainNetParams 214 assertStakeDiffParamsMainNet(t, params) 215 minStakeDiff := params.MinimumStakeDiff 216 ticketMaturity := uint32(params.TicketMaturity) 217 stakeValidationHeight := params.StakeValidationHeight 218 219 tests := []struct { 220 name string 221 ticketInfo []ticketInfo 222 expectedDiff int64 223 }{ 224 { 225 // Next retarget is at 144. Prior to coinbase maturity, 226 // so will always be the minimum. 227 name: "genesis block", 228 ticketInfo: []ticketInfo{{0, 0, minStakeDiff}}, 229 expectedDiff: minStakeDiff, 230 }, 231 { 232 // Next retarget is at 144. Prior to coinbase maturity, 233 // so will always be the minimum. 234 name: "1st retarget, before coinbase", 235 ticketInfo: []ticketInfo{{143, 0, minStakeDiff}}, 236 expectedDiff: minStakeDiff, 237 }, 238 { 239 // Next retarget is at 288. 240 // 241 // Tickets could not possibly have been bought yet, but 242 // ensure the algorithm handles it properly. 243 name: "coinbase maturity with impossible num tickets", 244 ticketInfo: []ticketInfo{{255, 20, minStakeDiff}}, 245 expectedDiff: minStakeDiff, 246 }, 247 { 248 // Next retarget is at 288. 249 // 250 // Block 0 has no spendable outputs, so tickets could 251 // not have possibly been bought yet. 252 name: "coinbase maturity + 1", 253 ticketInfo: []ticketInfo{ 254 {256, 0, minStakeDiff}, 255 }, 256 expectedDiff: minStakeDiff, 257 }, 258 { 259 // Next retarget is at 288. 260 name: "2nd retarget interval - 1, 100% demand", 261 ticketInfo: []ticketInfo{ 262 {256, 0, minStakeDiff}, // 256 263 {30, 20, minStakeDiff}, // 286 264 }, 265 expectedDiff: minStakeDiff, 266 }, 267 { 268 // Next retarget is at 288. 269 name: "2nd retarget interval, 100% demand", 270 ticketInfo: []ticketInfo{ 271 {256, 0, minStakeDiff}, // 256 272 {31, 20, minStakeDiff}, // 287 273 }, 274 expectedDiff: minStakeDiff, 275 }, 276 { 277 // Next retarget is at 432. 278 name: "3rd retarget interval, 100% demand", 279 ticketInfo: []ticketInfo{ 280 {256, 0, minStakeDiff}, // 256 281 {175, 20, minStakeDiff}, // 431 282 }, 283 expectedDiff: minStakeDiff, 284 }, 285 { 286 // Next retarget is at 2304. 287 name: "16th retarget interval - 1, 100% demand", 288 ticketInfo: []ticketInfo{ 289 {256, 0, minStakeDiff}, // 256 290 {2046, 20, minStakeDiff}, // 2302 291 }, 292 expectedDiff: minStakeDiff, 293 }, 294 { 295 // Next retarget is at 2304. 296 name: "16th retarget interval, 100% demand", 297 ticketInfo: []ticketInfo{ 298 {256, 0, minStakeDiff}, // 256 299 {2047, 20, minStakeDiff}, // 2303 300 }, 301 expectedDiff: 208418769, 302 }, 303 { 304 // Next retarget is at 2448. 305 name: "17th retarget interval - 1, 100% demand", 306 ticketInfo: []ticketInfo{ 307 {256, 0, minStakeDiff}, // 256 308 {2047, 20, minStakeDiff}, // 2303 309 {143, 20, 208418769}, // 2446 310 }, 311 expectedDiff: 208418769, 312 }, 313 { 314 // Next retarget is at 2448. 315 name: "17th retarget interval, 100% demand", 316 ticketInfo: []ticketInfo{ 317 {256, 0, minStakeDiff}, // 256 318 {2047, 20, minStakeDiff}, // 2303 319 {144, 20, 208418769}, // 2447 320 }, 321 expectedDiff: 231326567, 322 }, 323 { 324 // Next retarget is at 2592. 325 name: "17th retarget interval+1, 100% demand", 326 ticketInfo: []ticketInfo{ 327 {256, 0, minStakeDiff}, // 256 328 {2047, 20, minStakeDiff}, // 2303 329 {144, 20, 208418769}, // 2447 330 {1, 20, 231326567}, // 2448 331 }, 332 expectedDiff: 231326567, 333 }, 334 { 335 // Next retarget is at 3456. 336 name: "24th retarget interval, varying demand", 337 ticketInfo: []ticketInfo{ 338 {256, 0, minStakeDiff}, // 256 339 {31, 20, minStakeDiff}, // 287 340 {144, 10, minStakeDiff}, // 431 341 {144, 20, minStakeDiff}, // 575 342 {144, 10, minStakeDiff}, // 719 343 {144, 20, minStakeDiff}, // 863 344 {144, 10, minStakeDiff}, // 1007 345 {144, 20, minStakeDiff}, // 1151 346 {144, 10, minStakeDiff}, // 1295 347 {144, 20, minStakeDiff}, // 1439 348 {144, 10, minStakeDiff}, // 1583 349 {144, 20, minStakeDiff}, // 1727 350 {144, 10, minStakeDiff}, // 1871 351 {144, 20, minStakeDiff}, // 2015 352 {144, 10, minStakeDiff}, // 2159 353 {144, 20, minStakeDiff}, // 2303 354 {144, 10, minStakeDiff}, // 2447 355 {144, 20, minStakeDiff}, // 2591 356 {144, 10, minStakeDiff}, // 2735 357 {144, 20, minStakeDiff}, // 2879 358 {144, 9, 201743368}, // 3023 359 {144, 20, 201093236}, // 3167 360 {144, 8, 222625877}, // 3311 361 {144, 20, 242331291}, // 3455 362 }, 363 expectedDiff: 291317641, 364 }, 365 { 366 // Next retarget is at 4176. Post stake validation 367 // height. 368 name: "29th retarget interval, 100% demand", 369 ticketInfo: []ticketInfo{ 370 {256, 0, minStakeDiff}, // 256 371 {2047, 20, minStakeDiff}, // 2303 372 {144, 20, 208418769}, // 2447 373 {144, 20, 231326567}, // 2591 374 {144, 20, 272451490}, // 2735 375 {144, 20, 339388424}, // 2879 376 {144, 20, 445827839}, // 3023 377 {144, 20, 615949254}, // 3167 378 {144, 20, 892862990}, // 3311 379 {144, 20, 1354989669}, // 3455 380 {144, 20, 2148473276}, // 3599 381 {144, 20, 3552797658}, // 3743 382 {144, 20, 6116808441}, // 3887 383 {144, 20, 10947547379}, // 4031 384 {144, 20, 20338554623}, // 4175 385 }, 386 expectedDiff: 22097687698, 387 }, 388 { 389 // Next retarget is at 4176. Post stake validation 390 // height. 391 name: "29th retarget interval, 50% demand", 392 ticketInfo: []ticketInfo{ 393 {256, 0, minStakeDiff}, // 256 394 {3919, 10, minStakeDiff}, // 4175 395 }, 396 expectedDiff: minStakeDiff, 397 }, 398 { 399 // Next retarget is at 4464. Post stake validation 400 // height. 401 name: "31st retarget interval, waning demand", 402 ticketInfo: []ticketInfo{ 403 {256, 0, minStakeDiff}, // 256 404 {2047, 20, minStakeDiff}, // 2303 405 {144, 20, 208418769}, // 2447 406 {144, 20, 231326567}, // 2591 407 {144, 20, 272451490}, // 2735 408 {144, 20, 339388424}, // 2879 409 {144, 20, 445827839}, // 3023 410 {144, 20, 615949254}, // 3167 411 {144, 20, 892862990}, // 3311 412 {144, 20, 1354989669}, // 3455 413 {144, 20, 2148473276}, // 3599 414 {144, 20, 3552797658}, // 3743 415 {144, 13, 6116808441}, // 3887 416 {144, 0, 10645659768}, // 4031 417 {144, 0, 18046712136}, // 4175 418 {144, 0, 22097687698}, // 4319 419 {144, 0, 22152524112}, // 4463 420 }, 421 expectedDiff: 22207360526, 422 }, 423 } 424 425 nextTest: 426 for _, test := range tests { 427 bc := newFakeChain(params) 428 429 // immatureTickets tracks which height the purchased tickets 430 // will mature and thus be eligible for admission to the live 431 // ticket pool. 432 immatureTickets := make(map[uint32]uint8) 433 var poolSize uint32 434 for _, ticketInfo := range test.ticketInfo { 435 // Ensure the test data isn't faking ticket purchases at 436 // an incorrect difficulty. 437 tip := bc.bestChain.Tip() 438 gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(tip) 439 if err != nil { 440 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 441 "unexpected error: %v", test.name, err) 442 continue nextTest 443 } 444 if gotDiff != ticketInfo.stakeDiff { 445 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 446 "did not get expected stake difficulty -- got "+ 447 "%d, want %d", test.name, gotDiff, 448 ticketInfo.stakeDiff) 449 continue nextTest 450 } 451 452 for i := uint32(0); i < ticketInfo.numNodes; i++ { 453 // Make up a header. 454 nextHeight := uint32(tip.height) + 1 455 header := &wire.BlockHeader{ 456 Version: 4, 457 SBits: ticketInfo.stakeDiff, 458 Height: nextHeight, 459 FreshStake: ticketInfo.newTickets, 460 PoolSize: poolSize, 461 } 462 tip = newBlockNode(header, tip) 463 464 // Update the pool size for the next header. 465 // Notice how tickets that mature for this block 466 // do not show up in the pool size until the 467 // next block. This is correct behavior. 468 poolSize += uint32(immatureTickets[nextHeight]) 469 delete(immatureTickets, nextHeight) 470 if int64(nextHeight) >= stakeValidationHeight { 471 poolSize -= uint32(params.TicketsPerBlock) 472 } 473 474 // Track maturity height for new ticket 475 // purchases. 476 maturityHeight := nextHeight + ticketMaturity 477 immatureTickets[maturityHeight] = ticketInfo.newTickets 478 479 // Update the chain to use the new fake node as 480 // the new best node. 481 bc.bestChain.SetTip(tip) 482 } 483 } 484 485 // Ensure the calculated difficulty matches the expected value. 486 gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestChain.Tip()) 487 if err != nil { 488 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 489 "unexpected error: %v", test.name, err) 490 continue 491 } 492 if gotDiff != test.expectedDiff { 493 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 494 "did not get expected stake difficulty -- got "+ 495 "%d, want %d", test.name, gotDiff, 496 test.expectedDiff) 497 continue 498 } 499 } 500 } 501 502 // TestEstimateNextStakeDiffV2 ensures the function that estimates the stake 503 // diff calculation for the algorithm defined by DCP0001 works as expected. 504 func TestEstimateNextStakeDiffV2(t *testing.T) { 505 t.Parallel() 506 507 // ticketInfo is used to control the tests by specifying the details 508 // about how many fake blocks to create with the specified number of 509 // tickets and stake difficulty. 510 type ticketInfo struct { 511 numNodes uint32 512 newTickets uint8 513 stakeDiff int64 514 } 515 516 // Assert the param values directly used by the tests are the expected 517 // ones. All of the test values will need to be updated if these 518 // parameters change since they are manually calculated based on them. 519 mainNetParams := &chaincfg.MainNetParams 520 testNetParams := &chaincfg.TestNet3Params 521 assertStakeDiffParamsMainNet(t, mainNetParams) 522 assertStakeDiffParamsTestNet(t, testNetParams) 523 minStakeDiffMainNet := mainNetParams.MinimumStakeDiff 524 minStakeDiffTestNet := testNetParams.MinimumStakeDiff 525 526 tests := []struct { 527 name string 528 params *chaincfg.Params 529 ticketInfo []ticketInfo 530 newTickets int64 531 useMaxTickets bool 532 expectedDiff int64 533 }{ 534 { 535 // Regardless of claiming tickets will be purchased, the 536 // resulting stake difficulty should be the minimum 537 // because the first retarget is before the start 538 // height. 539 name: "genesis block", 540 params: mainNetParams, 541 ticketInfo: []ticketInfo{{0, 0, minStakeDiffMainNet}}, 542 newTickets: 2860, 543 useMaxTickets: false, 544 expectedDiff: minStakeDiffMainNet, 545 }, 546 { 547 // Next retarget is 144. Resulting stake difficulty 548 // should be the minimum regardless of claimed ticket 549 // purchases because the previous pool size is still 0. 550 name: "during retarget, but before coinbase", 551 params: mainNetParams, 552 ticketInfo: []ticketInfo{{140, 0, minStakeDiffMainNet}}, 553 newTickets: 20 * 3, // blocks 141, 142, and 143. 554 useMaxTickets: true, 555 expectedDiff: minStakeDiffMainNet, 556 }, 557 { 558 // Next retarget is at 288. Regardless of claiming 559 // tickets will be purchased, the resulting stake 560 // difficulty should be the min because the previous 561 // pool size is still 0. 562 name: "at coinbase maturity", 563 params: mainNetParams, 564 ticketInfo: []ticketInfo{{256, 0, minStakeDiffMainNet}}, 565 useMaxTickets: true, 566 expectedDiff: minStakeDiffMainNet, 567 }, 568 { 569 // Next retarget is at 288. Regardless of actually 570 // purchasing tickets and claiming more tickets will be 571 // purchased, the resulting stake difficulty should be 572 // the min because the previous pool size is still 0. 573 name: "2nd retarget interval - 2, 100% demand", 574 params: mainNetParams, 575 ticketInfo: []ticketInfo{ 576 {256, 0, minStakeDiffMainNet}, // 256 577 {30, 20, minStakeDiffMainNet}, // 286 578 }, 579 useMaxTickets: true, 580 expectedDiff: minStakeDiffMainNet, 581 }, 582 { 583 // Next retarget is at 288. Still expect minimum stake 584 // difficulty since the raw result would be lower. 585 name: "2nd retarget interval - 1, 100% demand", 586 params: mainNetParams, 587 ticketInfo: []ticketInfo{ 588 {256, 0, minStakeDiffMainNet}, // 256 589 {31, 20, minStakeDiffMainNet}, // 287 590 }, 591 useMaxTickets: true, 592 expectedDiff: minStakeDiffMainNet, 593 }, 594 { 595 // Next retarget is at 432. 596 name: "3rd retarget interval, 100% demand, 1st block", 597 params: mainNetParams, 598 ticketInfo: []ticketInfo{ 599 {256, 0, minStakeDiffMainNet}, // 256 600 {32, 20, minStakeDiffMainNet}, // 288 601 }, 602 useMaxTickets: true, 603 expectedDiff: minStakeDiffMainNet, 604 }, 605 { 606 // Next retarget is at 2304. 607 name: "16th retarget interval, 100% demand, 1st block", 608 params: mainNetParams, 609 ticketInfo: []ticketInfo{ 610 {256, 0, minStakeDiffMainNet}, // 256 611 {1904, 20, minStakeDiffMainNet}, // 2160 612 }, 613 useMaxTickets: true, 614 expectedDiff: 208418769, 615 }, 616 { 617 // Next retarget is at 2304. 618 name: "16th retarget interval, 100% demand, 2nd block", 619 params: mainNetParams, 620 ticketInfo: []ticketInfo{ 621 {256, 0, minStakeDiffMainNet}, // 256 622 {1905, 20, minStakeDiffMainNet}, // 2161 623 }, 624 useMaxTickets: true, 625 expectedDiff: 208418769, 626 }, 627 { 628 // Next retarget is at 2304. 629 name: "16th retarget interval, 100% demand, final block", 630 params: mainNetParams, 631 ticketInfo: []ticketInfo{ 632 {256, 0, minStakeDiffMainNet}, // 256 633 {2047, 20, minStakeDiffMainNet}, // 2303 634 }, 635 useMaxTickets: true, 636 expectedDiff: 208418769, 637 }, 638 { 639 // Next retarget is at 3456. 640 name: "24th retarget interval, varying demand, 5th block", 641 params: mainNetParams, 642 ticketInfo: []ticketInfo{ 643 {256, 0, minStakeDiffMainNet}, // 256 644 {31, 20, minStakeDiffMainNet}, // 287 645 {144, 10, minStakeDiffMainNet}, // 431 646 {144, 20, minStakeDiffMainNet}, // 575 647 {144, 10, minStakeDiffMainNet}, // 719 648 {144, 20, minStakeDiffMainNet}, // 863 649 {144, 10, minStakeDiffMainNet}, // 1007 650 {144, 20, minStakeDiffMainNet}, // 1151 651 {144, 10, minStakeDiffMainNet}, // 1295 652 {144, 20, minStakeDiffMainNet}, // 1439 653 {144, 10, minStakeDiffMainNet}, // 1583 654 {144, 20, minStakeDiffMainNet}, // 1727 655 {144, 10, minStakeDiffMainNet}, // 1871 656 {144, 20, minStakeDiffMainNet}, // 2015 657 {144, 10, minStakeDiffMainNet}, // 2159 658 {144, 20, minStakeDiffMainNet}, // 2303 659 {144, 10, minStakeDiffMainNet}, // 2447 660 {144, 20, minStakeDiffMainNet}, // 2591 661 {144, 10, minStakeDiffMainNet}, // 2735 662 {144, 20, minStakeDiffMainNet}, // 2879 663 {144, 9, 201743368}, // 3023 664 {144, 20, 201093236}, // 3167 665 {144, 8, 222625877}, // 3311 666 {5, 20, 242331291}, // 3316 667 }, 668 useMaxTickets: true, 669 expectedDiff: 291317641, 670 }, 671 { 672 // Next retarget is at 4176. Post stake validation 673 // height. 674 name: "29th retarget interval, 100% demand, 10th block", 675 params: mainNetParams, 676 ticketInfo: []ticketInfo{ 677 {256, 0, minStakeDiffMainNet}, // 256 678 {2047, 20, minStakeDiffMainNet}, // 2303 679 {144, 20, 208418769}, // 2447 680 {144, 20, 231326567}, // 2591 681 {144, 20, 272451490}, // 2735 682 {144, 20, 339388424}, // 2879 683 {144, 20, 445827839}, // 3023 684 {144, 20, 615949254}, // 3167 685 {144, 20, 892862990}, // 3311 686 {144, 20, 1354989669}, // 3455 687 {144, 20, 2148473276}, // 3599 688 {144, 20, 3552797658}, // 3743 689 {144, 20, 6116808441}, // 3887 690 {144, 20, 10947547379}, // 4031 691 {10, 20, 20338554623}, // 4041 692 }, 693 useMaxTickets: true, 694 expectedDiff: 22097687698, 695 }, 696 { 697 // Next retarget is at 4176. Post stake validation 698 // height. 699 name: "29th retarget interval, 50% demand, 23rd block", 700 params: mainNetParams, 701 ticketInfo: []ticketInfo{ 702 {256, 0, minStakeDiffMainNet}, // 256 703 {3775, 10, minStakeDiffMainNet}, // 4031 704 {23, 10, minStakeDiffMainNet}, // 4054 705 }, 706 newTickets: 1210, // 121 * 10 707 useMaxTickets: false, 708 expectedDiff: minStakeDiffMainNet, 709 }, 710 { 711 // Next retarget is at 4464. Post stake validation 712 // height. 713 name: "31st retarget interval, waning demand, 117th block", 714 params: mainNetParams, 715 ticketInfo: []ticketInfo{ 716 {256, 0, minStakeDiffMainNet}, // 256 717 {2047, 20, minStakeDiffMainNet}, // 2303 718 {144, 20, 208418769}, // 2447 719 {144, 20, 231326567}, // 2591 720 {144, 20, 272451490}, // 2735 721 {144, 20, 339388424}, // 2879 722 {144, 20, 445827839}, // 3023 723 {144, 20, 615949254}, // 3167 724 {144, 20, 892862990}, // 3311 725 {144, 20, 1354989669}, // 3455 726 {144, 20, 2148473276}, // 3599 727 {144, 20, 3552797658}, // 3743 728 {144, 13, 6116808441}, // 3887 729 {144, 0, 10645659768}, // 4031 730 {144, 0, 18046712136}, // 4175 731 {144, 0, 22097687698}, // 4319 732 {117, 0, 22152524112}, // 4436 733 }, 734 useMaxTickets: false, 735 newTickets: 0, 736 expectedDiff: 22207360526, 737 }, 738 // -------------------------- 739 // TestNet params start here. 740 // -------------------------- 741 { 742 // Regardless of claiming tickets will be purchased, the 743 // resulting stake difficulty should be the minimum 744 // because the first retarget is before the start 745 // height. 746 name: "genesis block", 747 params: testNetParams, 748 ticketInfo: []ticketInfo{{0, 0, minStakeDiffTestNet}}, 749 newTickets: 2860, 750 useMaxTickets: false, 751 expectedDiff: minStakeDiffTestNet, 752 }, 753 { 754 // Next retarget is at 144. Regardless of claiming 755 // tickets will be purchased, the resulting stake 756 // difficulty should be the min because the previous 757 // pool size is still 0. 758 name: "at coinbase maturity", 759 params: testNetParams, 760 ticketInfo: []ticketInfo{{16, 0, minStakeDiffTestNet}}, 761 useMaxTickets: true, 762 expectedDiff: minStakeDiffTestNet, 763 }, 764 { 765 // Next retarget is at 144. Regardless of actually 766 // purchasing tickets and claiming more tickets will be 767 // purchased, the resulting stake difficulty should be 768 // the min because the previous pool size is still 0. 769 name: "1st retarget interval - 2, 100% demand", 770 params: testNetParams, 771 ticketInfo: []ticketInfo{ 772 {16, 0, minStakeDiffTestNet}, // 16 773 {126, 20, minStakeDiffTestNet}, // 142 774 }, 775 useMaxTickets: true, 776 expectedDiff: minStakeDiffTestNet, 777 }, 778 { 779 // Next retarget is at 288. Still expect minimum stake 780 // difficulty since the raw result would be lower. 781 name: "2nd retarget interval - 1, 30% demand", 782 params: testNetParams, 783 ticketInfo: []ticketInfo{ 784 {16, 0, minStakeDiffTestNet}, // 16 785 {271, 6, minStakeDiffTestNet}, // 287 786 }, 787 useMaxTickets: true, 788 expectedDiff: minStakeDiffTestNet, 789 }, 790 { 791 // Next retarget is at 288. Still expect minimum stake 792 // difficulty since the raw result would be lower. 793 // 794 // Since the ticket maturity is smaller than the 795 // retarget interval, this case ensures some of the 796 // nodes being estimated will mature during the 797 // interval. 798 name: "2nd retarget interval - 23, 30% demand", 799 params: testNetParams, 800 ticketInfo: []ticketInfo{ 801 {16, 0, minStakeDiffTestNet}, // 16 802 {249, 6, minStakeDiffTestNet}, // 265 803 }, 804 newTickets: 132, // 22 * 6 805 useMaxTickets: false, 806 expectedDiff: minStakeDiffTestNet, 807 }, 808 { 809 // Next retarget is at 288. Still expect minimum stake 810 // difficulty since the raw result would be lower. 811 // 812 // None of the nodes being estimated will mature during the 813 // interval. 814 name: "2nd retarget interval - 11, 30% demand", 815 params: testNetParams, 816 ticketInfo: []ticketInfo{ 817 {16, 0, minStakeDiffTestNet}, // 16 818 {261, 6, minStakeDiffTestNet}, // 277 819 }, 820 newTickets: 60, // 10 * 6 821 useMaxTickets: false, 822 expectedDiff: minStakeDiffTestNet, 823 }, 824 { 825 // Next retarget is at 432. 826 name: "3rd retarget interval, 100% demand, 1st block", 827 params: testNetParams, 828 ticketInfo: []ticketInfo{ 829 {16, 0, minStakeDiffTestNet}, // 16 830 {256, 20, minStakeDiffTestNet}, // 288 831 }, 832 useMaxTickets: true, 833 expectedDiff: 44505494, 834 }, 835 { 836 // Next retarget is at 432. 837 // 838 // None of the nodes being estimated will mature during the 839 // interval. 840 name: "3rd retarget interval - 11, 100% demand", 841 params: testNetParams, 842 ticketInfo: []ticketInfo{ 843 {16, 0, minStakeDiffTestNet}, // 16 844 {271, 20, minStakeDiffTestNet}, // 287 845 {134, 20, 44505494}, // 421 846 }, 847 useMaxTickets: true, 848 expectedDiff: 108661875, 849 }, 850 { 851 // Next retarget is at 576. 852 name: "4th retarget interval, 100% demand, 1st block", 853 params: testNetParams, 854 ticketInfo: []ticketInfo{ 855 {16, 0, minStakeDiffTestNet}, // 16 856 {271, 20, minStakeDiffTestNet}, // 287 857 {144, 20, 44505494}, // 431 858 {1, 20, 108661875}, // 432 859 }, 860 useMaxTickets: true, 861 expectedDiff: 314319918, 862 }, 863 { 864 // Next retarget is at 576. 865 name: "4th retarget interval, 100% demand, 2nd block", 866 params: testNetParams, 867 ticketInfo: []ticketInfo{ 868 {16, 0, minStakeDiffTestNet}, // 16 869 {271, 20, minStakeDiffTestNet}, // 287 870 {144, 20, 44505494}, // 431 871 {2, 20, 108661875}, // 433 872 }, 873 useMaxTickets: true, 874 expectedDiff: 314319918, 875 }, 876 { 877 // Next retarget is at 576. 878 name: "4th retarget interval, 100% demand, final block", 879 params: testNetParams, 880 ticketInfo: []ticketInfo{ 881 {16, 0, minStakeDiffTestNet}, // 16 882 {271, 20, minStakeDiffTestNet}, // 287 883 {144, 20, 44505494}, // 431 884 {144, 20, 108661875}, // 575 885 }, 886 useMaxTickets: true, 887 expectedDiff: 314319918, 888 }, 889 { 890 // Next retarget is at 1152. 891 name: "9th retarget interval, varying demand, 137th block", 892 params: testNetParams, 893 ticketInfo: []ticketInfo{ 894 {16, 0, minStakeDiffTestNet}, // 16 895 {127, 20, minStakeDiffTestNet}, // 143 896 {144, 10, minStakeDiffTestNet}, // 287 897 {144, 20, 24055097}, // 431 898 {144, 10, 54516186}, // 575 899 {144, 20, 105335577}, // 719 900 {144, 10, 304330579}, // 863 901 {144, 20, 772249463}, // 1007 902 {76, 10, 2497324513}, // 1083 903 {9, 0, 2497324513}, // 1092 904 {1, 10, 2497324513}, // 1093 905 {8, 0, 2497324513}, // 1101 906 {1, 10, 2497324513}, // 1102 907 {12, 0, 2497324513}, // 1114 908 {1, 10, 2497324513}, // 1115 909 {9, 0, 2497324513}, // 1124 910 {1, 10, 2497324513}, // 1125 911 {8, 0, 2497324513}, // 1133 912 {1, 10, 2497324513}, // 1134 913 {10, 0, 2497324513}, // 1144 914 }, 915 useMaxTickets: false, 916 newTickets: 10, 917 expectedDiff: 6976183842, 918 }, 919 { 920 // Next retarget is at 1440. The estimated number of 921 // tickets are such that they span the ticket maturity 922 // floor so that the estimation result is slightly 923 // different as compared to what it would be if each 924 // remaining node only had 10 ticket purchases. This is 925 // because it results in a different number of maturing 926 // tickets depending on how they are allocated on each 927 // side of the maturity floor. 928 name: "11th retarget interval, 50% demand, 127th block", 929 params: testNetParams, 930 ticketInfo: []ticketInfo{ 931 {16, 0, minStakeDiffTestNet}, // 16 932 {271, 10, minStakeDiffTestNet}, // 287 933 {144, 10, 22252747}, // 431 934 {144, 10, 27165468}, // 575 935 {144, 10, 39289988}, // 719 936 {144, 10, 66729608}, // 863 937 {144, 10, 116554208}, // 1007 938 {144, 10, 212709675}, // 1151 939 {144, 10, 417424410}, // 1295 940 {127, 10, 876591473}, // 1422 941 }, 942 useMaxTickets: false, 943 newTickets: 170, // 17 * 10 944 expectedDiff: 1965171141, 945 }, 946 { 947 // Next retarget is at 1440. This is similar to the 948 // last test except all of the estimated tickets are 949 // after the ticket maturity floor, so the estimate is 950 // the same as if each remaining node only had 10 ticket 951 // purchases. 952 name: "11th retarget interval, 50% demand, 128th block", 953 params: testNetParams, 954 ticketInfo: []ticketInfo{ 955 {16, 0, minStakeDiffTestNet}, // 16 956 {271, 10, minStakeDiffTestNet}, // 287 957 {144, 10, 22252747}, // 431 958 {144, 10, 27165468}, // 575 959 {144, 10, 39289988}, // 719 960 {144, 10, 66729608}, // 863 961 {144, 10, 116554208}, // 1007 962 {144, 10, 212709675}, // 1151 963 {144, 10, 417424410}, // 1295 964 {128, 10, 876591473}, // 1423 965 }, 966 useMaxTickets: false, 967 newTickets: 160, // 16 * 10 968 expectedDiff: 1961558695, 969 }, 970 } 971 972 nextTest: 973 for _, test := range tests { 974 stakeValidationHeight := test.params.StakeValidationHeight 975 ticketMaturity := uint32(test.params.TicketMaturity) 976 ticketsPerBlock := uint32(test.params.TicketsPerBlock) 977 978 bc := newFakeChain(test.params) 979 980 // immatureTickets track which height the purchased tickets will 981 // mature and thus be eligible for admission to the live ticket 982 // pool. 983 immatureTickets := make(map[uint32]uint8) 984 var poolSize uint32 985 for _, ticketInfo := range test.ticketInfo { 986 // Ensure the test data isn't faking ticket purchases at 987 // an incorrect difficulty. 988 tip := bc.bestChain.Tip() 989 reqDiff, err := bc.calcNextRequiredStakeDifficultyV2(tip) 990 if err != nil { 991 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 992 "unexpected error: %v", test.name, err) 993 continue nextTest 994 } 995 if ticketInfo.stakeDiff != reqDiff { 996 t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+ 997 "test data has incorrect stake difficulty: "+ 998 "has %d, requires %d", test.name, 999 ticketInfo.stakeDiff, reqDiff) 1000 continue nextTest 1001 } 1002 1003 for i := uint32(0); i < ticketInfo.numNodes; i++ { 1004 // Make up a header. 1005 nextHeight := uint32(tip.height) + 1 1006 header := &wire.BlockHeader{ 1007 Version: 4, 1008 SBits: ticketInfo.stakeDiff, 1009 Height: nextHeight, 1010 FreshStake: ticketInfo.newTickets, 1011 PoolSize: poolSize, 1012 } 1013 tip = newBlockNode(header, tip) 1014 1015 // Update the pool size for the next header. 1016 // Notice how tickets that mature for this block 1017 // do not show up in the pool size until the 1018 // next block. This is correct behavior. 1019 poolSize += uint32(immatureTickets[nextHeight]) 1020 delete(immatureTickets, nextHeight) 1021 if int64(nextHeight) >= stakeValidationHeight { 1022 poolSize -= ticketsPerBlock 1023 } 1024 1025 // Track maturity height for new ticket 1026 // purchases. 1027 maturityHeight := nextHeight + ticketMaturity 1028 immatureTickets[maturityHeight] = ticketInfo.newTickets 1029 1030 // Update the chain to use the new fake node as 1031 // the new best node. 1032 bc.bestChain.SetTip(tip) 1033 } 1034 } 1035 1036 // Ensure the calculated difficulty matches the expected value. 1037 gotDiff, err := bc.estimateNextStakeDifficultyV2(bc.bestChain.Tip(), 1038 test.newTickets, test.useMaxTickets) 1039 if err != nil { 1040 t.Errorf("estimateNextStakeDifficultyV2 (%s): "+ 1041 "unexpected error: %v", test.name, err) 1042 continue 1043 } 1044 if gotDiff != test.expectedDiff { 1045 t.Errorf("estimateNextStakeDifficultyV2 (%s): did not "+ 1046 "get expected stake difficulty -- got %d, "+ 1047 "want %d", test.name, gotDiff, test.expectedDiff) 1048 continue 1049 } 1050 } 1051 } 1052 1053 // TestMinDifficultyReduction ensures the code which results in reducing the 1054 // minimum required difficulty, when the network params allow it, works as 1055 // expected. 1056 func TestMinDifficultyReduction(t *testing.T) { 1057 // Create chain params based on regnet params, but set the fields related to 1058 // proof-of-work difficulty to specific values expected by the tests. 1059 params := chaincfg.RegNetParams 1060 params.ReduceMinDifficulty = true 1061 params.TargetTimePerBlock = time.Minute * 2 1062 params.MinDiffReductionTime = time.Minute * 10 // ~99.3% chance to be mined 1063 params.WorkDiffAlpha = 1 1064 params.WorkDiffWindowSize = 144 1065 params.WorkDiffWindows = 20 1066 params.TargetTimespan = params.TargetTimePerBlock * 1067 time.Duration(params.WorkDiffWindowSize) 1068 params.RetargetAdjustmentFactor = 4 1069 1070 tests := []struct { 1071 name string 1072 timeAdjustment func(i int) time.Duration 1073 numBlocks int64 1074 expectedDiff func(i int) uint32 1075 }{ 1076 { 1077 name: "genesis block", 1078 timeAdjustment: func(i int) time.Duration { return time.Second }, 1079 numBlocks: 1, 1080 expectedDiff: func(i int) uint32 { return params.PowLimitBits }, 1081 }, 1082 { 1083 name: "create difficulty spike - part 1", 1084 timeAdjustment: func(i int) time.Duration { return time.Second }, 1085 numBlocks: params.WorkDiffWindowSize - 2, 1086 expectedDiff: func(i int) uint32 { return 545259519 }, 1087 }, 1088 { 1089 name: "create difficulty spike - part 2", 1090 timeAdjustment: func(i int) time.Duration { return time.Second }, 1091 numBlocks: params.WorkDiffWindowSize, 1092 expectedDiff: func(i int) uint32 { return 545259519 }, 1093 }, 1094 { 1095 name: "create difficulty spike - part 3", 1096 timeAdjustment: func(i int) time.Duration { return time.Second }, 1097 numBlocks: params.WorkDiffWindowSize, 1098 expectedDiff: func(i int) uint32 { return 541100164 }, 1099 }, 1100 { 1101 name: "create difficulty spike - part 4", 1102 timeAdjustment: func(i int) time.Duration { return time.Second }, 1103 numBlocks: params.WorkDiffWindowSize, 1104 expectedDiff: func(i int) uint32 { return 537954654 }, 1105 }, 1106 { 1107 name: "create difficulty spike - part 5", 1108 timeAdjustment: func(i int) time.Duration { return time.Second }, 1109 numBlocks: params.WorkDiffWindowSize, 1110 expectedDiff: func(i int) uint32 { return 537141847 }, 1111 }, 1112 { 1113 name: "create difficulty spike - part 6", 1114 timeAdjustment: func(i int) time.Duration { return time.Second }, 1115 numBlocks: params.WorkDiffWindowSize, 1116 expectedDiff: func(i int) uint32 { return 536938645 }, 1117 }, 1118 { 1119 name: "create difficulty spike - part 7", 1120 timeAdjustment: func(i int) time.Duration { return time.Second }, 1121 numBlocks: params.WorkDiffWindowSize, 1122 expectedDiff: func(i int) uint32 { return 524428608 }, 1123 }, 1124 { 1125 name: "create difficulty spike - part 8", 1126 timeAdjustment: func(i int) time.Duration { return time.Second }, 1127 numBlocks: params.WorkDiffWindowSize, 1128 expectedDiff: func(i int) uint32 { return 521177424 }, 1129 }, 1130 { 1131 name: "create difficulty spike - part 9", 1132 timeAdjustment: func(i int) time.Duration { return time.Second }, 1133 numBlocks: params.WorkDiffWindowSize, 1134 expectedDiff: func(i int) uint32 { return 520364628 }, 1135 }, 1136 { 1137 name: "create difficulty spike - part 10", 1138 timeAdjustment: func(i int) time.Duration { return time.Second }, 1139 numBlocks: params.WorkDiffWindowSize, 1140 expectedDiff: func(i int) uint32 { return 520161429 }, 1141 }, 1142 { 1143 name: "alternate min diff blocks", 1144 timeAdjustment: func(i int) time.Duration { 1145 if i%2 == 0 { 1146 return params.MinDiffReductionTime + time.Second 1147 } 1148 return params.TargetTimePerBlock 1149 }, 1150 numBlocks: params.WorkDiffWindowSize, 1151 expectedDiff: func(i int) uint32 { 1152 if i%2 == 0 && i != 0 { 1153 return params.PowLimitBits 1154 } 1155 return 507651392 1156 }, 1157 }, 1158 { 1159 name: "interval of blocks taking twice the target time - part 1", 1160 timeAdjustment: func(i int) time.Duration { 1161 return params.TargetTimePerBlock * 2 1162 }, 1163 numBlocks: params.WorkDiffWindowSize, 1164 expectedDiff: func(i int) uint32 { return 509850141 }, 1165 }, 1166 { 1167 name: "interval of blocks taking twice the target time - part 2", 1168 timeAdjustment: func(i int) time.Duration { 1169 return params.TargetTimePerBlock * 2 1170 }, 1171 numBlocks: params.WorkDiffWindowSize, 1172 expectedDiff: func(i int) uint32 { return 520138451 }, 1173 }, 1174 { 1175 name: "interval of blocks taking twice the target time - part 3", 1176 timeAdjustment: func(i int) time.Duration { 1177 return params.TargetTimePerBlock * 2 1178 }, 1179 numBlocks: params.WorkDiffWindowSize, 1180 expectedDiff: func(i int) uint32 { return 520177692 }, 1181 }, 1182 } 1183 1184 bc := newFakeChain(¶ms) 1185 node := bc.bestChain.Tip() 1186 blockTime := time.Unix(node.timestamp, 0) 1187 for _, test := range tests { 1188 for i := 0; i < int(test.numBlocks); i++ { 1189 // Update the block time according to the test data and calculate 1190 // the difficulty for the next block. 1191 blockTime = blockTime.Add(test.timeAdjustment(i)) 1192 diff, err := bc.calcNextRequiredDifficulty(node, blockTime) 1193 if err != nil { 1194 t.Fatalf("calcNextRequiredDifficulty: unexpected err: %v", err) 1195 } 1196 1197 // Ensure the calculated difficulty matches the expected value. 1198 expectedDiff := test.expectedDiff(i) 1199 if diff != expectedDiff { 1200 t.Fatalf("calcNextRequiredDifficulty (%s): did not get "+ 1201 "expected difficulty -- got %d, want %d", test.name, diff, 1202 expectedDiff) 1203 } 1204 1205 node = newFakeNode(node, 1, 1, diff, blockTime) 1206 bc.index.AddNode(node) 1207 bc.bestChain.SetTip(node) 1208 } 1209 } 1210 }