github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/coinparam/difficulty.go (about)

     1  package coinparam
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"math/big"
     7  
     8  	"github.com/mit-dci/lit/logging"
     9  	"github.com/mit-dci/lit/wire"
    10  )
    11  
    12  /* calcDiff returns a bool given two block headers.  This bool is
    13  true if the correct difficulty adjustment is seen in the "next" header.
    14  Only feed it headers n-2016 and n-1, otherwise it will calculate a difficulty
    15  when no adjustment should take place, and return false.
    16  Note that the epoch is actually 2015 blocks long, which is confusing. */
    17  func calcDiffAdjustBitcoin(start, end *wire.BlockHeader, p *Params) uint32 {
    18  
    19  	duration := end.Timestamp.Unix() - start.Timestamp.Unix()
    20  
    21  	minRetargetTimespan :=
    22  		int64(p.TargetTimespan.Seconds()) / p.RetargetAdjustmentFactor
    23  	maxRetargetTimespan :=
    24  		int64(p.TargetTimespan.Seconds()) * p.RetargetAdjustmentFactor
    25  
    26  	if duration < minRetargetTimespan {
    27  		duration = minRetargetTimespan
    28  	} else if duration > maxRetargetTimespan {
    29  		duration = maxRetargetTimespan
    30  	}
    31  
    32  	// calculation of new 32-byte difficulty target
    33  	// first turn the previous target into a big int
    34  	prevTarget := CompactToBig(end.Bits)
    35  	// new target is old * duration...
    36  	newTarget := new(big.Int).Mul(prevTarget, big.NewInt(duration))
    37  	// divided by 2 weeks
    38  	newTarget.Div(newTarget, big.NewInt(int64(p.TargetTimespan.Seconds())))
    39  
    40  	// clip again if above minimum target (too easy)
    41  	if newTarget.Cmp(p.PowLimit) > 0 {
    42  		newTarget.Set(p.PowLimit)
    43  	}
    44  
    45  	// calculate and return 4-byte 'bits' difficulty from 32-byte target
    46  	return BigToCompact(newTarget)
    47  }
    48  
    49  // diffBitcoin checks the difficulty of the last header in the slice presented
    50  // give at least an epochlength of headers if this is a new epoch;
    51  // otherwise give at least 2
    52  // it's pretty ugly that it needs Params.  There must be some trick to get
    53  // rid of that since diffBitcoin itself is already in the Params...
    54  func diffBitcoin(
    55  	headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
    56  
    57  	ltcmode := p.Name == "litetest4" || p.Name == "litereg" || p.Name == "vtcreg" ||
    58  		p.Name == "litecoin" || p.Name == "vtctest" || p.Name == "vtc"
    59  
    60  	//if p.Name == "regtest" {
    61  	//	logging.Error("REKT")
    62  	//	return 0x207fffff, nil
    63  	//}
    64  
    65  	if len(headers) < 2 {
    66  		logging.Error("Less than 2 headers given to diffBitcoin")
    67  		return 0, fmt.Errorf(
    68  			"%d headers given to diffBitcoin, expect >2", len(headers))
    69  	}
    70  	prev := headers[len(headers)-2]
    71  	cur := headers[len(headers)-1]
    72  
    73  	// normal, no adjustment; Dn = Dn-1
    74  	var rightBits uint32
    75  	if prev.Bits != 0 {
    76  		rightBits = prev.Bits
    77  	} else {
    78  		// invalid block, prev bits are zero, return min diff.
    79  		logging.Error("Got blocks with diff 0. Returning error")
    80  		return 0, fmt.Errorf("Got blocks with diff 0. Returning error")
    81  	}
    82  
    83  	epochLength := int(p.TargetTimespan / p.TargetTimePerBlock)
    84  
    85  	epochHeight := int(height) % epochLength
    86  	maxHeader := len(headers) - 1
    87  
    88  	// must include an epoch start header
    89  	if epochHeight > maxHeader && maxHeader+10 > epochHeight {
    90  		// assuming max 10 block reorg, if something more,  you're safer
    91  		// restarting your node. Also, if you're syncing from scratch and
    92  		// get a reorg in 10 blocks, you're doing soemthign wrong.
    93  		// TODO: handle case when reorg happens over diff reset.
    94  		return p.PowLimitBits, nil
    95  	} else if epochHeight > maxHeader {
    96  		return 0, fmt.Errorf("diffBitcoin got insufficient headers")
    97  	}
    98  	epochStart := headers[maxHeader-epochHeight]
    99  
   100  	// see if we're on a difficulty adjustment block
   101  	if epochHeight == 0 {
   102  		// if so, we need at least an epoch's worth of headers
   103  		if maxHeader < int(epochLength) {
   104  			return 0, fmt.Errorf("diffBitcoin not enough headers, got %d, need %d",
   105  				len(headers), epochLength)
   106  		}
   107  
   108  		if ltcmode {
   109  			if int(height) == epochLength {
   110  				epochStart = headers[maxHeader-epochLength]
   111  			} else {
   112  				epochStart = headers[maxHeader-(epochLength-1)]
   113  			}
   114  		} else {
   115  			epochStart = headers[maxHeader-epochLength]
   116  		}
   117  		// if so, check if difficulty adjustment is valid.
   118  		// That whole "controlled supply" thing.
   119  		// calculate diff n based on n-2016 ... n-1
   120  		rightBits = calcDiffAdjustBitcoin(epochStart, prev, p)
   121  		// logging.Infof("h %d diff adjust %x -> %x\n", height, prev.Bits, rightBits)
   122  	} else if p.ReduceMinDifficulty { // not a new epoch
   123  		// if on testnet, check for difficulty nerfing
   124  		if cur.Timestamp.After(
   125  			prev.Timestamp.Add(p.TargetTimePerBlock * 2)) {
   126  			rightBits = p.PowLimitBits // difficulty 1
   127  			// no block was found in the last 20 minutes, so the next diff must be 1
   128  		} else {
   129  			// actually need to iterate back to last nerfed block,
   130  			// then take the diff from the one behind it
   131  			// btcd code is findPrevTestNetDifficulty()
   132  			// code in bitcoin/cpp:
   133  			// while (pindex->pprev &&
   134  			// pindex->nHeight % params.DifficultyAdjustmentInterval() != 0 &&
   135  			// pindex->nBits == nProofOfWorkLimit)
   136  
   137  			// ugh I don't know, and whatever this is testnet.
   138  			// well, lets do what btcd does
   139  			tempCur := headers[len(headers)-1]
   140  			tempHeight := height
   141  			arrIndex := len(headers) - 1
   142  			i := 0
   143  			for tempCur != nil && tempHeight%2016 != 0 &&
   144  				tempCur.Bits == p.PowLimitBits {
   145  				arrIndex -= 1
   146  				tempCur = headers[arrIndex]
   147  				tempHeight -= 1
   148  				i++
   149  			}
   150  			// Return the found difficulty or the minimum difficulty if no
   151  			// appropriate block was found.
   152  			rightBits = p.PowLimitBits
   153  			if tempCur != nil && tempCur.Bits != 0 { //weird bug
   154  				rightBits = tempCur.Bits
   155  			}
   156  			// rightBits = epochStart.Bits // original line
   157  		}
   158  	}
   159  	return rightBits, nil
   160  }
   161  
   162  // Uses Kimoto Gravity Well for difficulty adjustment. Used in VTC, MONA etc
   163  func calcDiffAdjustKGW(
   164  	headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
   165  	var minBlocks, maxBlocks int32
   166  	minBlocks = 144
   167  	maxBlocks = 4032
   168  
   169  	if height-1 < minBlocks {
   170  		return p.PowLimitBits, nil
   171  	}
   172  
   173  	idx := -2
   174  	currentBlock := headers[len(headers)+idx]
   175  	lastSolved := currentBlock
   176  
   177  	var blocksScanned, actualRate, targetRate int64
   178  	var difficultyAverage, previousDifficultyAverage big.Int
   179  	var rateAdjustmentRatio, eventHorizonDeviation float64
   180  	var eventHorizonDeviationFast, eventHorizonDevationSlow float64
   181  
   182  	currentHeight := height - 1
   183  
   184  	var i int32
   185  
   186  	for i = 1; currentHeight > 0; i++ {
   187  		if i > maxBlocks {
   188  			break
   189  		}
   190  
   191  		blocksScanned++
   192  
   193  		if i == 1 {
   194  			difficultyAverage = *CompactToBig(currentBlock.Bits)
   195  		} else {
   196  			compact := CompactToBig(currentBlock.Bits)
   197  
   198  			difference := new(big.Int).Sub(compact, &previousDifficultyAverage)
   199  			difference.Div(difference, big.NewInt(int64(i)))
   200  			difference.Add(difference, &previousDifficultyAverage)
   201  			difficultyAverage = *difference
   202  		}
   203  
   204  		previousDifficultyAverage = difficultyAverage
   205  
   206  		actualRate = lastSolved.Timestamp.Unix() - currentBlock.Timestamp.Unix()
   207  		targetRate = int64(p.TargetTimePerBlock.Seconds()) * blocksScanned
   208  		rateAdjustmentRatio = 1
   209  
   210  		if actualRate < 0 {
   211  			actualRate = 0
   212  		}
   213  
   214  		if actualRate != 0 && targetRate != 0 {
   215  			rateAdjustmentRatio = float64(targetRate) / float64(actualRate)
   216  		}
   217  
   218  		eventHorizonDeviation = 1 + (0.7084 *
   219  			math.Pow(float64(blocksScanned)/float64(minBlocks), -1.228))
   220  		eventHorizonDeviationFast = eventHorizonDeviation
   221  		eventHorizonDevationSlow = 1 / eventHorizonDeviation
   222  
   223  		if blocksScanned >= int64(minBlocks) &&
   224  			(rateAdjustmentRatio <= eventHorizonDevationSlow ||
   225  				rateAdjustmentRatio >= eventHorizonDeviationFast) {
   226  			break
   227  		}
   228  
   229  		if currentHeight <= 1 {
   230  			break
   231  		}
   232  
   233  		currentHeight--
   234  		idx--
   235  
   236  		currentBlock = headers[len(headers)+idx]
   237  	}
   238  
   239  	newTarget := difficultyAverage
   240  	if actualRate != 0 && targetRate != 0 {
   241  		newTarget.Mul(&newTarget, big.NewInt(actualRate))
   242  
   243  		newTarget.Div(&newTarget, big.NewInt(targetRate))
   244  	}
   245  
   246  	if newTarget.Cmp(p.PowLimit) == 1 {
   247  		newTarget = *p.PowLimit
   248  	}
   249  
   250  	return BigToCompact(&newTarget), nil
   251  }
   252  
   253  //  VTC diff calc
   254  func diffVTC(
   255  	headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
   256  	return calcDiffAdjustKGW(headers, height, p)
   257  }
   258  
   259  func diffVTCtest(headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
   260  	if height < 2116 {
   261  		return diffBitcoin(headers, height, p)
   262  	}
   263  
   264  	// Testnet retargets only every 12 blocks
   265  	if height%12 != 0 {
   266  		return headers[len(headers)-2].Bits, nil
   267  	}
   268  
   269  	// Run KGW
   270  	return calcDiffAdjustKGW(headers, height, p)
   271  }
   272  
   273  func diffVTCregtest(headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
   274  	return p.PowLimitBits, nil
   275  }