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
}
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
utils.SetupMetrics(&cfg.Metrics)

View file

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

View file

@ -527,6 +527,11 @@ var (
Value: "{}",
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.
RPCGlobalGasCapFlag = &cli.Uint64Flag{
Name: "rpc.gascap",
@ -2184,6 +2189,14 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
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.
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil)
if err != nil {

View file

@ -20,9 +20,11 @@ import (
"errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"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
// an error if they don't match.
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 nil

View file

@ -2539,3 +2539,26 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
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)
}
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() {
continue
}
obj := s.stateObjects[addr] // closure for the task runner below
workers.Go(func() error {
if s.db.TrieDB().IsVerkle() {
log.Warn("Updating Verkle trie", "addr", addr.Hex())
obj.updateTrie()
} else {
obj.updateRoot()

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"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)
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 {
ProcessBeaconBlockRoot(*beaconRoot, evm)
}

View file

@ -20,12 +20,14 @@ import (
"fmt"
"math"
"math/big"
"slices"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@ -156,7 +158,12 @@ type Message struct {
// TransactionToMessage converts a transaction into a Message.
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{
From: from,
Nonce: tx.Nonce(),
GasLimit: tx.Gas(),
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
}
}
var err error
msg.From, err = types.Sender(s, tx)
return msg, err
}
@ -285,6 +291,8 @@ func (st *stateTransition) buyGas() error {
st.initialGas = st.msg.GasLimit
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)
return nil
}
@ -503,18 +511,58 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
}
}
effectiveTipU256, _ := uint256.FromBig(effectiveTip)
log.Info("Effective tip", "effectiveTip", effectiveTipU256)
if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 {
// Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls.
} else {
fee := new(uint256.Int).SetUint64(st.gasUsed())
fee.Mul(fee, effectiveTipU256)
st.state.AddBalance(st.evm.Context.Coinbase, fee, tracing.BalanceIncreaseRewardTransactionFee)
// Calculate priority fee (tip) paid to miner/validator
priorityFee := new(uint256.Int).SetUint64(st.gasUsed())
priorityFee.Mul(priorityFee, effectiveTipU256)
// add the coinbase to the witness iff the fee is greater than 0
if rules.IsEIP4762 && fee.Sign() != 0 {
// Set the treasury account address
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)
}
}

View file

@ -30,9 +30,10 @@ import (
// Config are the configuration options for the Interpreter
type Config struct {
Tracer *tracing.Hooks
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
ExtraEips []int // Additional EIPS that are to be enabled
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
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)
}

View file

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

View file

@ -153,6 +153,9 @@ type Config struct {
// OverrideVerkle (TODO: remove after the fork)
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.

View file

@ -250,6 +250,22 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase
}
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.
return &environment{
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,
header: header,
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
}