-
Notifications
You must be signed in to change notification settings - Fork 128
/
subsidy.go
140 lines (120 loc) · 4.37 KB
/
subsidy.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (c) 2015-2022, The Decred developers
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txhelpers
import (
"math"
"sync"
"github.com/decred/dcrd/blockchain/standalone/v2"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/dcrd/wire"
)
type netHeight struct {
net uint32
dcp10height int64
}
type subsidySumCache struct {
sync.RWMutex
m map[netHeight]int64
}
func (ssc *subsidySumCache) load(nh netHeight) (int64, bool) {
ssc.RLock()
defer ssc.RUnlock()
total, ok := ssc.m[nh]
return total, ok
}
func (ssc *subsidySumCache) store(nh netHeight, total int64) {
ssc.Lock()
defer ssc.Unlock()
ssc.m[nh] = total
}
// ultimateSubsidies stores ultimate subsidy values computed by UltimateSubsidy.
var ultimateSubsidies = subsidySumCache{m: map[netHeight]int64{}}
// UltimateSubsidy computes the total subsidy over the entire subsidy
// distribution period of the network, given a known height at which the subsidy
// split change goes into effect (DCP0010). If the height is unknown, provide -1
// to perform the computation with the original subsidy split for all heights.
func UltimateSubsidy(params *chaincfg.Params, subsidySplitChangeHeight int64) int64 {
// Check previously computed ultimate subsidies.
nh := netHeight{uint32(params.Net), subsidySplitChangeHeight}
result, ok := ultimateSubsidies.load(nh)
if ok {
return result
}
if subsidySplitChangeHeight == -1 {
subsidySplitChangeHeight = math.MaxInt64
}
votesPerBlock := params.VotesPerBlock()
stakeValidationHeight := params.StakeValidationBeginHeight()
reductionInterval := params.SubsidyReductionIntervalBlocks()
subsidyCache := networkSubsidyCache(params)
subsidySum := func(height int64) int64 {
useDCP0010 := height >= subsidySplitChangeHeight
work := subsidyCache.CalcWorkSubsidyV2(height, votesPerBlock, useDCP0010)
vote := subsidyCache.CalcStakeVoteSubsidyV2(height, useDCP0010) * int64(votesPerBlock)
// With voters set to max (votesPerBlock), treasury bool is unimportant.
treasury := subsidyCache.CalcTreasurySubsidy(height, votesPerBlock, false)
return work + vote + treasury
}
totalSubsidy := params.BlockOneSubsidy()
for i := int64(0); ; i++ {
// 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 i == 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) * nonVotingBlocks
// Account for the blocks remaining in the interval once voting
// begins.
subsidyCalcHeight = stakeValidationHeight
votingBlocks := reductionInterval - subsidyCalcHeight
totalSubsidy += subsidySum(subsidyCalcHeight) * votingBlocks
continue
}
// Account for the all other reduction intervals until all subsidy has
// been produced.
subsidyCalcHeight := i * reductionInterval
sum := subsidySum(subsidyCalcHeight)
if sum == 0 {
break
}
totalSubsidy += sum * reductionInterval
}
// Update the ultimate subsidy store.
ultimateSubsidies.store(nh, totalSubsidy)
return totalSubsidy
}
var scs = struct {
sync.Mutex
m map[wire.CurrencyNet]*standalone.SubsidyCache
}{
m: map[wire.CurrencyNet]*standalone.SubsidyCache{},
}
func networkSubsidyCache(p *chaincfg.Params) *standalone.SubsidyCache {
scs.Lock()
defer scs.Unlock()
sc, ok := scs.m[p.Net]
if ok {
return sc
}
sc = standalone.NewSubsidyCache(p)
scs.m[p.Net] = sc
return sc
}
// RewardsAtBlock computes the PoW, PoS (per vote), and project fund subsidies
// at for the specified block index, assuming a certain number of votes. The
// stake reward is for a single vote. The total reward for the block is thus
// work + stake * votes + tax.
func RewardsAtBlock(blockIdx int64, votes uint16, p *chaincfg.Params, useDCP0010 bool) (work, stake, tax int64) {
subsidyCache := networkSubsidyCache(p)
work = subsidyCache.CalcWorkSubsidyV2(blockIdx, votes, useDCP0010)
stake = subsidyCache.CalcStakeVoteSubsidyV2(blockIdx, useDCP0010)
treasuryActive := IsTreasuryActive(p.Net, blockIdx)
tax = subsidyCache.CalcTreasurySubsidy(blockIdx, votes, treasuryActive)
return
}