forkhalcyon.go raw

   1  package blockchain
   2  
   3  import (
   4  	"fmt"
   5  	"github.com/p9c/p9/pkg/bits"
   6  	"github.com/p9c/p9/pkg/fork"
   7  	"math/big"
   8  )
   9  
  10  // CalcNextRequiredDifficultyHalcyon calculates the required difficulty for the block after the passed previous block
  11  // node based on the difficulty retarget rules. This function differs from the exported CalcNextRequiredDifficulty in
  12  // that the exported version uses the current best chain as the previous block node while this function accepts any
  13  // block node.
  14  func (b *BlockChain) CalcNextRequiredDifficultyHalcyon(
  15  	lastNode *BlockNode,
  16  	algoname string,
  17  	l bool,
  18  ) (newTargetBits uint32, e error) {
  19  	nH := lastNode.height + 1
  20  	if lastNode == nil {
  21  		if l {
  22  			D.Ln("lastNode is nil")
  23  		}
  24  		return newTargetBits, nil
  25  	}
  26  	// this sanitises invalid block versions according to legacy consensus quirks
  27  	algo := fork.GetAlgoVer(algoname, nH)
  28  	algoName := fork.GetAlgoName(algo, nH)
  29  	newTargetBits = fork.GetMinBits(algoName, nH)
  30  	prevNode := lastNode.GetLastWithAlgo(algo)
  31  	if prevNode == nil {
  32  		if l {
  33  			D.Ln("prevNode is nil")
  34  		}
  35  		return newTargetBits, nil
  36  	}
  37  	firstNode := prevNode
  38  	for i := int32(0); firstNode != nil &&
  39  		i < fork.GetAveragingInterval(nH)-1; i++ {
  40  		firstNode = firstNode.RelativeAncestor(1)
  41  		firstNode = firstNode.GetLastWithAlgo(algo)
  42  	}
  43  	if firstNode == nil {
  44  		return newTargetBits, nil
  45  	}
  46  	actualTimespan := prevNode.timestamp - firstNode.timestamp
  47  	adjustedTimespan := actualTimespan
  48  	if l {
  49  		T.F("actual %d", actualTimespan)
  50  	}
  51  	if actualTimespan < b.params.MinActualTimespan {
  52  		adjustedTimespan = b.params.MinActualTimespan
  53  	} else if actualTimespan > b.params.MaxActualTimespan {
  54  		adjustedTimespan = b.params.MaxActualTimespan
  55  	}
  56  	if l {
  57  		T.F("adjusted %d", adjustedTimespan)
  58  	}
  59  	oldTarget := bits.CompactToBig(prevNode.bits)
  60  	newTarget := new(big.Int).
  61  		Mul(oldTarget, big.NewInt(adjustedTimespan))
  62  	newTarget = newTarget.
  63  		Div(newTarget, big.NewInt(b.params.AveragingTargetTimespan))
  64  	if newTarget.Cmp(bits.CompactToBig(newTargetBits)) > 0 {
  65  		newTarget.Set(bits.CompactToBig(newTargetBits))
  66  	}
  67  	newTargetBits = bits.BigToCompact(newTarget)
  68  	if l {
  69  		T.F(
  70  			"difficulty retarget at block height %d, old %08x new %08x",
  71  			lastNode.height+1,
  72  			prevNode.bits,
  73  			newTargetBits,
  74  		)
  75  	}
  76  	if l {
  77  		T.C(func() string {
  78  			return fmt.Sprintf(
  79  				"actual timespan %v, adjusted timespan %v, target timespan %v"+
  80  					"\nOld %064x\nNew %064x",
  81  				actualTimespan,
  82  				adjustedTimespan,
  83  				b.params.AveragingTargetTimespan,
  84  				oldTarget,
  85  				bits.CompactToBig(newTargetBits),
  86  			)
  87  		},
  88  		)
  89  	}
  90  	return newTargetBits, nil
  91  }
  92