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  }