github.com/cgcardona/r-subnet-evm@v0.1.5/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/cgcardona/r-subnet-evm/commontype"
    12  	"github.com/cgcardona/r-subnet-evm/core/types"
    13  	"github.com/cgcardona/r-subnet-evm/params"
    14  	"github.com/ethereum/go-ethereum/common/math"
    15  	"github.com/ethereum/go-ethereum/log"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  var testMinBaseFee = big.NewInt(75_000_000_000)
    20  
    21  func testRollup(t *testing.T, longs []uint64, roll int) {
    22  	slice := make([]byte, len(longs)*8)
    23  	numLongs := len(longs)
    24  	for i := 0; i < numLongs; i++ {
    25  		binary.BigEndian.PutUint64(slice[8*i:], longs[i])
    26  	}
    27  
    28  	newSlice, err := rollLongWindow(slice, roll)
    29  	if err != nil {
    30  		t.Fatal(err)
    31  	}
    32  	// numCopies is the number of longs that should have been copied over from the previous
    33  	// slice as opposed to being left empty.
    34  	numCopies := numLongs - roll
    35  	for i := 0; i < numLongs; i++ {
    36  		// Extract the long value that is encoded at position [i] in [newSlice]
    37  		num := binary.BigEndian.Uint64(newSlice[8*i:])
    38  		// If the current index is past the point where we should have copied the value
    39  		// over from the previous slice, assert that the value encoded in [newSlice]
    40  		// is 0
    41  		if i >= numCopies {
    42  			if num != 0 {
    43  				t.Errorf("Expected num encoded in newSlice at position %d to be 0, but found %d", i, num)
    44  			}
    45  		} else {
    46  			// Otherwise, check that the value was copied over correctly
    47  			prevIndex := i + roll
    48  			prevNum := longs[prevIndex]
    49  			if prevNum != num {
    50  				t.Errorf("Expected num encoded in new slice at position %d to be %d, but found %d", i, prevNum, num)
    51  			}
    52  		}
    53  	}
    54  }
    55  
    56  func TestRollupWindow(t *testing.T) {
    57  	type test struct {
    58  		longs []uint64
    59  		roll  int
    60  	}
    61  
    62  	var tests []test = []test{
    63  		{
    64  			[]uint64{1, 2, 3, 4},
    65  			0,
    66  		},
    67  		{
    68  			[]uint64{1, 2, 3, 4},
    69  			1,
    70  		},
    71  		{
    72  			[]uint64{1, 2, 3, 4},
    73  			2,
    74  		},
    75  		{
    76  			[]uint64{1, 2, 3, 4},
    77  			3,
    78  		},
    79  		{
    80  			[]uint64{1, 2, 3, 4},
    81  			4,
    82  		},
    83  		{
    84  			[]uint64{1, 2, 3, 4},
    85  			5,
    86  		},
    87  		{
    88  			[]uint64{121, 232, 432},
    89  			2,
    90  		},
    91  	}
    92  
    93  	for _, test := range tests {
    94  		testRollup(t, test.longs, test.roll)
    95  	}
    96  }
    97  
    98  type blockDefinition struct {
    99  	timestamp uint64
   100  	gasUsed   uint64
   101  }
   102  
   103  type test struct {
   104  	baseFee   *big.Int
   105  	genBlocks func() []blockDefinition
   106  	minFee    *big.Int
   107  }
   108  
   109  func TestDynamicFees(t *testing.T) {
   110  	spacedTimestamps := []uint64{1, 1, 2, 5, 15, 120}
   111  
   112  	var tests []test = []test{
   113  		// Test minimal gas usage
   114  		{
   115  			baseFee: nil,
   116  			minFee:  testMinBaseFee,
   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  			baseFee: nil,
   131  			minFee:  testMinBaseFee,
   132  			genBlocks: func() []blockDefinition {
   133  				blocks := make([]blockDefinition, 0, len(spacedTimestamps))
   134  				for _, timestamp := range spacedTimestamps {
   135  					blocks = append(blocks, blockDefinition{
   136  						timestamp: timestamp,
   137  						gasUsed:   math.MaxUint64,
   138  					})
   139  				}
   140  				return blocks
   141  			},
   142  		},
   143  		// Test update increase handling
   144  		{
   145  			baseFee: big.NewInt(50_000_000_000),
   146  			minFee:  testMinBaseFee,
   147  			genBlocks: func() []blockDefinition {
   148  				blocks := make([]blockDefinition, 0, len(spacedTimestamps))
   149  				for _, timestamp := range spacedTimestamps {
   150  					blocks = append(blocks, blockDefinition{
   151  						timestamp: timestamp,
   152  						gasUsed:   math.MaxUint64,
   153  					})
   154  				}
   155  				return blocks
   156  			},
   157  		},
   158  		{
   159  			baseFee: nil,
   160  			minFee:  testMinBaseFee,
   161  			genBlocks: func() []blockDefinition {
   162  				return []blockDefinition{
   163  					{
   164  						timestamp: 1,
   165  						gasUsed:   1_000_000,
   166  					},
   167  					{
   168  						timestamp: 3,
   169  						gasUsed:   1_000_000,
   170  					},
   171  					{
   172  						timestamp: 5,
   173  						gasUsed:   2_000_000,
   174  					},
   175  					{
   176  						timestamp: 5,
   177  						gasUsed:   6_000_000,
   178  					},
   179  					{
   180  						timestamp: 7,
   181  						gasUsed:   6_000_000,
   182  					},
   183  					{
   184  						timestamp: 1000,
   185  						gasUsed:   6_000_000,
   186  					},
   187  					{
   188  						timestamp: 1001,
   189  						gasUsed:   6_000_000,
   190  					},
   191  					{
   192  						timestamp: 1002,
   193  						gasUsed:   6_000_000,
   194  					},
   195  				}
   196  			},
   197  		},
   198  	}
   199  
   200  	for _, test := range tests {
   201  		testDynamicFeesStaysWithinRange(t, test)
   202  	}
   203  }
   204  
   205  func testDynamicFeesStaysWithinRange(t *testing.T, test test) {
   206  	blocks := test.genBlocks()
   207  	initialBlock := blocks[0]
   208  	header := &types.Header{
   209  		Time:    initialBlock.timestamp,
   210  		GasUsed: initialBlock.gasUsed,
   211  		Number:  big.NewInt(0),
   212  		BaseFee: test.baseFee,
   213  	}
   214  
   215  	for index, block := range blocks[1:] {
   216  		testFeeConfig := commontype.FeeConfig{
   217  			GasLimit:        big.NewInt(8_000_000),
   218  			TargetBlockRate: 2, // in seconds
   219  
   220  			MinBaseFee:               test.minFee,
   221  			TargetGas:                big.NewInt(15_000_000),
   222  			BaseFeeChangeDenominator: big.NewInt(36),
   223  
   224  			MinBlockGasCost:  big.NewInt(0),
   225  			MaxBlockGasCost:  big.NewInt(1_000_000),
   226  			BlockGasCostStep: big.NewInt(200_000),
   227  		}
   228  
   229  		nextExtraData, nextBaseFee, err := CalcBaseFee(params.TestChainConfig, testFeeConfig, header, block.timestamp)
   230  		if err != nil {
   231  			t.Fatalf("Failed to calculate base fee at index %d: %s", index, err)
   232  		}
   233  		if nextBaseFee.Cmp(test.minFee) < 0 {
   234  			t.Fatalf("Expected fee to stay greater than %d, but found %d", test.minFee, nextBaseFee)
   235  		}
   236  		log.Info("Update", "baseFee", nextBaseFee)
   237  		header = &types.Header{
   238  			Time:    block.timestamp,
   239  			GasUsed: block.gasUsed,
   240  			Number:  big.NewInt(int64(index) + 1),
   241  			BaseFee: nextBaseFee,
   242  			Extra:   nextExtraData,
   243  		}
   244  	}
   245  }
   246  
   247  func TestLongWindow(t *testing.T) {
   248  	longs := []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   249  	sumLongs := uint64(0)
   250  	longWindow := make([]byte, 10*8)
   251  	for i, long := range longs {
   252  		sumLongs = sumLongs + long
   253  		binary.BigEndian.PutUint64(longWindow[i*8:], long)
   254  	}
   255  
   256  	sum := sumLongWindow(longWindow, 10)
   257  	if sum != sumLongs {
   258  		t.Fatalf("Expected sum to be %d but found %d", sumLongs, sum)
   259  	}
   260  
   261  	for i := uint64(0); i < 10; i++ {
   262  		updateLongWindow(longWindow, i*8, i)
   263  		sum = sumLongWindow(longWindow, 10)
   264  		sumLongs += i
   265  
   266  		if sum != sumLongs {
   267  			t.Fatalf("Expected sum to be %d but found %d (iteration: %d)", sumLongs, sum, i)
   268  		}
   269  	}
   270  }
   271  
   272  func TestLongWindowOverflow(t *testing.T) {
   273  	longs := []uint64{0, 0, 0, 0, 0, 0, 0, 0, 2, math.MaxUint64 - 1}
   274  	longWindow := make([]byte, 10*8)
   275  	for i, long := range longs {
   276  		binary.BigEndian.PutUint64(longWindow[i*8:], long)
   277  	}
   278  
   279  	sum := sumLongWindow(longWindow, 10)
   280  	if sum != math.MaxUint64 {
   281  		t.Fatalf("Expected sum to be maxUint64 (%d), but found %d", uint64(math.MaxUint64), sum)
   282  	}
   283  
   284  	for i := uint64(0); i < 10; i++ {
   285  		updateLongWindow(longWindow, i*8, i)
   286  		sum = sumLongWindow(longWindow, 10)
   287  
   288  		if sum != math.MaxUint64 {
   289  			t.Fatalf("Expected sum to be maxUint64 (%d), but found %d", uint64(math.MaxUint64), sum)
   290  		}
   291  	}
   292  }
   293  
   294  func TestSelectBigWithinBounds(t *testing.T) {
   295  	type test struct {
   296  		lower, value, upper, expected *big.Int
   297  	}
   298  
   299  	tests := map[string]test{
   300  		"value within bounds": {
   301  			lower:    big.NewInt(0),
   302  			value:    big.NewInt(5),
   303  			upper:    big.NewInt(10),
   304  			expected: big.NewInt(5),
   305  		},
   306  		"value below lower bound": {
   307  			lower:    big.NewInt(0),
   308  			value:    big.NewInt(-1),
   309  			upper:    big.NewInt(10),
   310  			expected: big.NewInt(0),
   311  		},
   312  		"value above upper bound": {
   313  			lower:    big.NewInt(0),
   314  			value:    big.NewInt(11),
   315  			upper:    big.NewInt(10),
   316  			expected: big.NewInt(10),
   317  		},
   318  		"value matches lower bound": {
   319  			lower:    big.NewInt(0),
   320  			value:    big.NewInt(0),
   321  			upper:    big.NewInt(10),
   322  			expected: big.NewInt(0),
   323  		},
   324  		"value matches upper bound": {
   325  			lower:    big.NewInt(0),
   326  			value:    big.NewInt(10),
   327  			upper:    big.NewInt(10),
   328  			expected: big.NewInt(10),
   329  		},
   330  	}
   331  
   332  	for name, test := range tests {
   333  		t.Run(name, func(t *testing.T) {
   334  			v := selectBigWithinBounds(test.lower, test.value, test.upper)
   335  			if v.Cmp(test.expected) != 0 {
   336  				t.Fatalf("Expected (%d), found (%d)", test.expected, v)
   337  			}
   338  		})
   339  	}
   340  }
   341  
   342  func TestCalcBlockGasCost(t *testing.T) {
   343  	tests := map[string]struct {
   344  		parentBlockGasCost      *big.Int
   345  		parentTime, currentTime uint64
   346  
   347  		expected *big.Int
   348  	}{
   349  		"Nil parentBlockGasCost": {
   350  			parentBlockGasCost: nil,
   351  			parentTime:         1,
   352  			currentTime:        1,
   353  			expected:           params.DefaultFeeConfig.MinBlockGasCost,
   354  		},
   355  		"Same timestamp from 0": {
   356  			parentBlockGasCost: big.NewInt(0),
   357  			parentTime:         1,
   358  			currentTime:        1,
   359  			expected:           big.NewInt(100_000),
   360  		},
   361  		"1s from 0": {
   362  			parentBlockGasCost: big.NewInt(0),
   363  			parentTime:         1,
   364  			currentTime:        2,
   365  			expected:           big.NewInt(50_000),
   366  		},
   367  		"Same timestamp from non-zero": {
   368  			parentBlockGasCost: big.NewInt(50_000),
   369  			parentTime:         1,
   370  			currentTime:        1,
   371  			expected:           big.NewInt(150_000),
   372  		},
   373  		"0s Difference (MAX)": {
   374  			parentBlockGasCost: big.NewInt(1_000_000),
   375  			parentTime:         1,
   376  			currentTime:        1,
   377  			expected:           big.NewInt(1_000_000),
   378  		},
   379  		"1s Difference (MAX)": {
   380  			parentBlockGasCost: big.NewInt(1_000_000),
   381  			parentTime:         1,
   382  			currentTime:        2,
   383  			expected:           big.NewInt(1_000_000),
   384  		},
   385  		"2s Difference": {
   386  			parentBlockGasCost: big.NewInt(900_000),
   387  			parentTime:         1,
   388  			currentTime:        3,
   389  			expected:           big.NewInt(900_000),
   390  		},
   391  		"3s Difference": {
   392  			parentBlockGasCost: big.NewInt(1_000_000),
   393  			parentTime:         1,
   394  			currentTime:        4,
   395  			expected:           big.NewInt(950_000),
   396  		},
   397  		"10s Difference": {
   398  			parentBlockGasCost: big.NewInt(1_000_000),
   399  			parentTime:         1,
   400  			currentTime:        11,
   401  			expected:           big.NewInt(600_000),
   402  		},
   403  		"20s Difference": {
   404  			parentBlockGasCost: big.NewInt(1_000_000),
   405  			parentTime:         1,
   406  			currentTime:        21,
   407  			expected:           big.NewInt(100_000),
   408  		},
   409  		"22s Difference": {
   410  			parentBlockGasCost: big.NewInt(1_000_000),
   411  			parentTime:         1,
   412  			currentTime:        23,
   413  			expected:           big.NewInt(0),
   414  		},
   415  		"23s Difference": {
   416  			parentBlockGasCost: big.NewInt(1_000_000),
   417  			parentTime:         1,
   418  			currentTime:        24,
   419  			expected:           big.NewInt(0),
   420  		},
   421  		"-1s Difference": {
   422  			parentBlockGasCost: big.NewInt(50_000),
   423  			parentTime:         1,
   424  			currentTime:        0,
   425  			expected:           big.NewInt(150_000),
   426  		},
   427  	}
   428  
   429  	for name, test := range tests {
   430  		t.Run(name, func(t *testing.T) {
   431  			assert.Zero(t, test.expected.Cmp(calcBlockGasCost(
   432  				params.DefaultFeeConfig.TargetBlockRate,
   433  				params.DefaultFeeConfig.MinBlockGasCost,
   434  				params.DefaultFeeConfig.MaxBlockGasCost,
   435  				testBlockGasCostStep,
   436  				test.parentBlockGasCost,
   437  				test.parentTime,
   438  				test.currentTime,
   439  			)))
   440  		})
   441  	}
   442  }