Files

136 lines
3.5 KiB
Go

// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package header
import (
"bytes"
"errors"
"fmt"
"github.com/luxfi/evmgpu/params/extras"
"github.com/luxfi/evmgpu/plugin/evm/upgrade/feewindow"
"github.com/luxfi/geth/core/types"
)
const (
maximumExtraDataSize = 64 // Maximum size extra data may be after Genesis.
)
var (
errInvalidExtraPrefix = errors.New("invalid header.Extra prefix")
errInvalidExtraLength = errors.New("invalid header.Extra length")
)
// ExtraPrefix takes the previous header and the timestamp of its child
// block and calculates the expected extra prefix for the child block.
func ExtraPrefix(
config *extras.ChainConfig,
parent *types.Header,
header *types.Header,
) ([]byte, error) {
switch {
case config.IsEVM(header.Time):
window, err := feeWindow(config, parent, header.Time)
if err != nil {
return nil, fmt.Errorf("failed to calculate fee window: %w", err)
}
return window.Bytes(), nil
default:
// Prior to EVM there was no expected extra prefix.
return nil, nil
}
}
// VerifyExtraPrefix verifies that the header's Extra field is correctly
// formatted.
func VerifyExtraPrefix(
config *extras.ChainConfig,
parent *types.Header,
header *types.Header,
) error {
switch {
case config.IsEVM(header.Time):
feeWindow, err := feeWindow(config, parent, header.Time)
if err != nil {
return fmt.Errorf("calculating expected fee window: %w", err)
}
feeWindowBytes := feeWindow.Bytes()
if !bytes.HasPrefix(header.Extra, feeWindowBytes) {
return fmt.Errorf("%w: expected %x as prefix, found %x",
errInvalidExtraPrefix,
feeWindowBytes,
header.Extra,
)
}
}
return nil
}
// VerifyExtra verifies that the header's Extra field is correctly formatted for
// rules.
//
// TODO: Should this be merged with VerifyExtraPrefix?
func VerifyExtra(rules extras.LuxRules, extra []byte) error {
extraLen := len(extra)
switch {
case rules.IsDurango:
if extraLen < feewindow.WindowSize {
return fmt.Errorf(
"%w: expected >= %d but got %d",
errInvalidExtraLength,
feewindow.WindowSize,
extraLen,
)
}
case rules.IsEVM:
if extraLen != feewindow.WindowSize {
return fmt.Errorf(
"%w: expected %d but got %d",
errInvalidExtraLength,
feewindow.WindowSize,
extraLen,
)
}
default:
if uint64(extraLen) > maximumExtraDataSize {
return fmt.Errorf(
"%w: expected <= %d but got %d",
errInvalidExtraLength,
maximumExtraDataSize,
extraLen,
)
}
}
return nil
}
// PredicateBytesFromExtra returns the predicate result bytes from the header's
// extra data. If the extra data is not long enough, an empty slice is returned.
func PredicateBytesFromExtra(extra []byte) []byte {
offset := feewindow.WindowSize
// Prior to Durango, the VM enforces the extra data is smaller than or equal
// to `offset`.
// After Durango, the VM pre-verifies the extra data past `offset` is valid.
if len(extra) <= offset {
return nil
}
return extra[offset:]
}
// SetPredicateBytesInExtra sets the predicate result bytes in the header's extra
// data. If the extra data is not long enough (i.e., an incomplete header.Extra
// as built in the miner), it is padded with zeros.
func SetPredicateBytesInExtra(extra []byte, predicateBytes []byte) []byte {
offset := feewindow.WindowSize
if len(extra) < offset {
// pad extra with zeros
extra = append(extra, make([]byte, offset-len(extra))...)
} else {
// truncate extra to the offset
extra = extra[:offset]
}
extra = append(extra, predicateBytes...)
return extra
}