Make trace_block results closer to what OpenEthereum returns (#1862)

* Add block rewards to trace_block

* Add rewards to trace_block

* Remove printouts

* Fix trace_block and trace_transaction

* Fix getBlockByNumber

* Fix for parent/non parent block

* Reverse fix for trace_call

* Fix eth_getTransactionBy

* Fixes for TIMESTAMP etc opcodes

* More fixes

* Fixes to tracers

* Don't call CaptureEnd twice

* Corrent gasUsed for CaptureEnd in create

* Do CaptureFault consistently

* Correct gasUsed for create

* Remove insufficient balance trace

* Catch contract collision error

* Compatibility

* Compatibility

* More error names

* Out of gas

* Clean up

* more error messages

* Restore CaptureFault

* Errors

* Fix test

Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro.local>
This commit is contained in:
ledgerwatch 2021-05-03 20:49:55 +01:00 committed by GitHub
parent 964f8f76a2
commit 5168287784
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 172 additions and 90 deletions

View File

@ -25,7 +25,7 @@ func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber
}
additionalFields := make(map[string]interface{})
block, err := rawdb.ReadBlockByNumber(tx, blockNum)
block, _, err := rawdb.ReadBlockByNumberWithSenders(tx, blockNum)
if err != nil {
return nil, err
}

View File

@ -35,7 +35,7 @@ func (api *APIImpl) GetTransactionByBlockHashAndIndex(ctx context.Context, block
defer tx.Rollback()
// https://infura.io/docs/ethereum/json-rpc/eth-getTransactionByBlockHashAndIndex
block, err := rawdb.ReadBlockByHash(tx, blockHash)
block, _, err := rawdb.ReadBlockByHashWithSenders(tx, blockHash)
if err != nil {
return nil, err
}

View File

@ -34,6 +34,7 @@ const (
STATICCALL = "staticcall"
CREATE = "create"
SUICIDE = "suicide"
REWARD = "reward"
TraceTypeTrace = "trace"
TraceTypeStateDiff = "stateDiff"
TraceTypeVmTrace = "vmTrace"
@ -146,6 +147,7 @@ type OeTracer struct {
r *TraceCallResult
traceAddr []int
traceStack []*ParityTrace
lastTop *ParityTrace
precompile bool // Whether the last CaptureStart was called with `precompile = true`
}
@ -229,22 +231,45 @@ func (ot *OeTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.
ot.r.Output = common.CopyBytes(output)
}
topTrace := ot.traceStack[len(ot.traceStack)-1]
ot.lastTop = topTrace
if err != nil {
switch err {
case vm.ErrInvalidJump:
topTrace.Error = "Bad jump destination"
case vm.ErrOutOfGas:
topTrace.Error = "Out of gas"
case vm.ErrExecutionReverted:
topTrace.Error = "Reverted"
default:
switch err.(type) {
case *vm.ErrStackUnderflow:
topTrace.Error = "Stack underflow"
case *vm.ErrInvalidOpCode:
topTrace.Error = "Bad instruction"
if topTrace.Type == CREATE {
switch err {
case vm.ErrContractAddressCollision, vm.ErrCodeStoreOutOfGas, vm.ErrOutOfGas:
topTrace.Error = "Out of gas" // Only to be compatible with OE
case vm.ErrExecutionReverted:
if depth == 0 {
topTrace.Error = "Reverted"
} else {
topTrace.Error = "Out of gas"
}
default:
topTrace.Error = err.Error()
switch err.(type) {
case *vm.ErrStackUnderflow:
topTrace.Error = "Stack underflow"
case *vm.ErrInvalidOpCode:
topTrace.Error = "Bad instruction"
default:
topTrace.Error = err.Error()
}
}
} else {
switch err {
case vm.ErrInvalidJump:
topTrace.Error = "Bad jump destination"
case vm.ErrOutOfGas:
topTrace.Error = "Out of gas"
case vm.ErrExecutionReverted:
topTrace.Error = "Reverted"
default:
switch err.(type) {
case *vm.ErrStackUnderflow:
topTrace.Error = "Stack underflow"
case *vm.ErrInvalidOpCode:
topTrace.Error = "Bad instruction"
default:
topTrace.Error = err.Error()
}
}
}
topTrace.Result = nil
@ -278,7 +303,6 @@ func (ot *OeTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
}
func (ot *OeTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, opDepth int, err error) error {
//fmt.Printf("CaptureFault depth %d\n", opDepth)
return nil
}
@ -518,6 +542,7 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
msg := args.ToMessage(api.gasCap)
blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, dbtx)
//blockCtx.BlockNumber++
evm := vm.NewEVM(blockCtx, txCtx, ibs, chainConfig, vm.Config{Debug: traceTypeTrace, Tracer: &ot})
@ -530,11 +555,12 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
gp := new(core.GasPool).AddGas(msg.Gas())
var execResult *core.ExecutionResult
ibs.Prepare(common.Hash{}, common.Hash{}, 0)
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */)
if err != nil {
return nil, err
}
traceResult.Output = execResult.ReturnData
traceResult.Output = common.CopyBytes(execResult.ReturnData)
if traceTypeStateDiff {
sdMap := make(map[common.Address]*StateDiffAccount)
traceResult.StateDiff = sdMap
@ -566,25 +592,25 @@ func (api *TraceAPIImpl) CallMany(ctx context.Context, calls json.RawMessage, bl
}
defer dbtx.Rollback()
return api.doCallMany(ctx, dbtx, calls, blockNrOrHash)
return api.doCallMany(ctx, dbtx, calls, blockNrOrHash, nil)
}
func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx ethdb.Tx, calls json.RawMessage, blockNrOrHash *rpc.BlockNumberOrHash) ([]*TraceCallResult, error) {
func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx ethdb.Tx, calls json.RawMessage, parentNrOrHash *rpc.BlockNumberOrHash, header *types.Header) ([]*TraceCallResult, error) {
chainConfig, err := api.chainConfig(dbtx)
if err != nil {
return nil, err
}
if blockNrOrHash == nil {
if parentNrOrHash == nil {
var num = rpc.LatestBlockNumber
blockNrOrHash = &rpc.BlockNumberOrHash{BlockNumber: &num}
parentNrOrHash = &rpc.BlockNumberOrHash{BlockNumber: &num}
}
blockNumber, hash, err := rpchelper.GetBlockNumber(*blockNrOrHash, dbtx, api.pending)
blockNumber, hash, err := rpchelper.GetBlockNumber(*parentNrOrHash, dbtx, api.pending)
if err != nil {
return nil, err
}
var stateReader state.StateReader
if num, ok := blockNrOrHash.Number(); ok && num == rpc.LatestBlockNumber {
if num, ok := parentNrOrHash.Number(); ok && num == rpc.LatestBlockNumber {
stateReader = state.NewPlainStateReader(dbtx)
} else {
stateReader = state.NewPlainKvState(dbtx, blockNumber)
@ -594,9 +620,9 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx ethdb.Tx, calls js
noop := state.NewNoopWriter()
cachedWriter := state.NewCachedWriter(noop, stateCache)
header := rawdb.ReadHeader(ethdb.NewRoTxDb(dbtx), hash, blockNumber)
if header == nil {
return nil, fmt.Errorf("block %d(%x) not found", blockNumber, hash)
parentHeader := rawdb.ReadHeader(ethdb.NewRoTxDb(dbtx), hash, blockNumber)
if parentHeader == nil {
return nil, fmt.Errorf("parent header %d(%x) not found", blockNumber, hash)
}
// Setup context so it may be cancelled the call has completed
@ -668,7 +694,10 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx ethdb.Tx, calls js
// Get a new instance of the EVM.
msg := args.ToMessage(api.gasCap)
blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, dbtx)
if header == nil {
header = parentHeader
}
blockCtx, txCtx := transactions.GetEvmContext(msg, header, parentNrOrHash.RequireCanonical, dbtx)
ibs := state.New(cachedReader)
// Create initial IntraBlockState, we will compare it with ibs (IntraBlockState after the transaction)
@ -682,11 +711,12 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx ethdb.Tx, calls js
cloneCache := stateCache.Clone()
cloneReader = state.NewCachedReader(stateReader, cloneCache)
}
ibs.Prepare(common.Hash{}, header.Hash(), txIndex)
execResult, err = core.ApplyMessage(evm, msg, gp, true /* refunds */, true /* gasBailout */)
if err != nil {
return nil, fmt.Errorf("first run for txIndex %d error: %w", txIndex, err)
}
traceResult.Output = execResult.ReturnData
traceResult.Output = common.CopyBytes(execResult.ReturnData)
if traceTypeStateDiff {
initialIbs := state.New(cloneReader)
sdMap := make(map[common.Address]*StateDiffAccount)

View File

@ -65,13 +65,13 @@ func (api *TraceAPIImpl) Transaction(ctx context.Context, txHash common.Hash) (P
}
}
baseBn := bn
if baseBn > 0 {
baseBn -= 1
parentNr := bn
if parentNr > 0 {
parentNr -= 1
}
// Returns an array of trace arrays, one trace array for each transaction
traces, err := api.callManyTransactions(ctx, tx, txs, hash, rpc.BlockNumber(baseBn))
traces, err := api.callManyTransactions(ctx, tx, txs, block.ParentHash(), rpc.BlockNumber(parentNr), block.Header())
if err != nil {
return nil, err
}
@ -158,12 +158,12 @@ func (api *TraceAPIImpl) Block(ctx context.Context, blockNr rpc.BlockNumber) (Pa
})
}
baseBn := bn
if baseBn > 0 {
baseBn -= 1
parentNr := bn
if parentNr > 0 {
parentNr -= 1
}
traces, err := api.callManyTransactions(ctx, tx, txs, hash, rpc.BlockNumber(baseBn))
traces, err := api.callManyTransactions(ctx, tx, txs, block.ParentHash(), rpc.BlockNumber(parentNr), block.Header())
if err != nil {
return nil, err
}
@ -181,6 +181,41 @@ func (api *TraceAPIImpl) Block(ctx context.Context, blockNr rpc.BlockNumber) (Pa
out = append(out, *pt)
}
}
chainConfig, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
minerReward, uncleRewards := ethash.AccumulateRewards(chainConfig, block.Header(), block.Uncles())
var tr ParityTrace
var rewardAction = &RewardTraceAction{}
rewardAction.Author = block.Coinbase()
rewardAction.RewardType = "block" // nolint: goconst
rewardAction.Value.ToInt().Set(minerReward.ToBig())
tr.Action = rewardAction
tr.BlockHash = &common.Hash{}
copy(tr.BlockHash[:], block.Hash().Bytes())
tr.BlockNumber = new(uint64)
*tr.BlockNumber = block.NumberU64()
tr.Type = "reward" // nolint: goconst
tr.TraceAddress = []int{}
out = append(out, tr)
for i, uncle := range block.Uncles() {
if i < len(uncleRewards) {
var tr ParityTrace
rewardAction = &RewardTraceAction{}
rewardAction.Author = uncle.Coinbase
rewardAction.RewardType = "uncle" // nolint: goconst
rewardAction.Value.ToInt().Set(uncleRewards[i].ToBig())
tr.Action = rewardAction
tr.BlockHash = &common.Hash{}
copy(tr.BlockHash[:], block.Hash().Bytes())
tr.BlockNumber = new(uint64)
*tr.BlockNumber = block.NumberU64()
tr.Type = "reward" // nolint: goconst
tr.TraceAddress = []int{}
out = append(out, tr)
}
}
return out, err
}
@ -411,7 +446,7 @@ type TransactionWithSender struct {
sender common.Address
}
func (api *TraceAPIImpl) callManyTransactions(ctx context.Context, dbtx ethdb.Tx, txs []TransactionWithSender, blockHash common.Hash, blockNo rpc.BlockNumber) ([]*TraceCallResult, error) {
func (api *TraceAPIImpl) callManyTransactions(ctx context.Context, dbtx ethdb.Tx, txs []TransactionWithSender, parentHash common.Hash, parentNo rpc.BlockNumber, header *types.Header) ([]*TraceCallResult, error) {
toExecute := []interface{}{}
for _, txWithSender := range txs {
@ -435,10 +470,10 @@ func (api *TraceAPIImpl) callManyTransactions(ctx context.Context, dbtx ethdb.Tx
return nil, callsErr
}
traces, cmErr := api.doCallMany(ctx, dbtx, calls, &rpc.BlockNumberOrHash{
BlockNumber: &blockNo,
BlockHash: &blockHash,
BlockNumber: &parentNo,
BlockHash: &parentHash,
RequireCanonical: true,
})
}, header)
if cmErr != nil {
return nil, cmErr

View File

@ -41,11 +41,11 @@ type ParityTrace struct {
BlockHash *common.Hash `json:"blockHash,omitempty"`
BlockNumber *uint64 `json:"blockNumber,omitempty"`
Error string `json:"error,omitempty"`
Result interface{} `json:"result,omitempty"`
Result interface{} `json:"result"`
Subtraces int `json:"subtraces"`
TraceAddress []int `json:"traceAddress"`
TransactionHash *common.Hash `json:"transactionHash,omitempty"`
TransactionPosition *uint64 `json:"transactionPosition,omitempty"`
TransactionHash *common.Hash `json:"transactionHash"`
TransactionPosition *uint64 `json:"transactionPosition"`
Type string `json:"type"`
}
@ -71,10 +71,10 @@ type TraceAction struct {
type CallTraceAction struct {
From common.Address `json:"from"`
To common.Address `json:"to"`
CallType string `json:"callType"`
Gas hexutil.Big `json:"gas"`
Input hexutil.Bytes `json:"input"`
To common.Address `json:"to"`
Value hexutil.Big `json:"value"`
}
@ -91,6 +91,12 @@ type SuicideTraceAction struct {
Balance hexutil.Big `json:"balance"`
}
type RewardTraceAction struct {
Author common.Address `json:"author"`
RewardType string `json:"rewardType"`
Value hexutil.Big `json:"value"`
}
type CreateTraceResult struct {
// Do not change the ordering of these fields -- allows for easier comparison with other clients
Address *common.Address `json:"address,omitempty"`

View File

@ -12,9 +12,9 @@ import (
// Transactions on which OpenEthereum reports incorrect traces
var wrongTxs = []string{
"0xe47180a05a7cc25c187b426fed5390365874add72a5681242ac4b288d4a6833a", // Block 7000000
//"0xe47180a05a7cc25c187b426fed5390365874add72a5681242ac4b288d4a6833a", // Block 7000000
"0x76e720b0530aa72926319853f62c97c5907b26e2fc5c8ad5f51173f531d98d11", // Block 7000063
"0xc6f3cadc90aece146a7a90191d6668c7f152a2daf759ae97bde7df7f5d78ab3a", // Block 7000068
//"0xc6f3cadc90aece146a7a90191d6668c7f152a2daf759ae97bde7df7f5d78ab3a", // Block 7000068
"0xd5a9b32b262202cda422dd5a2ccf8d7d56e9b3425ba7d350548e62a5bd26b481", // Block 8000011
"0x1953ad3591fa0f6f3f00dfa0f93a57e1dc7fa003e2192a18c64c71847cf64e0c", // Block 8000035
"0xfbd66bcbc4cb374946f350ca6835571b09f68c5f635ff9fc533c3fa2ac0d19cb", // Block 9000004

View File

@ -1148,6 +1148,14 @@ func ReadBlockByHash(db ethdb.Tx, hash common.Hash) (*types.Block, error) {
return ReadBlock(db, hash, *number), nil
}
func ReadBlockByHashWithSenders(db ethdb.Tx, hash common.Hash) (*types.Block, []common.Address, error) {
number := ReadHeaderNumber(db, hash)
if number == nil {
return nil, nil, nil
}
return ReadBlockWithSenders(db, hash, *number)
}
func ReadBlocksByHash(db ethdb.Getter, hash common.Hash, n int) (blocks []*types.Block, err error) {
number := ReadHeaderNumber(db, hash)
if number == nil {

View File

@ -89,6 +89,12 @@ func ReadTransaction(db ethdb.Tx, hash common.Hash) (types.Transaction, common.H
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash)
return nil, common.Hash{}, 0, 0
}
senders, err1 := ReadSenders(db, blockHash, *blockNumber)
if err1 != nil {
log.Error("ReadSenders failed", "err", err)
return nil, common.Hash{}, 0, 0
}
body.SendersToTxs(senders)
for txIndex, tx := range body.Transactions {
if tx.Hash() == hash {
return tx, blockHash, *blockNumber, uint64(txIndex)

View File

@ -73,6 +73,9 @@ func TestLookupStorage(t *testing.T) {
if err := WriteBlock(tx, block); err != nil {
t.Fatal(err)
}
if err := WriteSenders(tx, block.Hash(), block.NumberU64(), block.Body().SendersFromTxs()); err != nil {
t.Fatal(err)
}
tc.writeTxLookupEntries(tx, block)
for i, txn := range txs {

View File

@ -221,24 +221,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
}
p, isPrecompile := evm.precompile(addr)
var (
to = AccountRef(addr)
snapshot = evm.IntraBlockState.Snapshot()
)
if !evm.IntraBlockState.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig())
_ = evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, 0, 0, nil)
}
return nil, gas, nil
}
evm.IntraBlockState.CreateAccount(addr, false)
}
evm.Context.Transfer(evm.IntraBlockState, caller.Address(), to.Address(), value, bailout)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false /* create */, CALLT, input, gas, value.ToBig())
@ -247,6 +229,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}(gas, time.Now())
}
var (
to = AccountRef(addr)
snapshot = evm.IntraBlockState.Snapshot()
)
if !evm.IntraBlockState.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
return nil, gas, nil
}
evm.IntraBlockState.CreateAccount(addr, false)
}
evm.Context.Transfer(evm.IntraBlockState, caller.Address(), to.Address(), value, bailout)
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
@ -302,10 +296,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
if !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
var (
snapshot = evm.IntraBlockState.Snapshot()
)
p, isPrecompile := evm.precompile(addr)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
@ -314,6 +304,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, startGas-gas, time.Since(startTime), err) //nolint:errcheck
}(gas, time.Now())
}
var (
snapshot = evm.IntraBlockState.Snapshot()
)
// It is allowed to call precompiles, even via delegatecall
if isPrecompile {
@ -349,8 +342,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
snapshot := evm.IntraBlockState.Snapshot()
p, isPrecompile := evm.precompile(addr)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
@ -359,6 +350,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, startGas-gas, time.Since(startTime), err) //nolint:errcheck
}(gas, time.Now())
}
snapshot := evm.IntraBlockState.Snapshot()
// It is allowed to call precompiles, even via delegatecall
if isPrecompile {
@ -392,6 +384,14 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
p, isPrecompile := evm.precompile(addr)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2))
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, startGas-gas, time.Since(startTime), err) //nolint:errcheck
}(gas, time.Now())
}
// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
// However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
// after all empty accounts were deleted, so this is not required. However, if we omit this,
@ -405,15 +405,6 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// future scenarios
evm.IntraBlockState.AddBalance(addr, u256.Num0)
p, isPrecompile := evm.precompile(addr)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), addr, isPrecompile, false, STATICCALLT, input, gas, big.NewInt(-2))
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, startGas-gas, time.Since(startTime), err) //nolint:errcheck
}(gas, time.Now())
}
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
@ -454,6 +445,8 @@ func (c *codeAndHash) Hash() common.Hash {
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, calltype CallType) ([]byte, common.Address, uint64, error) {
var ret []byte
var err error
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@ -462,6 +455,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if !evm.Context.CanTransfer(evm.IntraBlockState, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig())
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, startGas-gas, time.Since(startTime), err) //nolint:errcheck
}(gas, time.Now())
}
nonce := evm.IntraBlockState.GetNonce(caller.Address())
evm.IntraBlockState.SetNonce(caller.Address(), nonce+1)
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
@ -472,7 +471,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Ensure there's no existing contract already at the designated address
contractHash := evm.IntraBlockState.GetCodeHash(address)
if evm.IntraBlockState.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
return nil, common.Address{}, 0, ErrContractAddressCollision
err = ErrContractAddressCollision
return nil, common.Address{}, 0, err
}
// Create a new account on the state
snapshot := evm.IntraBlockState.Snapshot()
@ -491,12 +491,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
return nil, address, gas, nil
}
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureStart(evm.depth, caller.Address(), address, false /* precompile */, true /* create */, calltype, codeAndHash.code, gas, value.ToBig())
}
start := time.Now()
ret, err := run(evm, contract, nil, false)
ret, err = run(evm, contract, nil, false)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize
@ -529,13 +524,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contract.UseGas(contract.Gas)
}
}
gas = contract.Gas // For the CaptureEnd to work corrently with gasUsed
// Assign err if contract code size exceeds the max while the err is still empty.
if maxCodeSizeExceeded && err == nil {
err = ErrMaxCodeSizeExceeded
}
if evm.vmConfig.Debug {
_ = evm.vmConfig.Tracer.CaptureEnd(evm.depth, ret, gas-contract.Gas, time.Since(start), err)
}
return ret, address, contract.Gas, err
}