diff --git a/core/evm.go b/core/evm.go index 496e39a4c2..aea08ab849 100644 --- a/core/evm.go +++ b/core/evm.go @@ -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)) + } } diff --git a/core/types/log.go b/core/types/log.go index f0e6a3a745..85df7b8891 100644 --- a/core/types/log.go +++ b/core/types/log.go @@ -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(), + } +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 412cb28793..3cb039afca 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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. diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 7fe76b0a63..e9b1d3771b 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -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{} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index c9f79b05c4..83023c3465 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -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 { diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go index 79531f78d2..f0bf9cf1e4 100644 --- a/core/vm/interpreter_test.go +++ b/core/vm/interpreter_test.go @@ -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 {