feat: adds zerofee with vm fix

This commit is contained in:
Kartik Chopra 2025-03-17 11:45:20 -04:00
parent e5d5a3806a
commit b30ff9d58f
12 changed files with 158 additions and 11 deletions

View file

@ -192,6 +192,14 @@ func makeFullNode(ctx *cli.Context) *node.Node {
cfg.Eth.OverrideVerkle = &v cfg.Eth.OverrideVerkle = &v
} }
if ctx.IsSet(utils.ZeroFeeAddressesFlag.Name) {
for _, addr := range ctx.StringSlice(utils.ZeroFeeAddressesFlag.Name) {
cfg.Eth.ZeroFeeAddresses = append(
cfg.Eth.ZeroFeeAddresses,
common.HexToAddress(strings.TrimSpace(addr)),
)
}
}
// Start metrics export if enabled // Start metrics export if enabled
utils.SetupMetrics(&cfg.Metrics) utils.SetupMetrics(&cfg.Metrics)

View file

@ -154,6 +154,7 @@ var (
utils.BeaconGenesisRootFlag, utils.BeaconGenesisRootFlag,
utils.BeaconGenesisTimeFlag, utils.BeaconGenesisTimeFlag,
utils.BeaconCheckpointFlag, utils.BeaconCheckpointFlag,
utils.ZeroFeeAddressesFlag,
}, utils.NetworkFlags, utils.DatabaseFlags) }, utils.NetworkFlags, utils.DatabaseFlags)
rpcFlags = []cli.Flag{ rpcFlags = []cli.Flag{

View file

@ -527,6 +527,11 @@ var (
Value: "{}", Value: "{}",
Category: flags.VMCategory, Category: flags.VMCategory,
} }
ZeroFeeAddressesFlag = &cli.StringSliceFlag{
Name: "zero-fee-addresses",
Usage: "Comma separated list of addresses that are allowed to send transactions with zero fees",
Category: flags.VMCategory,
}
// API options. // API options.
RPCGlobalGasCapFlag = &cli.Uint64Flag{ RPCGlobalGasCapFlag = &cli.Uint64Flag{
Name: "rpc.gascap", Name: "rpc.gascap",
@ -2184,6 +2189,14 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
vmcfg.Tracer = t vmcfg.Tracer = t
} }
} }
if ctx.IsSet(ZeroFeeAddressesFlag.Name) {
for _, addr := range ctx.StringSlice(ZeroFeeAddressesFlag.Name) {
vmcfg.ZeroFeeAddresses = append(
vmcfg.ZeroFeeAddresses,
common.HexToAddress(strings.TrimSpace(addr)),
)
}
}
// Disable transaction indexing/unindexing by default. // Disable transaction indexing/unindexing by default.
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil) chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil)
if err != nil { if err != nil {

View file

@ -20,9 +20,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -155,6 +157,19 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
// Validate the state root against the received state root and throw // Validate the state root against the received state root and throw
// an error if they don't match. // an error if they don't match.
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
// Add detailed debug logging for the state root mismatch
log.Error("State root mismatch during validation",
"block", header.Number,
"block_hash", header.Hash(),
"header_root", header.Root.String(),
"calculated_root", root.String(),
"error", statedb.Error())
// Log treasury account balance for debugging
treasuryAccount := common.HexToAddress("0xfA0B0f5d298d28EFE4d35641724141ef19C05684")
log.Error("Treasury account balance",
"balance", statedb.GetBalance(treasuryAccount))
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error())
} }
return nil return nil

View file

@ -2539,3 +2539,26 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration { func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load()) return time.Duration(bc.flushInterval.Load())
} }
// AddZeroFeeAddress adds an address to the zero fee address list in the VM configuration
func (bc *BlockChain) AddZeroFeeAddress(address common.Address) {
// Check if the address is already in the list
for _, addr := range bc.vmConfig.ZeroFeeAddresses {
if addr == address {
return
}
}
// Add the address to the list
bc.vmConfig.ZeroFeeAddresses = append(bc.vmConfig.ZeroFeeAddresses, address)
log.Info("Added zero fee address", "address", address.String())
}
// GetZeroFeeAddresses returns the current list of zero fee addresses
func (bc *BlockChain) GetZeroFeeAddresses() []common.Address {
return bc.vmConfig.ZeroFeeAddresses
}
// VMConfig returns the current VM configuration
func (bc *BlockChain) VMConfig() vm.Config {
return bc.vmConfig
}

View file

@ -798,12 +798,15 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
workers.SetLimit(1) workers.SetLimit(1)
} }
for addr, op := range s.mutations { for addr, op := range s.mutations {
// Log the operation and address for debugging purposes
log.Info("Processing state mutation", "operation", op.typ, "address", addr.Hex())
if op.applied || op.isDelete() { if op.applied || op.isDelete() {
continue continue
} }
obj := s.stateObjects[addr] // closure for the task runner below obj := s.stateObjects[addr] // closure for the task runner below
workers.Go(func() error { workers.Go(func() error {
if s.db.TrieDB().IsVerkle() { if s.db.TrieDB().IsVerkle() {
log.Warn("Updating Verkle trie", "addr", addr.Hex())
obj.updateTrie() obj.updateTrie()
} else { } else {
obj.updateRoot() obj.updateRoot()

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -82,6 +83,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
context = NewEVMBlockContext(header, p.chain, nil) context = NewEVMBlockContext(header, p.chain, nil)
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg) evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
// Add logging for VM configuration during block processing
log.Debug("Created EVM for block processing",
"block_number", header.Number,
"block_hash", block.Hash().String(),
"zero_fee_addresses_count", len(cfg.ZeroFeeAddresses))
if len(cfg.ZeroFeeAddresses) > 0 {
for i, addr := range cfg.ZeroFeeAddresses {
log.Debug("Zero fee address in block processing",
"index", i,
"address", addr.String())
}
}
if beaconRoot := block.BeaconRoot(); beaconRoot != nil { if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, evm) ProcessBeaconBlockRoot(*beaconRoot, evm)
} }

View file

@ -20,12 +20,14 @@ import (
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
"slices"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -156,7 +158,12 @@ type Message struct {
// TransactionToMessage converts a transaction into a Message. // TransactionToMessage converts a transaction into a Message.
func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) {
from, err := s.Sender(tx)
if err != nil {
return nil, err
}
msg := &Message{ msg := &Message{
From: from,
Nonce: tx.Nonce(), Nonce: tx.Nonce(),
GasLimit: tx.Gas(), GasLimit: tx.Gas(),
GasPrice: new(big.Int).Set(tx.GasPrice()), GasPrice: new(big.Int).Set(tx.GasPrice()),
@ -179,7 +186,6 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
msg.GasPrice = msg.GasFeeCap msg.GasPrice = msg.GasFeeCap
} }
} }
var err error
msg.From, err = types.Sender(s, tx) msg.From, err = types.Sender(s, tx)
return msg, err return msg, err
} }
@ -285,6 +291,8 @@ func (st *stateTransition) buyGas() error {
st.initialGas = st.msg.GasLimit st.initialGas = st.msg.GasLimit
mgvalU256, _ := uint256.FromBig(mgval) mgvalU256, _ := uint256.FromBig(mgval)
// Log who the funds are being decremented from
log.Warn("Deducting gas fee from account", "from", st.msg.From.Hex(), "amount", mgvalU256)
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy) st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
return nil return nil
} }
@ -503,18 +511,58 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
} }
effectiveTipU256, _ := uint256.FromBig(effectiveTip) effectiveTipU256, _ := uint256.FromBig(effectiveTip)
log.Info("Effective tip", "effectiveTip", effectiveTipU256)
if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 { if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 {
// Skip fee payment when NoBaseFee is set and the fee fields // Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to // are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls. // the coinbase when simulating calls.
} else { } else {
fee := new(uint256.Int).SetUint64(st.gasUsed()) // Calculate priority fee (tip) paid to miner/validator
fee.Mul(fee, effectiveTipU256) priorityFee := new(uint256.Int).SetUint64(st.gasUsed())
st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee) priorityFee.Mul(priorityFee, effectiveTipU256)
// add the coinbase to the witness iff the fee is greater than 0 // Set the treasury account address
if rules.IsEIP4762 && fee.Sign() != 0 { treasuryAccount := common.HexToAddress("0xfA0B0f5d298d28EFE4d35641724141ef19C05684")
// Add detailed logging for debugging
log.Info("Fee handling in state transition",
"sender", msg.From.String(),
"block_number", st.evm.Context.BlockNumber,
"zero_fee_addresses_count", len(st.evm.Config.ZeroFeeAddresses),
"is_in_zero_fee_list", slices.Contains(st.evm.Config.ZeroFeeAddresses, msg.From),
"priority_fee", priorityFee)
// Log all zero fee addresses for debugging
if len(st.evm.Config.ZeroFeeAddresses) > 0 {
for i, addr := range st.evm.Config.ZeroFeeAddresses {
log.Debug("Zero fee address in EVM config",
"index", i,
"address", addr.String())
}
}
// Calculate total fee including base fee
totalFee := new(uint256.Int).SetUint64(st.gasUsed())
totalFee.Mul(totalFee, uint256.MustFromBig(msg.GasPrice))
// Handle zero fee addresses - they get their fees refunded
if slices.Contains(st.evm.Config.ZeroFeeAddresses, msg.From) {
log.Info("Refunding fee to zero-fee address",
"address", msg.From.String(),
"priority_fee", priorityFee,
"total_fee", totalFee)
st.state.AddBalance(msg.From, totalFee, tracing.BalanceIncreaseRewardTransactionFee)
} else {
log.Info("Sending fee to treasury",
"address", treasuryAccount.String(),
"amount", priorityFee,
"total_fee", totalFee)
st.state.AddBalance(treasuryAccount, priorityFee, tracing.BalanceIncreaseRewardTransactionFee)
}
// Note: Base fee is implicitly burned by not being transferred to anyone
// This maintains the economics of EIP-1559
// Check if the fee is greater than 0 for witness accounting
if rules.IsEIP4762 && priorityFee.Sign() != 0 {
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true) st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
} }
} }

View file

@ -30,9 +30,10 @@ import (
// Config are the configuration options for the Interpreter // Config are the configuration options for the Interpreter
type Config struct { type Config struct {
Tracer *tracing.Hooks Tracer *tracing.Hooks
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls) NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
ExtraEips []int // Additional EIPS that are to be enabled ExtraEips []int // Additional EIPS that are to be enabled
ZeroFeeAddresses []common.Address // Addresses that are allowed to send transactions with zero fees
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose) StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
} }

View file

@ -185,6 +185,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
var ( var (
vmConfig = vm.Config{ vmConfig = vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording, EnablePreimageRecording: config.EnablePreimageRecording,
ZeroFeeAddresses: config.ZeroFeeAddresses,
} }
cacheConfig = &core.CacheConfig{ cacheConfig = &core.CacheConfig{
TrieCleanLimit: config.TrieCleanCache, TrieCleanLimit: config.TrieCleanCache,

View file

@ -153,6 +153,9 @@ type Config struct {
// OverrideVerkle (TODO: remove after the fork) // OverrideVerkle (TODO: remove after the fork)
OverrideVerkle *uint64 `toml:",omitempty"` OverrideVerkle *uint64 `toml:",omitempty"`
// ZeroFeeAddresses is a list of addresses that are exempt from the zero fee policy.
ZeroFeeAddresses []common.Address `toml:",omitempty"`
} }
// CreateConsensusEngine creates a consensus engine for the given chain config. // CreateConsensusEngine creates a consensus engine for the given chain config.

View file

@ -250,6 +250,22 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
} }
state.StartPrefetcher("miner", bundle) state.StartPrefetcher("miner", bundle)
} }
// Get the VM configuration from the blockchain to ensure consistency
vmConfig := miner.chain.VMConfig()
// Add debug logging about VM configuration
log.Debug("Making mining environment with VM config",
"zero_fee_addresses_count", len(vmConfig.ZeroFeeAddresses))
if len(vmConfig.ZeroFeeAddresses) > 0 {
for i, addr := range vmConfig.ZeroFeeAddresses {
log.Debug("Zero fee address in mining",
"index", i,
"address", addr.String())
}
}
// Note the passed coinbase may be different with header.Coinbase. // Note the passed coinbase may be different with header.Coinbase.
return &environment{ return &environment{
signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time),
@ -257,7 +273,7 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
coinbase: coinbase, coinbase: coinbase,
header: header, header: header,
witness: state.Witness(), witness: state.Witness(),
evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vm.Config{}), evm: vm.NewEVM(core.NewEVMBlockContext(header, miner.chain, &coinbase), state, miner.chainConfig, vmConfig),
}, nil }, nil
} }