From 311c6f8a3fed5ac03ee4b442fd0f420072bc41b4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 15 Oct 2014 17:12:26 +0200 Subject: [PATCH] Fixed remote Arithmetic tests --- ethchain/state_transition.go | 3 +- ethstate/state.go | 9 +++ ethstate/state_object.go | 10 ++- ethutil/big.go | 29 ++++++--- ethvm/common.go | 3 +- ethvm/vm.go | 65 +++++++++++++------ ethvm/vm_debug.go | 117 ++++++++++++++++++++++------------- tests/helper/http.go | 11 ++-- tests/helper/init.go | 5 +- tests/helper/vm.go | 8 +-- tests/vm/gh_test.go | 27 +++++--- 11 files changed, 187 insertions(+), 100 deletions(-) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 10159929b..719e5fd66 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -270,7 +270,8 @@ func (self *StateTransition) Eval(msg *ethstate.Message, script []byte, context callerClosure = ethvm.NewClosure(msg, transactor, context, script, self.gas, self.gasPrice) ) - vm := ethvm.New(env, ethvm.Type(ethutil.Config.VmType)) + //vm := ethvm.New(env, ethvm.Type(ethutil.Config.VmType)) + vm := ethutil.New(env, ethvm.DebugVmTy) ret, _, err = callerClosure.Call(vm, self.tx.Data) diff --git a/ethstate/state.go b/ethstate/state.go index c2486849b..b897b7ce3 100644 --- a/ethstate/state.go +++ b/ethstate/state.go @@ -57,6 +57,15 @@ func (self *State) GetCode(addr []byte) []byte { return nil } +func (self *State) GetState(a, b []byte) []byte { + stateObject := self.GetStateObject(a) + if stateObject != nil { + return stateObject.GetState(b).Bytes() + } + + return nil +} + // // Setting, updating & deleting state object methods // diff --git a/ethstate/state_object.go b/ethstate/state_object.go index fe4c5f73b..4d2aae1a7 100644 --- a/ethstate/state_object.go +++ b/ethstate/state_object.go @@ -98,13 +98,13 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { } func (self *StateObject) GetStorage(key *big.Int) *ethutil.Value { - return self.getStorage(key.Bytes()) + return self.GetState(key.Bytes()) } func (self *StateObject) SetStorage(key *big.Int, value *ethutil.Value) { - self.setStorage(key.Bytes(), value) + self.SetState(key.Bytes(), value) } -func (self *StateObject) getStorage(k []byte) *ethutil.Value { +func (self *StateObject) GetState(k []byte) *ethutil.Value { key := ethutil.LeftPadBytes(k, 32) value := self.storage[string(key)] @@ -117,11 +117,9 @@ func (self *StateObject) getStorage(k []byte) *ethutil.Value { } return value - - //return self.GetAddr(key) } -func (self *StateObject) setStorage(k []byte, value *ethutil.Value) { +func (self *StateObject) SetState(k []byte, value *ethutil.Value) { key := ethutil.LeftPadBytes(k, 32) self.storage[string(key)] = value.Copy() } diff --git a/ethutil/big.go b/ethutil/big.go index e23d8f659..bdcf86421 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -1,8 +1,8 @@ package ethutil -import ( - "math/big" -) +import "math/big" + +var MaxInt256 *big.Int = BigD(Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // Big pow // @@ -37,18 +37,29 @@ func BigD(data []byte) *big.Int { // To256 // // "cast" the big int to a 256 big int (i.e., limit to) -var tt256 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt256 = new(big.Int).Lsh(big.NewInt(1), 256) +var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1)) +var tt255 = new(big.Int).Lsh(big.NewInt(1), 255) -func To256(x *big.Int) *big.Int { - x.And(x, tt256) +func U256(x *big.Int) *big.Int { + //if x.Cmp(Big0) < 0 { + // return new(big.Int).Add(tt256, x) + // } - if x.Cmp(new(big.Int)) < 0 { - x.SetInt64(0) - } + x.And(x, tt256m1) return x } +func S256(x *big.Int) *big.Int { + if x.Cmp(tt255) < 0 { + return x + } else { + // We don't want to modify x, ever + return new(big.Int).Sub(x, tt256) + } +} + // Big to bytes // // Returns the bytes of a big integer with the size specified by **base** diff --git a/ethvm/common.go b/ethvm/common.go index 6854a5219..617a8279f 100644 --- a/ethvm/common.go +++ b/ethvm/common.go @@ -35,7 +35,8 @@ var ( LogTyPretty byte = 0x1 LogTyDiff byte = 0x2 - To256 = ethutil.To256 + U256 = ethutil.U256 + S256 = ethutil.S256 ) const MaxCallDepth = 1024 diff --git a/ethvm/vm.go b/ethvm/vm.go index dad031e01..4df98328e 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -8,6 +8,9 @@ import ( "github.com/ethereum/eth-go/ethutil" ) +// BIG FAT WARNING. THIS VM IS NOT YET IS USE! +// I want to get all VM tests pass first before updating this VM + type Vm struct { env Environment err error @@ -170,7 +173,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Add(y, x) - To256(base) + U256(base) // Pop result back on the stack stack.Push(base) @@ -180,7 +183,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Sub(y, x) - To256(base) + U256(base) // Pop result back on the stack stack.Push(base) @@ -190,7 +193,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mul(y, x) - To256(base) + U256(base) // Pop result back on the stack stack.Push(base) @@ -202,21 +205,29 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Div(y, x) } - To256(base) + U256(base) // Pop result back on the stack stack.Push(base) case SDIV: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) - if x.Cmp(ethutil.Big0) != 0 { - base.Div(y, x) + if x.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if new(big.Int).Mul(y, x).Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + base.Div(y.Abs(y), x.Mul(x.Abs(x), n)) + + U256(base) } - To256(base) - - // Pop result back on the stack stack.Push(base) case MOD: require(2) @@ -224,16 +235,27 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mod(y, x) - To256(base) + U256(base) stack.Push(base) case SMOD: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) - base.Mod(y, x) + if x.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if y.Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } - To256(base) + base.Mod(y.Abs(y), x.Mul(x.Abs(x), n)) + + U256(base) + } stack.Push(base) @@ -243,12 +265,15 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Exp(y, x, Pow256) - To256(base) + U256(base) stack.Push(base) case NEG: require(1) base.Sub(Pow256, stack.Pop()) + + base = U256(base) + stack.Push(base) case LT: require(2) @@ -272,16 +297,16 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { case SLT: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) // x < y - if y.Cmp(x) < 0 { + if y.Cmp(S256(x)) < 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case SGT: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) // x > y if y.Cmp(x) > 0 { @@ -345,7 +370,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Add(x, y) base.Mod(base, z) - To256(base) + U256(base) stack.Push(base) case MULMOD: @@ -358,7 +383,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mul(x, y) base.Mod(base, z) - To256(base) + U256(base) stack.Push(base) diff --git a/ethvm/vm_debug.go b/ethvm/vm_debug.go index df8cbb10c..d3d3d7696 100644 --- a/ethvm/vm_debug.go +++ b/ethvm/vm_debug.go @@ -40,10 +40,27 @@ func NewDebugVm(env Environment) *DebugVm { func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { self.depth++ + var ( + op OpCode + + mem = &Memory{} + stack = NewStack() + pc = big.NewInt(0) + step = 0 + prevStep = 0 + require = func(m int) { + if stack.Len() < m { + panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m)) + } + } + ) + if self.Recoverable { // Recover from any require exception defer func() { if r := recover(); r != nil { + self.Endl() + ret = closure.Return(nil) err = fmt.Errorf("%v", r) } @@ -62,21 +79,6 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { vmlogger.Debugf("(%s) %x gas: %v (d) %x\n", self.Fn, closure.Address(), closure.Gas, closure.Args) - var ( - op OpCode - - mem = &Memory{} - stack = NewStack() - pc = big.NewInt(0) - step = 0 - prevStep = 0 - require = func(m int) { - if stack.Len() < m { - panic(fmt.Sprintf("%04v (%v) stack err size = %d, required = %d", pc, op, stack.Len(), m)) - } - } - ) - for { prevStep = step // The base for all big integer arithmetic @@ -223,7 +225,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Add(y, x) - To256(base) + U256(base) self.Printf(" = %v", base) // Pop result back on the stack @@ -235,7 +237,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Sub(y, x) - To256(base) + U256(base) self.Printf(" = %v", base) // Pop result back on the stack @@ -247,60 +249,84 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mul(y, x) - To256(base) + U256(base) self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case DIV: require(2) - x, y := stack.Popn() - self.Printf(" %v / %v", y, x) + x, y := stack.Pop(), stack.Pop() + self.Printf(" %v / %v", x, y) - if x.Cmp(ethutil.Big0) != 0 { - base.Div(y, x) + if y.Cmp(ethutil.Big0) != 0 { + base.Div(x, y) } - To256(base) + U256(base) self.Printf(" = %v", base) // Pop result back on the stack stack.Push(base) case SDIV: require(2) - x, y := stack.Popn() - self.Printf(" %v / %v", y, x) + x, y := S256(stack.Pop()), S256(stack.Pop()) - if x.Cmp(ethutil.Big0) != 0 { - base.Div(y, x) + self.Printf(" %v / %v", x, y) + + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if new(big.Int).Mul(x, y).Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + base.Div(x.Abs(x), y.Abs(y)).Mul(base, n) + + U256(base) } - To256(base) - self.Printf(" = %v", base) - // Pop result back on the stack stack.Push(base) case MOD: require(2) - x, y := stack.Popn() + x, y := stack.Pop(), stack.Pop() - self.Printf(" %v %% %v", y, x) + self.Printf(" %v %% %v", x, y) - base.Mod(y, x) + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + base.Mod(x, y) + } - To256(base) + U256(base) self.Printf(" = %v", base) stack.Push(base) case SMOD: require(2) - x, y := stack.Popn() + x, y := S256(stack.Pop()), S256(stack.Pop()) - self.Printf(" %v %% %v", y, x) + self.Printf(" %v %% %v", x, y) - base.Mod(y, x) + if y.Cmp(ethutil.Big0) == 0 { + base.Set(ethutil.Big0) + } else { + n := new(big.Int) + if x.Cmp(ethutil.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } - To256(base) + base.Mod(x.Abs(x), y.Abs(y)).Mul(base, n) + + U256(base) + } self.Printf(" = %v", base) stack.Push(base) @@ -313,7 +339,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Exp(y, x, Pow256) - To256(base) + U256(base) self.Printf(" = %v", base) @@ -321,6 +347,9 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { case NEG: require(1) base.Sub(Pow256, stack.Pop()) + + base = U256(base) + stack.Push(base) case LT: require(2) @@ -346,17 +375,17 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { case SLT: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) self.Printf(" %v < %v", y, x) // x < y - if y.Cmp(x) < 0 { + if y.Cmp(S256(x)) < 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) } case SGT: require(2) - x, y := stack.Popn() + y, x := S256(stack.Pop()), S256(stack.Pop()) self.Printf(" %v > %v", y, x) // x > y @@ -426,7 +455,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Add(x, y) base.Mod(base, z) - To256(base) + U256(base) self.Printf(" = %v", base) @@ -441,7 +470,7 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) { base.Mul(x, y) base.Mod(base, z) - To256(base) + U256(base) self.Printf(" = %v", base) diff --git a/tests/helper/http.go b/tests/helper/http.go index b379695d0..3c570d106 100644 --- a/tests/helper/http.go +++ b/tests/helper/http.go @@ -4,12 +4,15 @@ import ( "encoding/json" "io/ioutil" "net/http" + "testing" ) -func CreateTests(uri string, value interface{}) error { +func CreateTests(t *testing.T, uri string, value interface{}) { resp, err := http.Get(uri) if err != nil { - panic(err) + t.Error(err) + + return } defer resp.Body.Close() @@ -17,8 +20,6 @@ func CreateTests(uri string, value interface{}) error { err = json.Unmarshal(data, &value) if err != nil { - return err + t.Error(err) } - - return nil } diff --git a/tests/helper/init.go b/tests/helper/init.go index 1f2ee208a..5f95dfc2f 100644 --- a/tests/helper/init.go +++ b/tests/helper/init.go @@ -8,8 +8,11 @@ import ( "github.com/ethereum/eth-go/ethutil" ) +var Logger ethlog.LogSystem + func init() { - ethlog.AddLogSystem(ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4))) + Logger = ethlog.NewStdLogSystem(os.Stdout, log.LstdFlags, ethlog.LogLevel(4)) + ethlog.AddLogSystem(Logger) ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "") } diff --git a/tests/helper/vm.go b/tests/helper/vm.go index 49a0bfeae..44eb8fda1 100644 --- a/tests/helper/vm.go +++ b/tests/helper/vm.go @@ -1,7 +1,6 @@ package helper import ( - "fmt" "math/big" "github.com/ethereum/eth-go/ethstate" @@ -51,16 +50,13 @@ func (self *Env) BlockHash() []byte { return nil } // This is likely to fail if anything ever gets looked up in the state trie :-) func (self *Env) State() *ethstate.State { return ethstate.New(ethtrie.New(nil, "")) } -func RunVm(state *ethstate.State, env, exec map[string]string) ([]byte, *big.Int) { +func RunVm(state *ethstate.State, env, exec map[string]string) ([]byte, *big.Int, error) { caller := state.NewStateObject(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"])) vm := ethvm.New(NewEnvFromMap(state, env, exec), ethvm.DebugVmTy) ret, _, e := closure.Call(vm, nil) - if e != nil { - fmt.Println(e) - } - return ret, closure.Gas + return ret, closure.Gas, e } diff --git a/tests/vm/gh_test.go b/tests/vm/gh_test.go index ac8d47fe6..067b1fe8e 100644 --- a/tests/vm/gh_test.go +++ b/tests/vm/gh_test.go @@ -2,7 +2,6 @@ package ethvm import ( "bytes" - "log" "testing" "github.com/ethereum/eth-go/ethstate" @@ -40,12 +39,9 @@ type VmTest struct { Pre map[string]Account } -func TestRemote(t *testing.T) { +func RunVmTest(url string, t *testing.T) { tests := make(map[string]VmTest) - err := helper.CreateTests("https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmSha3Test.json", &tests) - if err != nil { - log.Fatal(err) - } + helper.CreateTests(t, url, &tests) for name, test := range tests { state := ethstate.New(helper.NewTrie()) @@ -54,7 +50,10 @@ func TestRemote(t *testing.T) { state.SetStateObject(obj) } - ret, gas := helper.RunVm(state, test.Env, test.Exec) + ret, gas, err := helper.RunVm(state, test.Env, test.Exec) + if err != nil { + t.Errorf("%s's execution failed. %v\n", name, err) + } rexp := helper.FromHex(test.Out) if bytes.Compare(rexp, ret) != 0 { @@ -79,3 +78,17 @@ func TestRemote(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" + RunVmTest(url, t) +} + +func TestVMArithmetic(t *testing.T) { + const url = "https://raw.githubusercontent.com/ethereum/tests/master/vmtests/vmArithmeticTest.json" + RunVmTest(url, t) +}