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