github.com/dim4egster/coreth@v0.10.2/consensus/dummy/dynamic_fees_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package dummy
     5  
     6  import (
     7  	"encoding/binary"
     8  	"math/big"
     9  	"testing"
    10  
    11  	"github.com/dim4egster/coreth/core/types"
    12  	"github.com/dim4egster/coreth/params"
    13  	"github.com/ethereum/go-ethereum/common/math"
    14  	"github.com/ethereum/go-ethereum/log"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  func testRollup(t *testing.T, longs []uint64, roll int) {
    19  	slice := make([]byte, len(longs)*8)
    20  	numLongs := len(longs)
    21  	for i := 0; i < numLongs; i++ {
    22  		binary.BigEndian.PutUint64(slice[8*i:], longs[i])
    23  	}
    24  
    25  	newSlice, err := rollLongWindow(slice, roll)
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	// numCopies is the number of longs that should have been copied over from the previous
    30  	// slice as opposed to being left empty.
    31  	numCopies := numLongs - roll
    32  	for i := 0; i < numLongs; i++ {
    33  		// Extract the long value that is encoded at position [i] in [newSlice]
    34  		num := binary.BigEndian.Uint64(newSlice[8*i:])
    35  		// If the current index is past the point where we should have copied the value
    36  		// over from the previous slice, assert that the value encoded in [newSlice]
    37  		// is 0
    38  		if i >= numCopies {
    39  			if num != 0 {
    40  				t.Errorf("Expected num encoded in newSlice at position %d to be 0, but found %d", i, num)
    41  			}
    42  		} else {
    43  			// Otherwise, check that the value was copied over correctly
    44  			prevIndex := i + roll
    45  			prevNum := longs[prevIndex]
    46  			if prevNum != num {
    47  				t.Errorf("Expected num encoded in new slice at position %d to be %d, but found %d", i, prevNum, num)
    48  			}
    49  		}
    50  	}
    51  }
    52  
    53  func TestRollupWindow(t *testing.T) {
    54  	type test struct {
    55  		longs []uint64
    56  		roll  int
    57  	}
    58  
    59  	var tests []test = []test{
    60  		{
    61  			[]uint64{1, 2, 3, 4},
    62  			0,
    63  		},
    64  		{
    65  			[]uint64{1, 2, 3, 4},
    66  			1,
    67  		},
    68  		{
    69  			[]uint64{1, 2, 3, 4},
    70  			2,
    71  		},
    72  		{
    73  			[]uint64{1, 2, 3, 4},
    74  			3,
    75  		},
    76  		{
    77  			[]uint64{1, 2, 3, 4},
    78  			4,
    79  		},
    80  		{
    81  			[]uint64{1, 2, 3, 4},
    82  			5,
    83  		},
    84  		{
    85  			[]uint64{121, 232, 432},
    86  			2,
    87  		},
    88  	}
    89  
    90  	for _, test := range tests {
    91  		testRollup(t, test.longs, test.roll)
    92  	}
    93  }
    94  
    95  type blockDefinition struct {
    96  	timestamp      uint64
    97  	gasUsed        uint64
    98  	extDataGasUsed *big.Int
    99  }
   100  
   101  type test struct {
   102  	extraData      []byte
   103  	baseFee        *big.Int
   104  	genBlocks      func() []blockDefinition
   105  	minFee, maxFee *big.Int
   106  }
   107  
   108  func TestDynamicFees(t *testing.T) {
   109  	spacedTimestamps := []uint64{1, 1, 2, 5, 15, 120}
   110  	var tests []test = []test{
   111  		// Test minimal gas usage
   112  		{
   113  			extraData: nil,
   114  			baseFee:   nil,
   115  			minFee:    big.NewInt(params.ApricotPhase3MinBaseFee),
   116  			maxFee:    big.NewInt(params.ApricotPhase3MaxBaseFee),
   117  			genBlocks: func() []blockDefinition {
   118  				blocks := make([]blockDefinition, 0, len(spacedTimestamps))
   119  				for _, timestamp := range spacedTimestamps {
   120  					blocks = append(blocks, blockDefinition{
   121  						timestamp: timestamp,
   122  						gasUsed:   21000,
   123  					})
   124  				}
   125  				return blocks
   126  			},
   127  		},
   128  		// Test overflow handling
   129  		{
   130  			extraData: nil,
   131  			baseFee:   nil,
   132  			minFee:    big.NewInt(params.ApricotPhase3MinBaseFee),
   133  			maxFee:    big.NewInt(params.ApricotPhase3MaxBaseFee),
   134  			genBlocks: func() []blockDefinition {
   135  				blocks := make([]blockDefinition, 0, len(spacedTimestamps))
   136  				for _, timestamp := range spacedTimestamps {
   137  					blocks = append(blocks, blockDefinition{
   138  						timestamp: timestamp,
   139  						gasUsed:   math.MaxUint64,
   140  					})
   141  				}
   142  				return blocks
   143  			},
   144  		},
   145  		{
   146  			extraData: nil,
   147  			baseFee:   nil,
   148  			minFee:    big.NewInt(params.ApricotPhase3MinBaseFee),
   149  			maxFee:    big.NewInt(params.ApricotPhase3MaxBaseFee),
   150  			genBlocks: func() []blockDefinition {
   151  				return []blockDefinition{
   152  					{
   153  						timestamp: 1,
   154  						gasUsed:   1_000_000,
   155  					},
   156  					{
   157  						timestamp: 3,
   158  						gasUsed:   1_000_000,
   159  					},
   160  					{
   161  						timestamp: 5,
   162  						gasUsed:   2_000_000,
   163  					},
   164  					{
   165  						timestamp: 5,
   166  						gasUsed:   6_000_000,
   167  					},
   168  					{
   169  						timestamp: 7,
   170  						gasUsed:   6_000_000,
   171  					},
   172  					{
   173  						timestamp: 1000,
   174  						gasUsed:   6_000_000,
   175  					},
   176  					{
   177  						timestamp: 1001,
   178  						gasUsed:   6_000_000,
   179  					},
   180  					{
   181  						timestamp: 1002,
   182  						gasUsed:   6_000_000,
   183  					},
   184  				}
   185  			},
   186  		},
   187  	}
   188  
   189  	for _, test := range tests {
   190  		testDynamicFeesStaysWithinRange(t, test)
   191  	}
   192  }
   193  
   194  func testDynamicFeesStaysWithinRange(t *testing.T, test test) {
   195  	blocks := test.genBlocks()
   196  	initialBlock := blocks[0]
   197  	header := &types.Header{
   198  		Time:    initialBlock.timestamp,
   199  		GasUsed: initialBlock.gasUsed,
   200  		Number:  big.NewInt(0),
   201  		BaseFee: test.baseFee,
   202  		Extra:   test.extraData,
   203  	}
   204  
   205  	for index, block := range blocks[1:] {
   206  		nextExtraData, nextBaseFee, err := CalcBaseFee(params.TestApricotPhase3Config, header, block.timestamp)
   207  		if err != nil {
   208  			t.Fatalf("Failed to calculate base fee at index %d: %s", index, err)
   209  		}
   210  		if nextBaseFee.Cmp(test.maxFee) > 0 {
   211  			t.Fatalf("Expected fee to stay less than %d, but found %d", test.maxFee, nextBaseFee)
   212  		}
   213  		if nextBaseFee.Cmp(test.minFee) < 0 {
   214  			t.Fatalf("Expected fee to stay greater than %d, but found %d", test.minFee, nextBaseFee)
   215  		}
   216  		log.Info("Update", "baseFee", nextBaseFee)
   217  		header = &types.Header{
   218  			Time:    block.timestamp,
   219  			GasUsed: block.gasUsed,
   220  			Number:  big.NewInt(int64(index) + 1),
   221  			BaseFee: nextBaseFee,
   222  			Extra:   nextExtraData,
   223  		}
   224  	}
   225  }
   226  
   227  func TestLongWindow(t *testing.T) {
   228  	longs := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   229  	sumLongs := uint64(0)
   230  	longWindow := make([]byte, 10*8)
   231  	for i, long := range longs {
   232  		sumLongs = sumLongs + long
   233  		binary.BigEndian.PutUint64(longWindow[i*8:], long)
   234  	}
   235  
   236  	sum := sumLongWindow(longWindow, 10)
   237  	if sum != sumLongs {
   238  		t.Fatalf("Expected sum to be %d but found %d", sumLongs, sum)
   239  	}
   240  
   241  	for i := uint64(0); i < 10; i++ {
   242  		updateLongWindow(longWindow, i*8, i)
   243  		sum = sumLongWindow(longWindow, 10)
   244  		sumLongs += i
   245  
   246  		if sum != sumLongs {
   247  			t.Fatalf("Expected sum to be %d but found %d (iteration: %d)", sumLongs, sum, i)
   248  		}
   249  	}
   250  }
   251  
   252  func TestLongWindowOverflow(t *testing.T) {
   253  	longs := []uint64{0, 0, 0, 0, 0, 0, 0, 0, 2, math.MaxUint64 - 1}
   254  	longWindow := make([]byte, 10*8)
   255  	for i, long := range longs {
   256  		binary.BigEndian.PutUint64(longWindow[i*8:], long)
   257  	}
   258  
   259  	sum := sumLongWindow(longWindow, 10)
   260  	if sum != math.MaxUint64 {
   261  		t.Fatalf("Expected sum to be maxUint64 (%d), but found %d", uint64(math.MaxUint64), sum)
   262  	}
   263  
   264  	for i := uint64(0); i < 10; i++ {
   265  		updateLongWindow(longWindow, i*8, i)
   266  		sum = sumLongWindow(longWindow, 10)
   267  
   268  		if sum != math.MaxUint64 {
   269  			t.Fatalf("Expected sum to be maxUint64 (%d), but found %d", uint64(math.MaxUint64), sum)
   270  		}
   271  	}
   272  }
   273  
   274  func TestSelectBigWithinBounds(t *testing.T) {
   275  	type test struct {
   276  		lower, value, upper, expected *big.Int
   277  	}
   278  
   279  	var tests = map[string]test{
   280  		"value within bounds": {
   281  			lower:    big.NewInt(0),
   282  			value:    big.NewInt(5),
   283  			upper:    big.NewInt(10),
   284  			expected: big.NewInt(5),
   285  		},
   286  		"value below lower bound": {
   287  			lower:    big.NewInt(0),
   288  			value:    big.NewInt(-1),
   289  			upper:    big.NewInt(10),
   290  			expected: big.NewInt(0),
   291  		},
   292  		"value above upper bound": {
   293  			lower:    big.NewInt(0),
   294  			value:    big.NewInt(11),
   295  			upper:    big.NewInt(10),
   296  			expected: big.NewInt(10),
   297  		},
   298  		"value matches lower bound": {
   299  			lower:    big.NewInt(0),
   300  			value:    big.NewInt(0),
   301  			upper:    big.NewInt(10),
   302  			expected: big.NewInt(0),
   303  		},
   304  		"value matches upper bound": {
   305  			lower:    big.NewInt(0),
   306  			value:    big.NewInt(10),
   307  			upper:    big.NewInt(10),
   308  			expected: big.NewInt(10),
   309  		},
   310  	}
   311  
   312  	for name, test := range tests {
   313  		t.Run(name, func(t *testing.T) {
   314  			v := selectBigWithinBounds(test.lower, test.value, test.upper)
   315  			if v.Cmp(test.expected) != 0 {
   316  				t.Fatalf("Expected (%d), found (%d)", test.expected, v)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  // TestCalcBaseFeeAP4 confirms that the inclusion of ExtDataGasUsage increases
   323  // the base fee.
   324  func TestCalcBaseFeeAP4(t *testing.T) {
   325  	events := []struct {
   326  		block             blockDefinition
   327  		extDataFeeGreater bool
   328  	}{
   329  		{
   330  			block: blockDefinition{
   331  				timestamp:      1,
   332  				gasUsed:        1_000_000,
   333  				extDataGasUsed: big.NewInt(100_000),
   334  			},
   335  		},
   336  		{
   337  			block: blockDefinition{
   338  				timestamp:      3,
   339  				gasUsed:        1_000_000,
   340  				extDataGasUsed: big.NewInt(10_000),
   341  			},
   342  			extDataFeeGreater: true,
   343  		},
   344  		{
   345  			block: blockDefinition{
   346  				timestamp:      5,
   347  				gasUsed:        2_000_000,
   348  				extDataGasUsed: big.NewInt(50_000),
   349  			},
   350  			extDataFeeGreater: true,
   351  		},
   352  		{
   353  			block: blockDefinition{
   354  				timestamp:      5,
   355  				gasUsed:        6_000_000,
   356  				extDataGasUsed: big.NewInt(50_000),
   357  			},
   358  			extDataFeeGreater: true,
   359  		},
   360  		{
   361  			block: blockDefinition{
   362  				timestamp:      7,
   363  				gasUsed:        6_000_000,
   364  				extDataGasUsed: big.NewInt(0),
   365  			},
   366  			extDataFeeGreater: true,
   367  		},
   368  		{
   369  			block: blockDefinition{
   370  				timestamp:      1000,
   371  				gasUsed:        6_000_000,
   372  				extDataGasUsed: big.NewInt(0),
   373  			},
   374  		},
   375  		{
   376  			block: blockDefinition{
   377  				timestamp:      1001,
   378  				gasUsed:        6_000_000,
   379  				extDataGasUsed: big.NewInt(10_000),
   380  			},
   381  		},
   382  		{
   383  			block: blockDefinition{
   384  				timestamp:      1002,
   385  				gasUsed:        6_000_000,
   386  				extDataGasUsed: big.NewInt(0),
   387  			},
   388  			extDataFeeGreater: true,
   389  		},
   390  	}
   391  
   392  	header := &types.Header{
   393  		Time:    0,
   394  		GasUsed: 1_000_000,
   395  		Number:  big.NewInt(0),
   396  		BaseFee: big.NewInt(225 * params.GWei),
   397  		Extra:   nil,
   398  	}
   399  	extDataHeader := &types.Header{
   400  		Time:    0,
   401  		GasUsed: 1_000_000,
   402  		Number:  big.NewInt(0),
   403  		BaseFee: big.NewInt(225 * params.GWei),
   404  		Extra:   nil,
   405  		// ExtDataGasUsage is set to be nil to ensure CalcBaseFee can handle the
   406  		// AP3/AP4 boundary.
   407  	}
   408  
   409  	for index, event := range events {
   410  		block := event.block
   411  		nextExtraData, nextBaseFee, err := CalcBaseFee(params.TestApricotPhase4Config, header, block.timestamp)
   412  		assert.NoError(t, err)
   413  		log.Info("Update", "baseFee", nextBaseFee)
   414  		header = &types.Header{
   415  			Time:    block.timestamp,
   416  			GasUsed: block.gasUsed,
   417  			Number:  big.NewInt(int64(index) + 1),
   418  			BaseFee: nextBaseFee,
   419  			Extra:   nextExtraData,
   420  		}
   421  
   422  		nextExtraData, nextBaseFee, err = CalcBaseFee(params.TestApricotPhase4Config, extDataHeader, block.timestamp)
   423  		assert.NoError(t, err)
   424  		log.Info("Update", "baseFee (w/extData)", nextBaseFee)
   425  		extDataHeader = &types.Header{
   426  			Time:           block.timestamp,
   427  			GasUsed:        block.gasUsed,
   428  			Number:         big.NewInt(int64(index) + 1),
   429  			BaseFee:        nextBaseFee,
   430  			Extra:          nextExtraData,
   431  			ExtDataGasUsed: block.extDataGasUsed,
   432  		}
   433  
   434  		assert.Equal(t, event.extDataFeeGreater, extDataHeader.BaseFee.Cmp(header.BaseFee) == 1, "unexpected cmp for index %d", index)
   435  	}
   436  }
   437  
   438  func TestCalcBlockGasCost(t *testing.T) {
   439  	tests := map[string]struct {
   440  		parentBlockGasCost      *big.Int
   441  		parentTime, currentTime uint64
   442  
   443  		expected *big.Int
   444  	}{
   445  		"Nil parentBlockGasCost": {
   446  			parentBlockGasCost: nil,
   447  			parentTime:         1,
   448  			currentTime:        1,
   449  			expected:           ApricotPhase4MinBlockGasCost,
   450  		},
   451  		"Same timestamp from 0": {
   452  			parentBlockGasCost: big.NewInt(0),
   453  			parentTime:         1,
   454  			currentTime:        1,
   455  			expected:           big.NewInt(100_000),
   456  		},
   457  		"1s from 0": {
   458  			parentBlockGasCost: big.NewInt(0),
   459  			parentTime:         1,
   460  			currentTime:        2,
   461  			expected:           big.NewInt(50_000),
   462  		},
   463  		"Same timestamp from non-zero": {
   464  			parentBlockGasCost: big.NewInt(50_000),
   465  			parentTime:         1,
   466  			currentTime:        1,
   467  			expected:           big.NewInt(150_000),
   468  		},
   469  		"0s Difference (MAX)": {
   470  			parentBlockGasCost: big.NewInt(1_000_000),
   471  			parentTime:         1,
   472  			currentTime:        1,
   473  			expected:           big.NewInt(1_000_000),
   474  		},
   475  		"1s Difference (MAX)": {
   476  			parentBlockGasCost: big.NewInt(1_000_000),
   477  			parentTime:         1,
   478  			currentTime:        2,
   479  			expected:           big.NewInt(1_000_000),
   480  		},
   481  		"2s Difference": {
   482  			parentBlockGasCost: big.NewInt(900_000),
   483  			parentTime:         1,
   484  			currentTime:        3,
   485  			expected:           big.NewInt(900_000),
   486  		},
   487  		"3s Difference": {
   488  			parentBlockGasCost: big.NewInt(1_000_000),
   489  			parentTime:         1,
   490  			currentTime:        4,
   491  			expected:           big.NewInt(950_000),
   492  		},
   493  		"10s Difference": {
   494  			parentBlockGasCost: big.NewInt(1_000_000),
   495  			parentTime:         1,
   496  			currentTime:        11,
   497  			expected:           big.NewInt(600_000),
   498  		},
   499  		"20s Difference": {
   500  			parentBlockGasCost: big.NewInt(1_000_000),
   501  			parentTime:         1,
   502  			currentTime:        21,
   503  			expected:           big.NewInt(100_000),
   504  		},
   505  		"22s Difference": {
   506  			parentBlockGasCost: big.NewInt(1_000_000),
   507  			parentTime:         1,
   508  			currentTime:        23,
   509  			expected:           big.NewInt(0),
   510  		},
   511  		"23s Difference": {
   512  			parentBlockGasCost: big.NewInt(1_000_000),
   513  			parentTime:         1,
   514  			currentTime:        24,
   515  			expected:           big.NewInt(0),
   516  		},
   517  		"-1s Difference": {
   518  			parentBlockGasCost: big.NewInt(50_000),
   519  			parentTime:         1,
   520  			currentTime:        0,
   521  			expected:           big.NewInt(150_000),
   522  		},
   523  	}
   524  
   525  	for name, test := range tests {
   526  		t.Run(name, func(t *testing.T) {
   527  			assert.Zero(t, test.expected.Cmp(calcBlockGasCost(
   528  				ApricotPhase4TargetBlockRate,
   529  				ApricotPhase4MinBlockGasCost,
   530  				ApricotPhase4MaxBlockGasCost,
   531  				ApricotPhase4BlockGasCostStep,
   532  				test.parentBlockGasCost,
   533  				test.parentTime,
   534  				test.currentTime,
   535  			)))
   536  		})
   537  	}
   538  }