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 }