1 package blockchain
2 3 import (
4 "fmt"
5 6 "github.com/p9c/p9/pkg/chainhash"
7 )
8 9 // ThresholdState define the various threshold states used when voting on consensus changes.
10 type ThresholdState byte
11 12 // These constants are used to identify specific threshold states.
13 const (
14 // ThresholdDefined is the first state for each deployment and is the state for the genesis block has by definition
15 // for all deployments.
16 ThresholdDefined ThresholdState = iota
17 // ThresholdStarted is the state for a deployment once its start time has been reached.
18 ThresholdStarted
19 // ThresholdLockedIn is the state for a deployment during the retarget period which is after the ThresholdStarted
20 // state period and the number of blocks that have voted for the deployment equal or exceed the required number of
21 // votes for the deployment.
22 ThresholdLockedIn
23 // ThresholdActive is the state for a deployment for all blocks after a retarget period in which the deployment was
24 // in the ThresholdLockedIn state.
25 ThresholdActive
26 // ThresholdFailed is the state for a deployment once its expiration time has been reached and it did not reach the
27 // ThresholdLockedIn state.
28 ThresholdFailed
29 // numThresholdsStates is the maximum number of threshold states used in tests.
30 numThresholdsStates
31 )
32 33 // thresholdStateStrings is a map of ThresholdState values back to their constant names for pretty printing.
34 var thresholdStateStrings = map[ThresholdState]string{
35 ThresholdDefined: "ThresholdDefined",
36 ThresholdStarted: "ThresholdStarted",
37 ThresholdLockedIn: "ThresholdLockedIn",
38 ThresholdActive: "ThresholdActive",
39 ThresholdFailed: "ThresholdFailed",
40 }
41 42 // String returns the ThresholdState as a human-readable name.
43 func (t ThresholdState) String() string {
44 if s := thresholdStateStrings[t]; s != "" {
45 return s
46 }
47 return fmt.Sprintf("Unknown ThresholdState (%d)", int(t))
48 }
49 50 // thresholdConditionChecker provides a generic interface that is invoked to determine when a consensus rule change
51 // threshold should be changed.
52 type thresholdConditionChecker interface {
53 // BeginTime returns the unix timestamp for the median block time after which voting on a rule change starts (at the
54 // next window).
55 BeginTime() uint64
56 // EndTime returns the unix timestamp for the median block time after which an attempted rule change fails if it has
57 // not already been locked in or activated.
58 EndTime() uint64
59 // RuleChangeActivationThreshold is the number of blocks for which the condition must be true in order to lock in a
60 // rule change.
61 RuleChangeActivationThreshold() uint32
62 // MinerConfirmationWindow is the number of blocks in each threshold state retarget window.
63 MinerConfirmationWindow() uint32
64 // Condition returns whether or not the rule change activation condition has been met. This typically involves
65 // checking whether or not the bit associated with the condition is set, but can be more complex as needed.
66 Condition(*BlockNode) (bool, error)
67 }
68 69 // thresholdStateCache provides a type to cache the threshold states of each threshold window for a set of IDs.
70 type thresholdStateCache struct {
71 entries map[chainhash.Hash]ThresholdState
72 }
73 74 // Lookup returns the threshold state associated with the given hash along with a boolean that indicates whether or not
75 // it is valid.
76 func (c *thresholdStateCache) Lookup(hash *chainhash.Hash) (ThresholdState, bool) {
77 state, ok := c.entries[*hash]
78 return state, ok
79 }
80 81 // Update updates the cache to contain the provided hash to threshold state mapping.
82 func (c *thresholdStateCache) Update(hash *chainhash.Hash, state ThresholdState) {
83 c.entries[*hash] = state
84 }
85 86 // newThresholdCaches returns a new array of caches to be used when calculating threshold states.
87 func newThresholdCaches(numCaches uint32) []thresholdStateCache {
88 caches := make([]thresholdStateCache, numCaches)
89 for i := 0; i < len(caches); i++ {
90 caches[i] = thresholdStateCache{
91 entries: make(map[chainhash.Hash]ThresholdState),
92 }
93 }
94 return caches
95 }
96 97 //
98 // // thresholdState returns the current rule change threshold state for the block AFTER the given node and deployment ID.
99 // // The cache is used to ensure the threshold states for previous windows are only calculated once. This function MUST be
100 // // called with the chain state lock held (for writes).
101 // func (b *BlockChain) thresholdState(
102 // prevNode *BlockNode,
103 // checker thresholdConditionChecker,
104 // cache *thresholdStateCache,
105 // ) (ThresholdState, error) {
106 // // The threshold state for the window that contains the genesis block is defined by definition.
107 // confirmationWindow := int32(checker.MinerConfirmationWindow())
108 // if prevNode == nil || (prevNode.height+1) < confirmationWindow {
109 // return ThresholdDefined, nil
110 // }
111 // // Get the ancestor that is the last block of the previous confirmation window in order to get its threshold state.
112 // // This can be done because the state is the same for all blocks within a given window.
113 // prevNode = prevNode.Ancestor(
114 // prevNode.height -
115 // (prevNode.height+1)%confirmationWindow,
116 // )
117 // // Iterate backwards through each of the previous confirmation windows to find the most recently cached threshold state.
118 // var neededStates []*BlockNode
119 // for prevNode != nil {
120 // // Nothing more to do if the state of the block is already cached.
121 // if _, ok := cache.Lookup(&prevNode.hash); ok {
122 // break
123 // }
124 // // The start and expiration times are based on the median block time, so calculate it now.
125 // medianTime := prevNode.CalcPastMedianTime()
126 // // The state is simply defined if the start time hasn't been been reached yet.
127 // if uint64(medianTime.Unix()) < checker.BeginTime() {
128 // cache.Update(&prevNode.hash, ThresholdDefined)
129 // break
130 // }
131 // // Add this node to the list of nodes that need the state calculated and cached.
132 // neededStates = append(neededStates, prevNode)
133 // // Get the ancestor that is the last block of the previous confirmation window.
134 // prevNode = prevNode.RelativeAncestor(confirmationWindow)
135 // }
136 // // Start with the threshold state for the most recent confirmation window that has a cached state.
137 // state := ThresholdDefined
138 // if prevNode != nil {
139 // var ok bool
140 // state, ok = cache.Lookup(&prevNode.hash)
141 // if !ok {
142 // return ThresholdFailed, AssertError(
143 // fmt.Sprintf(
144 // "thresholdState: cache lookup failed for %v",
145 // prevNode.hash,
146 // ),
147 // )
148 // }
149 // }
150 // // Since each threshold state depends on the state of the previous window, iterate starting from the oldest unknown
151 // // window.
152 // for neededNum := len(neededStates) - 1; neededNum >= 0; neededNum-- {
153 // prevNode := neededStates[neededNum]
154 // switch state {
155 // case ThresholdDefined:
156 // // The deployment of the rule change fails if it expires before it is accepted and locked in.
157 // medianTime := prevNode.CalcPastMedianTime()
158 // medianTimeUnix := uint64(medianTime.Unix())
159 // if medianTimeUnix >= checker.EndTime() {
160 // state = ThresholdFailed
161 // break
162 // }
163 // // The state for the rule moves to the started state once its start time has been reached (and it hasn't
164 // // already expired per the above).
165 // if medianTimeUnix >= checker.BeginTime() {
166 // state = ThresholdStarted
167 // }
168 // case ThresholdStarted:
169 // // The deployment of the rule change fails if it expires before it is accepted and locked in.
170 // medianTime := prevNode.CalcPastMedianTime()
171 // if uint64(medianTime.Unix()) >= checker.EndTime() {
172 // state = ThresholdFailed
173 // break
174 // }
175 // // At this point, the rule change is still being voted on by the miners, so iterate backwards through the
176 // // confirmation window to count all of the votes in it.
177 // var count uint32
178 // countNode := prevNode
179 // for i := int32(0); i < confirmationWindow; i++ {
180 // condition, e := checker.Condition(countNode)
181 // if e != nil {
182 // return ThresholdFailed, e
183 // }
184 // if condition {
185 // count++
186 // }
187 // // Get the previous block node.
188 // countNode = countNode.parent
189 // }
190 // // The state is locked in if the number of blocks in the period that voted for the rule change meets the
191 // // activation threshold.
192 // if count >= checker.RuleChangeActivationThreshold() {
193 // state = ThresholdLockedIn
194 // }
195 // case ThresholdLockedIn:
196 // // The new rule becomes active when its previous state was locked in.
197 // state = ThresholdActive
198 // // Nothing to do if the previous state is active or failed since they are both terminal states.
199 // case ThresholdActive:
200 // case ThresholdFailed:
201 // }
202 // // Update the cache to avoid recalculating the state in the future.
203 // cache.Update(&prevNode.hash, state)
204 // }
205 // return state, nil
206 // }
207 208 // // ThresholdState returns the current rule change threshold state of the given deployment ID for the block AFTER the end
209 // // of the current best chain.
210 // //
211 // // This function is safe for concurrent access.
212 // func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
213 // b.chainLock.Lock()
214 // state, e := b.deploymentState(b.BestChain.Tip(), deploymentID)
215 // b.chainLock.Unlock()
216 // return state, err
217 // }
218 219 // // IsDeploymentActive returns true if the target deploymentID is active, and false otherwise.
220 // //
221 // // This function is safe for concurrent access.
222 // func (b *BlockChain) IsDeploymentActive(deploymentID uint32) (bool, error) {
223 // b.chainLock.Lock()
224 // state, e := b.deploymentState(b.BestChain.Tip(), deploymentID)
225 // b.chainLock.Unlock()
226 // if e != nil {
227 // // return false, err
228 // }
229 // return state == ThresholdActive, nil
230 // }
231 232 // // deploymentState returns the current rule change threshold for a given deploymentID. The threshold is evaluated from
233 // // the point of view of the block node passed in as the first argument to this method. It is important to note that, as
234 // // the variable name indicates, this function expects the block node prior to the block for which the deployment state
235 // // is desired. In other words, the returned deployment state is for the block AFTER the passed node.
236 // //
237 // // This function MUST be called with the chain state lock held (for writes).
238 // func (b *BlockChain) deploymentState(prevNode *BlockNode, deploymentID uint32) (ThresholdState, error) {
239 // if deploymentID > uint32(len(b.params.Deployments)) {
240 // return ThresholdFailed, DeploymentError(deploymentID)
241 // }
242 // deployment := &b.params.Deployments[deploymentID]
243 // checker := deploymentChecker{deployment: deployment, chain: b}
244 // cache := &b.deploymentCaches[deploymentID]
245 // return b.thresholdState(prevNode, checker, cache)
246 // }
247 248 // initThresholdCaches initializes the threshold state caches for each warning bit and defined deployment and provides
249 // // warnings if the chain is current per the warnUnknownVersions and warnUnknownRuleActivations functions.
250 // func (b *BlockChain) initThresholdCaches() (e error) {
251 // // Initialize the warning and deployment caches by calculating the threshold state for each of them. This will
252 // // ensure the caches are populated and any states that needed to be recalculated due to definition changes is done
253 // // now.
254 // prevNode := b.BestChain.Tip().parent
255 // for bit := uint32(0); bit < vbNumBits; bit++ {
256 // checker := bitConditionChecker{bit: bit, chain: b}
257 // cache := &b.warningCaches[bit]
258 // _, e := b.thresholdState(prevNode, checker, cache)
259 // if e != nil {
260 // // return e
261 // }
262 // }
263 // for id := 0; id < len(b.params.Deployments); id++ {
264 // deployment := &b.params.Deployments[id]
265 // cache := &b.deploymentCaches[id]
266 // checker := deploymentChecker{deployment: deployment, chain: b}
267 // _, e := b.thresholdState(prevNode, checker, cache)
268 // if e != nil {
269 // // return e
270 // }
271 // }
272 // // No warnings about unknown rules or versions until the chain is current.
273 // if b.isCurrent() {
274 // // Warn if a high enough percentage of the last blocks have unexpected versions.
275 // bestNode := b.BestChain.Tip()
276 // // if e := b.warnUnknownVersions(bestNode); E.Chk(e) {
277 // // return e
278 // // }
279 // //
280 // // Warn if any unknown new rules are either about to activate or have already been activated.
281 // if e := b.warnUnknownRuleActivations(bestNode); E.Chk(e) {
282 // return e
283 // }
284 // }
285 // return nil
286 // }
287