gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/consensus/difficulty_test.go (about) 1 package consensus 2 3 import ( 4 "math/big" 5 "testing" 6 7 bolt "github.com/coreos/bbolt" 8 9 "gitlab.com/SiaPrime/SiaPrime/types" 10 ) 11 12 // TestChildTargetOak checks the childTargetOak function, especially for edge 13 // cases like overflows and underflows. 14 func TestChildTargetOak(t *testing.T) { 15 // NOTE: Test must not be run in parallel. 16 if testing.Short() { 17 t.SkipNow() 18 } 19 cst, err := createConsensusSetTester(t.Name()) 20 if err != nil { 21 t.Fatal(err) 22 } 23 defer cst.Close() 24 cs := cst.cs 25 // NOTE: Test must not be run in parallel. 26 // 27 // Set the constants to match the real-network constants, and then make sure 28 // they are reset at the end of the test. 29 oldFreq := types.BlockFrequency 30 oldMaxRise := types.OakMaxRise 31 oldMaxDrop := types.OakMaxDrop 32 oldRootTarget := types.RootTarget 33 types.BlockFrequency = 600 34 types.OakMaxRise = big.NewRat(1004, 1e3) 35 types.OakMaxDrop = big.NewRat(1e3, 1004) 36 types.RootTarget = types.Target{0, 0, 0, 1} 37 defer func() { 38 types.BlockFrequency = oldFreq 39 types.OakMaxRise = oldMaxRise 40 types.OakMaxDrop = oldMaxDrop 41 types.RootTarget = oldRootTarget 42 }() 43 44 // Start with some values that are normal, resulting in no change in target. 45 parentHeight := types.BlockHeight(100) 46 // The total time and total target will be set to 100 uncompressed blocks. 47 // Though the actual algorithm is compressing the times to achieve an 48 // exponential weighting, this test only requires that the visible hashrate 49 // be measured as equal to the root target per block. 50 parentTotalTime := int64(types.BlockFrequency * parentHeight) 51 parentTotalTarget := types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 52 parentTimestamp := types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) 53 parentTarget := types.RootTarget 54 // newTarget should match the root target, as the hashrate and blocktime all 55 // match the existing target - there should be no reason for adjustment. 56 newTarget := cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 57 // New target should be barely moving. Some imprecision may cause slight 58 // adjustments, but the total difference should be less than 0.01%. 59 maxNewTarget := parentTarget.MulDifficulty(big.NewRat(10e3, 10001)) 60 minNewTarget := parentTarget.MulDifficulty(big.NewRat(10001, 10e3)) 61 if newTarget.Cmp(maxNewTarget) > 0 { 62 t.Error("The target shifted too much for a constant hashrate") 63 } 64 if newTarget.Cmp(minNewTarget) < 0 { 65 t.Error("The target shifted too much for a constant hashrate") 66 } 67 68 // Set the total time such that the difficulty needs to be adjusted down. 69 // Shifter clamps and adjustment clamps will both be in effect. 70 parentHeight = types.BlockHeight(100) 71 // Set the visible hashrate to types.RootTarget per block. 72 parentTotalTime = int64(types.BlockFrequency * parentHeight) 73 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 74 // Set the timestamp far in the future, triggering the shifter to increase 75 // the block time to the point that the shifter clamps activate. 76 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) + 500e6 77 // Set the target to types.RootTarget, causing the max difficulty adjustment 78 // clamp to be in effect. 79 parentTarget = types.RootTarget 80 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 81 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) <= 0 { 82 t.Error("Difficulty did not decrease in response to increased total time") 83 } 84 // Check that the difficulty decreased by the maximum amount. 85 minNewTarget = parentTarget.MulDifficulty(types.OakMaxDrop) 86 if minNewTarget.Difficulty().Cmp(newTarget.Difficulty()) != 0 { 87 t.Error("Difficulty did not decrease by the maximum amount") 88 } 89 90 // Set the total time such that the difficulty needs to be adjusted up. 91 // Shifter clamps and adjustment clamps will both be in effect. 92 parentHeight = types.BlockHeight(100) 93 // Set the visible hashrate to types.RootTarget per block. 94 parentTotalTime = int64(types.BlockFrequency * parentHeight) 95 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 96 // Set the timestamp far in the past, triggering the shifter to decrease the 97 // block time to the point that the shifter clamps activate. 98 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) - 500e6 99 // Set the target to types.RootTarget, causing the max difficulty adjustment 100 // clamp to be in effect. 101 parentTarget = types.RootTarget 102 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 103 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) >= 0 { 104 t.Error("Difficulty did not increase in response to decreased total time") 105 } 106 // Check that the difficulty decreased by the maximum amount. 107 minNewTarget = parentTarget.MulDifficulty(types.OakMaxRise) 108 if minNewTarget.Difficulty().Cmp(newTarget.Difficulty()) != 0 { 109 t.Error("Difficulty did not increase by the maximum amount") 110 } 111 112 // Set the total time such that the difficulty needs to be adjusted down. 113 // Neither the shiftor clamps nor the adjustor clamps will be in effect. 114 parentHeight = types.BlockHeight(100) 115 // Set the visible hashrate to types.RootTarget per block. 116 parentTotalTime = int64(types.BlockFrequency * parentHeight) 117 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 118 // Set the timestamp in the future, but little enough in the future that 119 // neither the shifter clamp nor the adjustment clamp will trigger. 120 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) + 5e3 121 // Set the target to types.RootTarget. 122 parentTarget = types.RootTarget 123 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 124 // Check that the difficulty decreased, but not by the max amount. 125 minNewTarget = parentTarget.MulDifficulty(types.OakMaxDrop) 126 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) <= 0 { 127 t.Error("Difficulty did not decrease") 128 } 129 if minNewTarget.Difficulty().Cmp(newTarget.Difficulty()) >= 0 { 130 t.Error("Difficulty decreased by the clamped amount") 131 } 132 133 // Set the total time such that the difficulty needs to be adjusted up. 134 // Neither the shiftor clamps nor the adjustor clamps will be in effect. 135 parentHeight = types.BlockHeight(100) 136 // Set the visible hashrate to types.RootTarget per block. 137 parentTotalTime = int64(types.BlockFrequency * parentHeight) 138 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 139 // Set the timestamp in the past, but little enough in the future that 140 // neither the shifter clamp nor the adjustment clamp will trigger. 141 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) - 5e3 142 // Set the target to types.RootTarget. 143 parentTarget = types.RootTarget 144 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 145 // Check that the difficulty increased, but not by the max amount. 146 maxNewTarget = parentTarget.MulDifficulty(types.OakMaxRise) 147 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) >= 0 { 148 t.Error("Difficulty did not decrease") 149 } 150 if maxNewTarget.Difficulty().Cmp(newTarget.Difficulty()) <= 0 { 151 t.Error("Difficulty decreased by the clamped amount") 152 } 153 154 // Set the total time such that the difficulty needs to be adjusted down. 155 // Adjustor clamps will be in effect, shifter clamps will not be in effect. 156 parentHeight = types.BlockHeight(100) 157 // Set the visible hashrate to types.RootTarget per block. 158 parentTotalTime = int64(types.BlockFrequency * parentHeight) 159 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 160 // Set the timestamp in the future, but little enough in the future that 161 // neither the shifter clamp nor the adjustment clamp will trigger. 162 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) + 10e3 163 // Set the target to types.RootTarget. 164 parentTarget = types.RootTarget 165 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 166 // Check that the difficulty decreased, but not by the max amount. 167 minNewTarget = parentTarget.MulDifficulty(types.OakMaxDrop) 168 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) <= 0 { 169 t.Error("Difficulty did not decrease") 170 } 171 if minNewTarget.Difficulty().Cmp(newTarget.Difficulty()) != 0 { 172 t.Error("Difficulty decreased by the clamped amount") 173 } 174 175 // Set the total time such that the difficulty needs to be adjusted up. 176 // Adjustor clamps will be in effect, shifter clamps will not be in effect. 177 parentHeight = types.BlockHeight(100) 178 // Set the visible hashrate to types.RootTarget per block. 179 parentTotalTime = int64(types.BlockFrequency * parentHeight) 180 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 181 // Set the timestamp in the past, but little enough in the future that 182 // neither the shifter clamp nor the adjustment clamp will trigger. 183 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) - 10e3 184 // Set the target to types.RootTarget. 185 parentTarget = types.RootTarget 186 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 187 // Check that the difficulty increased, but not by the max amount. 188 maxNewTarget = parentTarget.MulDifficulty(types.OakMaxRise) 189 if parentTarget.Difficulty().Cmp(newTarget.Difficulty()) >= 0 { 190 t.Error("Difficulty did not decrease") 191 } 192 if maxNewTarget.Difficulty().Cmp(newTarget.Difficulty()) != 0 { 193 t.Error("Difficulty decreased by the clamped amount") 194 } 195 196 // Set the total time such that the difficulty needs to be adjusted down. 197 // Shifter clamps will be in effect, adjustor clamps will not be in effect. 198 parentHeight = types.BlockHeight(100) 199 // Set the visible hashrate to types.RootTarget per block. 200 parentTotalTime = int64(types.BlockFrequency * parentHeight) 201 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 202 // Set the timestamp in the future, but little enough in the future that 203 // neither the shifter clamp nor the adjustment clamp will trigger. 204 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) + 500e6 205 // Set the target to types.RootTarget. 206 parentTarget = types.RootTarget.MulDifficulty(big.NewRat(1, types.OakMaxBlockShift)) 207 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 208 // New target should be barely moving. Some imprecision may cause slight 209 // adjustments, but the total difference should be less than 0.01%. 210 maxNewTarget = parentTarget.MulDifficulty(big.NewRat(10e3, 10001)) 211 minNewTarget = parentTarget.MulDifficulty(big.NewRat(10001, 10e3)) 212 if newTarget.Cmp(maxNewTarget) > 0 { 213 t.Error("The target shifted too much for a constant hashrate") 214 } 215 if newTarget.Cmp(minNewTarget) < 0 { 216 t.Error("The target shifted too much for a constant hashrate") 217 } 218 219 // Set the total time such that the difficulty needs to be adjusted up. 220 // Shifter clamps will be in effect, adjustor clamps will not be in effect. 221 parentHeight = types.BlockHeight(100) 222 // Set the visible hashrate to types.RootTarget per block. 223 parentTotalTime = int64(types.BlockFrequency * parentHeight) 224 parentTotalTarget = types.RootTarget.MulDifficulty(big.NewRat(int64(parentHeight), 1)) 225 // Set the timestamp in the past, but little enough in the future that 226 // neither the shifter clamp nor the adjustment clamp will trigger. 227 parentTimestamp = types.GenesisTimestamp + types.Timestamp((types.BlockFrequency * parentHeight)) - 500e6 228 // Set the target to types.RootTarget. 229 parentTarget = types.RootTarget.MulDifficulty(big.NewRat(types.OakMaxBlockShift, 1)) 230 newTarget = cs.childTargetOak(parentTotalTime, parentTotalTarget, parentTarget, parentHeight, parentTimestamp) 231 // New target should be barely moving. Some imprecision may cause slight 232 // adjustments, but the total difference should be less than 0.01%. 233 maxNewTarget = parentTarget.MulDifficulty(big.NewRat(10e3, 10001)) 234 minNewTarget = parentTarget.MulDifficulty(big.NewRat(10001, 10e3)) 235 if newTarget.Cmp(maxNewTarget) > 0 { 236 t.Error("The target shifted too much for a constant hashrate") 237 } 238 if newTarget.Cmp(minNewTarget) < 0 { 239 t.Error("The target shifted too much for a constant hashrate") 240 } 241 } 242 243 // TestStoreBlockTotals checks features of the storeBlockTotals and 244 // getBlockTotals code. 245 func TestStoreBlockTotals(t *testing.T) { 246 247 // NOTE: Test must not be run in parallel. 248 if testing.Short() { 249 t.SkipNow() 250 } 251 cst, err := createConsensusSetTester(t.Name()) 252 if err != nil { 253 t.Fatal(err) 254 } 255 defer cst.Close() 256 cs := cst.cs 257 // NOTE: Test must not be run in parallel. 258 // 259 // Set the constants to match the real-network constants, and then make sure 260 // they are reset at the end of the test. 261 oldFreq := types.BlockFrequency 262 oldDecayNum := types.OakDecayNum 263 oldDecayDenom := types.OakDecayDenom 264 oldMaxRise := types.OakMaxRise 265 oldMaxDrop := types.OakMaxDrop 266 oldRootTarget := types.RootTarget 267 types.BlockFrequency = 600 268 types.OakDecayNum = 995 269 types.OakDecayDenom = 1e3 270 types.OakMaxRise = big.NewRat(1004, 1e3) 271 types.OakMaxDrop = big.NewRat(1e3, 1004) 272 types.RootTarget = types.Target{0, 0, 0, 1} 273 defer func() { 274 types.BlockFrequency = oldFreq 275 types.OakDecayNum = oldDecayNum 276 types.OakDecayDenom = oldDecayDenom 277 types.OakMaxRise = oldMaxRise 278 types.OakMaxDrop = oldMaxDrop 279 types.RootTarget = oldRootTarget 280 }() 281 282 // Check that as totals get stored over and over, the values getting 283 // returned follow a decay. While storing repeatedly, check that the 284 // getBlockTotals values match the values that were stored. 285 err = cs.db.Update(func(tx *bolt.Tx) error { 286 var totalTime int64 287 var id types.BlockID 288 var parentTimestamp, currentTimestamp types.Timestamp 289 currentTarget := types.RootTarget 290 totalTarget := types.RootDepth 291 for i := types.BlockHeight(0); i < 8000; i++ { 292 id[i/256] = byte(i % 256) 293 parentTimestamp = currentTimestamp 294 currentTimestamp += types.Timestamp(types.BlockFrequency) 295 totalTime, totalTarget, err = cs.storeBlockTotals(tx, i, id, totalTime, parentTimestamp, currentTimestamp, totalTarget, currentTarget) 296 if err != nil { 297 return err 298 } 299 300 // Check that the fetched values match the stored values. 301 getTime, getTarg := cs.getBlockTotals(tx, id) 302 if getTime != totalTime || getTarg != totalTarget { 303 t.Error("fetch failed - retrieving time and target did not work") 304 } 305 } 306 // Do a final iteration, but keep the old totals. After 8000 iterations, 307 // the totals should no longer be changing, yet they should be hundreds 308 // of times larger than the original values. 309 id[8001/256] = byte(8001 % 256) 310 parentTimestamp = currentTimestamp 311 currentTimestamp += types.Timestamp(types.BlockFrequency) 312 newTotalTime, newTotalTarget, err := cs.storeBlockTotals(tx, 8001, id, totalTime, parentTimestamp, currentTimestamp, totalTarget, currentTarget) 313 if err != nil { 314 return err 315 } 316 if newTotalTime != totalTime || newTotalTarget.Difficulty().Cmp(totalTarget.Difficulty()) != 0 { 317 t.Log(newTotalTime) 318 t.Log(totalTime) 319 t.Log(newTotalTarget) 320 t.Log(totalTarget) 321 t.Error("Total time and target did not seem to converge to a result") 322 } 323 if newTotalTime < int64(types.BlockFrequency)*199 { 324 t.Error("decay seems to be happening too rapidly") 325 } 326 if newTotalTime > int64(types.BlockFrequency)*205 { 327 t.Error("decay seems to be happening too slowly") 328 } 329 if newTotalTarget.Difficulty().Cmp(types.RootTarget.Difficulty().Mul64(199)) < 0 { 330 t.Error("decay seems to be happening too rapidly") 331 } 332 if newTotalTarget.Difficulty().Cmp(types.RootTarget.Difficulty().Mul64(205)) > 0 { 333 t.Error("decay seems to be happening too slowly") 334 } 335 return nil 336 }) 337 if err != nil { 338 t.Fatal(err) 339 } 340 }