Skip to content

Commit b898609

Browse files
authored
Backports for 6.0.7-beta (#21)
* Fixed state transition logic for edge case (#17) * Update morden bootnodes (#16) * Updates bootnodes * what a fantastic test :) * Core execution refactor, EIP 684, edge case fixes, starting nonce for morden (#19) * Refactored Call * Refactored CallCode * Refactored DelegateCall * Refactored StaticCall * Added create before contract collision check * Refactored Call * Refactored CallCode * Refactored DelegateCall * Refactored StaticCall * Added create before contract collision check * Fixed state transition logic for edge case * Added create collision logic * Fix for edge case tests * Removed outdated contract collision tests and changed format of test output to be consistent * Rename exec function * utilize StartingNonce from state instead of explicit 0 nonce checks for contract collision and empty objects * Fixes morden seg fault edge case * adding starting nonce to created contracts
1 parent 6280e59 commit b898609

14 files changed

Lines changed: 280 additions & 623 deletions

core/execution.go

Lines changed: 198 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -17,53 +17,217 @@
1717
package core
1818

1919
import (
20+
"errors"
2021
"fmt"
2122
"math/big"
2223

2324
"github.com/eth-classic/go-ethereum/common"
25+
"github.com/eth-classic/go-ethereum/core/state"
2426
"github.com/eth-classic/go-ethereum/core/vm"
2527
"github.com/eth-classic/go-ethereum/crypto"
28+
"github.com/eth-classic/go-ethereum/params"
2629
)
2730

2831
var (
32+
emptyCodeHash = crypto.Keccak256Hash(nil)
33+
2934
callCreateDepthMax = 1024 // limit call/create stack
3035
errCallCreateDepth = fmt.Errorf("Max call depth exceeded (%d)", callCreateDepthMax)
3136

3237
maxCodeSize = 24576
3338
errMaxCodeSizeExceeded = fmt.Errorf("Max Code Size exceeded (%d)", maxCodeSize)
39+
40+
errContractAddressCollision = errors.New("contract address collision")
3441
)
3542

36-
// Call executes within the given contract
43+
// Call executes the contract associated with the addr with the given input as
44+
// parameters. It also handles any necessary value transfer required and takes
45+
// the necessary steps to create accounts and reverses the state in case of an
46+
// execution error or failed value transfer.
3747
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
38-
ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value, false)
48+
evm := env.Vm()
49+
// Depth check execution. Fail if we're trying to execute above the limit.
50+
if env.Depth() > callCreateDepthMax {
51+
caller.ReturnGas(gas, gasPrice)
52+
53+
return nil, errCallCreateDepth
54+
}
55+
56+
if !env.CanTransfer(caller.Address(), value) {
57+
caller.ReturnGas(gas, gasPrice)
58+
59+
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
60+
}
61+
62+
var (
63+
from = env.Db().GetAccount(caller.Address())
64+
to vm.Account
65+
snapshot = env.SnapshotDatabase()
66+
isAtlantis = env.RuleSet().IsAtlantis(env.BlockNumber())
67+
)
68+
if !env.Db().Exist(addr) {
69+
precompiles := vm.PrecompiledPreAtlantis
70+
if isAtlantis {
71+
precompiles = vm.PrecompiledAtlantis
72+
}
73+
if precompiles[addr.Str()] == nil && isAtlantis && value.BitLen() == 0 {
74+
caller.ReturnGas(gas, gasPrice)
75+
return nil, nil
76+
}
77+
to = env.Db().CreateAccount(addr)
78+
} else {
79+
to = env.Db().GetAccount(addr)
80+
}
81+
env.Transfer(from, to, value)
82+
// Initialise a new contract and set the code that is to be used by the EVM.
83+
// The contract is a scoped environment for this execution context only.
84+
contract := vm.NewContract(caller, to, value, gas, gasPrice)
85+
contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
86+
defer contract.Finalise()
87+
88+
// Even if the account has no code, we need to continue because it might be a precompile
89+
ret, err = evm.Run(contract, input, false)
90+
91+
// When an error was returned by the EVM or when setting the creation code
92+
// above we revert to the snapshot and consume any gas remaining. Additionally
93+
// when we're in homestead this also counts for code storage gas errors.
94+
if err != nil {
95+
env.RevertToSnapshot(snapshot)
96+
if err != vm.ErrRevert {
97+
contract.UseGas(contract.Gas)
98+
}
99+
}
39100
return ret, err
40101
}
41102

42103
// CallCode executes the given address' code as the given contract address
43104
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
44-
callerAddr := caller.Address()
45-
ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value, false)
105+
evm := env.Vm()
106+
// Depth check execution. Fail if we're trying to execute above the limit.
107+
if env.Depth() > callCreateDepthMax {
108+
caller.ReturnGas(gas, gasPrice)
109+
110+
return nil, errCallCreateDepth
111+
}
112+
113+
if !env.CanTransfer(caller.Address(), value) {
114+
caller.ReturnGas(gas, gasPrice)
115+
116+
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
117+
}
118+
119+
var (
120+
to = env.Db().GetAccount(caller.Address())
121+
snapshot = env.SnapshotDatabase()
122+
)
123+
// Initialise a new contract and set the code that is to be used by the EVM.
124+
// The contract is a scoped environment for this execution context only.
125+
contract := vm.NewContract(caller, to, value, gas, gasPrice)
126+
contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
127+
defer contract.Finalise()
128+
129+
// Even if the account has no code, we need to continue because it might be a precompile
130+
ret, err = evm.Run(contract, input, false)
131+
132+
if err != nil {
133+
env.RevertToSnapshot(snapshot)
134+
if err != vm.ErrRevert {
135+
contract.UseGas(contract.Gas)
136+
}
137+
}
46138
return ret, err
47139
}
48140

49141
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
50142
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
51-
callerAddr := caller.Address()
52-
originAddr := env.Origin()
53-
callerValue := caller.Value()
54-
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
143+
evm := env.Vm()
144+
// Depth check execution. Fail if we're trying to execute above the limit.
145+
if env.Depth() > callCreateDepthMax {
146+
caller.ReturnGas(gas, gasPrice)
147+
148+
return nil, errCallCreateDepth
149+
}
150+
151+
var (
152+
to vm.Account
153+
snapshot = env.SnapshotDatabase()
154+
)
155+
if !env.Db().Exist(caller.Address()) {
156+
to = env.Db().CreateAccount(caller.Address())
157+
} else {
158+
to = env.Db().GetAccount(caller.Address())
159+
}
160+
161+
// Initialise a new contract and set the code that is to be used by the EVM.
162+
// The contract is a scoped environment for this execution context only.
163+
contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate()
164+
contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
165+
defer contract.Finalise()
166+
167+
// Even if the account has no code, we need to continue because it might be a precompile
168+
ret, err = evm.Run(contract, input, false)
169+
170+
// When an error was returned by the EVM or when setting the creation code
171+
// above we revert to the snapshot and consume any gas remaining. Additionally
172+
// when we're in homestead this also counts for code storage gas errors.
173+
if err != nil {
174+
env.RevertToSnapshot(snapshot)
175+
if err != vm.ErrRevert {
176+
contract.UseGas(contract.Gas)
177+
}
178+
}
55179
return ret, err
56180
}
57181

58182
// StaticCall executes within the given contract and throws exception if state is attempted to be changed
59183
func StaticCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
60-
ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, new(big.Int), true)
184+
evm := env.Vm()
185+
// Depth check execution. Fail if we're trying to execute above the limit.
186+
if env.Depth() > callCreateDepthMax {
187+
caller.ReturnGas(gas, gasPrice)
188+
189+
return nil, errCallCreateDepth
190+
}
191+
192+
var (
193+
to vm.Account
194+
snapshot = env.SnapshotDatabase()
195+
)
196+
if !env.Db().Exist(addr) {
197+
to = env.Db().CreateAccount(addr)
198+
} else {
199+
to = env.Db().GetAccount(addr)
200+
}
201+
202+
// Initialise a new contract and set the code that is to be used by the EVM.
203+
// The contract is a scoped environment for this execution context only.
204+
contract := vm.NewContract(caller, to, new(big.Int), gas, gasPrice)
205+
contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
206+
defer contract.Finalise()
207+
208+
// We do an AddBalance of zero here, just in order to trigger a touch.
209+
// This is done to keep consensus with other clients since empty objects
210+
// get touched to be deleted even in a StaticCall context
211+
env.Db().AddBalance(addr, big.NewInt(0))
212+
213+
// Even if the account has no code, we need to continue because it might be a precompile
214+
ret, err = evm.Run(contract, input, true)
215+
216+
// When an error was returned by the EVM or when setting the creation code
217+
// above we revert to the snapshot and consume any gas remaining. Additionally
218+
// when we're in homestead this also counts for code storage gas errors.
219+
if err != nil {
220+
env.RevertToSnapshot(snapshot)
221+
if err != vm.ErrRevert {
222+
contract.UseGas(contract.Gas)
223+
}
224+
}
61225
return ret, err
62226
}
63227

64228
// Create creates a new contract with the given code
65229
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
66-
ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value, false)
230+
ret, address, err = create(env, caller, crypto.Keccak256Hash(code), code, gas, gasPrice, value)
67231
// Here we get an error if we run into maximum stack depth,
68232
// See: https://github.com/ethereum/yellowpaper/pull/131
69233
// and YP definitions for CREATE
@@ -75,10 +239,9 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
75239
return ret, address, err
76240
}
77241

78-
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int, readOnly bool) (ret []byte, addr common.Address, err error) {
242+
func create(env vm.Environment, caller vm.ContractRef, codeHash common.Hash, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
79243
evm := env.Vm()
80-
// Depth check execution. Fail if we're trying to execute above the
81-
// limit.
244+
// Depth check execution. Fail if we're trying to execute above the limit.
82245
if env.Depth() > callCreateDepthMax {
83246
caller.ReturnGas(gas, gasPrice)
84247

@@ -91,39 +254,25 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
91254
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
92255
}
93256

94-
var createAccount bool
95-
if address == nil {
96-
// Create a new account on the state
97-
nonce := env.Db().GetNonce(caller.Address())
98-
env.Db().SetNonce(caller.Address(), nonce+1)
99-
addr = crypto.CreateAddress(caller.Address(), nonce)
100-
address = &addr
101-
createAccount = true
257+
// Create a new account on the state
258+
nonce := env.Db().GetNonce(caller.Address())
259+
env.Db().SetNonce(caller.Address(), nonce+1)
260+
address = crypto.CreateAddress(caller.Address(), nonce)
261+
262+
// Ensure there's no existing contract already at the designated address
263+
contractHash := env.Db().GetCodeHash(address)
264+
if env.Db().GetNonce(address) != state.StartingNonce || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
265+
return nil, common.Address{}, errContractAddressCollision
102266
}
103267

104-
snapshotPreTransfer := env.SnapshotDatabase()
105268
var (
106-
from = env.Db().GetAccount(caller.Address())
107-
to vm.Account
269+
snapshot = env.SnapshotDatabase()
270+
from = env.Db().GetAccount(caller.Address())
271+
to = env.Db().CreateAccount(address)
108272
)
109273

110-
if createAccount {
111-
to = env.Db().CreateAccount(*address)
112-
113-
if env.RuleSet().IsAtlantis(env.BlockNumber()) {
114-
env.Db().SetNonce(*address, 1)
115-
}
116-
} else {
117-
if !env.Db().Exist(*address) {
118-
//no account may change state from non-existent to existent-but-empty. Refund sender.
119-
if vm.PrecompiledAtlantis[(*address).Str()] == nil && env.RuleSet().IsAtlantis(env.BlockNumber()) && value.BitLen() == 0 {
120-
caller.ReturnGas(gas, gasPrice)
121-
return nil, common.Address{}, nil
122-
}
123-
to = env.Db().CreateAccount(*address)
124-
} else {
125-
to = env.Db().GetAccount(*address)
126-
}
274+
if env.RuleSet().IsAtlantis(env.BlockNumber()) {
275+
env.Db().SetNonce(address, state.StartingNonce+1)
127276
}
128277

129278
env.Transfer(from, to, value)
@@ -132,22 +281,22 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
132281
// EVM. The contract is a scoped environment for this execution context
133282
// only.
134283
contract := vm.NewContract(caller, to, value, gas, gasPrice)
135-
contract.SetCallCode(codeAddr, codeHash, code)
284+
contract.SetCallCode(nil, codeHash, code)
136285
defer contract.Finalise()
137286

138-
ret, err = evm.Run(contract, input, readOnly)
287+
ret, err = evm.Run(contract, nil, false)
139288

140289
maxCodeSizeExceeded := len(ret) > maxCodeSize && env.RuleSet().IsAtlantis(env.BlockNumber())
141290
// if the contract creation ran successfully and no errors were returned
142291
// calculate the gas required to store the code. If the code could not
143292
// be stored due to not enough gas set an error and let it be handled
144293
// by the error checking condition below.
145-
if err == nil && createAccount && !maxCodeSizeExceeded {
294+
if err == nil && !maxCodeSizeExceeded {
146295
dataGas := big.NewInt(int64(len(ret)))
147296
// create data gas
148-
dataGas.Mul(dataGas, big.NewInt(200))
297+
dataGas.Mul(dataGas, params.CreateDataGas)
149298
if contract.UseGas(dataGas) {
150-
env.Db().SetCode(*address, ret)
299+
env.Db().SetCode(address, ret)
151300
} else {
152301
err = vm.CodeStoreOutOfGasError
153302
}
@@ -156,8 +305,8 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
156305
// When an error was returned by the EVM or when setting the creation code
157306
// above we revert to the snapshot and consume any gas remaining. Additionally
158307
// when we're in homestead this also counts for code storage gas errors.
159-
if createAccount && maxCodeSizeExceeded || (err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
160-
env.RevertToSnapshot(snapshotPreTransfer)
308+
if maxCodeSizeExceeded || (err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
309+
env.RevertToSnapshot(snapshot)
161310
if err != vm.ErrRevert {
162311
contract.UseGas(contract.Gas)
163312
}
@@ -168,42 +317,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
168317
err = errMaxCodeSizeExceeded
169318
}
170319

171-
return ret, addr, err
172-
}
173-
174-
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
175-
evm := env.Vm()
176-
// Depth check execution. Fail if we're trying to execute above the
177-
// limit.
178-
if env.Depth() > callCreateDepthMax {
179-
caller.ReturnGas(gas, gasPrice)
180-
return nil, common.Address{}, errCallCreateDepth
181-
}
182-
183-
snapshot := env.SnapshotDatabase()
184-
185-
var to vm.Account
186-
if !env.Db().Exist(*toAddr) {
187-
to = env.Db().CreateAccount(*toAddr)
188-
} else {
189-
to = env.Db().GetAccount(*toAddr)
190-
}
191-
192-
// Iinitialise a new contract and make initialise the delegate values
193-
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
194-
contract.SetCallCode(codeAddr, codeHash, code)
195-
defer contract.Finalise()
196-
197-
ret, err = evm.Run(contract, input, false)
198-
199-
if err != nil {
200-
env.RevertToSnapshot(snapshot)
201-
if err != vm.ErrRevert {
202-
contract.UseGas(contract.Gas)
203-
}
204-
}
205-
206-
return ret, addr, err
320+
return ret, address, err
207321
}
208322

209323
// generic transfer method

core/state/state_object.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/eth-classic/go-ethereum/logger/glog"
2929
"github.com/eth-classic/go-ethereum/rlp"
3030
"github.com/eth-classic/go-ethereum/trie"
31+
3132
)
3233

3334
var emptyCodeHash = crypto.Keccak256(nil)
@@ -93,7 +94,7 @@ type StateObject struct {
9394

9495
// empty returns whether the account is considered empty.
9596
func (s *StateObject) empty() bool {
96-
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
97+
return s.data.Nonce == StartingNonce && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
9798
}
9899

99100
// Account is the Ethereum consensus representation of accounts.

0 commit comments

Comments
 (0)