From 34c8045d5be6488e9800c24e1e696e1b912f344c Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 17 Jun 2014 18:05:46 +0200 Subject: [PATCH 1/6] Fixed issue where JUMPI would do an equally check with 1 instead of GT --- ethchain/state.go | 2 ++ ethchain/state_transition.go | 22 ++---------------- ethchain/transaction.go | 2 +- ethchain/vm.go | 14 +++++++---- ethutil/trie_test.go | 45 ++++++++++++++++++++++++++++-------- 5 files changed, 49 insertions(+), 36 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index 993f1fb08..616ab77e0 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -120,6 +120,8 @@ func (self *State) GetOrNewStateObject(addr []byte) *StateObject { } func (self *State) NewStateObject(addr []byte) *StateObject { + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(+) %x\n", addr) + stateObject := NewStateObject(addr) self.stateObjects[string(addr)] = stateObject diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 23175b0f3..dc465bbbd 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -97,7 +97,6 @@ func (self *StateTransition) BuyGas() error { if err != nil { return err } - //self.state.UpdateStateObject(coinbase) self.AddGas(self.tx.Gas) sender.SubAmount(self.tx.GasValue()) @@ -115,7 +114,7 @@ func (self *StateTransition) RefundGas() { } func (self *StateTransition) TransitionState() (err error) { - //snapshot := st.state.Snapshot() + ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(~) %x\n", self.tx.Hash()) /* defer func() { @@ -132,8 +131,6 @@ func (self *StateTransition) TransitionState() (err error) { receiver *StateObject ) - ethutil.Config.Log.Printf(ethutil.LogLevelInfo, "(~) %x\n", tx.Hash()) - // Make sure this transaction's nonce is correct if sender.Nonce != tx.Nonce { return NonceError(tx.Nonce, sender.Nonce) @@ -146,26 +143,11 @@ func (self *StateTransition) TransitionState() (err error) { // XXX Transactions after this point are considered valid. - defer func() { - self.RefundGas() - - /* - if sender != nil { - self.state.UpdateStateObject(sender) - } - - if receiver != nil { - self.state.UpdateStateObject(receiver) - } - - self.state.UpdateStateObject(self.Coinbase()) - */ - }() + defer self.RefundGas() // Increment the nonce for the next transaction sender.Nonce += 1 - // Get the receiver (TODO fix this, if coinbase is the receiver we need to save/retrieve) receiver = self.Receiver() // Transaction gas diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 3d52e4f73..9044f586e 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -65,7 +65,7 @@ func (tx *Transaction) CreatesContract() bool { return tx.contractCreation } -/* Depricated */ +/* Deprecated */ func (tx *Transaction) IsContract() bool { return tx.CreatesContract() } diff --git a/ethchain/vm.go b/ethchain/vm.go index 77a08faa6..690433180 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -120,7 +120,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro var newMemSize uint64 = 0 switch op { case STOP: + gas.Set(ethutil.Big0) case SUICIDE: + gas.Set(ethutil.Big0) case SLOAD: gas.Set(GasSLoad) case SSTORE: @@ -296,6 +298,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case EQ: require(2) x, y := stack.Popn() + fmt.Printf("%x == %x\n", x, y) // x == y if x.Cmp(y) == 0 { stack.Push(ethutil.BigTrue) @@ -365,12 +368,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro offset := stack.Pop().Int64() var data []byte - if len(closure.Args) >= int(offset+32) { - data = closure.Args[offset : offset+32] + if len(closure.Args) >= int(offset) { + l := int64(math.Min(float64(offset+32), float64(len(closure.Args)))) + data = closure.Args[offset : offset+l] } else { data = []byte{0} } + fmt.Println("CALLDATALOAD", string(data), len(data), "==", len(closure.Args)) stack.Push(ethutil.BigD(data)) case CALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) @@ -452,12 +457,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(1) loc := stack.Pop() val := closure.GetMem(loc) - //fmt.Println("get", val.BigInt(), "@", loc) stack.Push(val.BigInt()) case SSTORE: require(2) val, loc := stack.Popn() - //fmt.Println("storing", val, "@", loc) + fmt.Println("storing", string(val.Bytes()), "@", string(loc.Bytes())) closure.SetStorage(loc, ethutil.NewValue(val)) // Add the change to manifest @@ -471,7 +475,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case JUMPI: require(2) cond, pos := stack.Popn() - if cond.Cmp(ethutil.BigTrue) == 0 { + if cond.Cmp(ethutil.BigTrue) >= 0 { pc = pos //pc.Sub(pc, ethutil.Big1) continue diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index c89f2fbb7..15dbc5567 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,7 +1,12 @@ package ethutil import ( + "bytes" + "encoding/json" "fmt" + "io" + "io/ioutil" + "net/http" "reflect" "testing" ) @@ -171,14 +176,34 @@ func TestTriePurge(t *testing.T) { } } -func TestTrieIt(t *testing.T) { - _, trie := New() - trie.Update("c", LONG_WORD) - trie.Update("ca", LONG_WORD) - trie.Update("cat", LONG_WORD) - - it := trie.NewIterator() - it.Each(func(key string, node *Value) { - fmt.Println(key, ":", node.Str()) - }) +type TestItem struct { + Name string + Inputs [][]string + Expectations string +} + +func TestTrieIt(t *testing.T) { + //_, trie := New() + resp, err := http.Get("https://raw.githubusercontent.com/ethereum/tests/master/trietest.json") + if err != nil { + t.Fail() + } + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fail() + } + + dec := json.NewDecoder(bytes.NewReader(body)) + for { + var test TestItem + if err := dec.Decode(&test); err == io.EOF { + break + } else if err != nil { + t.Error("Fail something", err) + break + } + fmt.Println(test) + } } From ca79360fd7621a96382c0304e74e0d1f39b739fc Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 17 Jun 2014 18:49:26 +0200 Subject: [PATCH 2/6] Verbose logging for VM --- ethchain/state_transition.go | 1 + ethchain/vm.go | 45 ++++++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index dc465bbbd..c70dc54b4 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -236,6 +236,7 @@ func (self *StateTransition) Eval(script []byte, context *StateObject) (ret []by Diff: block.Difficulty, Value: tx.Value, }) + vm.Verbose = true ret, _, err = closure.Call(vm, tx.Data, nil) return diff --git a/ethchain/vm.go b/ethchain/vm.go index 690433180..7a4e30a33 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -45,6 +45,10 @@ type Vm struct { state *State stateManager *StateManager + + Verbose bool + + logStr string } type RuntimeVars struct { @@ -58,6 +62,23 @@ type RuntimeVars struct { Value *big.Int } +func (self *Vm) Printf(format string, v ...interface{}) *Vm { + if self.Verbose { + self.logStr += fmt.Sprintf(format, v...) + } + + return self +} + +func (self *Vm) Endl() *Vm { + if self.Verbose { + ethutil.Config.Log.Infoln(self.logStr) + self.logStr = "" + } + + return self +} + func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { return &Vm{vars: vars, state: state, stateManager: stateManager} } @@ -95,8 +116,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro step := 0 prevStep := 0 - ethutil.Config.Log.Debugf("# op\n") - for { prevStep = step // The base for all big integer arithmetic @@ -108,7 +127,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + vm.Printf("(pc) %-3d -o- %-14s", pc, op.String()) + //ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) gas := new(big.Int) addStepGasUsage := func(amount *big.Int) { @@ -193,6 +213,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + vm.Printf(" (g) %-3v (%v)", gas, closure.Gas) + mem.Resize(newMemSize) switch op { @@ -428,6 +450,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro pc.Add(pc, a.Sub(a, big.NewInt(1))) step += int(op) - int(PUSH1) + 1 + + vm.Printf(" => %#x", data.Bytes()) case POP: require(1) stack.Pop() @@ -448,11 +472,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) + + vm.Printf(" => %#x", val) case MSTORE8: require(2) val, mStart := stack.Popn() base.And(val, new(big.Int).SetInt64(0xff)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) + + vm.Printf(" => %#x", val) case SLOAD: require(1) loc := stack.Pop() @@ -466,18 +494,23 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Add the change to manifest vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) + + vm.Printf(" => %#x", val) case JUMP: require(1) pc = stack.Pop() // Reduce pc by one because of the increment that's at the end of this for loop - //pc.Sub(pc, ethutil.Big1) + vm.Printf(" ~> %v", pc).Endl() + continue case JUMPI: require(2) cond, pos := stack.Popn() if cond.Cmp(ethutil.BigTrue) >= 0 { pc = pos - //pc.Sub(pc, ethutil.Big1) + + vm.Printf(" ~> %v", pc).Endl() + continue } case PC: @@ -603,6 +636,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro pc.Add(pc, ethutil.Big1) + vm.Endl() + if hook != nil { if !hook(prevStep, op, mem, stack, closure.Object()) { return nil, nil From 8a885c2606fbf675770cf40b31f9ceb5ef8acae9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 18 Jun 2014 00:25:58 +0200 Subject: [PATCH 3/6] Fixed GT and LT --- ethchain/asm.go | 2 +- ethchain/types.go | 2 +- ethchain/vm.go | 35 ++++++++++++++++++++++++----------- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/ethchain/asm.go b/ethchain/asm.go index c267f9b55..277326ff9 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -28,7 +28,7 @@ func Disassemble(script []byte) (asm []string) { if len(data) == 0 { data = []byte{0} } - asm = append(asm, fmt.Sprintf("%#x", data)) + asm = append(asm, fmt.Sprintf("0x%x", data)) pc.Add(pc, big.NewInt(a-1)) } diff --git a/ethchain/types.go b/ethchain/types.go index d89fad147..ee70a8d28 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -226,7 +226,7 @@ var opCodeToString = map[OpCode]string{ func (o OpCode) String() string { str := opCodeToString[o] if len(str) == 0 { - return fmt.Sprintf("Missing opcode %#x", int(o)) + return fmt.Sprintf("Missing opcode 0x%x", int(o)) } return str diff --git a/ethchain/vm.go b/ethchain/vm.go index 7a4e30a33..9dbc13ead 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -97,7 +97,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } }() - ethutil.Config.Log.Debugf("[VM] Running closure %x\n", closure.object.Address()) + ethutil.Config.Log.Debugf("[VM] Running %x\n", closure.object.Address()) // Memory for the current closure mem := &Memory{} @@ -301,7 +301,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(base) case LT: require(2) - x, y := stack.Popn() + y, x := stack.Popn() // x < y if x.Cmp(y) < 0 { stack.Push(ethutil.BigTrue) @@ -310,7 +310,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } case GT: require(2) - x, y := stack.Popn() + y, x := stack.Popn() + vm.Printf(" %v > %v", x, y) + // x > y if x.Cmp(y) > 0 { stack.Push(ethutil.BigTrue) @@ -382,7 +384,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case ORIGIN: stack.Push(ethutil.BigD(vm.vars.Origin)) case CALLER: - stack.Push(ethutil.BigD(closure.caller.Address())) + caller := closure.caller.Address() + stack.Push(ethutil.BigD(caller)) + + vm.Printf(" => %x", caller) case CALLVALUE: stack.Push(vm.vars.Value) case CALLDATALOAD: @@ -397,10 +402,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro data = []byte{0} } - fmt.Println("CALLDATALOAD", string(data), len(data), "==", len(closure.Args)) + vm.Printf(" => 0x%x", data) + stack.Push(ethutil.BigD(data)) case CALLDATASIZE: - stack.Push(big.NewInt(int64(len(closure.Args)))) + l := int64(len(closure.Args)) + stack.Push(big.NewInt(l)) + + vm.Printf(" => %d", l) case CALLDATACOPY: case CODESIZE: case CODECOPY: @@ -451,7 +460,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro step += int(op) - int(PUSH1) + 1 - vm.Printf(" => %#x", data.Bytes()) + vm.Printf(" => 0x%x", data.Bytes()) case POP: require(1) stack.Pop() @@ -473,19 +482,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) - vm.Printf(" => %#x", val) + vm.Printf(" => 0x%x", val) case MSTORE8: require(2) val, mStart := stack.Popn() base.And(val, new(big.Int).SetInt64(0xff)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) - vm.Printf(" => %#x", val) + vm.Printf(" => 0x%x", val) case SLOAD: require(1) loc := stack.Pop() val := closure.GetMem(loc) stack.Push(val.BigInt()) + + vm.Printf(" {} 0x%x", val) case SSTORE: require(2) val, loc := stack.Popn() @@ -495,7 +506,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Add the change to manifest vm.state.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) - vm.Printf(" => %#x", val) + vm.Printf(" => 0x%x", val) case JUMP: require(1) pc = stack.Pop() @@ -509,9 +520,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro if cond.Cmp(ethutil.BigTrue) >= 0 { pc = pos - vm.Printf(" ~> %v", pc).Endl() + vm.Printf(" (t) ~> %v", pc).Endl() continue + } else { + vm.Printf(" (f)") } case PC: stack.Push(pc) From 2565a79575c6a6b839a6585c9ad3ebbcf7db9115 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 18 Jun 2014 00:32:48 +0200 Subject: [PATCH 4/6] Swapped vars --- ethchain/vm.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 9dbc13ead..f83258430 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -226,28 +226,28 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(2) x, y := stack.Popn() // (x + y) % 2 ** 256 - base.Add(x, y) + base.Add(y, x) // Pop result back on the stack stack.Push(base) case SUB: require(2) x, y := stack.Popn() // (x - y) % 2 ** 256 - base.Sub(x, y) + base.Sub(y, x) // Pop result back on the stack stack.Push(base) case MUL: require(2) x, y := stack.Popn() // (x * y) % 2 ** 256 - base.Mul(x, y) + base.Mul(y, x) // Pop result back on the stack stack.Push(base) case DIV: require(2) x, y := stack.Popn() // floor(x / y) - base.Div(x, y) + base.Div(y, x) // Pop result back on the stack stack.Push(base) case SDIV: @@ -270,7 +270,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case MOD: require(2) x, y := stack.Popn() - base.Mod(x, y) + base.Mod(y, x) stack.Push(base) case SMOD: require(2) @@ -292,7 +292,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case EXP: require(2) x, y := stack.Popn() - base.Exp(x, y, Pow256) + base.Exp(y, x, Pow256) stack.Push(base) case NEG: @@ -302,6 +302,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case LT: require(2) y, x := stack.Popn() + vm.Printf(" %v < %v", x, y) // x < y if x.Cmp(y) < 0 { stack.Push(ethutil.BigTrue) @@ -342,20 +343,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case AND: require(2) x, y := stack.Popn() - if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } - + stack.Push(base.And(y, x)) case OR: require(2) x, y := stack.Popn() - if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) { - stack.Push(ethutil.BigTrue) - } else { - stack.Push(ethutil.BigFalse) - } + stack.Push(base.Or(y, x)) case XOR: require(2) x, y := stack.Popn() From dc9c9369e0b44873dedfdf37195268f0e524872d Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 18 Jun 2014 00:36:23 +0200 Subject: [PATCH 5/6] log --- ethchain/vm.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index f83258430..b8ba72c7e 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -323,7 +323,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case EQ: require(2) x, y := stack.Popn() - fmt.Printf("%x == %x\n", x, y) + vm.Printf(" %v == %v", y, x) + // x == y if x.Cmp(y) == 0 { stack.Push(ethutil.BigTrue) @@ -343,15 +344,21 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case AND: require(2) x, y := stack.Popn() + vm.Printf(" %v & %v", y, x) + stack.Push(base.And(y, x)) case OR: require(2) x, y := stack.Popn() + vm.Printf(" %v | %v", y, x) + stack.Push(base.Or(y, x)) case XOR: require(2) x, y := stack.Popn() - stack.Push(base.Xor(x, y)) + vm.Printf(" %v ^ %v", y, x) + + stack.Push(base.Xor(y, x)) case BYTE: require(2) val, th := stack.Popn() From 2fbcfd88249de8f55b7f06619d9003fadcc8e1e3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 18 Jun 2014 11:55:05 +0200 Subject: [PATCH 6/6] Proper checks for multiple data items. Fixes #80 --- ethpub/pub.go | 5 ----- ethutil/bytes.go | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ethpub/pub.go b/ethpub/pub.go index 20ba79d0b..b475453af 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -170,11 +170,6 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, sc tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script) } else { - // Just in case it was submitted as a 0x prefixed string - if len(scriptStr) > 0 && scriptStr[0:2] == "0x" { - scriptStr = scriptStr[2:len(scriptStr)] - } - data := ethutil.StringToByteFunc(scriptStr, func(s string) (ret []byte) { slice := strings.Split(s, "\n") for _, dataItem := range slice { diff --git a/ethutil/bytes.go b/ethutil/bytes.go index bd0df68ec..5e3ee4a6f 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "math/big" + "strings" ) // Number to bytes @@ -91,7 +92,7 @@ func IsHex(str string) bool { } func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) { - if len(str) > 1 && str[0:2] == "0x" { + if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") { ret = FromHex(str[2:]) } else { ret = cb(str)