Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

call frame tracing #1993

Merged
merged 4 commits into from Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 76 additions & 19 deletions blockchain/vm/evm.go
Expand Up @@ -242,9 +242,14 @@ func (evm *EVM) Call(caller types.ContractRef, addr common.Address, input []byte
precompiles := evm.GetPrecompiledContractMap(caller.Address())
if precompiles[addr] == nil || value.Sign() != 0 {
// Return an error if an enabled precompiled address is called or a value is transferred to a precompiled address.
if debug && evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
if debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
} else {
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, kerrors.ErrPrecompiledContractAddress
}
Expand All @@ -259,9 +264,14 @@ func (evm *EVM) Call(caller types.ContractRef, addr common.Address, input []byte
if !evm.StateDB.Exist(addr) {
if value.Sign() == 0 {
// Calling a non-existing account (probably contract), don't do anything, but ping the tracer
if debug && evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
if debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
} else {
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, nil
}
Expand All @@ -270,11 +280,19 @@ func (evm *EVM) Call(caller types.ContractRef, addr common.Address, input []byte
}
evm.Context.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

if debug && evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
}(gas)
if debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64) { // Lazy evaluation of the parameters
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
}(gas)
} else {
// Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
}

if isProgramAccount(evm, caller.Address(), addr, evm.StateDB) {
Expand Down Expand Up @@ -331,6 +349,15 @@ func (evm *EVM) CallCode(caller types.ContractRef, addr common.Address, input []
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Debug {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(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.
contract := NewContract(caller, to, value, gas)
Expand Down Expand Up @@ -370,6 +397,18 @@ func (evm *EVM) DelegateCall(caller types.ContractRef, addr common.Address, inpu
to = AccountRef(caller.Address())
)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Debug {
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
Expand Down Expand Up @@ -413,6 +452,15 @@ func (evm *EVM) StaticCall(caller types.ContractRef, addr common.Address, input
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Debug {
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(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.
contract := NewContract(caller, to, new(big.Int), gas)
Expand Down Expand Up @@ -444,7 +492,7 @@ func (c *codeAndHash) Hash() common.Hash {
}

// Create creates a new contract using code as deployment code.
func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, humanReadable bool, codeFormat params.CodeFormat) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode, humanReadable bool, codeFormat params.CodeFormat) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
Expand Down Expand Up @@ -502,8 +550,12 @@ func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas u
return nil, address, gas, nil
}

if evm.Config.Debug && evm.depth == 0 {
evm.Config.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}

ret, err = evm.interpreter.Run(contract, nil)
Expand Down Expand Up @@ -549,17 +601,22 @@ func (evm *EVM) create(caller types.ContractRef, codeAndHash *codeAndHash, gas u
err = ErrInvalidCode
}

if evm.Config.Debug && evm.depth == 0 {
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
} else {
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}

return ret, address, contract.Gas, err
}

// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller types.ContractRef, code []byte, gas uint64, value *big.Int, codeFormat params.CodeFormat) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, codeAndHash, gas, value, contractAddr, false, codeFormat)
return evm.create(caller, codeAndHash, gas, value, contractAddr, CREATE, false, codeFormat)
}

// Create2 creates a new contract using code as deployment code.
Expand All @@ -569,14 +626,14 @@ func (evm *EVM) Create(caller types.ContractRef, code []byte, gas uint64, value
func (evm *EVM) Create2(caller types.ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int, codeFormat params.CodeFormat) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, false, codeFormat)
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2, false, codeFormat)
}

// CreateWithAddress creates a new contract using code as deployment code with given address and humanReadable.
func (evm *EVM) CreateWithAddress(caller types.ContractRef, code []byte, gas uint64, value *big.Int, contractAddr common.Address, humanReadable bool, codeFormat params.CodeFormat) ([]byte, common.Address, uint64, error) {
codeAndHash := &codeAndHash{code: code}
codeAndHash.Hash()
return evm.create(caller, codeAndHash, gas, value, contractAddr, humanReadable, codeFormat)
return evm.create(caller, codeAndHash, gas, value, contractAddr, CREATE, humanReadable, codeFormat)
}

func (evm *EVM) GetPrecompiledContractMap(addr common.Address) map[common.Address]PrecompiledContract {
Expand Down
41 changes: 19 additions & 22 deletions blockchain/vm/instructions.go
Expand Up @@ -891,21 +891,6 @@ func opStop(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
return nil, errStopToken
}

func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
// TODO-klaytn: call frame tracing https://github.com/ethereum/go-ethereum/pull/23087
// beneficiary := scope.Stack.pop()
balance := evm.StateDB.GetBalance(scope.Contract.Address())
evm.StateDB.AddBalance(common.BigToAddress(scope.Stack.pop()), balance)
evm.StateDB.SelfDestruct(scope.Contract.Address())

// if evm.interpreter.cfg.Debug {
// evm.interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), common.BigToAddress(beneficiary), []byte{}, 0, balance)
// evm.interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
// }

return nil, errStopToken
}

// opPush1 is a specialized version of pushN
func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
var (
Expand All @@ -921,6 +906,21 @@ func opPush1(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
return nil, nil
}

func opSelfdestruct(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.interpreter.readOnly {
return nil, ErrWriteProtection
}
blukat29 marked this conversation as resolved.
Show resolved Hide resolved
beneficiary := scope.Stack.pop()
balance := evm.StateDB.GetBalance(scope.Contract.Address())
evm.StateDB.AddBalance(common.BigToAddress(beneficiary), balance)
evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := evm.Config.Tracer; tracer != nil {
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), common.BigToAddress(beneficiary), []byte{}, 0, balance)
tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
}

func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
if evm.interpreter.readOnly {
return nil, ErrWriteProtection
Expand All @@ -930,13 +930,10 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
evm.StateDB.SubBalance(scope.Contract.Address(), balance)
evm.StateDB.AddBalance(common.BigToAddress(beneficiary), balance)
evm.StateDB.SelfDestruct6780(scope.Contract.Address())

// TODO-klaytn: call frame tracing https://github.com/ethereum/go-ethereum/pull/23087
// if tracer := interpreter.evm.Config.Tracer; tracer != nil {
// tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), common.BigToAddress(beneficiary), []byte{}, 0, balance)
// tracer.CaptureExit([]byte{}, 0, nil)
// }

if tracer := evm.Config.Tracer; tracer != nil {
tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), common.BigToAddress(beneficiary), []byte{}, 0, balance)
tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
}

Expand Down