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