dcrd/blockchain/difficulty_test.go
Dave Collins 2296b31e5d
blockchain: Refactor main block index logic.
This refactors the block index logic into a separate struct and
introduces an individual lock for it so it can be queried independent of
the chain lock.

It also modifies the `newBlockNode` function to accept nil for the
ticket spend information parameter and updates all of the test code that
doesn't require it to use nil.
2018-01-30 13:21:57 -06:00

772 lines
23 KiB
Go

// Copyright (c) 2014 The btcsuite developers
// Copyright (c) 2015-2018 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain
import (
"math/big"
"runtime"
"testing"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/wire"
)
func TestBigToCompact(t *testing.T) {
tests := []struct {
in int64
out uint32
}{
{0, 0},
{-1, 25231360},
}
for x, test := range tests {
n := big.NewInt(test.in)
r := BigToCompact(n)
if r != test.out {
t.Errorf("TestBigToCompact test #%d failed: got %d want %d\n",
x, r, test.out)
return
}
}
}
func TestCompactToBig(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
n := CompactToBig(test.in)
want := big.NewInt(test.out)
if n.Cmp(want) != 0 {
t.Errorf("TestCompactToBig test #%d failed: got %d want %d\n",
x, n.Int64(), want.Int64())
return
}
}
}
func TestCalcWork(t *testing.T) {
tests := []struct {
in uint32
out int64
}{
{10000000, 0},
}
for x, test := range tests {
bits := uint32(test.in)
r := CalcWork(bits)
if r.Int64() != test.out {
t.Errorf("TestCalcWork test #%d failed: got %v want %d\n",
x, r.Int64(), test.out)
return
}
}
}
// TestEstimateSupply ensures the supply estimation function used in the stake
// difficulty algorithm defined by DCP0001 works as expected.
func TestEstimateSupply(t *testing.T) {
t.Parallel()
// The parameters used for the supply estimation.
params := &chaincfg.MainNetParams
baseSubsidy := params.BaseSubsidy
reduxInterval := params.SubsidyReductionInterval
blockOneSubsidy := params.BlockOneSubsidy()
// intervalSubsidy is a helper function to return the full block subsidy
// for the given reduction interval.
intervalSubsidy := func(interval int) int64 {
subsidy := baseSubsidy
for i := 0; i < interval; i++ {
subsidy *= params.MulSubsidy
subsidy /= params.DivSubsidy
}
return subsidy
}
// Useful calculations for the tests below.
intervalOneSubsidy := intervalSubsidy(1)
intervalTwoSubsidy := intervalSubsidy(2)
reduxIntervalMinusOneSupply := blockOneSubsidy + (baseSubsidy * (reduxInterval - 2))
reduxIntervalTwoMinusOneSupply := reduxIntervalMinusOneSupply + (intervalOneSubsidy * reduxInterval)
tests := []struct {
height int64
expected int64
}{
{height: -1, expected: 0},
{height: 0, expected: 0},
{height: 1, expected: blockOneSubsidy},
{height: 2, expected: blockOneSubsidy + baseSubsidy},
{height: 3, expected: blockOneSubsidy + baseSubsidy*2},
{height: reduxInterval - 1, expected: reduxIntervalMinusOneSupply},
{height: reduxInterval, expected: reduxIntervalMinusOneSupply + intervalOneSubsidy},
{height: reduxInterval + 1, expected: reduxIntervalMinusOneSupply + intervalOneSubsidy*2},
{height: reduxInterval*2 - 1, expected: reduxIntervalTwoMinusOneSupply},
{height: reduxInterval * 2, expected: reduxIntervalTwoMinusOneSupply + intervalTwoSubsidy},
{height: reduxInterval*2 + 1, expected: reduxIntervalTwoMinusOneSupply + intervalTwoSubsidy*2},
}
for _, test := range tests {
// Ensure the function to calculate the estimated supply is
// working properly.
gotSupply := estimateSupply(params, test.height)
if gotSupply != test.expected {
t.Errorf("estimateSupply (height %d): did not get "+
"expected supply - got %d, want %d", test.height,
gotSupply, test.expected)
continue
}
}
}
// assertStakeDiffParams ensure the passed params have the values used in the
// tests related to stake difficulty calculation.
func assertStakeDiffParams(t *testing.T, params *chaincfg.Params) {
if params.MinimumStakeDiff != 200000000 {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d -- expect params with minimum stake diff of "+
"%d, got %d", file, line, 200000000,
params.MinimumStakeDiff)
}
if params.TicketMaturity != 256 {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d -- expect params with ticket maturity of "+
"%d, got %d", file, line, 256, params.TicketMaturity)
}
if params.StakeValidationHeight != 4096 {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d -- expect params with stake val height of %d, "+
"got %d", file, line, 4096, params.StakeValidationHeight)
}
if params.StakeDiffWindowSize != 144 {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d -- expect params with stake diff interval of "+
"%d, got %d", file, line, 144, params.StakeDiffWindowSize)
}
if params.TicketsPerBlock != 5 {
_, file, line, _ := runtime.Caller(1)
t.Fatalf("%s:%d -- expect params with tickets per block of "+
"%d, got %d", file, line, 5, params.TicketsPerBlock)
}
}
// TestCalcNextRequiredStakeDiffV2 ensure the stake diff calculation function
// for the algorithm defined by DCP0001 works as expected.
func TestCalcNextRequiredStakeDiffV2(t *testing.T) {
t.Parallel()
// ticketInfo is used to control the tests by specifying the details
// about how many fake blocks to create with the specified number of
// ticket and stake difficulty.
type ticketInfo struct {
numNodes uint32
newTickets uint8
stakeDiff int64
}
// Specify the params used in the tests and assert the values directly
// used by the tests are the expected ones. All of the test values will
// need to be updated if these parameters change since they are manually
// calculated based on them.
params := &chaincfg.MainNetParams
assertStakeDiffParams(t, params)
minStakeDiff := params.MinimumStakeDiff
ticketMaturity := uint32(params.TicketMaturity)
stakeValidationHeight := params.StakeValidationHeight
tests := []struct {
name string
ticketInfo []ticketInfo
expectedDiff int64
}{
{
// Next retarget is at 144. Prior to coinbase maturity,
// so will always be the minimum.
name: "genesis block",
ticketInfo: []ticketInfo{{0, 0, minStakeDiff}},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 144. Prior to coinbase maturity,
// so will always be the minimum.
name: "1st retarget, before coinbase",
ticketInfo: []ticketInfo{{143, 0, minStakeDiff}},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288.
//
// Tickets could not possibly have been bought yet, but
// ensure the algorithm handles it properly.
name: "coinbase maturity with impossible num tickets",
ticketInfo: []ticketInfo{{255, 20, minStakeDiff}},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288.
//
// Block 0 has no spendable outputs, so tickets could
// not have possibly been bought yet.
name: "coinbase maturity + 1",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff},
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288.
name: "2nd retarget interval - 1, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{30, 20, minStakeDiff}, // 286
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288.
name: "2nd retarget interval, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{31, 20, minStakeDiff}, // 287
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 432.
name: "3rd retarget interval, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{175, 20, minStakeDiff}, // 431
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 2304.
name: "16th retarget interval - 1, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2046, 20, minStakeDiff}, // 2302
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 2304.
name: "16th retarget interval, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
},
expectedDiff: 208418769,
},
{
// Next retarget is at 2448.
name: "17th retarget interval - 1, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{143, 20, 208418769}, // 2446
},
expectedDiff: 208418769,
},
{
// Next retarget is at 2448.
name: "17th retarget interval, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
},
expectedDiff: 231326567,
},
{
// Next retarget is at 2592.
name: "17th retarget interval+1, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
{1, 20, 231326567}, // 2448
},
expectedDiff: 231326567,
},
{
// Next retarget is at 3456.
name: "24th retarget interval, varying demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{31, 20, minStakeDiff}, // 287
{144, 10, minStakeDiff}, // 431
{144, 20, minStakeDiff}, // 575
{144, 10, minStakeDiff}, // 719
{144, 20, minStakeDiff}, // 863
{144, 10, minStakeDiff}, // 1007
{144, 20, minStakeDiff}, // 1151
{144, 10, minStakeDiff}, // 1295
{144, 20, minStakeDiff}, // 1439
{144, 10, minStakeDiff}, // 1583
{144, 20, minStakeDiff}, // 1727
{144, 10, minStakeDiff}, // 1871
{144, 20, minStakeDiff}, // 2015
{144, 10, minStakeDiff}, // 2159
{144, 20, minStakeDiff}, // 2303
{144, 10, minStakeDiff}, // 2447
{144, 20, minStakeDiff}, // 2591
{144, 10, minStakeDiff}, // 2735
{144, 20, minStakeDiff}, // 2879
{144, 9, 201743368}, // 3023
{144, 20, 201093236}, // 3167
{144, 8, 222625877}, // 3311
{144, 20, 242331291}, // 3455
},
expectedDiff: 291317641,
},
{
// Next retarget is at 4176. Post stake validation
// height.
name: "29th retarget interval, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
{144, 20, 231326567}, // 2591
{144, 20, 272451490}, // 2735
{144, 20, 339388424}, // 2879
{144, 20, 445827839}, // 3023
{144, 20, 615949254}, // 3167
{144, 20, 892862990}, // 3311
{144, 20, 1354989669}, // 3455
{144, 20, 2148473276}, // 3599
{144, 20, 3552797658}, // 3743
{144, 20, 6116808441}, // 3887
{144, 20, 10947547379}, // 4031
{144, 20, 20338554623}, // 4175
},
expectedDiff: 22097687698,
},
{
// Next retarget is at 4176. Post stake validation
// height.
name: "29th retarget interval, 50% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{3919, 10, minStakeDiff}, // 4175
},
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 4464. Post stake validation
// height.
name: "31st retarget interval, waning demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
{144, 20, 231326567}, // 2591
{144, 20, 272451490}, // 2735
{144, 20, 339388424}, // 2879
{144, 20, 445827839}, // 3023
{144, 20, 615949254}, // 3167
{144, 20, 892862990}, // 3311
{144, 20, 1354989669}, // 3455
{144, 20, 2148473276}, // 3599
{144, 20, 3552797658}, // 3743
{144, 13, 6116808441}, // 3887
{144, 0, 10645659768}, // 4031
{144, 0, 18046712136}, // 4175
{144, 0, 22097687698}, // 4319
{144, 0, 22152524112}, // 4463
},
expectedDiff: 22207360526,
},
}
nextTest:
for _, test := range tests {
bc := newFakeChain(params)
// immatureTickets tracks which height the purchased tickets
// will mature and thus be eligible for admission to the live
// ticket pool.
immatureTickets := make(map[uint32]uint8)
var poolSize uint32
for _, ticketInfo := range test.ticketInfo {
// Ensure the test data isn't faking ticket purchases at
// an incorrect difficulty.
gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode)
if err != nil {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"unexpected error: %v", test.name, err)
continue nextTest
}
if gotDiff != ticketInfo.stakeDiff {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"did not get expected stake difficulty -- got "+
"%d, want %d", test.name, gotDiff,
ticketInfo.stakeDiff)
continue nextTest
}
for i := uint32(0); i < ticketInfo.numNodes; i++ {
// Make up a header.
nextHeight := uint32(bc.bestNode.height) + 1
header := &wire.BlockHeader{
Version: 4,
SBits: ticketInfo.stakeDiff,
Height: nextHeight,
FreshStake: ticketInfo.newTickets,
PoolSize: poolSize,
}
node := newBlockNode(header, nil)
node.parent = bc.bestNode
// Update the pool size for the next header.
// Notice how tickets that mature for this block
// do not show up in the pool size until the
// next block. This is correct behavior.
poolSize += uint32(immatureTickets[nextHeight])
delete(immatureTickets, nextHeight)
if int64(nextHeight) >= stakeValidationHeight {
poolSize -= uint32(params.TicketsPerBlock)
}
// Track maturity height for new ticket
// purchases.
maturityHeight := nextHeight + ticketMaturity
immatureTickets[maturityHeight] = ticketInfo.newTickets
// Update the chain to use the new fake node as
// the new best node.
bc.bestNode = node
}
}
// Ensure the calculated difficulty matches the expected value.
gotDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode)
if err != nil {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"unexpected error: %v", test.name, err)
continue
}
if gotDiff != test.expectedDiff {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"did not get expected stake difficulty -- got "+
"%d, want %d", test.name, gotDiff,
test.expectedDiff)
continue
}
}
}
// TestEstimateNextStakeDiffV2 ensures the function that estimates the stake
// diff calculation for the algorithm defined by DCP0001 works as expected.
func TestEstimateNextStakeDiffV2(t *testing.T) {
t.Parallel()
// ticketInfo is used to control the tests by specifying the details
// about how many fake blocks to create with the specified number of
// ticket and stake difficulty.
type ticketInfo struct {
numNodes uint32
newTickets uint8
stakeDiff int64
}
// Specify the params used in the tests and assert the values directly
// used by the tests are the expected ones. All of the test values will
// need to be updated if these parameters change since they are manually
// calculated based on them.
params := &chaincfg.MainNetParams
assertStakeDiffParams(t, params)
minStakeDiff := params.MinimumStakeDiff
ticketMaturity := uint32(params.TicketMaturity)
stakeValidationHeight := params.StakeValidationHeight
tests := []struct {
name string
ticketInfo []ticketInfo
newTickets int64
useMaxTickets bool
expectedDiff int64
}{
{
// Regardless of claiming tickets will be purchased, the
// resulting stake difficulty should be the minimum
// because the first retarget is before the start
// height.
name: "genesis block",
ticketInfo: []ticketInfo{{0, 0, minStakeDiff}},
newTickets: 2860,
useMaxTickets: false,
expectedDiff: minStakeDiff,
},
{
// Next retarget is 144. Resulting stake difficulty
// should be the minimum regardless of claimed ticket
// purchases because the previous pool size is still 0.
name: "during retarget, but before coinbase",
ticketInfo: []ticketInfo{{140, 0, minStakeDiff}},
newTickets: 20 * 3, // blocks 141, 142, and 143.
useMaxTickets: true,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288. Regardless of claiming
// tickets will be purchased, the resulting stake
// difficulty should be the min because the previous
// pool size is still 0.
name: "at coinbase maturity",
ticketInfo: []ticketInfo{{256, 0, minStakeDiff}},
useMaxTickets: true,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288. Regardless of actually
// purchasing tickets and claiming more tickets will be
// purchased, the resulting stake difficulty should be
// the min because the previous pool size is still 0.
name: "2nd retarget interval - 2, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{30, 20, minStakeDiff}, // 286
},
useMaxTickets: true,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 288. Still expect minimum stake
// difficulty since the raw result would be lower.
name: "2nd retarget interval - 1, 100% demand",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{31, 20, minStakeDiff}, // 287
},
useMaxTickets: true,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 432.
name: "3rd retarget interval, 100% demand, 1st block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{32, 20, minStakeDiff}, // 288
},
useMaxTickets: true,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 2304.
name: "16th retarget interval, 100% demand, 1st block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{1904, 20, minStakeDiff}, // 2160
},
useMaxTickets: true,
expectedDiff: 208418769,
},
{
// Next retarget is at 2304.
name: "16th retarget interval, 100% demand, 2nd block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{1905, 20, minStakeDiff}, // 2161
},
useMaxTickets: true,
expectedDiff: 208418769,
},
{
// Next retarget is at 2304.
name: "16th retarget interval, 100% demand, final block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
},
useMaxTickets: true,
expectedDiff: 208418769,
},
{
// Next retarget is at 3456.
name: "24th retarget interval, varying demand, 5th block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{31, 20, minStakeDiff}, // 287
{144, 10, minStakeDiff}, // 431
{144, 20, minStakeDiff}, // 575
{144, 10, minStakeDiff}, // 719
{144, 20, minStakeDiff}, // 863
{144, 10, minStakeDiff}, // 1007
{144, 20, minStakeDiff}, // 1151
{144, 10, minStakeDiff}, // 1295
{144, 20, minStakeDiff}, // 1439
{144, 10, minStakeDiff}, // 1583
{144, 20, minStakeDiff}, // 1727
{144, 10, minStakeDiff}, // 1871
{144, 20, minStakeDiff}, // 2015
{144, 10, minStakeDiff}, // 2159
{144, 20, minStakeDiff}, // 2303
{144, 10, minStakeDiff}, // 2447
{144, 20, minStakeDiff}, // 2591
{144, 10, minStakeDiff}, // 2735
{144, 20, minStakeDiff}, // 2879
{144, 9, 201743368}, // 3023
{144, 20, 201093236}, // 3167
{144, 8, 222625877}, // 3311
{5, 20, 242331291}, // 3316
},
useMaxTickets: true,
expectedDiff: 291317641,
},
{
// Next retarget is at 4176. Post stake validation
// height.
name: "29th retarget interval, 100% demand, 10th block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
{144, 20, 231326567}, // 2591
{144, 20, 272451490}, // 2735
{144, 20, 339388424}, // 2879
{144, 20, 445827839}, // 3023
{144, 20, 615949254}, // 3167
{144, 20, 892862990}, // 3311
{144, 20, 1354989669}, // 3455
{144, 20, 2148473276}, // 3599
{144, 20, 3552797658}, // 3743
{144, 20, 6116808441}, // 3887
{144, 20, 10947547379}, // 4031
{10, 20, 20338554623}, // 4041
},
useMaxTickets: true,
expectedDiff: 22097687698,
},
{
// Next retarget is at 4176. Post stake validation
// height.
name: "29th retarget interval, 50% demand, 23rd block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{3775, 10, minStakeDiff}, // 4031
{23, 10, minStakeDiff}, // 4054
},
newTickets: 1210, // 121 * 10
useMaxTickets: false,
expectedDiff: minStakeDiff,
},
{
// Next retarget is at 4464. Post stake validation
// height.
name: "31st retarget interval, waning demand, 117th block",
ticketInfo: []ticketInfo{
{256, 0, minStakeDiff}, // 256
{2047, 20, minStakeDiff}, // 2303
{144, 20, 208418769}, // 2447
{144, 20, 231326567}, // 2591
{144, 20, 272451490}, // 2735
{144, 20, 339388424}, // 2879
{144, 20, 445827839}, // 3023
{144, 20, 615949254}, // 3167
{144, 20, 892862990}, // 3311
{144, 20, 1354989669}, // 3455
{144, 20, 2148473276}, // 3599
{144, 20, 3552797658}, // 3743
{144, 13, 6116808441}, // 3887
{144, 0, 10645659768}, // 4031
{144, 0, 18046712136}, // 4175
{144, 0, 22097687698}, // 4319
{117, 0, 22152524112}, // 4436
},
useMaxTickets: false,
newTickets: 0,
expectedDiff: 22207360526,
},
}
nextTest:
for _, test := range tests {
bc := newFakeChain(params)
// immatureTickets track which height the purchased tickets will
// mature and thus be eligible for admission to the live ticket
// pool.
immatureTickets := make(map[uint32]uint8)
var poolSize uint32
for _, ticketInfo := range test.ticketInfo {
// Ensure the test data isn't faking ticket purchases at
// an incorrect difficulty.
reqDiff, err := bc.calcNextRequiredStakeDifficultyV2(bc.bestNode)
if err != nil {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"unexpected error: %v", test.name, err)
continue nextTest
}
if ticketInfo.stakeDiff != reqDiff {
t.Errorf("calcNextRequiredStakeDifficultyV2 (%s): "+
"test data has incorrect stake difficulty: "+
"has %d, requires %d", test.name,
ticketInfo.stakeDiff, reqDiff)
continue nextTest
}
for i := uint32(0); i < ticketInfo.numNodes; i++ {
// Make up a header.
nextHeight := uint32(bc.bestNode.height) + 1
header := &wire.BlockHeader{
Version: 4,
SBits: ticketInfo.stakeDiff,
Height: nextHeight,
FreshStake: ticketInfo.newTickets,
PoolSize: poolSize,
}
node := newBlockNode(header, nil)
node.parent = bc.bestNode
// Update the pool size for the next header.
// Notice how tickets that mature for this block
// do not show up in the pool size until the
// next block. This is correct behavior.
poolSize += uint32(immatureTickets[nextHeight])
delete(immatureTickets, nextHeight)
if int64(nextHeight) >= stakeValidationHeight {
poolSize -= uint32(params.TicketsPerBlock)
}
// Track maturity height for new ticket
// purchases.
maturityHeight := nextHeight + ticketMaturity
immatureTickets[maturityHeight] = ticketInfo.newTickets
// Update the chain to use the new fake node as
// the new best node.
bc.bestNode = node
}
}
// Ensure the calculated difficulty matches the expected value.
gotDiff, err := bc.estimateNextStakeDifficultyV2(bc.bestNode,
test.newTickets, test.useMaxTickets)
if err != nil {
t.Errorf("estimateNextStakeDifficultyV2 (%s): "+
"unexpected error: %v", test.name, err)
continue
}
if gotDiff != test.expectedDiff {
t.Errorf("estimateNextStakeDifficultyV2 (%s): did not "+
"get expected stake difficulty -- got %d, "+
"want %d", test.name, gotDiff, test.expectedDiff)
continue
}
}
}