gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/consensus/aquahash/difficulty.go (about)

     1  // Copyright 2018 The aquachain Authors
     2  // This file is part of the aquachain library.
     3  //
     4  // The aquachain library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The aquachain library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the aquachain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package aquahash
    18  
    19  import (
    20  	"math/big"
    21  
    22  	"gitlab.com/aquachain/aquachain/common/math"
    23  	"gitlab.com/aquachain/aquachain/core/types"
    24  	"gitlab.com/aquachain/aquachain/params"
    25  )
    26  
    27  // Some weird constants to avoid constant memory allocs for them.
    28  var (
    29  	expDiffPeriod = big.NewInt(100000)
    30  	big1          = big.NewInt(1)
    31  	big2          = big.NewInt(2)
    32  	big10         = big.NewInt(10)
    33  	big240        = big.NewInt(240)
    34  	bigMinus99    = big.NewInt(-99)
    35  	big10000      = big.NewInt(10000)
    36  )
    37  
    38  // calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
    39  // the difficulty that a new block should have when created at time given the
    40  // parent block's time and difficulty. The calculation uses the Homestead rules.
    41  func calcDifficultyStarting(time uint64, parent *types.Header, chainID uint64) *big.Int {
    42  	// https://github.com/aquanetwork/EIPs/blob/master/EIPS/eip-2.md
    43  	// algorithm:
    44  	// diff = (parent_diff +
    45  	//         (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
    46  	//        ) + 2^(periodCount - 2)
    47  	bigTime := new(big.Int).SetUint64(time)
    48  	bigParentTime := new(big.Int).Set(parent.Time)
    49  
    50  	// holds intermediate values to make the algo easier to read & audit
    51  	x := new(big.Int)
    52  	y := new(big.Int)
    53  
    54  	// 1 - (block_timestamp - parent_timestamp) // 10
    55  	x.Sub(bigTime, bigParentTime)
    56  	x.Div(x, big10)
    57  	x.Sub(big1, x)
    58  
    59  	// max(1 - (block_timestamp - parent_timestamp) // 10, -99)
    60  	if x.Cmp(bigMinus99) < 0 {
    61  		x.Set(bigMinus99)
    62  	}
    63  	// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
    64  	y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
    65  	x.Mul(y, x)
    66  	x.Add(parent.Difficulty, x)
    67  
    68  	// testnet no minimum
    69  	if chainID == params.MainnetChainConfig.ChainId.Uint64() {
    70  		x = math.BigMax(x, params.MinimumDifficultyGenesis)
    71  	}
    72  	return x
    73  }
    74  
    75  // calcDifficultyHF1 is the difficulty adjustment algorithm. It returns
    76  // the difficulty that a new block should have when created at time given the
    77  // parent block's time and difficulty. The calculation uses modified Homestead rules.
    78  // It is flawed, target 10 seconds
    79  func calcDifficultyHF1(time uint64, parent *types.Header, chainID uint64) *big.Int {
    80  	bigTime := new(big.Int).SetUint64(time)
    81  	bigParentTime := new(big.Int).Set(parent.Time)
    82  
    83  	// holds intermediate values to make the algo easier to read & audit
    84  	x := new(big.Int)
    85  	y := new(big.Int)
    86  
    87  	// 1 - (block_timestamp - parent_timestamp) // 10
    88  	x.Sub(bigTime, bigParentTime)
    89  	x.Div(x, big10)
    90  	x.Sub(big1, x)
    91  
    92  	// max(1 - (block_timestamp - parent_timestamp) // 10, -99)
    93  	if x.Cmp(bigMinus99) < 0 {
    94  		x.Set(bigMinus99)
    95  	}
    96  	// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
    97  	y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
    98  	x.Mul(y, x)
    99  	x.Add(parent.Difficulty, x)
   100  
   101  	// minimum difficulty can ever be (before exponential factor)
   102  	if chainID == params.MainnetChainConfig.ChainId.Uint64() {
   103  		x = math.BigMax(x, params.MinimumDifficultyHF1)
   104  	}
   105  	return x
   106  }
   107  
   108  // calcDifficultyHFX combines all difficulty algorithms
   109  func calcDifficultyHFX(config *params.ChainConfig, time uint64, parent, grandparent *types.Header) *big.Int {
   110  	var (
   111  		diff          = new(big.Int)
   112  		next          = new(big.Int).Add(parent.Number, big1)
   113  		chainID       = config.ChainId.Uint64()
   114  		hf            = config.UseHF(next)
   115  		adjust        *big.Int
   116  		bigTime       = new(big.Int)
   117  		bigParentTime = new(big.Int)
   118  		limit         = params.DurationLimitHF6 // target 240 seconds
   119  		min           = params.MinimumDifficultyHF5
   120  		mainnet       = params.MainnetChainConfig.ChainId.Uint64() == chainID // bool
   121  		ethnet        = params.EthnetChainConfig.ChainId.Uint64() == chainID  // bool
   122  	)
   123  	if config == params.Testnet3ChainConfig {
   124  		return calcDifficultyGrandparent(time, parent, grandparent, hf, chainID)
   125  	}
   126  	if ethnet {
   127  		return EthCalcDifficulty(config, time, parent)
   128  	}
   129  	if !mainnet {
   130  		min = params.MinimumDifficultyHF5Testnet
   131  	}
   132  
   133  	if hf > params.KnownHF {
   134  		panic("unknown HF not implemented")
   135  	}
   136  
   137  	switch hf {
   138  	case 9:
   139  		return calcDifficultyGrandparent(time, parent, grandparent, hf, chainID)
   140  	case 8:
   141  		if next.Cmp(config.GetHF(8)) == 0 && mainnet {
   142  			return params.MinimumDifficultyHF5
   143  		}
   144  		if next.Cmp(config.GetHF(8)) == 0 && !mainnet {
   145  			return params.MinimumDifficultyHF8Testnet
   146  		}
   147  		adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisorHF8)
   148  		if !mainnet {
   149  			adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisorHF8Testnet)
   150  			min = params.MinimumDifficultyHF8Testnet
   151  		}
   152  	case 6, 7:
   153  		adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisorHF6)
   154  	case 5:
   155  		if next.Cmp(config.GetHF(5)) == 0 && mainnet {
   156  			return params.MinimumDifficultyHF5
   157  		}
   158  		if next.Cmp(config.GetHF(5)) == 0 && !mainnet {
   159  			return params.MinimumDifficultyHF5Testnet
   160  		}
   161  		limit = params.DurationLimit // not accurate, fixed in hf6
   162  		adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisorHF5)
   163  		if mainnet {
   164  			min = params.MinimumDifficultyHF5
   165  		}
   166  	case 3, 4:
   167  		limit = params.DurationLimit // not accurate, fixed in hf6
   168  		adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
   169  		if mainnet {
   170  			min = params.MinimumDifficultyHF3
   171  		}
   172  	case 2:
   173  		limit = params.DurationLimit // not accurate, fixed in hf6
   174  		adjust = new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
   175  		if mainnet {
   176  			min = params.MinimumDifficultyHF1
   177  		}
   178  	case 1:
   179  		return calcDifficultyHF1(time, parent, chainID)
   180  	case 0:
   181  		return calcDifficultyStarting(time, parent, chainID)
   182  	default:
   183  		panic("calcDifficulty: invalid hf")
   184  	}
   185  
   186  	bigTime.SetUint64(time)
   187  	bigParentTime.Set(parent.Time)
   188  
   189  	// calculate difficulty
   190  	if bigTime.Sub(bigTime, bigParentTime).Cmp(limit) < 0 {
   191  		diff.Add(parent.Difficulty, adjust)
   192  	} else {
   193  		diff.Sub(parent.Difficulty, adjust)
   194  	}
   195  
   196  	if diff.Cmp(min) < 0 {
   197  		diff.Set(min)
   198  	}
   199  
   200  	return diff
   201  }
   202  
   203  // calcDifficultyGrandparent experimental
   204  func calcDifficultyGrandparent(time uint64, parent, grandparent *types.Header, hf int, chainID uint64) *big.Int {
   205  	if grandparent == nil {
   206  		return new(big.Int).Set(parent.Difficulty)
   207  	}
   208  	bigGrandparentTime := new(big.Int).Set(grandparent.Time)
   209  	bigParentTime := new(big.Int).Set(parent.Time)
   210  	if bigParentTime.Cmp(bigGrandparentTime) <= 0 {
   211  		panic("invalid code")
   212  	}
   213  	// holds intermediate values to make the algo easier to read & audit
   214  	x := new(big.Int)
   215  	y := new(big.Int)
   216  
   217  	divisor := params.DifficultyBoundDivisorHF5
   218  
   219  	// 1 - (block_timestamp - parent_timestamp) // 240
   220  	x.Sub(bigParentTime, bigGrandparentTime)
   221  	x.Div(x, big240)
   222  	x.Sub(big1, x)
   223  
   224  	// max(1 - (block_timestamp - parent_timestamp) // 240, -99)
   225  	if x.Cmp(bigMinus99) < 0 {
   226  		x.Set(bigMinus99)
   227  	}
   228  
   229  	if grandparent.Difficulty.Cmp(big10000) < 0 {
   230  		divisor = params.DifficultyBoundDivisorHF5
   231  	} else {
   232  		divisor = params.DifficultyBoundDivisorHF8
   233  	}
   234  	// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
   235  	y.Div(grandparent.Difficulty, divisor)
   236  	x.Mul(y, x)
   237  	x.Add(grandparent.Difficulty, x)
   238  
   239  	// minimum difficulty can ever be (before exponential factor)
   240  	if chainID == params.MainnetChainConfig.ChainId.Uint64() {
   241  		x = math.BigMax(x, params.MinimumDifficultyHF5)
   242  	} else {
   243  		x = math.BigMax(x, params.MinimumDifficultyHF5Testnet)
   244  	}
   245  	return x
   246  }
   247  
   248  var big100 = big.NewInt(100)
   249  var big1000 = big.NewInt(1000)
   250  var big20 = big.NewInt(20)
   251  
   252  func calcDifficultyTestnet3(time uint64, parent, grandparent *types.Header) *big.Int {
   253  	if grandparent == nil {
   254  		return new(big.Int).Set(parent.Difficulty)
   255  	}
   256  	bigTime := new(big.Int).SetUint64(time)
   257  	bigParentTime := new(big.Int).Set(parent.Time)
   258  	bigGParentTime := new(big.Int).Set(grandparent.Time)
   259  	difference := new(big.Int).Sub(bigTime, bigParentTime)
   260  	gdifference := new(big.Int).Sub(bigGParentTime, bigParentTime)
   261  	if difference.Cmp(big10) < 0 && gdifference.Cmp(big10) < 0 {
   262  		return new(big.Int).Add(parent.Difficulty, big1000)
   263  	}
   264  	if difference.Cmp(big20) > 0 && gdifference.Cmp(big20) > 0 {
   265  		return new(big.Int).Sub(parent.Difficulty, big1000)
   266  	}
   267  	if difference.Cmp(big100) > 0 && gdifference.Cmp(big100) > 0 {
   268  		return new(big.Int).Quo(parent.Difficulty, big2)
   269  	}
   270  	return new(big.Int).Set(parent.Difficulty)
   271  }