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

Storage and edge case fixes #28

Merged
merged 2 commits into from
Aug 13, 2019
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 10 additions & 24 deletions core/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ var (
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the limit.
if env.Depth() > callCreateDepthMax {
caller.ReturnGas(gas, gasPrice)
Expand Down Expand Up @@ -86,7 +85,7 @@ func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input
defer contract.Finalise()

// Even if the account has no code, we need to continue because it might be a precompile
ret, err = evm.Run(contract, input, false)
ret, err = env.Vm().Run(contract, input, false)

// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
Expand All @@ -102,7 +101,6 @@ func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input

// CallCode executes the given address' code as the given contract address
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the limit.
if env.Depth() > callCreateDepthMax {
caller.ReturnGas(gas, gasPrice)
Expand All @@ -127,7 +125,7 @@ func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, in
defer contract.Finalise()

// Even if the account has no code, we need to continue because it might be a precompile
ret, err = evm.Run(contract, input, false)
ret, err = env.Vm().Run(contract, input, false)

if err != nil {
env.RevertToSnapshot(snapshot)
Expand All @@ -140,7 +138,6 @@ func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, in

// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the limit.
if env.Depth() > callCreateDepthMax {
caller.ReturnGas(gas, gasPrice)
Expand All @@ -165,7 +162,7 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
defer contract.Finalise()

// Even if the account has no code, we need to continue because it might be a precompile
ret, err = evm.Run(contract, input, false)
ret, err = env.Vm().Run(contract, input, false)

// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
Expand All @@ -181,7 +178,6 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address

// StaticCall executes within the given contract and throws exception if state is attempted to be changed
func StaticCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the limit.
if env.Depth() > callCreateDepthMax {
caller.ReturnGas(gas, gasPrice)
Expand Down Expand Up @@ -211,7 +207,7 @@ func StaticCall(env vm.Environment, caller vm.ContractRef, addr common.Address,
env.Db().AddBalance(addr, big.NewInt(0))

// Even if the account has no code, we need to continue because it might be a precompile
ret, err = evm.Run(contract, input, true)
ret, err = env.Vm().Run(contract, input, true)

// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
Expand All @@ -227,20 +223,6 @@ func StaticCall(env vm.Environment, caller vm.ContractRef, addr common.Address,

// Create creates a new contract with the given code
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
ret, address, err = create(env, caller, crypto.Keccak256Hash(code), code, gas, gasPrice, value)
// Here we get an error if we run into maximum stack depth,
// See: https://github.com/ethereum/yellowpaper/pull/131
// and YP definitions for CREATE

//if there's an error we return nothing
if err != nil && err != vm.ErrRevert {
return nil, address, err
}
return ret, address, err
}

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) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the limit.
if env.Depth() > callCreateDepthMax {
caller.ReturnGas(gas, gasPrice)
Expand Down Expand Up @@ -281,10 +263,10 @@ func create(env vm.Environment, caller vm.ContractRef, codeHash common.Hash, cod
// EVM. The contract is a scoped environment for this execution context
// only.
contract := vm.NewContract(caller, to, value, gas, gasPrice)
contract.SetCallCode(nil, codeHash, code)
contract.SetCallCode(nil, crypto.Keccak256Hash(code), code)
defer contract.Finalise()

ret, err = evm.Run(contract, nil, false)
ret, err = env.Vm().Run(contract, nil, false)

maxCodeSizeExceeded := len(ret) > maxCodeSize && env.RuleSet().IsAtlantis(env.BlockNumber())
// if the contract creation ran successfully and no errors were returned
Expand Down Expand Up @@ -317,6 +299,10 @@ func create(env vm.Environment, caller vm.ContractRef, codeHash common.Hash, cod
err = errMaxCodeSizeExceeded
}

//if there's an error we return nothing
if err != nil && err != vm.ErrRevert {
return nil, address, err
}
return ret, address, err
}

Expand Down
29 changes: 19 additions & 10 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,18 +386,27 @@ func (self *StateObject) Value() *big.Int {
panic("Value on StateObject should never be called")
}

func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
// When iterating over the storage check the cache first
for h, value := range self.cachedStorage {
cb(h, value)
}

it := trie.NewIterator(self.getTrie(self.db.db).NodeIterator(nil))
func (s *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) error {
it := trie.NewIterator(s.getTrie(s.db.db).NodeIterator(nil))
for it.Next() {
// ignore cached values
key := common.BytesToHash(self.trie.GetKey(it.Key))
if _, ok := self.cachedStorage[key]; !ok {
cb(key, common.BytesToHash(it.Value))
key := common.BytesToHash(s.trie.GetKey(it.Key))
if value, dirty := s.dirtyStorage[key]; dirty {
if cb(key, value) {
return nil
}
continue
}

if len(it.Value) > 0 {
_, content, _, err := rlp.Split(it.Value)
if err != nil {
return err
}
if !cb(key, common.BytesToHash(content)) {
return nil
}
}
}
return nil
}
56 changes: 24 additions & 32 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,12 @@ func (self *StateDB) Copy() *StateDB {
}

for hash, logs := range self.logs {
state.logs[hash] = make(vm.Logs, len(logs))
copy(state.logs[hash], logs)
cpy := make([]*vm.Log, len(logs))
for i, l := range logs {
cpy[i] = new(vm.Log)
*cpy[i] = *l
}
state.logs[hash] = cpy
}
for hash, preimage := range self.preimages {
state.preimages[hash] = preimage
Expand Down Expand Up @@ -558,27 +562,6 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
s.clearJournalAndRefund()
}

// DeleteSuicides flags the suicided objects for deletion so that it
// won't be referenced again when called / queried up on.
//
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use this method.
s.clearJournalAndRefund()

for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]

// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.suicided {
stateObject.deleted = true
}
delete(s.stateObjectsDirty, addr)
}
}

func (s *StateDB) clearJournalAndRefund() {
s.journal = newJournal()
s.validRevisions = s.validRevisions[:0]
Expand Down Expand Up @@ -624,23 +607,32 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
return root, err
}

func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) {
func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
so := db.getStateObject(addr)
if so == nil {
return
}

// When iterating over the storage check the cache first
for h, value := range so.cachedStorage {
cb(h, value)
return nil
}

it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key))
if _, ok := so.cachedStorage[key]; !ok {
cb(key, common.BytesToHash(it.Value))
if value, dirty := so.dirtyStorage[key]; dirty {
if cb(key, value) {
return nil
}
continue
}

if len(it.Value) > 0 {
_, content, _, err := rlp.Split(it.Value)
if err != nil {
return err
}
if !cb(key, common.BytesToHash(content)) {
return nil
}
}
}
return nil
}
4 changes: 2 additions & 2 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (ty
if UseSputnikVM != "true" {
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas)
if err != nil {
return nil, nil, totalUsedGas, err
return nil, nil, nil, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...)
continue
}
receipt, logs, _, err := ApplyMultiVmTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas)
if err != nil {
return nil, nil, totalUsedGas, err
return nil, nil, nil, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...)
Expand Down
2 changes: 1 addition & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) (e error) {
return
}

intrGas := IntrinsicGas(tx.Data(), MessageCreatesContract(tx), pool.homestead)
intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
if tx.Gas().Cmp(intrGas) < 0 {
e = ErrIntrinsicGas
return
Expand Down
6 changes: 3 additions & 3 deletions core/vm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ContractRef interface {
Address() common.Address
Value() *big.Int
SetCode(common.Hash, []byte)
ForEachStorage(callback func(key, value common.Hash) bool)
ForEachStorage(callback func(key, value common.Hash) bool) error
}

// Contract represents an ethereum contract in the state database. It contains
Expand Down Expand Up @@ -159,8 +159,8 @@ func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code [

// EachStorage iterates the contract's storage and calls a method for every key
// value pair.
func (self *Contract) ForEachStorage(cb func(key, value common.Hash) bool) {
self.caller.ForEachStorage(cb)
func (self *Contract) ForEachStorage(cb func(key, value common.Hash) bool) error {
return self.caller.ForEachStorage(cb)
}

func (c *Contract) isValidJump(pc *uint64, to *big.Int) bool {
Expand Down
2 changes: 1 addition & 1 deletion core/vm/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,6 @@ type Account interface {
Address() common.Address
ReturnGas(*big.Int, *big.Int)
SetCode(common.Hash, []byte)
ForEachStorage(cb func(key, value common.Hash) bool)
ForEachStorage(cb func(key, value common.Hash) bool) error
Value() *big.Int
}
16 changes: 8 additions & 8 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,14 +568,14 @@ type PublicBlockChainAPI struct {
// NewPublicBlockChainAPI creates a new Etheruem blockchain API.
func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
api := &PublicBlockChainAPI{
config: config,
bc: bc,
miner: m,
chainDb: chainDb,
eventMux: eventMux,
am: am,
config: config,
bc: bc,
miner: m,
chainDb: chainDb,
eventMux: eventMux,
am: am,
newBlockSubscriptions: make(map[string]func(core.ChainEvent) error),
gpo: gpo,
gpo: gpo,
}

go api.subscriptionLoop()
Expand Down Expand Up @@ -2154,7 +2154,7 @@ func (s *PublicDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int) (core.
if err != nil {
return nil, nil, fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
}
statedb.DeleteSuicides()
statedb.Finalise(true)
}
return nil, nil, fmt.Errorf("tx index %d out of range for block %x", txIndex, blockHash)
}
Expand Down