From cb84e3f02953f2df166ae69369d222dcbbd7d78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Sat, 1 Oct 2016 15:44:53 +0300 Subject: [PATCH] cmd, core, internal, light, tests: avoid hashing the code in the VM --- cmd/evm/main.go | 5 ++++- core/execution.go | 16 ++++++++-------- core/state/state_object.go | 4 ++-- core/state/state_test.go | 7 ++++--- core/state/statedb.go | 11 ++++++++++- core/state/statedb_test.go | 9 +++++---- core/state/sync_test.go | 2 +- core/vm/contract.go | 11 +++++++---- core/vm/environment.go | 3 ++- core/vm/jit_test.go | 2 +- core/vm/logger_test.go | 2 +- core/vm/runtime/runtime.go | 2 +- core/vm/vm.go | 9 +++++---- internal/ethapi/tracer_test.go | 2 +- light/state_test.go | 3 ++- tests/block_test_util.go | 3 ++- tests/util.go | 5 +++-- 17 files changed, 59 insertions(+), 37 deletions(-) diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 3f44e0f3c0..09ade1577d 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" "gopkg.in/urfave/cli.v1" @@ -146,7 +147,9 @@ func run(ctx *cli.Context) error { ) } else { receiver := statedb.CreateAccount(common.StringToAddress("receiver")) - receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))) + + code := common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)) + receiver.SetCode(crypto.Keccak256Hash(code), code) ret, err = vmenv.Call( sender, receiver.Address(), diff --git a/core/execution.go b/core/execution.go index 82143443c7..1bc02f7fb2 100644 --- a/core/execution.go +++ b/core/execution.go @@ -27,14 +27,14 @@ import ( // Call executes within the given contract func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) { - ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) + ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) return ret, err } // 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) { callerAddr := caller.Address() - ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value) + ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value) return ret, err } @@ -43,13 +43,13 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address callerAddr := caller.Address() originAddr := env.Origin() callerValue := caller.Value() - ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue) + ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue) return ret, err } // 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 = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value) + ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, 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 instruction @@ -59,7 +59,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric return ret, address, err } -func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { +func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. @@ -105,7 +105,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // EVM. The contract is a scoped environment for this execution context // only. contract := vm.NewContract(caller, to, value, gas, gasPrice) - contract.SetCallCode(codeAddr, code) + contract.SetCallCode(codeAddr, codeHash, code) defer contract.Finalise() ret, err = evm.Run(contract, input) @@ -135,7 +135,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A return ret, addr, err } -func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) { +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) { evm := env.Vm() // Depth check execution. Fail if we're trying to execute above the // limit. @@ -155,7 +155,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA // Iinitialise a new contract and make initialise the delegate values contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate() - contract.SetCallCode(codeAddr, code) + contract.SetCallCode(codeAddr, codeHash, code) defer contract.Finalise() ret, err = evm.Run(contract, input) diff --git a/core/state/state_object.go b/core/state/state_object.go index a54620d555..121a2ec5c3 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -273,9 +273,9 @@ func (self *StateObject) Code(db trie.Database) []byte { return code } -func (self *StateObject) SetCode(code []byte) { +func (self *StateObject) SetCode(codeHash common.Hash, code []byte) { self.code = code - self.data.CodeHash = crypto.Keccak256(code) + self.data.CodeHash = codeHash[:] self.dirtyCode = true if self.onDirty != nil { self.onDirty(self.Address()) diff --git a/core/state/state_test.go b/core/state/state_test.go index fcdc385884..5fe98939ba 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -24,6 +24,7 @@ import ( checker "gopkg.in/check.v1" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) @@ -40,7 +41,7 @@ func (s *StateSuite) TestDump(c *checker.C) { obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01})) obj1.AddBalance(big.NewInt(22)) obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02})) - obj2.SetCode([]byte{3, 3, 3, 3, 3, 3, 3}) + obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3}) obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02})) obj3.SetBalance(big.NewInt(44)) @@ -148,7 +149,7 @@ func TestSnapshot2(t *testing.T) { so0 := state.GetStateObject(stateobjaddr0) so0.SetBalance(big.NewInt(42)) so0.SetNonce(43) - so0.SetCode([]byte{'c', 'a', 'f', 'e'}) + so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.remove = false so0.deleted = false state.SetStateObject(so0) @@ -160,7 +161,7 @@ func TestSnapshot2(t *testing.T) { so1 := state.GetStateObject(stateobjaddr1) so1.SetBalance(big.NewInt(52)) so1.SetNonce(53) - so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'}) + so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.remove = true so1.deleted = true state.SetStateObject(so1) diff --git a/core/state/statedb.go b/core/state/statedb.go index 5c51e3b59b..4204c456e0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -246,6 +247,14 @@ func (self *StateDB) GetCodeSize(addr common.Address) int { return size } +func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { + stateObject := self.GetStateObject(addr) + if stateObject == nil { + return common.Hash{} + } + return common.BytesToHash(stateObject.CodeHash()) +} + func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash { stateObject := self.GetStateObject(a) if stateObject != nil { @@ -283,7 +292,7 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { func (self *StateDB) SetCode(addr common.Address, code []byte) { stateObject := self.GetOrNewStateObject(addr) if stateObject != nil { - stateObject.SetCode(code) + stateObject.SetCode(crypto.Keccak256Hash(code), code) } } diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 9283334597..7930b620d4 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) @@ -40,7 +41,7 @@ func TestUpdateLeaks(t *testing.T) { obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i})) } if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}) } state.UpdateStateObject(obj) } @@ -70,7 +71,7 @@ func TestIntermediateLeaks(t *testing.T) { obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0})) } if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i, 0}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 0}), []byte{i, i, i, i, i, 0}) } transState.UpdateStateObject(obj) @@ -82,7 +83,7 @@ func TestIntermediateLeaks(t *testing.T) { obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1})) } if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i, 1}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 1}), []byte{i, i, i, i, i, 1}) } transState.UpdateStateObject(obj) @@ -94,7 +95,7 @@ func TestIntermediateLeaks(t *testing.T) { obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1})) } if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i, 1}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 1}), []byte{i, i, i, i, i, 1}) } finalState.UpdateStateObject(obj) } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index c768781a47..670e1fb1bf 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -54,7 +54,7 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) { acc.nonce = uint64(42 * i) if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}) acc.code = []byte{i, i, i, i, i} } state.UpdateStateObject(obj) diff --git a/core/vm/contract.go b/core/vm/contract.go index 844d3f328e..70455a4c23 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -27,7 +27,7 @@ type ContractRef interface { ReturnGas(*big.Int, *big.Int) Address() common.Address Value() *big.Int - SetCode([]byte) + SetCode(common.Hash, []byte) ForEachStorage(callback func(key, value common.Hash) bool) } @@ -44,8 +44,9 @@ type Contract struct { jumpdests destinations // result of JUMPDEST analysis. Code []byte - Input []byte + CodeHash common.Hash CodeAddr *common.Address + Input []byte value, Gas, UsedGas, Price *big.Int @@ -143,14 +144,16 @@ func (c *Contract) Value() *big.Int { } // SetCode sets the code to the contract -func (self *Contract) SetCode(code []byte) { +func (self *Contract) SetCode(hash common.Hash, code []byte) { self.Code = code + self.CodeHash = hash } // SetCallCode sets the code of the contract and address of the backing data // object -func (self *Contract) SetCallCode(addr *common.Address, code []byte) { +func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) { self.Code = code + self.CodeHash = hash self.CodeAddr = addr } diff --git a/core/vm/environment.go b/core/vm/environment.go index 4bd03de7eb..daf6fb90d5 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -94,6 +94,7 @@ type Database interface { GetNonce(common.Address) uint64 SetNonce(common.Address, uint64) + GetCodeHash(common.Address) common.Hash GetCodeSize(common.Address) int GetCode(common.Address) []byte SetCode(common.Address, []byte) @@ -118,7 +119,7 @@ type Account interface { Balance() *big.Int Address() common.Address ReturnGas(*big.Int, *big.Int) - SetCode([]byte) + SetCode(common.Hash, []byte) ForEachStorage(cb func(key, value common.Hash) bool) Value() *big.Int } diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 809abfea97..e6922aeb74 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -135,7 +135,7 @@ func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } func (account) ReturnGas(*big.Int, *big.Int) {} -func (account) SetCode([]byte) {} +func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func runVmBench(test vmBench, b *testing.B) { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index e85bca2277..d4d164eb6f 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -30,7 +30,7 @@ type dummyContractRef struct { func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {} func (dummyContractRef) Address() common.Address { return common.Address{} } func (dummyContractRef) Value() *big.Int { return new(big.Int) } -func (dummyContractRef) SetCode([]byte) {} +func (dummyContractRef) SetCode(common.Hash, []byte) {} func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) { d.calledForEach = true } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 309d508c3d..6d980cb327 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -104,7 +104,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { receiver = cfg.State.CreateAccount(common.StringToAddress("contract")) ) // set the receiver's (the executing contract) code for execution. - receiver.SetCode(code) + receiver.SetCode(crypto.Keccak256Hash(code), code) // Call the code with the given configuration. ret, err := vmenv.Call( diff --git a/core/vm/vm.go b/core/vm/vm.go index 9d7b550587..5d78b4a2ad 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -71,10 +71,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) { return nil, nil } - var ( - codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching - program *Program - ) + codehash := contract.CodeHash // codehash is used when doing jump dest caching + if codehash == (common.Hash{}) { + codehash = crypto.Keccak256Hash(contract.Code) + } + var program *Program if evm.cfg.EnableJit { // If the JIT is enabled check the status of the JIT program, // if it doesn't exist compile a new program in a separate diff --git a/internal/ethapi/tracer_test.go b/internal/ethapi/tracer_test.go index 301ff48404..7c831d2990 100644 --- a/internal/ethapi/tracer_test.go +++ b/internal/ethapi/tracer_test.go @@ -93,7 +93,7 @@ func (account) SetNonce(uint64) {} func (account) Balance() *big.Int { return nil } func (account) Address() common.Address { return common.Address{} } func (account) ReturnGas(*big.Int, *big.Int) {} -func (account) SetCode([]byte) {} +func (account) SetCode(common.Hash, []byte) {} func (account) ForEachStorage(cb func(key, value common.Hash) bool) {} func runTrace(tracer *JavascriptTracer) (interface{}, error) { diff --git a/light/state_test.go b/light/state_test.go index d7014a2dcc..d4fe950229 100644 --- a/light/state_test.go +++ b/light/state_test.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" "golang.org/x/net/context" @@ -60,7 +61,7 @@ func makeTestState() (common.Hash, ethdb.Database) { so.SetNonce(100) } so.AddBalance(big.NewInt(int64(i))) - so.SetCode([]byte{i, i, i}) + so.SetCode(crypto.Keccak256Hash([]byte{i, i, i}), []byte{i, i, i}) so.UpdateRoot(sdb) st.UpdateStateObject(so) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 7da15cebe3..7096b866d4 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/logger/glog" @@ -219,7 +220,7 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) { return nil, err } obj := statedb.CreateAccount(common.HexToAddress(addrString)) - obj.SetCode(code) + obj.SetCode(crypto.Keccak256Hash(code), code) obj.SetBalance(balance) obj.SetNonce(nonce) for k, v := range acct.Storage { diff --git a/tests/util.go b/tests/util.go index 08fac2dd1b..ffbcb9d56d 100644 --- a/tests/util.go +++ b/tests/util.go @@ -108,12 +108,13 @@ func StateObjectFromAccount(db ethdb.Database, addr string, account Account, onD account.Code = account.Code[2:] } code := common.Hex2Bytes(account.Code) + codeHash := crypto.Keccak256Hash(code) obj := state.NewObject(common.HexToAddress(addr), state.Account{ Balance: common.Big(account.Balance), - CodeHash: crypto.Keccak256(code), + CodeHash: codeHash[:], Nonce: common.Big(account.Nonce).Uint64(), }, onDirty) - obj.SetCode(code) + obj.SetCode(codeHash, code) return obj }