standalone: Add modified subsidy split calcs.

This adds two new exported functions to the subsidy cache in
blockchain/standalone to calculate calculate the work and stake vote
subsidies according to either the original or modified values defined in
DCP0010 per a provided flag along with tests to ensure expected
behavior.

It should be noted that the values for the modified split are hard coded
as opposed to using the subsidy in order to avoid the need for a major
module bump that would be required if the subsidy params interface were
changed.  The values are the same for all networks, so no additional
logic is necessary on a per-network basis.
This commit is contained in:
Dave Collins 2021-12-13 14:05:19 -06:00
parent 2f2ec08d60
commit d8527c0443
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
2 changed files with 405 additions and 28 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) 2015-2020 The Decred developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -47,7 +47,8 @@ type SubsidyParams interface {
// block subsidy between PoW, PoS, and the Treasury.
// WorkSubsidyProportion returns the comparative proportion of the subsidy
// generated for creating a block (PoW).
// generated for creating a block (PoW) that was in effect prior to the
// modified value defined in DCP0010.
//
// The proportional split between PoW, PoS, and the Treasury is calculated
// by treating each of the proportional parameters as a ratio to the sum of
@ -61,7 +62,8 @@ type SubsidyParams interface {
WorkSubsidyProportion() uint16
// StakeSubsidyProportion returns the comparative proportion of the subsidy
// generated for casting stake votes (collectively, per block). See the
// generated for casting stake votes (collectively, per block) that was in
// effect prior to the modified value defined in DCP0010. See the
// documentation for WorkSubsidyProportion for more details on how the
// parameter is used.
StakeSubsidyProportion() uint16
@ -236,17 +238,17 @@ func (c *SubsidyCache) CalcBlockSubsidy(height int64) int64 {
return subsidy
}
// CalcWorkSubsidy returns the proof of work subsidy for a block for a given
// number of votes. It is calculated as a proportion of the total subsidy and
// further reduced proportionally depending on the number of votes once the
// height at which voting begins has been reached.
// calcWorkSubsidy returns the proof of work subsidy for a block for a given
// number of votes using the provided work subsidy proportion and total
// proportions. This is the primary implementation logic used by
// CalcWorkSubsidy and CalcWorkSubsidyV2.
//
// Note that passing a number of voters fewer than the minimum required for a
// block to be valid by consensus along with a height greater than or equal to
// the height at which voting begins will return zero.
// See the comments of those functions for further details.
//
// This function is safe for concurrent access.
func (c *SubsidyCache) CalcWorkSubsidy(height int64, voters uint16) int64 {
func (c *SubsidyCache) calcWorkSubsidy(height int64, voters uint16, proportion,
totalProportions uint16) int64 {
// The first block has special subsidy rules.
if height == 1 {
return c.params.BlockOneSubsidy()
@ -262,8 +264,8 @@ func (c *SubsidyCache) CalcWorkSubsidy(height int64, voters uint16) int64 {
// Calculate the full block subsidy and reduce it according to the PoW
// proportion.
subsidy := c.CalcBlockSubsidy(height)
subsidy *= int64(c.params.WorkSubsidyProportion())
subsidy /= int64(c.totalProportions)
subsidy *= int64(proportion)
subsidy /= int64(totalProportions)
// Ignore any potential subsidy reductions due to the number of votes prior
// to the point voting begins.
@ -275,19 +277,67 @@ func (c *SubsidyCache) CalcWorkSubsidy(height int64, voters uint16) int64 {
return (int64(voters) * subsidy) / int64(c.params.VotesPerBlock())
}
// CalcStakeVoteSubsidy returns the subsidy for a single stake vote for a block.
// It is calculated as a proportion of the total subsidy and max potential
// number of votes per block.
// CalcWorkSubsidy returns the proof of work subsidy for a block for a given
// number of votes using the subsidy split that was in effect prior to the
// modifed value defined in DCP0010.
//
// Unlike the Proof-of-Work and Treasury subsidies, the subsidy that votes
// receive is not reduced when a block contains less than the maximum number of
// votes. Consequently, this does not accept the number of votes. However, it
// is important to note that blocks that do not receive the minimum required
// number of votes for a block to be valid by consensus won't actually produce
// any vote subsidy either since they are invalid.
// It is calculated as a proportion of the total subsidy and further reduced
// proportionally depending on the number of votes once the height at which
// voting begins has been reached.
//
// Note that passing a number of voters fewer than the minimum required for a
// block to be valid by consensus along with a height greater than or equal to
// the height at which voting begins will return zero.
//
// This function is safe for concurrent access.
func (c *SubsidyCache) CalcStakeVoteSubsidy(height int64) int64 {
//
// Deprecated: Use CalcWorkSubsidyV2 instead.
func (c *SubsidyCache) CalcWorkSubsidy(height int64, voters uint16) int64 {
return c.calcWorkSubsidy(height, voters, c.params.WorkSubsidyProportion(),
c.totalProportions)
}
// CalcWorkSubsidyV2 returns the proof of work subsidy for a block for a given
// number of votes using either the original subsidy split that was in effect at
// Decred launch or the modified subsidy split defined in DCP0010 according to
// the provided flag.
//
// It is calculated as a proportion of the total subsidy and further reduced
// proportionally depending on the number of votes once the height at which
// voting begins has been reached.
//
// Note that passing a number of voters fewer than the minimum required for a
// block to be valid by consensus along with a height greater than or equal to
// the height at which voting begins will return zero.
//
// This function is safe for concurrent access.
func (c *SubsidyCache) CalcWorkSubsidyV2(height int64, voters uint16, useDCP0010 bool) int64 {
if !useDCP0010 {
return c.CalcWorkSubsidy(height, voters)
}
// The work subsidy proportion defined in DCP0010 is 10%. Thus it is 1
// since 1/10 = 10%.
//
// Note that the value is hard coded here as opposed to using the subsidy
// params in order to avoid the need for a major module bump that would be
// required if the subsidy params interface were changed.
const workSubsidyProportion = 1
const totalProportions = 10
return c.calcWorkSubsidy(height, voters, workSubsidyProportion,
totalProportions)
}
// calcStakeVoteSubsidy returns the subsidy for a single stake vote for a block
// using the provided stake vote subsidy proportion. It is calculated as the
// provided proportion of the total subsidy and max potential number of votes
// per block. This is the primary implementation logic used by
// CalcStakeVoteSubsidy and CalcStakeVoteSubsidyV2.
//
// See the comments of those functions for further details.
//
// This function is safe for concurrent access.
func (c *SubsidyCache) calcStakeVoteSubsidy(height int64, proportion, totalProportions uint16) int64 {
// Votes have no subsidy prior to the point voting begins. The minus one
// accounts for the fact that vote subsidy are, unfortunately, based on the
// height that is being voted on as opposed to the block in which they are
@ -300,13 +350,68 @@ func (c *SubsidyCache) CalcStakeVoteSubsidy(height int64) int64 {
// proportion. Then divide it by the number of votes per block to arrive
// at the amount per vote.
subsidy := c.CalcBlockSubsidy(height)
proportions := int64(c.totalProportions)
subsidy *= int64(c.params.StakeSubsidyProportion())
proportions := int64(totalProportions)
subsidy *= int64(proportion)
subsidy /= (proportions * int64(c.params.VotesPerBlock()))
return subsidy
}
// CalcStakeVoteSubsidy returns the subsidy for a single stake vote for a block
// using the subsidy split that was in effect prior to the modifed value defined
// in DCP0010.
//
// It is calculated as a proportion of the total subsidy and max potential
// number of votes per block.
//
// Unlike the Proof-of-Work and Treasury subsidies, the subsidy that votes
// receive is not reduced when a block contains less than the maximum number of
// votes. Consequently, this does not accept the number of votes. However, it
// is important to note that blocks that do not receive the minimum required
// number of votes for a block to be valid by consensus won't actually produce
// any vote subsidy either since they are invalid.
//
// This function is safe for concurrent access.
//
// Deprecated: Use CalcStakeVoteSubsidyV2 instead.
func (c *SubsidyCache) CalcStakeVoteSubsidy(height int64) int64 {
return c.calcStakeVoteSubsidy(height, c.params.StakeSubsidyProportion(),
c.totalProportions)
}
// CalcStakeVoteSubsidyV2 returns the subsidy for a single stake vote for a
// block using either the original subsidy split that was in effect at Decred
// launch or the modified subsidy split defined in DCP0010 according to the
// provided flag.
//
// It is calculated as a proportion of the total subsidy and max potential
// number of votes per block.
//
// Unlike the Proof-of-Work and Treasury subsidies, the subsidy that votes
// receive is not reduced when a block contains less than the maximum number of
// votes. Consequently, this does not accept the number of votes. However, it
// is important to note that blocks that do not receive the minimum required
// number of votes for a block to be valid by consensus won't actually produce
// any vote subsidy either since they are invalid.
//
// This function is safe for concurrent access.
func (c *SubsidyCache) CalcStakeVoteSubsidyV2(height int64, useDCP0010 bool) int64 {
if !useDCP0010 {
return c.CalcStakeVoteSubsidy(height)
}
// The stake vote subsidy proportion defined in DCP0010 is 80%. Thus it is
// 8 since 8/10 = 80%.
//
// Note that the value is hard coded here as opposed to using the subsidy
// params in order to avoid the need for a major module bump that would be
// required if the subsidy params interface were changed.
const voteSubsidyProportion = 8
const totalProportions = 10
return c.calcStakeVoteSubsidy(height, voteSubsidyProportion,
totalProportions)
}
// CalcTreasurySubsidy returns the subsidy required to go to the treasury for
// a block. It is calculated as a proportion of the total subsidy and further
// reduced proportionally depending on the number of votes once the height at

View File

@ -1,4 +1,4 @@
// Copyright (c) 2019-2020 The Decred developers
// Copyright (c) 2019-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@ -157,6 +157,7 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork int64 // expected pow subsidy
wantVote int64 // expected single vote subsidy
wantTreasury int64 // expected treasury subsidy
useDCP0010 bool // use subsidy split defined in DCP0010
}{{
name: "negative height",
params: mockMainNetParams,
@ -166,6 +167,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
}, {
name: "negative height, use DCP0010",
params: mockMainNetParams,
height: -1,
numVotes: 0,
wantFull: 0,
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 0",
params: mockMainNetParams,
@ -175,6 +186,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
}, {
name: "height 0, use DCP0010",
params: mockMainNetParams,
height: 0,
numVotes: 0,
wantFull: 0,
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 1 (initial payouts)",
params: mockMainNetParams,
@ -184,6 +205,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 168000000000000,
wantVote: 0,
wantTreasury: 0,
}, {
name: "height 1 (initial payouts), use DCP0010",
params: mockMainNetParams,
height: 1,
numVotes: 0,
wantFull: 168000000000000,
wantWork: 168000000000000,
wantVote: 0,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 2 (first non-special block prior voting start)",
params: mockMainNetParams,
@ -193,6 +224,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1871749598,
wantVote: 0,
wantTreasury: 311958266,
}, {
name: "height 2 (first non-special block prior voting start), use DCP0010",
params: mockMainNetParams,
height: 2,
numVotes: 0,
wantFull: 3119582664,
wantWork: 311958266,
wantVote: 0,
wantTreasury: 311958266,
useDCP0010: true,
}, {
name: "height 4094 (two blocks prior to voting start)",
params: mockMainNetParams,
@ -202,6 +243,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1871749598,
wantVote: 0,
wantTreasury: 311958266,
}, {
name: "height 4094 (two blocks prior to voting start), use DCP0010",
params: mockMainNetParams,
height: 4094,
numVotes: 0,
wantFull: 3119582664,
wantWork: 311958266,
wantVote: 0,
wantTreasury: 311958266,
useDCP0010: true,
}, {
name: "height 4095 (final block prior to voting start)",
params: mockMainNetParams,
@ -211,6 +262,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1871749598,
wantVote: 187174959,
wantTreasury: 311958266,
}, {
name: "height 4095 (final block prior to voting start), use DCP0010",
params: mockMainNetParams,
height: 4095,
numVotes: 0,
wantFull: 3119582664,
wantWork: 311958266,
wantVote: 499133226,
wantTreasury: 311958266,
useDCP0010: true,
}, {
name: "height 4096 (voting start), 5 votes",
params: mockMainNetParams,
@ -220,6 +281,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1871749598,
wantVote: 187174959,
wantTreasury: 311958266,
}, {
name: "height 4096 (voting start), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 4096,
numVotes: 5,
wantFull: 3119582664,
wantWork: 311958266,
wantVote: 499133226,
wantTreasury: 311958266,
useDCP0010: true,
}, {
name: "height 4096 (voting start), 4 votes",
params: mockMainNetParams,
@ -229,6 +300,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1497399678,
wantVote: 187174959,
wantTreasury: 249566612,
}, {
name: "height 4096 (voting start), 4 votes, use DCP0010",
params: mockMainNetParams,
height: 4096,
numVotes: 4,
wantFull: 3119582664,
wantWork: 249566612,
wantVote: 499133226,
wantTreasury: 249566612,
useDCP0010: true,
}, {
name: "height 4096 (voting start), 3 votes",
params: mockMainNetParams,
@ -238,6 +319,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1123049758,
wantVote: 187174959,
wantTreasury: 187174959,
}, {
name: "height 4096 (voting start), 3 votes, use DCP0010",
params: mockMainNetParams,
height: 4096,
numVotes: 3,
wantFull: 3119582664,
wantWork: 187174959,
wantVote: 499133226,
wantTreasury: 187174959,
useDCP0010: true,
}, {
name: "height 4096 (voting start), 2 votes",
params: mockMainNetParams,
@ -247,6 +338,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 0,
wantVote: 187174959,
wantTreasury: 0,
}, {
name: "height 4096 (voting start), 2 votes, use DCP0010",
params: mockMainNetParams,
height: 4096,
numVotes: 2,
wantFull: 3119582664,
wantWork: 0,
wantVote: 499133226,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 6143 (final block prior to 1st reduction), 5 votes",
params: mockMainNetParams,
@ -256,6 +357,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1871749598,
wantVote: 187174959,
wantTreasury: 311958266,
}, {
name: "height 6143 (final block prior to 1st reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 6143,
numVotes: 5,
wantFull: 3119582664,
wantWork: 311958266,
wantVote: 499133226,
wantTreasury: 311958266,
useDCP0010: true,
}, {
name: "height 6144 (1st block in 1st reduction), 5 votes",
params: mockMainNetParams,
@ -265,6 +376,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1853217423,
wantVote: 185321742,
wantTreasury: 308869570,
}, {
name: "height 6144 (1st block in 1st reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 6144,
numVotes: 5,
wantFull: 3088695706,
wantWork: 308869570,
wantVote: 494191312,
wantTreasury: 308869570,
useDCP0010: true,
}, {
name: "height 6144 (1st block in 1st reduction), 4 votes",
params: mockMainNetParams,
@ -274,6 +395,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1482573938,
wantVote: 185321742,
wantTreasury: 247095656,
}, {
name: "height 6144 (1st block in 1st reduction), 4 votes, use DCP0010",
params: mockMainNetParams,
height: 6144,
numVotes: 4,
wantFull: 3088695706,
wantWork: 247095656,
wantVote: 494191312,
wantTreasury: 247095656,
useDCP0010: true,
}, {
name: "height 12287 (last block in 1st reduction), 5 votes",
params: mockMainNetParams,
@ -283,6 +414,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1853217423,
wantVote: 185321742,
wantTreasury: 308869570,
}, {
name: "height 12287 (last block in 1st reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 12287,
numVotes: 5,
wantFull: 3088695706,
wantWork: 308869570,
wantVote: 494191312,
wantTreasury: 308869570,
useDCP0010: true,
}, {
name: "height 12288 (1st block in 2nd reduction), 5 votes",
params: mockMainNetParams,
@ -292,6 +433,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1834868736,
wantVote: 183486873,
wantTreasury: 305811456,
}, {
name: "height 12288 (1st block in 2nd reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 12288,
numVotes: 5,
wantFull: 3058114560,
wantWork: 305811456,
wantVote: 489298329,
wantTreasury: 305811456,
useDCP0010: true,
}, {
name: "height 307200 (1st block in 50th reduction), 5 votes",
params: mockMainNetParams,
@ -301,6 +452,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 1138096413,
wantVote: 113809641,
wantTreasury: 189682735,
}, {
name: "height 307200 (1st block in 50th reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 307200,
numVotes: 5,
wantFull: 1896827356,
wantWork: 189682735,
wantVote: 303492376,
wantTreasury: 189682735,
useDCP0010: true,
}, {
name: "height 307200 (1st block in 50th reduction), 3 votes",
params: mockMainNetParams,
@ -310,6 +471,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 682857847,
wantVote: 113809641,
wantTreasury: 113809641,
}, {
name: "height 307200 (1st block in 50th reduction), 3 votes, use DCP0010",
params: mockMainNetParams,
height: 307200,
numVotes: 3,
wantFull: 1896827356,
wantWork: 113809641,
wantVote: 303492376,
wantTreasury: 113809641,
useDCP0010: true,
}, {
name: "height 10911744 (first zero vote subsidy 1776th reduction), 5 votes",
params: mockMainNetParams,
@ -328,6 +499,26 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 5,
wantVote: 0,
wantTreasury: 0,
}, {
name: "height 10954752 (first zero work subsidy with DCP0010 1783rd reduction), 5 votes",
params: mockMainNetParams,
height: 10954752,
numVotes: 5,
wantFull: 9,
wantWork: 0,
wantVote: 1,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 10973184 (first zero vote subsidy with DCP0010 1786th reduction), 5 votes",
params: mockMainNetParams,
height: 10973184,
numVotes: 5,
wantFull: 6,
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
useDCP0010: true,
}, {
name: "height 11003904 (first zero work subsidy 1791st reduction), 5 votes",
params: mockMainNetParams,
@ -346,6 +537,16 @@ func TestSubsidyCacheCalcs(t *testing.T) {
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
}, {
name: "height 11010048 (first zero full subsidy 1792nd reduction), 5 votes, use DCP0010",
params: mockMainNetParams,
height: 11010048,
numVotes: 5,
wantFull: 0,
wantWork: 0,
wantVote: 0,
wantTreasury: 0,
useDCP0010: true,
}}
for _, test := range tests {
@ -359,7 +560,8 @@ func TestSubsidyCacheCalcs(t *testing.T) {
}
// Ensure the PoW subsidy is the expected value.
workResult := cache.CalcWorkSubsidy(test.height, test.numVotes)
workResult := cache.CalcWorkSubsidyV2(test.height, test.numVotes,
test.useDCP0010)
if workResult != test.wantWork {
t.Errorf("%s: unexpected work subsidy result -- got %d, want %d",
test.name, workResult, test.wantWork)
@ -367,7 +569,7 @@ func TestSubsidyCacheCalcs(t *testing.T) {
}
// Ensure the vote subsidy is the expected value.
voteResult := cache.CalcStakeVoteSubsidy(test.height)
voteResult := cache.CalcStakeVoteSubsidyV2(test.height, test.useDCP0010)
if voteResult != test.wantVote {
t.Errorf("%s: unexpected vote subsidy result -- got %d, want %d",
test.name, voteResult, test.wantVote)
@ -753,6 +955,76 @@ func TestTotalSubsidyTreasury(t *testing.T) {
}
}
// TestTotalSubsidyDCP0010 ensures the estimated total subsidy produced with the
// subsidy split defined in DCP0010 matches the expected value.
func TestTotalSubsidyDCP0010(t *testing.T) {
// Locals for convenience.
mockMainNetParams := mockMainNetParams()
reductionInterval := mockMainNetParams.SubsidyReductionIntervalBlocks()
stakeValidationHeight := mockMainNetParams.StakeValidationBeginHeight()
votesPerBlock := mockMainNetParams.VotesPerBlock()
// subsidySum returns the sum of the individual subsidies for the given
// height using either the original subsidy split or the modified split
// defined in DCP0010. Note that this value is not exactly the same as the
// full subsidy originally used to calculate the individual proportions due
// to the use of integer math.
cache := NewSubsidyCache(mockMainNetParams)
subsidySum := func(height int64, useDCP0010 bool) int64 {
work := cache.CalcWorkSubsidyV2(height, votesPerBlock, useDCP0010)
vote := cache.CalcStakeVoteSubsidyV2(height, useDCP0010) *
int64(votesPerBlock)
treasury := cache.CalcTreasurySubsidy(height, votesPerBlock, noTreasury)
return work + vote + treasury
}
// Calculate the total possible subsidy.
totalSubsidy := mockMainNetParams.BlockOneSubsidy()
for reductionNum := int64(0); ; reductionNum++ {
// The first interval contains a few special cases:
// 1) Block 0 does not produce any subsidy
// 2) Block 1 consists of a special initial coin distribution
// 3) Votes do not produce subsidy until voting begins
if reductionNum == 0 {
// Account for the block up to the point voting begins ignoring the
// first two special blocks.
subsidyCalcHeight := int64(2)
nonVotingBlocks := stakeValidationHeight - subsidyCalcHeight
totalSubsidy += subsidySum(subsidyCalcHeight, false) * nonVotingBlocks
// Account for the blocks remaining in the interval once voting
// begins.
subsidyCalcHeight = stakeValidationHeight
votingBlocks := reductionInterval - subsidyCalcHeight
totalSubsidy += subsidySum(subsidyCalcHeight, false) * votingBlocks
continue
}
// Account for the all other reduction intervals until all subsidy has
// been produced.
//
// Note that this is necessarily an estimate since the exact height at
// which DCP0010 should be activated is impossible to know at the time
// of this writing. For testing purposes, the activation height is
// estimated to be 638976, or in other words, the 104th reduction
// interval on mainnet.
subsidyCalcHeight := reductionNum * reductionInterval
useDCP0010 := subsidyCalcHeight >= reductionInterval*104
sum := subsidySum(subsidyCalcHeight, useDCP0010)
if sum == 0 {
break
}
totalSubsidy += sum * reductionInterval
}
// Ensure the total calculated subsidy is the expected value.
const expectedTotalSubsidy = 2100000000015952
if totalSubsidy != expectedTotalSubsidy {
t.Fatalf("mismatched total subsidy -- got %d, want %d", totalSubsidy,
expectedTotalSubsidy)
}
}
// TestCalcBlockSubsidySparseCaching ensures the cache calculations work
// properly when accessed sparsely and out of order.
func TestCalcBlockSubsidySparseCaching(t *testing.T) {