diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 93b991c45b..5bb084ae49 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -72,12 +72,6 @@ func (self *StateTransition) Receiver() *ethstate.StateObject { return self.rec } -func (self *StateTransition) MakeStateObject(state *ethstate.State, tx *Transaction) *ethstate.StateObject { - contract := MakeContract(tx, state) - - return contract -} - func (self *StateTransition) UseGas(amount *big.Int) error { if self.gas.Cmp(amount) < 0 { return OutOfGasError() @@ -190,7 +184,7 @@ func (self *StateTransition) TransitionState() (err error) { snapshot = self.state.Copy() // Create a new state object for the contract - receiver = self.MakeStateObject(self.state, tx) + receiver := MakeContract(tx, self.state) self.rec = receiver if receiver == nil { return fmt.Errorf("Unable to create contract") diff --git a/ethchain/vm_env.go b/ethchain/vm_env.go index 30f9497faf..4600878d1f 100644 --- a/ethchain/vm_env.go +++ b/ethchain/vm_env.go @@ -29,3 +29,4 @@ func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) Value() *big.Int { return self.tx.Value } func (self *VMEnv) State() *ethstate.State { return self.state } +func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit } diff --git a/ethpipe/vm_env.go b/ethpipe/vm_env.go index 822a9e5c73..10ce0e5616 100644 --- a/ethpipe/vm_env.go +++ b/ethpipe/vm_env.go @@ -32,3 +32,4 @@ func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty } func (self *VMEnv) BlockHash() []byte { return self.block.Hash() } func (self *VMEnv) Value() *big.Int { return self.value } func (self *VMEnv) State() *ethstate.State { return self.state } +func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit } diff --git a/ethvm/environment.go b/ethvm/environment.go index 38dbc6499d..b04fa332e3 100644 --- a/ethvm/environment.go +++ b/ethvm/environment.go @@ -17,6 +17,7 @@ type Environment interface { Time() int64 Difficulty() *big.Int BlockHash() []byte + GasLimit() *big.Int } type Object interface { diff --git a/ethvm/execution.go b/ethvm/execution.go index ce90ade154..f5f1ce70df 100644 --- a/ethvm/execution.go +++ b/ethvm/execution.go @@ -10,21 +10,27 @@ import ( type Execution struct { vm VirtualMachine - closure *Closure address, input []byte - gas, price, value *big.Int + Gas, price, value *big.Int object *ethstate.StateObject } func NewExecution(vm VirtualMachine, address, input []byte, gas, gasPrice, value *big.Int) *Execution { - return &Execution{vm: vm, address: address, input: input, gas: gas, price: gasPrice, value: value} + return &Execution{vm: vm, address: address, input: input, Gas: gas, price: gasPrice, value: value} } func (self *Execution) Addr() []byte { return self.address } -func (self *Execution) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err error) { +func (self *Execution) Exec(codeAddr []byte, caller ClosureRef) ([]byte, error) { + // Retrieve the executing code + code := self.vm.Env().State().GetCode(codeAddr) + + return self.exec(code, codeAddr, caller) +} + +func (self *Execution) exec(code, caddr []byte, caller ClosureRef) (ret []byte, err error) { env := self.vm.Env() snapshot := env.State().Copy() @@ -44,7 +50,7 @@ func (self *Execution) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err object := caller.Object() if object.Balance.Cmp(self.value) < 0 { - caller.ReturnGas(self.gas, self.price) + caller.ReturnGas(self.Gas, self.price) err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, object.Balance) } else { @@ -55,23 +61,23 @@ func (self *Execution) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err stateObject.AddAmount(self.value) // Pre-compiled contracts (address.go) 1, 2 & 3. - naddr := ethutil.BigD(codeAddr).Uint64() + naddr := ethutil.BigD(caddr).Uint64() if p := Precompiled[naddr]; p != nil { - if self.gas.Cmp(p.Gas) >= 0 { + if self.Gas.Cmp(p.Gas) >= 0 { ret = p.Call(self.input) self.vm.Printf("NATIVE_FUNC(%x) => %x", naddr, ret) } } else { + // Create a new callable closure + c := NewClosure(msg, caller, stateObject, code, self.Gas, self.price) + c.exe = self + if self.vm.Depth() == MaxCallDepth { - return nil, fmt.Errorf("Max call depth exceeded (%d)", MaxCallDepth) - } + c.UseGas(c.Gas) - // Retrieve the executing code - code := env.State().GetCode(codeAddr) + return c.Return(nil), fmt.Errorf("Max call depth exceeded (%d)", MaxCallDepth) + } - // Create a new callable closure - c := NewClosure(msg, caller, stateObject, code, self.gas, self.price) - c.exe = self // Executer the closure and get the return value (if any) ret, _, err = c.Call(self.vm, self.input) @@ -81,3 +87,7 @@ func (self *Execution) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err return } + +func (self *Execution) Create(caller ClosureRef) (ret []byte, err error) { + return self.exec(self.input, nil, caller) +} diff --git a/ethvm/vm_debug.go b/ethvm/vm_debug.go index 6d42c6b1e1..da9ad8d597 100644 --- a/ethvm/vm_debug.go +++ b/ethvm/vm_debug.go @@ -54,6 +54,25 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m)) } } + + jump = func(pos *big.Int) { + p := int(pos.Int64()) + + self.Printf(" ~> %v", pos) + // Return to start + if p == 0 { + pc = big.NewInt(0) + } else { + nop := OpCode(closure.GetOp(p - 1)) + if nop != JUMPDEST { + panic(fmt.Sprintf("JUMP missed JUMPDEST (%v) %v", nop, p)) + } + + pc = pos + } + + self.Endl() + } ) if self.Recoverable { @@ -440,14 +459,18 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { case BYTE: require(2) val, th := stack.Popn() - if th.Cmp(big.NewInt(32)) < 0 && th.Cmp(big.NewInt(int64(len(val.Bytes())))) < 0 { + + if th.Cmp(big.NewInt(32)) < 0 { byt := big.NewInt(int64(ethutil.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) - stack.Push(byt) - self.Printf(" => 0x%x", byt.Bytes()) + base.Set(byt) } else { - stack.Push(ethutil.BigFalse) + base.Set(ethutil.BigFalse) } + + self.Printf(" => 0x%x", base.Bytes()) + + stack.Push(base) case ADDMOD: require(3) @@ -562,7 +585,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { mem.Set(mOff, l, code) case CODESIZE, EXTCODESIZE: var code []byte - if op == EXTCODECOPY { + if op == EXTCODESIZE { addr := stack.Pop().Bytes() code = state.GetCode(addr) @@ -638,8 +661,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf(" => 0x%x", difficulty.Bytes()) case GASLIMIT: - // TODO - stack.Push(big.NewInt(0)) + stack.Push(self.env.GasLimit()) // 0x50 range case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: @@ -713,32 +735,21 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf(" {0x%x : 0x%x}", loc.Bytes(), val.Bytes()) case JUMP: require(1) - pc = stack.Pop() - - if OpCode(closure.Get(pc).Uint()) != JUMPDEST { - panic(fmt.Sprintf("JUMP missed JUMPDEST %v", pc)) - } - // Reduce pc by one because of the increment that's at the end of this for loop - self.Printf(" ~> %v", pc).Endl() + jump(stack.Pop()) continue case JUMPI: require(2) cond, pos := stack.Popn() - if cond.Cmp(ethutil.BigTrue) >= 0 { - pc = pos - if OpCode(closure.Get(pc).Uint()) != JUMPDEST { - panic(fmt.Sprintf("JUMP missed JUMPDEST %v", pc)) - } + if cond.Cmp(ethutil.BigTrue) >= 0 { + jump(pos) continue - } else { - self.Printf(" (f)") } + case JUMPDEST: - self.Printf(" ~> %v (t)", pc).Endl() case PC: stack.Push(pc) case MSIZE: @@ -771,7 +782,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { closure.UseGas(closure.Gas) msg := NewExecution(self, addr, input, gas, closure.Price, value) - ret, err := msg.Exec(addr, closure) + ret, err := msg.Create(closure) if err != nil { stack.Push(ethutil.BigFalse) @@ -857,6 +868,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) //panic(fmt.Sprintf("Invalid opcode %x", op)) + closure.ReturnGas(big.NewInt(1), nil) return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) } diff --git a/tests/helper/init.go b/tests/helper/init.go index 5f95dfc2fd..7c34913fc1 100644 --- a/tests/helper/init.go +++ b/tests/helper/init.go @@ -9,9 +9,10 @@ import ( ) var Logger ethlog.LogSystem +var Log = ethlog.NewLogger("TEST") func init() { - Logger = ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4)) + Logger = ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(0)) ethlog.AddLogSystem(Logger) ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") diff --git a/tests/helper/vm.go b/tests/helper/vm.go index 4a0d2f8b19..da06b2cc2e 100644 --- a/tests/helper/vm.go +++ b/tests/helper/vm.go @@ -18,6 +18,7 @@ type Env struct { number *big.Int time int64 difficulty *big.Int + gasLimit *big.Int } func NewEnv(state *ethstate.State) *Env { @@ -33,7 +34,9 @@ func NewEnvFromMap(state *ethstate.State, envValues map[string]string, exeValues env.parent = ethutil.Hex2Bytes(envValues["previousHash"]) env.coinbase = ethutil.Hex2Bytes(envValues["currentCoinbase"]) env.number = ethutil.Big(envValues["currentNumber"]) - env.time = ethutil.Big(envValues["currentTime"]).Int64() + env.time = ethutil.Big(envValues["currentTimestamp"]).Int64() + env.difficulty = ethutil.Big(envValues["currentDifficulty"]) + env.gasLimit = ethutil.Big(envValues["currentGasLimit"]) return env } @@ -46,14 +49,17 @@ func (self *Env) Time() int64 { return self.time } func (self *Env) Difficulty() *big.Int { return self.difficulty } func (self *Env) BlockHash() []byte { return nil } func (self *Env) State() *ethstate.State { return self.state } +func (self *Env) GasLimit() *big.Int { return self.gasLimit } func RunVm(state *ethstate.State, env, exec map[string]string) ([]byte, *big.Int, error) { - caller := state.GetOrNewStateObject(ethutil.Hex2Bytes(exec["caller"])) - callee := state.GetStateObject(ethutil.Hex2Bytes(exec["address"])) - closure := ethvm.NewClosure(nil, caller, callee, callee.Code, ethutil.Big(exec["gas"]), ethutil.Big(exec["gasPrice"])) + address := FromHex(exec["address"]) + caller := state.GetOrNewStateObject(FromHex(exec["caller"])) + caller.Balance = ethutil.Big(exec["value"]) vm := ethvm.New(NewEnvFromMap(state, env, exec), ethvm.DebugVmTy) - ret, _, e := closure.Call(vm, nil) - return ret, closure.Gas, e + execution := ethvm.NewExecution(vm, address, FromHex(exec["data"]), ethutil.Big(exec["gas"]), ethutil.Big(exec["gasPrice"]), ethutil.Big(exec["value"])) + ret, err := execution.Exec(address, caller) + + return ret, execution.Gas, err } diff --git a/tests/vm/gh_test.go b/tests/vm/gh_test.go index 6ae1cf29a0..5de5b64330 100644 --- a/tests/vm/gh_test.go +++ b/tests/vm/gh_test.go @@ -2,7 +2,6 @@ package ethvm import ( "bytes" - "fmt" "testing" "github.com/ethereum/eth-go/ethstate" @@ -55,7 +54,7 @@ func RunVmTest(url string, t *testing.T) { // When an error is returned it doesn't always mean the tests fails. // Have to come up with some conditional failing mechanism. if err != nil { - fmt.Println(err) + helper.Log.Infoln(err) } /* if err != nil { @@ -80,7 +79,7 @@ func RunVmTest(url string, t *testing.T) { vexp := helper.FromHex(value) if bytes.Compare(v, vexp) != 0 { - t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x\n", name, obj.Address()[0:4], addr, vexp, v) + t.Errorf("%s's : (%x: %s) storage failed. Expected %x, got %x (%v %v)\n", name, obj.Address()[0:4], addr, vexp, v, ethutil.BigD(vexp), ethutil.BigD(v)) } } } @@ -88,29 +87,49 @@ func RunVmTest(url string, t *testing.T) { } // I've created a new function for each tests so it's easier to identify where the problem lies if any of them fail. -func TestVMSha3(t *testing.T) { - helper.Logger.SetLogLevel(0) - defer helper.Logger.SetLogLevel(4) - const url = "https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmSha3Test.json" +func TestVMArithmetic(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmArithmeticTest.json" RunVmTest(url, t) } -func TestVMArithmetic(t *testing.T) { - helper.Logger.SetLogLevel(0) - defer helper.Logger.SetLogLevel(4) +func TestVMSystemOperation(t *testing.T) { + //helper.Logger.SetLogLevel(5) + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmSystemOperationsTest.json" + RunVmTest(url, t) +} + +func TestBitwiseLogicOperation(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmBitwiseLogicOperationTest.json" + RunVmTest(url, t) +} + +func TestBlockInfo(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmBlockInfoTest.json" + RunVmTest(url, t) +} + +func TestEnvironmentalInfo(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmEnvironmentalInfoTest.json" + RunVmTest(url, t) +} - const url = "https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmArithmeticTest.json" +func TestFlowOperation(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmIOandFlowOperationsTest.json" RunVmTest(url, t) } -func TestVMSystemOperations(t *testing.T) { - const url = "https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmSystemOperationsTest.json" +func TestPushDupSwap(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmPushDupSwapTest.json" + RunVmTest(url, t) +} + +func TestVMSha3(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmSha3Test.json" RunVmTest(url, t) } -func TestOperations(t *testing.T) { - t.Skip() - const url = "https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmSystemOperationsTest.json" +func TestVm(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/develop/vmtests/vmtests.json" RunVmTest(url, t) }