diff --git a/core/vm/evm.go b/core/vm/evm.go index 616668d565..971a17e9cf 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -205,7 +205,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas } evm.StateDB.CreateAccount(addr) } + evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value) + if shouldLogNonZeroNativeTransfers := value.Sign() > 0 && evm.chainRules.IsEIP7708; shouldLogNonZeroNativeTransfers { + evm.StateDB.AddLog(&types.Log{ + Address: params.LogNativeTransferContractAddress, + Topics: []common.Hash{params.LogNativeTransferTopicMagic, common.BytesToHash(caller.Address().Bytes()), common.BytesToHash(addr.Bytes())}, + Data: value.Bytes(), + BlockNumber: evm.Context.BlockNumber.Uint64(), + }) + } if isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer) @@ -213,15 +222,16 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas // 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. code := evm.StateDB.GetCode(addr) - if witness := evm.StateDB.Witness(); witness != nil { - witness.AddCode(code) - } - if len(code) == 0 { + // Call is to contract if "there is contract code", if not it's just + // transfer to EOA (Externally owned account) + if isToEOA := len(code) == 0; isToEOA { ret, err = nil, nil // gas is unchanged } else { + if witness := evm.StateDB.Witness(); witness != nil { + witness.AddCode(code) + } + addrCopy := addr - // If the account has no code, we can abort here - // The depth-check is already done, and precompiles handled above contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) ret, err = evm.interpreter.Run(contract, input, false) @@ -273,7 +283,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, gas, ErrInsufficientBalance } - var snapshot = evm.StateDB.Snapshot() + snapshot := evm.StateDB.Snapshot() // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { @@ -324,7 +334,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth } - var snapshot = evm.StateDB.Snapshot() + snapshot := evm.StateDB.Snapshot() // It is allowed to call precompiles, even via delegatecall if p, isPrecompile := evm.precompile(addr); isPrecompile { @@ -373,7 +383,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte // after all empty accounts were deleted, so this is not required. However, if we omit this, // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json. // We could change this, but for now it's left for legacy reasons - var snapshot = evm.StateDB.Snapshot() + snapshot := evm.StateDB.Snapshot() // We do an AddBalance of zero here, just in order to trigger a touch. // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium, diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 35d6393fba..03ff745167 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -328,7 +328,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte return nil, ErrReturnDataOutOfBounds } // we can reuse dataOffset now (aliasing it for clarity) - var end = dataOffset + end := dataOffset end.Add(&dataOffset, &length) end64, overflow := end.Uint64WithOverflow() if overflow || uint64(len(interpreter.returnData)) < end64 { @@ -899,6 +899,16 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext beneficiary := scope.Stack.pop() balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + + if shouldLogNonZeroNativeTransfers := balance.Sign() > 0 && interpreter.evm.chainRules.IsEIP7708; shouldLogNonZeroNativeTransfers { + interpreter.evm.StateDB.AddLog(&types.Log{ + Address: params.LogNativeTransferContractAddress, + Topics: []common.Hash{params.LogNativeTransferTopicMagic, common.BytesToHash(scope.Contract.Address().Bytes()), common.BytesToHash(scope.Caller().Bytes())}, + Data: balance.Bytes(), + BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(), + }) + } + interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { @@ -919,6 +929,16 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address()) interpreter.evm.StateDB.SubBalance(scope.Contract.Address(), balance, tracing.BalanceDecreaseSelfdestruct) interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance, tracing.BalanceIncreaseSelfdestruct) + + if shouldLogNonZeroNativeTransfers := balance.Sign() > 0 && interpreter.evm.chainRules.IsEIP7708; shouldLogNonZeroNativeTransfers { + interpreter.evm.StateDB.AddLog(&types.Log{ + Address: params.LogNativeTransferContractAddress, + Topics: []common.Hash{params.LogNativeTransferTopicMagic, common.BytesToHash(scope.Contract.Address().Bytes()), common.BytesToHash(scope.Caller().Bytes())}, + Data: balance.Bytes(), + BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(), + }) + } + interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer.OnEnter != nil { diff --git a/params/config.go b/params/config.go index 9ecf465bb6..80b1350859 100644 --- a/params/config.go +++ b/params/config.go @@ -561,6 +561,12 @@ func (c *ChainConfig) IsEIP4762(num *big.Int, time uint64) bool { return c.IsVerkle(num, time) } +// IsEIP7708 returns whether [EIP-7708](https://eips.ethereum.org/EIPS/eip-7708) has been activated at given block. +func (c *ChainConfig) IsEIP7708(num *big.Int, time uint64) bool { + // TODO:Decide how to activate this EIP + return false +} + // CheckCompatible checks whether scheduled fork transitions have been imported // with a mismatching chain configuration. func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, time uint64) *ConfigCompatError { @@ -894,6 +900,7 @@ type Rules struct { IsBerlin, IsLondon bool IsMerge, IsShanghai, IsCancun, IsPrague bool IsVerkle bool + IsEIP7708 bool } // Rules ensures c's ChainID is not nil. @@ -924,5 +931,6 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsPrague: isMerge && c.IsPrague(num, timestamp), IsVerkle: isVerkle, IsEIP4762: isVerkle, + IsEIP7708: c.IsEIP7708(num, timestamp), } } diff --git a/params/protocol_params.go b/params/protocol_params.go index 638f58a339..acc02fb3db 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -20,6 +20,7 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" ) const ( @@ -200,3 +201,10 @@ var ( // HistoryStorageCode is the code with getters for historical block hashes. HistoryStorageCode = common.FromHex("3373fffffffffffffffffffffffffffffffffffffffe1460575767ffffffffffffffff5f3511605357600143035f3511604b575f35612000014311604b57611fff5f3516545f5260205ff35b5f5f5260205ff35b5f5ffd5b5f35611fff60014303165500") ) + +// [EIP-7708](https://eips.ethereum.org/EIPS/eip-7708) specific params +var ( + LogNativeTransferTopicMagic = common.BytesToHash(crypto.Keccak256([]byte("0000000000000000000000000000000000000000000000000000000000000000"))) + // Proposed here: https://ethereum-magicians.org/t/eip-7708-eth-transfers-emit-a-log/20034/25 + LogNativeTransferContractAddress = common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") +)