core, core/vm: implement EIP-7708

This commit is contained in:
Zsolt Felfoldi 2026-01-19 18:51:09 +01:00 committed by Jared Wasinger
parent d2dbbe48c4
commit 50b7009201
6 changed files with 55 additions and 7 deletions

View file

@ -25,6 +25,7 @@ import (
"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/params"
"github.com/holiman/uint256"
)
@ -141,7 +142,10 @@ func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
}
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int, blockNumber *big.Int, rules *params.Rules) {
db.SubBalance(sender, amount, tracing.BalanceChangeTransfer)
db.AddBalance(recipient, amount, tracing.BalanceChangeTransfer)
if rules.IsAmsterdam && !amount.IsZero() {
db.AddLog(types.EthTransferLog(blockNumber, sender, recipient, amount))
}
}

View file

@ -17,8 +17,11 @@
package types
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/holiman/uint256"
)
//go:generate go run ../../rlp/rlpgen -type Log -out gen_log_rlp.go
@ -62,3 +65,30 @@ type logMarshaling struct {
BlockTimestamp hexutil.Uint64
Index hexutil.Uint
}
var (
// system contract address
EthTransferLogAddress = common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffffe")
// keccak256('Transfer(address,address,uint256)')
EthTransferLogTopic0 = common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
)
// EthTransferLog creates and ETH transfer log according to EIP-7708.
// Specification: https://eips.ethereum.org/EIPS/eip-7708
func EthTransferLog(blockNumber *big.Int, from, to common.Address, amount *uint256.Int) *Log {
topics := make([]common.Hash, 3)
topics[0] = EthTransferLogTopic0
copy(topics[1][common.HashLength-common.AddressLength:], from[:])
copy(topics[2][common.HashLength-common.AddressLength:], to[:])
amount32 := amount.Bytes32()
data := make([]byte, 32)
copy(data, amount32[:])
return &Log{
Address: EthTransferLogAddress,
Topics: topics,
Data: data,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
BlockNumber: blockNumber.Uint64(),
}
}

View file

@ -35,7 +35,7 @@ type (
// CanTransferFunc is the signature of a transfer guard function
CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool
// TransferFunc is the signature of a transfer function
TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)
TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int, *big.Int, *params.Rules)
// GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
@ -283,7 +283,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
}
evm.StateDB.CreateAccount(addr)
}
evm.Context.Transfer(evm.StateDB, caller, addr, value)
evm.Context.Transfer(evm.StateDB, caller, addr, value, evm.Context.BlockNumber, &evm.chainRules)
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
@ -560,7 +560,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui
}
gas = gas - consumed
}
evm.Context.Transfer(evm.StateDB, caller, address, value)
evm.Context.Transfer(evm.StateDB, caller, address, value, evm.Context.BlockNumber, &evm.chainRules)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.

View file

@ -94,7 +94,7 @@ func TestEIP2200(t *testing.T) {
vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *big.Int, *params.Rules) {},
}
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
@ -144,7 +144,7 @@ func TestCreateGas(t *testing.T) {
statedb.Finalise(true)
vmctx := BlockContext{
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *big.Int, *params.Rules) {},
BlockNumber: big.NewInt(0),
}
config := Config{}

View file

@ -895,6 +895,13 @@ func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
}
// Clear any leftover funds for the account being destructed.
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
if evm.chainRules.IsAmsterdam && !balance.IsZero() {
if this != beneficiary {
evm.StateDB.AddLog(types.EthTransferLog(evm.Context.BlockNumber, this, beneficiary, balance))
} else {
evm.StateDB.AddLog(types.EthTransferLog(evm.Context.BlockNumber, this, common.Address{}, balance))
}
}
evm.StateDB.SelfDestruct(this)
if tracer := evm.Config.Tracer; tracer != nil {
@ -933,6 +940,13 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
}
if evm.chainRules.IsAmsterdam && !balance.IsZero() {
if this != beneficiary {
evm.StateDB.AddLog(types.EthTransferLog(evm.Context.BlockNumber, this, beneficiary, balance))
} else if newContract {
evm.StateDB.AddLog(types.EthTransferLog(evm.Context.BlockNumber, this, common.Address{}, balance))
}
}
if tracer := evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {

View file

@ -40,7 +40,7 @@ var loopInterruptTests = []string{
func TestLoopInterrupt(t *testing.T) {
address := common.BytesToAddress([]byte("contract"))
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int, *big.Int, *params.Rules) {},
}
for i, tt := range loopInterruptTests {