mirror of
https://github.com/luxfi/evmgpu.git
synced 2026-10-30 16:57:11 +00:00
GetRulesExtra derived a Rules' Lux rules from a process-wide last context: the chain config and timestamp of whichever Rules any goroutine evaluated most recently. Rules are evaluated concurrently, by block processing, the miner, the tx pool and every eth_call, so a Rules could read another evaluation's upgrades: its IsDurango, its predicaters, its enabled precompiles, and the intrinsic gas that depends on them. The precompile overrider read the same global when the EVM asked it for a precompile, so which Lux precompiles a call reached could follow another goroutine's block. The context now travels with the Rules value. core's rules hook already put the chain config and timestamp in Rules.Payload for its precompile overrider; the overrider answers them, and GetRulesExtra reads them from the Rules it is given. The overrider decides its own enabled set from them too. RulesAt attaches them where no hook did. The global and SetRulesContext are gone. Every call site evaluates its Rules for the block it reads them for, so a caller that no other goroutine races sees the same rules as before. The blob pool's state checks passed a zero Rules and read the head's context the check before them had left; they now pass the head's Rules. TestRulesExtraIsTheRulesOwn evaluates two rule sets and reads the first; TestPrecompileOverrideIsTheRulesOwn asks each one's overrider for the tx allow list after the other was evaluated, in both orders; TestRulesExtraUnderConcurrentEvaluation reads four rule sets on four goroutines at once, across two times and two chains, including the intrinsic gas of a creation and the precompile a call reaches. TestActivePrecompiles, skipped for this bug, runs again. Co-authored-by: Hanzo Dev <[email protected]>
248 lines
7.5 KiB
Go
248 lines
7.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package core
|
|
|
|
import (
|
|
"context"
|
|
"math/big"
|
|
|
|
"github.com/holiman/uint256"
|
|
"github.com/luxfi/evmgpu/commontype"
|
|
"github.com/luxfi/evmgpu/params"
|
|
"github.com/luxfi/evmgpu/precompile/contract"
|
|
"github.com/luxfi/evmgpu/precompile/modules"
|
|
"github.com/luxfi/evmgpu/precompile/precompileconfig"
|
|
"github.com/luxfi/geth/common"
|
|
"github.com/luxfi/geth/core/tracing"
|
|
"github.com/luxfi/geth/core/types"
|
|
"github.com/luxfi/geth/core/vm"
|
|
gethparams "github.com/luxfi/geth/params"
|
|
)
|
|
|
|
func init() {
|
|
// Register the rules hook to populate Rules.Payload with our PrecompileOverrider
|
|
gethparams.SetRulesHook(precompileHook)
|
|
}
|
|
|
|
// precompileHook populates Rules.Payload with the LuxPrecompileOverrider,
|
|
// which carries the chain config and timestamp these Rules were evaluated at:
|
|
// params.GetRulesExtra reads them from there.
|
|
func precompileHook(c *gethparams.ChainConfig, rules *gethparams.Rules, num *big.Int, isMerge bool, timestamp uint64) {
|
|
rules.Payload = &LuxPrecompileOverrider{
|
|
chainConfig: c,
|
|
timestamp: timestamp,
|
|
}
|
|
}
|
|
|
|
// LuxPrecompileOverrider implements vm.PrecompileOverrider to provide
|
|
// custom Lux precompiles (warp, nativeminter, etc.) to the EVM.
|
|
type LuxPrecompileOverrider struct {
|
|
chainConfig *gethparams.ChainConfig
|
|
timestamp uint64
|
|
}
|
|
|
|
// RulesContext is the chain config and timestamp the Rules carrying this
|
|
// overrider were evaluated at, which params.GetRulesExtra derives their Lux
|
|
// rules from.
|
|
func (o *LuxPrecompileOverrider) RulesContext() (*gethparams.ChainConfig, uint64) {
|
|
return o.chainConfig, o.timestamp
|
|
}
|
|
|
|
// PrecompileOverride returns the precompile at the given address if it's
|
|
// an active Lux custom precompile.
|
|
//
|
|
// The enabled set is computed from this overrider's own chain config and
|
|
// timestamp, set in precompileHook when the EVM's Rules were evaluated. The EVM
|
|
// asks lazily, when a CALL targets the address, and other goroutines evaluate
|
|
// Rules for other chains and times in between; deciding from o.timestamp makes
|
|
// every replay of a given block see the same enabled set. params.ChainConfig is
|
|
// a type alias of geth's ChainConfig, so o.chainConfig is passed directly.
|
|
func (o *LuxPrecompileOverrider) PrecompileOverride(addr common.Address) (vm.PrecompiledContract, bool) {
|
|
extrasRules := params.GetExtrasRules(gethparams.Rules{}, o.chainConfig, o.timestamp)
|
|
if cfg, ok := extrasRules.Precompiles[addr]; !ok || cfg.IsDisabled() {
|
|
return nil, false
|
|
}
|
|
|
|
// Find the module for this address
|
|
for _, module := range modules.RegisteredModules() {
|
|
if module.Address == addr && module.Contract != nil {
|
|
// Wrap the contract in our adapter
|
|
return &precompileAdapter{
|
|
address: addr,
|
|
contract: module.Contract,
|
|
}, true
|
|
}
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
// precompileAdapter wraps a contract.StatefulPrecompiledContract to implement
|
|
// geth's vm.StatefulPrecompiledContract interface.
|
|
type precompileAdapter struct {
|
|
address common.Address
|
|
contract contract.StatefulPrecompiledContract
|
|
}
|
|
|
|
// Name returns the precompile name
|
|
func (p *precompileAdapter) Name() string {
|
|
// Get the config key for this address
|
|
for _, module := range modules.RegisteredModules() {
|
|
if module.Address == p.address {
|
|
return module.ConfigKey
|
|
}
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
// RequiredGas returns the gas required for this precompile
|
|
func (p *precompileAdapter) RequiredGas(input []byte) uint64 {
|
|
// For stateful precompiles, gas is calculated in RunStateful
|
|
return 0
|
|
}
|
|
|
|
// Run implements PrecompiledContract.Run (stateless - not used for stateful precompiles)
|
|
func (p *precompileAdapter) Run(input []byte) ([]byte, error) {
|
|
// Stateful precompiles use RunStateful instead
|
|
return nil, vm.ErrExecutionReverted
|
|
}
|
|
|
|
// RunStateful implements StatefulPrecompiledContract.RunStateful
|
|
func (p *precompileAdapter) RunStateful(env vm.PrecompileEnvironment, input []byte, suppliedGas uint64) ([]byte, uint64, error) {
|
|
// Create an AccessibleState adapter
|
|
accessibleState := &accessibleStateAdapter{env: env}
|
|
|
|
// Get caller and self addresses from environment
|
|
addresses := env.Addresses()
|
|
|
|
// Call the inner precompile
|
|
ret, remainingGas, err := p.contract.Run(
|
|
accessibleState,
|
|
addresses.Caller,
|
|
addresses.Self,
|
|
input,
|
|
suppliedGas,
|
|
env.ReadOnly(),
|
|
)
|
|
|
|
return ret, remainingGas, err
|
|
}
|
|
|
|
// accessibleStateAdapter adapts vm.PrecompileEnvironment to contract.AccessibleState
|
|
type accessibleStateAdapter struct {
|
|
env vm.PrecompileEnvironment
|
|
}
|
|
|
|
func (a *accessibleStateAdapter) GetStateDB() contract.StateDB {
|
|
return &stateDBAdapter{stateDB: a.env.StateDB()}
|
|
}
|
|
|
|
func (a *accessibleStateAdapter) GetBlockContext() contract.BlockContext {
|
|
return &blockContextAdapter{env: a.env}
|
|
}
|
|
|
|
func (a *accessibleStateAdapter) GetConsensusContext() context.Context {
|
|
return a.env.ConsensusContext()
|
|
}
|
|
|
|
func (a *accessibleStateAdapter) GetChainConfig() precompileconfig.ChainConfig {
|
|
return &chainConfigAdapter{config: a.env.ChainConfig()}
|
|
}
|
|
|
|
// stateDBAdapter adapts vm.StateDB to contract.StateDB
|
|
type stateDBAdapter struct {
|
|
stateDB vm.StateDB
|
|
}
|
|
|
|
func (s *stateDBAdapter) GetState(addr common.Address, hash common.Hash) common.Hash {
|
|
return s.stateDB.GetState(addr, hash)
|
|
}
|
|
|
|
func (s *stateDBAdapter) SetState(addr common.Address, key, value common.Hash) {
|
|
s.stateDB.SetState(addr, key, value)
|
|
}
|
|
|
|
func (s *stateDBAdapter) SetNonce(addr common.Address, nonce uint64) {
|
|
s.stateDB.SetNonce(addr, nonce, tracing.NonceChangeUnspecified)
|
|
}
|
|
|
|
func (s *stateDBAdapter) GetNonce(addr common.Address) uint64 {
|
|
return s.stateDB.GetNonce(addr)
|
|
}
|
|
|
|
func (s *stateDBAdapter) GetBalance(addr common.Address) *uint256.Int {
|
|
return s.stateDB.GetBalance(addr)
|
|
}
|
|
|
|
func (s *stateDBAdapter) AddBalance(addr common.Address, amount *uint256.Int) {
|
|
s.stateDB.AddBalance(addr, amount, tracing.BalanceChangeUnspecified)
|
|
}
|
|
|
|
func (s *stateDBAdapter) CreateAccount(addr common.Address) {
|
|
s.stateDB.CreateAccount(addr)
|
|
}
|
|
|
|
func (s *stateDBAdapter) Exist(addr common.Address) bool {
|
|
return s.stateDB.Exist(addr)
|
|
}
|
|
|
|
func (s *stateDBAdapter) AddLog(log *types.Log) {
|
|
s.stateDB.AddLog(log)
|
|
}
|
|
|
|
func (s *stateDBAdapter) GetPredicateStorageSlots(addr common.Address, index int) ([]byte, bool) {
|
|
// This requires accessing the predicate storage which is not exposed in the standard StateDB
|
|
// For now, return empty - this is only used for advanced predicates
|
|
return nil, false
|
|
}
|
|
|
|
func (s *stateDBAdapter) GetTxHash() common.Hash {
|
|
return s.stateDB.TxHash()
|
|
}
|
|
|
|
func (s *stateDBAdapter) Snapshot() int {
|
|
return s.stateDB.Snapshot()
|
|
}
|
|
|
|
func (s *stateDBAdapter) RevertToSnapshot(id int) {
|
|
s.stateDB.RevertToSnapshot(id)
|
|
}
|
|
|
|
// blockContextAdapter adapts vm.PrecompileEnvironment to contract.BlockContext
|
|
type blockContextAdapter struct {
|
|
env vm.PrecompileEnvironment
|
|
}
|
|
|
|
func (b *blockContextAdapter) Number() *big.Int {
|
|
return b.env.BlockNumber()
|
|
}
|
|
|
|
func (b *blockContextAdapter) Timestamp() uint64 {
|
|
return b.env.BlockTime()
|
|
}
|
|
|
|
func (b *blockContextAdapter) GetPredicateResults(txHash common.Hash, precompileAddress common.Address) []byte {
|
|
return nil // Not needed for standard precompiles
|
|
}
|
|
|
|
// chainConfigAdapter adapts geth's ChainConfig to precompileconfig.ChainConfig
|
|
type chainConfigAdapter struct {
|
|
config *gethparams.ChainConfig
|
|
}
|
|
|
|
func (c *chainConfigAdapter) GetFeeConfig() commontype.FeeConfig {
|
|
extra := params.GetExtra(c.config)
|
|
return extra.FeeConfig
|
|
}
|
|
|
|
func (c *chainConfigAdapter) AllowedFeeRecipients() bool {
|
|
extra := params.GetExtra(c.config)
|
|
return extra.AllowFeeRecipients
|
|
}
|
|
|
|
func (c *chainConfigAdapter) IsDurango(time uint64) bool {
|
|
extra := params.GetExtra(c.config)
|
|
return extra.IsDurango(time)
|
|
}
|