go-ethereum/eth/tracers/internal/tracetest/firehose/prestate_test.go
2025-06-06 15:53:31 -04:00

166 lines
4.8 KiB
Go

package firehose_test
import (
"encoding/json"
"math/big"
"os"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/internal/cli/server/chains"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func readPrestateData(t *testing.T, path string) *prestateData {
t.Helper()
// Call tracer test found, read if from disk
blob, err := os.ReadFile(path)
require.NoError(t, err)
test := new(prestateData)
require.NoError(t, json.Unmarshal(blob, test))
var genesisWithTD struct {
Genesis struct {
TotalDifficulty *math.HexOrDecimal256 `json:"totalDifficulty"`
} `json:"genesis"`
}
if err := json.Unmarshal(blob, &genesisWithTD); err == nil {
test.TotalDifficulty = (*big.Int)(genesisWithTD.Genesis.TotalDifficulty)
}
// Polygon overrides time based hard fork with block number based hard fork, let's try to handle that
// here.
//
// We read the "old" genesis Ethereum format which is time based, and we try to infer
// if we should set the time based hard fork values or not based on current time.
var genesisTimeBased struct {
Genesis struct {
Config struct {
ShanghaiTime *math.HexOrDecimal64 `json:"shanghaiTime"`
CancunTime *math.HexOrDecimal64 `json:"cancunTime"`
PragueTime *math.HexOrDecimal64 `json:"pragueTime"`
} `json:"config"`
} `json:"genesis"`
}
if err := json.Unmarshal(blob, &genesisTimeBased); err == nil {
timeBasedConfig := genesisTimeBased.Genesis.Config
blockBasedConfig := test.Genesis.Config
blockBasedConfig.CancunBlock = timeBasedToBlockBasedHardFork(timeBasedConfig.CancunTime)
blockBasedConfig.ShanghaiBlock = timeBasedToBlockBasedHardFork(timeBasedConfig.ShanghaiTime)
blockBasedConfig.PragueBlock = timeBasedToBlockBasedHardFork(timeBasedConfig.PragueTime)
}
chain, err := chains.GetChain("mainnet")
require.NoError(t, err)
test.Genesis.Config.Bor = chain.Genesis.Config.Bor
return test
}
func timeBasedToBlockBasedHardFork(in *math.HexOrDecimal64) *big.Int {
if in == nil {
return nil
}
// If the fork is active, we return 0 as the block number to activate to
if time.Now().Unix() > int64(*in) {
return big.NewInt(0)
}
// Otherwise, we return the block number that would be mined at that time
return nil
}
var _ core.ChainContext = (*prestateData)(nil)
type prestateData struct {
Genesis *core.Genesis `json:"genesis"`
Context *callContext `json:"context"`
Input string `json:"input"`
TotalDifficulty *big.Int `json:"-"`
TracerConfig json.RawMessage `json:"tracerConfig"`
// Populated after loading
genesisBlock *types.Block
}
// Config implements core.ChainContext.
func (p *prestateData) Config() *params.ChainConfig {
return p.Genesis.Config
}
// Engine implements core.ChainContext.
func (p *prestateData) Engine() consensus.Engine {
return ethash.NewFullFaker()
}
// GetHeader implements core.ChainContext.
func (p *prestateData) GetHeader(hash common.Hash, number uint64) *types.Header {
if p.Genesis == nil {
return nil
}
if p.genesisBlock == nil {
p.genesisBlock = p.Genesis.ToBlock()
}
if hash == p.genesisBlock.Hash() {
return p.genesisBlock.Header()
}
if number == p.genesisBlock.NumberU64() {
return p.genesisBlock.Header()
}
return nil
}
type callContext struct {
Number math.HexOrDecimal64 `json:"number"`
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
Time math.HexOrDecimal64 `json:"timestamp"`
GasLimit math.HexOrDecimal64 `json:"gasLimit"`
Miner common.Address `json:"miner"`
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
}
func (c *callContext) toBlockContext(genesis *core.Genesis) vm.BlockContext {
context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
Coinbase: c.Miner,
BlockNumber: new(big.Int).SetUint64(uint64(c.Number)),
Time: uint64(c.Time),
Difficulty: (*big.Int)(c.Difficulty),
GasLimit: uint64(c.GasLimit),
}
if genesis.Config.IsLondon(context.BlockNumber) {
context.BaseFee = (*big.Int)(c.BaseFee)
}
if genesis.Config.TerminalTotalDifficulty != nil && genesis.Config.TerminalTotalDifficulty.Sign() == 0 {
context.Random = &genesis.Mixhash
}
if genesis.ExcessBlobGas != nil && genesis.BlobGasUsed != nil {
header := &types.Header{Number: genesis.Config.CancunBlock, Time: 0}
excess := eip4844.CalcExcessBlobGas(genesis.Config, header, genesis.Timestamp)
header.ExcessBlobGas = &excess
context.BlobBaseFee = eip4844.CalcBlobFee(genesis.Config, header)
}
return context
}