forkplan9helpers.go raw
1 package blockchain
2
3 import (
4 "github.com/VividCortex/ewma"
5 "github.com/p9c/p9/pkg/fork"
6
7 "github.com/p9c/p9/pkg/wire"
8 )
9
10 func (b *BlockChain) GetCommonP9Averages(lastNode *BlockNode, nH int32) (
11 allTimeAv float64,
12 allTimeDiv float64,
13 qhourDiv float64,
14 hourDiv float64,
15 dayDiv float64,
16 ) {
17 const minAvSamples = 2
18 allTimeAv, allTimeDiv, qhourDiv, hourDiv, dayDiv = 1.0, 1.0, 1.0, 1.0, 1.0
19 ttpb := float64(fork.List[1].TargetTimePerBlock)
20 startHeight := fork.List[1].ActivationHeight
21 if b.params.Net == wire.TestNet3 {
22 startHeight = fork.List[1].TestnetStart
23 }
24 if nH <= startHeight {
25 D.Ln("on hard fork", nH, startHeight)
26 return
27 }
28 var oldestStamp int64
29 f, _ := b.BlockByHeight(startHeight)
30 if f != nil {
31 fh := f.WireBlock().Header.BlockHash()
32 first := b.Index.LookupNode(&fh)
33 allTime := float64(lastNode.timestamp - first.timestamp)
34 allBlocks := float64(lastNode.height - first.height)
35 // time from lastNode timestamp until start
36 if allBlocks == 0 {
37 allBlocks = 1
38 }
39 allTimeAv = allTime / allBlocks
40 if allTimeAv > 0 {
41 allTimeDiv = allTimeAv / ttpb
42 } else {
43 allTimeDiv = float64(1)
44 }
45 allTimeDiv *= allTimeDiv * allTimeDiv * allTimeDiv * allTimeDiv
46 oldestStamp = f.WireBlock().Header.Timestamp.Unix()
47 } else {
48 // the previous if should prevent this occurring
49 }
50 allTimeDiv = capP9Adjustment(allTimeDiv)
51 oneHour := 60 * 60 / fork.List[1].TargetTimePerBlock
52 oneDay := oneHour * 24
53 qHour := 60 * 60 / fork.List[1].TargetTimePerBlock / 4
54 dayBlock := lastNode.RelativeAncestor(oneDay)
55 dayDiv = allTimeDiv
56 if dayBlock != nil {
57 // collect timestamps within averaging interval
58 dayStamps := []int64{lastNode.timestamp}
59 for ln := lastNode; ln != nil && ln.height > startHeight+2 &&
60 len(dayStamps) <= int(fork.List[1].AveragingInterval); {
61 ln = ln.RelativeAncestor(oneDay)
62 if ln == nil || ln.timestamp < oldestStamp || ln.height < startHeight {
63 break
64 }
65 dayStamps = append(dayStamps, ln.timestamp)
66 }
67 if len(dayStamps) > minAvSamples {
68 intervals := float64(0)
69 // calculate intervals
70 dayIntervals := []int64{}
71 for i := range dayStamps {
72 if i > 0 {
73 r := dayStamps[i-1] - dayStamps[i]
74 intervals++
75 dayIntervals = append(dayIntervals, r)
76 }
77 }
78 if intervals >= minAvSamples {
79 // calculate exponential weighted moving average from intervals
80 dw := ewma.NewMovingAverage()
81 for _, x := range dayIntervals {
82 dw.Add(float64(x))
83 }
84 dayDiv = capP9Adjustment(dw.Value() / ttpb / float64(oneDay))
85 }
86 }
87 }
88 hourBlock := lastNode.RelativeAncestor(oneHour)
89 hourDiv = allTimeDiv
90 if hourBlock != nil {
91 // collect timestamps within averaging interval
92 hourStamps := []int64{lastNode.timestamp}
93 for ln := lastNode; ln.height > startHeight+2 &&
94 len(hourStamps) <= int(fork.List[1].AveragingInterval); {
95 ln = ln.RelativeAncestor(oneHour)
96 if ln == nil || ln.timestamp < oldestStamp || ln.height < startHeight {
97 break
98 }
99 hourStamps = append(hourStamps, ln.timestamp)
100 }
101 if len(hourStamps) > minAvSamples {
102 intervals := float64(0)
103 // calculate intervals
104 hourIntervals := []int64{}
105 for i := range hourStamps {
106 if i > 0 {
107 r := hourStamps[i-1] - hourStamps[i]
108 intervals++
109 hourIntervals = append(hourIntervals, r)
110 }
111 }
112 if intervals >= minAvSamples {
113 // calculate exponential weighted moving average from intervals
114 hw := ewma.NewMovingAverage()
115 for _, x := range hourIntervals {
116 hw.Add(float64(x))
117 }
118 hourDiv = capP9Adjustment(hw.Value() / ttpb / float64(oneHour))
119 }
120 }
121 }
122 qhourBlock := lastNode.RelativeAncestor(qHour)
123 qhourDiv = allTimeDiv
124 if qhourBlock != nil {
125 // collect timestamps within averaging interval
126 qhourStamps := []int64{lastNode.timestamp}
127 for ln := lastNode; ln != nil && ln.height > startHeight &&
128 len(qhourStamps) <= int(fork.List[1].AveragingInterval); {
129 ln = ln.RelativeAncestor(qHour)
130 if ln == nil || ln.timestamp < oldestStamp || ln.height < startHeight {
131 break
132 }
133 qhourStamps = append(qhourStamps, ln.timestamp)
134 }
135 if len(qhourStamps) > minAvSamples {
136 intervals := float64(0)
137 // calculate intervals
138 qhourIntervals := []uint64{}
139 for i := range qhourStamps {
140 if i > 0 {
141 r := uint64(qhourStamps[i-1]) - uint64(qhourStamps[i])
142 intervals++
143 qhourIntervals = append(qhourIntervals, r)
144 }
145 }
146 if intervals >= minAvSamples {
147 // calculate exponential weighted moving average from intervals
148 qhw := ewma.NewMovingAverage()
149 for _, x := range qhourIntervals {
150 qhw.Add(float64(x))
151 }
152 qhourDiv = capP9Adjustment(qhw.Value() / ttpb / float64(qHour))
153 }
154 }
155 }
156 return
157 }
158
159 func (b *BlockChain) GetP9AlgoDiv(
160 allTimeDiv float64,
161 last *BlockNode,
162 startHeight int32,
163 algoVer int32,
164 ttpb float64,
165 ) (algDiv float64) {
166 const minAvSamples = 9
167 // collect timestamps of same algo of equal number as avinterval
168 algDiv = allTimeDiv
169 algStamps := []uint64{uint64(last.timestamp)}
170 for ln := last; ln != nil && ln.height > startHeight &&
171 len(algStamps) <= int(fork.List[1].AveragingInterval); ln = ln.
172 RelativeAncestor(1) {
173 if ln.version == algoVer && ln.height > startHeight {
174 algStamps = append(algStamps, uint64(ln.timestamp))
175 }
176 }
177 if len(algStamps) > minAvSamples {
178 intervals := float64(0)
179 // calculate intervals
180 algIntervals := []uint64{}
181 for i := range algStamps {
182 if i > 0 {
183 r := algStamps[i-1] - algStamps[i]
184 intervals++
185 algIntervals = append(algIntervals, r)
186 }
187 }
188 if intervals >= minAvSamples {
189 // calculate exponential weighted moving average from intervals
190 awi := ewma.NewMovingAverage()
191 for _, x := range algIntervals {
192 awi.Add(float64(x))
193 }
194 algDiv = capP9Adjustment(awi.Value() / ttpb / float64(len(fork.P9Algos)))
195 }
196 }
197 return
198 }
199
200 func (b *BlockChain) GetP9Since(lastNode *BlockNode, algoVer int32) (
201 since float64,
202 ttpb float64,
203 timeSinceAlgo float64,
204 startHeight int32,
205 last *BlockNode,
206 ) {
207 last = lastNode
208 // find the most recent block of the same algo
209 ln := last
210 for ln.version != algoVer {
211 ln = ln.RelativeAncestor(1)
212 // if it found nothing, return baseline
213 if ln == nil {
214 last = nil
215 return
216 }
217 last = ln
218 }
219 since = float64(lastNode.timestamp - last.timestamp)
220 ttpb = float64(fork.List[1].TargetTimePerBlock)
221 tspb := ttpb * float64(len(fork.List[1].Algos))
222 // ratio of seconds since to target seconds per block times the all time divergence ensures the change scales with
223 // the divergence from the target, and favours algos that are later
224 timeSinceAlgo = capP9Adjustment((since / tspb) / 5)
225
226 return
227 }
228
229 func (b *BlockChain) IsP9HardFork(nH int32) bool {
230 // At activation difficulty resets
231 switch b.params.Net {
232 case wire.MainNet:
233 if fork.List[1].ActivationHeight == nH {
234 return true
235 }
236 case wire.TestNet3:
237 if fork.List[1].TestnetStart == nH {
238 return true
239 }
240 }
241 return false
242 }
243
244 func capP9Adjustment(adjustment float64) float64 {
245 const max float64 = 65536
246 const maxA, minA = max, 1 / max
247 if adjustment > maxA {
248 adjustment = maxA
249 }
250 if adjustment < minA {
251 adjustment = minA
252 }
253 return adjustment
254 }
255