fork.go raw
1 // Package fork handles tracking the hard fork status and is used to determine which consensus rules apply on a block
2 package fork
3
4 import (
5 "encoding/hex"
6 "fmt"
7 "github.com/p9c/p9/pkg/bits"
8 "github.com/p9c/p9/pkg/log"
9 "math/big"
10 "math/rand"
11 "sort"
12 "time"
13 )
14
15 const (
16 Scrypt = "scrypt"
17 SHA256d = "sha256d"
18 )
19
20 // AlgoParams are the identifying block version number and their minimum target bits
21 type AlgoParams struct {
22 Version int32
23 MinBits uint32
24 AlgoID uint32
25 VersionInterval int
26 }
27
28 // HardForks is the details related to a hard fork, number, name and activation height
29 type HardForks struct {
30 Number int
31 ActivationHeight int32
32 Name string
33 Algos map[string]AlgoParams
34 AlgoVers map[int32]string
35 TargetTimePerBlock int32
36 AveragingInterval int32
37 TestnetStart int32
38 }
39
40 type AlgoSpec struct {
41 Version int32
42 Name string
43 }
44 type AlgoSpecs []AlgoSpec
45
46 func (a AlgoSpecs) Len() int {
47 return len(a)
48 }
49
50 func (a AlgoSpecs) Less(i, j int) bool {
51 return a[i].Version > a[j].Version
52 }
53
54 func (a AlgoSpecs) Swap(i, j int) {
55 a[i], a[j] = a[j], a[i]
56 }
57
58 func init() {
59 ForkCalc()
60 }
61
62 var done bool
63
64 func ForkCalc() {
65 if done {
66 I.Ln(log.Caller("called again", 1))
67 return
68 }
69 done = true
70 T.Ln("running fork data init")
71 for i := range P9AlgosNumeric {
72 List[1].AlgoVers[i] = fmt.Sprintf("Div%d", P9AlgosNumeric[i].VersionInterval)
73 }
74 for i, v := range P9AlgoVers {
75 List[1].Algos[v] = P9AlgosNumeric[i]
76 }
77 AlgoSlices = append(AlgoSlices, AlgoSpecs{})
78 for i := range Algos {
79 AlgoSlices[0] = append(
80 AlgoSlices[0], AlgoSpec{
81 List[0].Algos[i].Version,
82 i,
83 },
84 )
85 }
86 AlgoSlices = append(AlgoSlices, AlgoSpecs{})
87 for i := range P9Algos {
88 AlgoSlices[1] = append(
89 AlgoSlices[1], AlgoSpec{
90 List[1].Algos[i].Version,
91 i,
92 },
93 )
94 }
95 sort.Sort(AlgoSlices[0])
96 sort.Sort(AlgoSlices[1])
97 D.Ln(P9AlgoVers)
98 baseVersionName := AlgoSlices[1][0].Name
99 baseVersionInterval := float64(P9Algos[baseVersionName].VersionInterval)
100 D.Ln(baseVersionName, baseVersionInterval)
101 P9Average = 0
102 for _, i := range AlgoSlices[1] {
103 vi := float64(P9Algos[i.Name].VersionInterval)
104 p9a := baseVersionInterval / vi
105 P9Average += p9a
106 // Tracef("P9Average %4.4f %4.4f %d %4.4f", p9a, P9Average, IntervalBase, vi)
107 }
108 D.Ln(P9Average)
109 P9Average = baseVersionInterval / P9Average
110 D.Ln(P9Average)}
111
112 var (
113 AlgoSlices []AlgoSpecs
114 // AlgoVers is the lookup for pre hardfork
115 //
116 AlgoVers = map[int32]string{
117 2: SHA256d,
118 514: Scrypt,
119 }
120 // Algos are the specifications identifying the algorithm used in the
121 // block proof
122 Algos = map[string]AlgoParams{
123 AlgoVers[2]: {
124 Version: 2,
125 MinBits: MainPowLimitBits,
126 },
127 AlgoVers[514]: {
128 Version: 514,
129 MinBits: MainPowLimitBits,
130 AlgoID: 1,
131 },
132 }
133 // FirstPowLimit is
134 FirstPowLimit = func() big.Int {
135 mplb, _ := hex.DecodeString(
136 "0fffff0000000000000000000000000000000000000000000000000000000000",
137 )
138 return *big.NewInt(0).SetBytes(mplb)
139 }()
140 // FirstPowLimitBits is
141 FirstPowLimitBits = bits.BigToCompact(&FirstPowLimit)
142
143 p9PowLimit = func() big.Int {
144 mplb, _ := hex.DecodeString(
145 // "0fffff0000000000000000000000000000000000000000000000000000000000",
146 "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
147 )
148 return *big.NewInt(0).SetBytes(mplb)
149 }()
150 p9PowLimitBits = bits.BigToCompact(&p9PowLimit)
151 // IsTestnet is set at startup here to be accessible to all other libraries
152 IsTestnet bool
153 // List is the list of existing hard forks and when they activate
154 List = []HardForks{
155 {
156 Number: 0,
157 Name: "Halcyon days",
158 ActivationHeight: 0,
159 Algos: Algos,
160 AlgoVers: AlgoVers,
161 TargetTimePerBlock: 300,
162 AveragingInterval: 10, // 50 minutes
163 TestnetStart: 0,
164 },
165 {
166 Number: 1,
167 Name: "Plan 9 from Crypto Space",
168 ActivationHeight: 2500000,
169 Algos: P9Algos,
170 AlgoVers: P9AlgoVers,
171 TargetTimePerBlock: 36,
172 AveragingInterval: 3600,
173 TestnetStart: 0,
174 },
175 }
176 // P9AlgoVers is the lookup for after 1st hardfork
177 P9AlgoVers = make(map[int32]string)
178
179 // P9PrimeSequence = []int{2, 5, 11, 7, 11, 13, 17, 19, 23}
180 // 2, .3, .5, 7, .11, 13, .17, 19, 23, 29, .31, 37, .41, 43, 47, 53, .59, 61, .67, 71, 73, 79, .83, 89, 97
181 P9PrimeSequence = []int{2, 4, 8, 16, 32, 64, 128, 256, 512}
182 IntervalDivisor = 1
183 IntervalBase = 9
184 // P9Algos is the algorithm specifications after the hard fork
185 P9Algos = make(map[string]AlgoParams)
186 P9AlgosNumeric = map[int32]AlgoParams{
187 5: {5, p9PowLimitBits, 0, IntervalBase * P9PrimeSequence[0] / IntervalDivisor}, // 2
188 6: {6, p9PowLimitBits, 1, IntervalBase * P9PrimeSequence[1] / IntervalDivisor}, // 3
189 7: {7, p9PowLimitBits, 2, IntervalBase * P9PrimeSequence[2] / IntervalDivisor}, // 5
190 8: {8, p9PowLimitBits, 3, IntervalBase * P9PrimeSequence[3] / IntervalDivisor}, // 7
191 9: {9, p9PowLimitBits, 4, IntervalBase * P9PrimeSequence[4] / IntervalDivisor}, // 11
192 10: {10, p9PowLimitBits, 5, IntervalBase * P9PrimeSequence[5] / IntervalDivisor}, // 13
193 11: {11, p9PowLimitBits, 7, IntervalBase * P9PrimeSequence[7] / IntervalDivisor}, // 17
194 12: {12, p9PowLimitBits, 6, IntervalBase * P9PrimeSequence[6] / IntervalDivisor}, // 19
195 13: {13, p9PowLimitBits, 8, IntervalBase * P9PrimeSequence[8] / IntervalDivisor}, // 23
196 }
197
198 P9Average float64
199
200 // SecondPowLimit is
201 SecondPowLimit = func() big.Int {
202 mplb, _ := hex.DecodeString(
203 // "01f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1")
204 "0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
205 )
206 return *big.NewInt(0).SetBytes(mplb)
207 }()
208 SecondPowLimitBits = bits.BigToCompact(&SecondPowLimit)
209 MainPowLimit = func() big.Int {
210 mplb, _ := hex.DecodeString(
211 "00000fffff000000000000000000000000000000000000000000000000000000",
212 )
213 return *big.NewInt(0).SetBytes(mplb)
214 }()
215 MainPowLimitBits = bits.BigToCompact(&MainPowLimit)
216 )
217
218 // GetAlgoID returns the 'algo_id' which in pre-hardfork is not the same as the block version number, but is afterwards
219 func GetAlgoID(algoname string, height int32) uint32 {
220 if GetCurrent(height) > 1 {
221 return P9Algos[algoname].AlgoID
222 }
223 return Algos[algoname].AlgoID
224 }
225
226 // GetAlgoName returns the string identifier of an algorithm depending on
227 // hard fork activation status
228 func GetAlgoName(algoVer int32, height int32) (name string) {
229 hf := GetCurrent(height)
230 var ok bool
231 name, ok = List[hf].AlgoVers[algoVer]
232 if hf < 1 && !ok {
233 name = SHA256d
234 }
235 // I.Ln("GetAlgoName", algoVer, height, name}
236 return
237 }
238
239 // GetRandomVersion returns a random version relevant to the current hard fork state and height
240 func GetRandomVersion(height int32) int32 {
241 rand.Seed(time.Now().UnixNano())
242 return int32(rand.Intn(len(List[GetCurrent(height)].Algos)) + 5)
243 }
244
245 // GetAlgoVer returns the version number for a given algorithm (by string name) at a given height. If "random" is given,
246 // a random number is taken from the system secure random source (for randomised cpu mining)
247 func GetAlgoVer(name string, height int32) (version int32) {
248 hf := GetCurrent(height)
249 n := AlgoSlices[hf][0].Name
250 // D.Ln("GetAlgoVer", name, height, hf, n)
251 if _, ok := List[hf].Algos[name]; ok {
252 n = name
253 }
254 version = List[hf].Algos[n].Version
255 return
256 }
257
258 var algoVerSlice [][]int32
259
260 func GetAlgoVerSlice(height int32) (o []int32) {
261 hf := GetCurrent(height)
262 if algoVerSlice == nil {
263 algoVerSlice = make([][]int32, 0, len(List))
264 for i := range List {
265 av := make([]int32, 0, len(List[i].AlgoVers))
266 for j := range List[i].AlgoVers {
267 av = append(av, j)
268 }
269 algoVerSlice = append(algoVerSlice, av)
270 }
271 }
272 // D.S(algoVerSlice)
273 return algoVerSlice[hf]
274 }
275
276 // AlgoVerIterator returns a next and more function to use in a for loop to
277 // iterate over block versions at current height
278 func AlgoVerIterator(height int32) (next func(), curr func() int32, more func() bool) {
279 current := GetCurrent(height)
280 var cursor int32
281 length := int32(GetNumAlgos(height))
282 var verNumbers []int32
283 for i := range List[current].AlgoVers {
284 verNumbers = append(verNumbers, List[current].Algos[List[current].AlgoVers[i]].Version)
285 }
286 curr = func() int32 {
287 return verNumbers[cursor]
288 }
289 more = func() bool {
290 return cursor < length
291 }
292 next = func() {
293 if more() {
294 cursor++
295 }
296 }
297
298 return
299 }
300
301 // GetAlgos returns the map of names and algorithm parameters
302 func GetAlgos(height int32) (o map[string]AlgoParams) {
303 current := GetCurrent(height)
304 for i := range List {
305 if List[i].Number == current {
306 o = List[i].Algos
307 break
308 }
309 }
310 return
311 }
312
313 // GetNumAlgos returns the number of algos at a given height
314 func GetNumAlgos(height int32) (numAlgos int) {
315 current := GetCurrent(height)
316 for i := range List {
317 if List[i].Number == current {
318 numAlgos = len(List[i].Algos)
319 break
320 }
321 }
322 return
323 }
324
325 // GetAveragingInterval returns the active block interval target based on hard fork status
326 func GetAveragingInterval(height int32) (r int32) {
327 r = List[GetCurrent(height)].AveragingInterval
328 return
329 }
330
331 // GetCurrent returns the hardfork number code
332 func GetCurrent(height int32) (curr int) {
333 // F.Ln("istestnet", IsTestnet)
334 if IsTestnet {
335 for i := range List {
336 if height >= List[i].TestnetStart {
337 curr = i
338 }
339 }
340 } else {
341 for i := range List {
342 if height >= List[i].ActivationHeight {
343 curr = i
344 }
345 }
346 }
347 return
348 }
349
350 // GetMinBits returns the minimum diff bits based on height and testnet
351 func GetMinBits(algoname string, height int32) (mb uint32) {
352 curr := GetCurrent(height)
353 // F.Ln("GetMinBits", algoname, height, curr, List[curr].Algos)
354 mb = List[curr].Algos[algoname].MinBits
355 // TraceF("minbits %08x, %d", mb, mb)
356 return
357 }
358
359 // GetMinDiff returns the minimum difficulty in uint256 form
360 func GetMinDiff(algoname string, height int32) (md *big.Int) {
361 // F.Ln("GetMinDiff", algoname)
362 minbits := GetMinBits(algoname, height)
363 // TraceF("mindiff minbits %08x", minbits)
364 return bits.CompactToBig(minbits)
365 }
366
367 // GetTargetTimePerBlock returns the active block interval target based on hard fork status
368 func GetTargetTimePerBlock(height int32) (r int64) {
369 r = int64(List[GetCurrent(height)].TargetTimePerBlock)
370 return
371 }
372