Skip to content

Commit

Permalink
Merge branch 'master' into qdm12/libevm/metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 authored Feb 7, 2025
2 parents 82af695 + 235e9a5 commit 2fa1b87
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 263 deletions.
88 changes: 88 additions & 0 deletions consensus/dummy/dynamic_fee_window.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// (c) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package dummy

import (
"encoding/binary"
"errors"
"fmt"

"github.com/ava-labs/avalanchego/utils/wrappers"
"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common/math"
)

var ErrDynamicFeeWindowInsufficientLength = errors.New("insufficient length for dynamic fee window")

// DynamicFeeWindow is a window of the last [params.RollupWindow] seconds of gas
// usage.
//
// Index 0 is the oldest entry, and [params.RollupWindow]-1 is the current
// entry.
type DynamicFeeWindow [params.RollupWindow]uint64

func ParseDynamicFeeWindow(bytes []byte) (DynamicFeeWindow, error) {
if len(bytes) < params.DynamicFeeExtraDataSize {
return DynamicFeeWindow{}, fmt.Errorf("%w: expected at least %d bytes but got %d bytes",
ErrDynamicFeeWindowInsufficientLength,
params.DynamicFeeExtraDataSize,
len(bytes),
)
}

var window DynamicFeeWindow
for i := range window {
offset := i * wrappers.LongLen
window[i] = binary.BigEndian.Uint64(bytes[offset:])
}
return window, nil
}

// Add adds the amounts to the most recent entry in the window.
//
// If the most recent entry overflows, it is set to [math.MaxUint64].
func (w *DynamicFeeWindow) Add(amounts ...uint64) {
const lastIndex uint = params.RollupWindow - 1
w[lastIndex] = add(w[lastIndex], amounts...)
}

// Shift removes the oldest n entries from the window and adds n new empty
// entries.
func (w *DynamicFeeWindow) Shift(n uint64) {
if n >= params.RollupWindow {
*w = DynamicFeeWindow{}
return
}

var newWindow DynamicFeeWindow
copy(newWindow[:], w[n:])
*w = newWindow
}

// Sum returns the sum of all the entries in the window.
//
// If the sum overflows, [math.MaxUint64] is returned.
func (w *DynamicFeeWindow) Sum() uint64 {
return add(0, w[:]...)
}

func (w *DynamicFeeWindow) Bytes() []byte {
bytes := make([]byte, params.DynamicFeeExtraDataSize)
for i, v := range w {
offset := i * wrappers.LongLen
binary.BigEndian.PutUint64(bytes[offset:], v)
}
return bytes
}

func add(sum uint64, values ...uint64) uint64 {
var overflow bool
for _, v := range values {
sum, overflow = math.SafeAdd(sum, v)
if overflow {
return math.MaxUint64
}
}
return sum
}
246 changes: 246 additions & 0 deletions consensus/dummy/dynamic_fee_window_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// (c) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package dummy

import (
"strconv"
"testing"

"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common/math"
"github.com/stretchr/testify/require"
)

func TestDynamicFeeWindow_Add(t *testing.T) {
tests := []struct {
name string
window DynamicFeeWindow
amount uint64
expected DynamicFeeWindow
}{
{
name: "normal_addition",
window: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
},
amount: 5,
expected: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, 15,
},
},
{
name: "amount_overflow",
window: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
},
amount: math.MaxUint64,
expected: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, math.MaxUint64,
},
},
{
name: "window_overflow",
window: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, math.MaxUint64,
},
amount: 5,
expected: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, math.MaxUint64,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
test.window.Add(test.amount)
require.Equal(t, test.expected, test.window)
})
}
}

func TestDynamicFeeWindow_Shift(t *testing.T) {
window := DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
}
tests := []struct {
n uint64
expected DynamicFeeWindow
}{
{
n: 0,
expected: window,
},
{
n: 1,
expected: DynamicFeeWindow{
2, 3, 4, 5, 6, 7, 8, 9, 10,
},
},
{
n: 2,
expected: DynamicFeeWindow{
3, 4, 5, 6, 7, 8, 9, 10,
},
},
{
n: 3,
expected: DynamicFeeWindow{
4, 5, 6, 7, 8, 9, 10,
},
},
{
n: 4,
expected: DynamicFeeWindow{
5, 6, 7, 8, 9, 10,
},
},
{
n: 5,
expected: DynamicFeeWindow{
6, 7, 8, 9, 10,
},
},
{
n: 6,
expected: DynamicFeeWindow{
7, 8, 9, 10,
},
},
{
n: 7,
expected: DynamicFeeWindow{
8, 9, 10,
},
},
{
n: 8,
expected: DynamicFeeWindow{
9, 10,
},
},
{
n: 9,
expected: DynamicFeeWindow{
10,
},
},
{
n: 10,
expected: DynamicFeeWindow{},
},
{
n: 100,
expected: DynamicFeeWindow{},
},
}
for _, test := range tests {
t.Run(strconv.FormatUint(test.n, 10), func(t *testing.T) {
window := window
window.Shift(test.n)
require.Equal(t, test.expected, window)
})
}
}

func TestDynamicFeeWindow_Sum(t *testing.T) {
tests := []struct {
name string
window DynamicFeeWindow
expected uint64
}{
{
name: "empty",
window: DynamicFeeWindow{},
expected: 0,
},
{
name: "full",
window: DynamicFeeWindow{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
},
expected: 55,
},
{
name: "overflow",
window: DynamicFeeWindow{
math.MaxUint64, 2, 3, 4, 5, 6, 7, 8, 9, 10,
},
expected: math.MaxUint64,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, test.window.Sum())
})
}
}

func TestDynamicFeeWindow_Bytes(t *testing.T) {
tests := []struct {
name string
bytes []byte
window DynamicFeeWindow
parseErr error
}{
{
name: "insufficient_length",
bytes: make([]byte, params.DynamicFeeExtraDataSize-1),
parseErr: ErrDynamicFeeWindowInsufficientLength,
},
{
name: "zero_window",
bytes: make([]byte, params.DynamicFeeExtraDataSize),
window: DynamicFeeWindow{},
},
{
name: "truncate_bytes",
bytes: []byte{
params.DynamicFeeExtraDataSize: 1,
},
window: DynamicFeeWindow{},
},
{
name: "endianess",
bytes: []byte{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
},
window: DynamicFeeWindow{
0x0102030405060708,
0x1112131415161718,
0x2122232425262728,
0x3132333435363738,
0x4142434445464748,
0x5152535455565758,
0x6162636465666768,
0x7172737475767778,
0x8182838485868788,
0x9192939495969798,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require := require.New(t)

window, err := ParseDynamicFeeWindow(test.bytes)
require.Equal(test.window, window)
require.ErrorIs(err, test.parseErr)
if test.parseErr != nil {
return
}

expectedBytes := test.bytes[:params.DynamicFeeExtraDataSize]
bytes := window.Bytes()
require.Equal(expectedBytes, bytes)
})
}
}
Loading

0 comments on commit 2fa1b87

Please sign in to comment.