diff --git a/cmd/evm/main.go b/cmd/evm/main.go index 1b54cb0a69..ef7caf83c6 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -129,21 +129,23 @@ func run(ctx *cli.Context) { fmt.Println() - vmstart = time.Now() - ret, e = vmenv.Call( - sender, - receiver.Address(), - common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), - common.Big(ctx.GlobalString(GasFlag.Name)), - common.Big(ctx.GlobalString(PriceFlag.Name)), - common.Big(ctx.GlobalString(ValueFlag.Name)), - ) + if !vm.DisableSegmentation { + vmstart = time.Now() + ret, e = vmenv.Call( + sender, + receiver.Address(), + common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), + common.Big(ctx.GlobalString(GasFlag.Name)), + common.Big(ctx.GlobalString(PriceFlag.Name)), + common.Big(ctx.GlobalString(ValueFlag.Name)), + ) - if e != nil { - fmt.Println(e) - os.Exit(1) + if e != nil { + fmt.Println(e) + os.Exit(1) + } + fmt.Println("with segmentation", time.Since(vmstart)) } - fmt.Println("with segmentation", time.Since(vmstart)) if ctx.GlobalBool(DumpFlag.Name) { fmt.Println(string(statedb.Dump())) diff --git a/core/vm/native.go b/core/vm/native.go new file mode 100644 index 0000000000..96f149af78 --- /dev/null +++ b/core/vm/native.go @@ -0,0 +1,283 @@ +package vm + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/hashicorp/golang-lru" +) + +var ( + segments *lru.Cache + DisableSegmentation bool +) + +func init() { + segments, _ = lru.New(256) +} + +type codeSegments map[uint64]*segment + +// operation is a "native" implementation of an OpCode +type operation struct { + fn func(*stack, []byte) + data []byte +} + +type segment struct { + cstart, cend, next uint64 + msize uint64 + gas *big.Int + + ssize int + + ops []operation +} + +func (c *segment) String() string { + return fmt.Sprintf("{%d %d %d %v}", c.cstart, c.cend, c.msize, c.gas) +} + +// native operations + +func opPush(s *stack, data []byte) { + s.push(common.BytesToBig(data)) +} + +func opAdd(s *stack, data []byte) { + s.push(U256(new(big.Int).Add(s.pop(), s.pop()))) +} + +func opSub(s *stack, data []byte) { + s.push(U256(new(big.Int).Sub(s.pop(), s.pop()))) +} + +func opMul(s *stack, data []byte) { + s.push(U256(new(big.Int).Mul(s.pop(), s.pop()))) +} + +func opDiv(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + if y.Cmp(common.Big0) != 0 { + new(big.Int).Div(x, y) + } + + U256(new(big.Int)) + + // pop result back on the s + s.push(new(big.Int)) +} + +func opSdiv(s *stack, data []byte) { + x, y := S256(s.pop()), S256(s.pop()) + + if y.Cmp(common.Big0) == 0 { + new(big.Int).Set(common.Big0) + } else { + n := new(big.Int) + if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + new(big.Int).Div(x.Abs(x), y.Abs(y)).Mul(new(big.Int), n) + + U256(new(big.Int)) + } + + s.push(new(big.Int)) +} + +func opMod(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + if y.Cmp(common.Big0) == 0 { + new(big.Int).Set(common.Big0) + } else { + new(big.Int).Mod(x, y) + } + + U256(new(big.Int)) + + s.push(new(big.Int)) +} + +func opSmod(s *stack, data []byte) { + x, y := S256(s.pop()), S256(s.pop()) + + if y.Cmp(common.Big0) == 0 { + new(big.Int).Set(common.Big0) + } else { + n := new(big.Int) + if x.Cmp(common.Big0) < 0 { + n.SetInt64(-1) + } else { + n.SetInt64(1) + } + + new(big.Int).Mod(x.Abs(x), y.Abs(y)).Mul(new(big.Int), n) + + U256(new(big.Int)) + } + + s.push(new(big.Int)) + +} + +func opExp(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + new(big.Int).Exp(x, y, Pow256) + + U256(new(big.Int)) + + s.push(new(big.Int)) +} + +func opSignextend(s *stack, data []byte) { + back := s.pop() + if back.Cmp(big.NewInt(31)) < 0 { + bit := uint(back.Uint64()*8 + 7) + num := s.pop() + mask := new(big.Int).Lsh(common.Big1, bit) + mask.Sub(mask, common.Big1) + if common.BitTest(num, int(bit)) { + num.Or(num, mask.Not(mask)) + } else { + num.And(num, mask) + } + + num = U256(num) + + s.push(num) + } +} + +func opNot(s *stack, data []byte) { + s.push(U256(new(big.Int).Not(s.pop()))) +} + +func opLt(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + // x < y + if x.Cmp(y) < 0 { + s.push(common.BigTrue) + } else { + s.push(common.BigFalse) + } +} + +func opGt(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + // x > y + if x.Cmp(y) > 0 { + s.push(common.BigTrue) + } else { + s.push(common.BigFalse) + } +} + +func opSlt(s *stack, data []byte) { + x, y := S256(s.pop()), S256(s.pop()) + + // x < y + if x.Cmp(S256(y)) < 0 { + s.push(common.BigTrue) + } else { + s.push(common.BigFalse) + } +} + +func opSgt(s *stack, data []byte) { + x, y := S256(s.pop()), S256(s.pop()) + + // x > y + if x.Cmp(y) > 0 { + s.push(common.BigTrue) + } else { + s.push(common.BigFalse) + } +} + +func opEq(s *stack, data []byte) { + x, y := s.pop(), s.pop() + + // x == y + if x.Cmp(y) == 0 { + s.push(common.BigTrue) + } else { + s.push(common.BigFalse) + } +} + +func opIszero(s *stack, data []byte) { + x := s.pop() + if x.Cmp(common.BigFalse) > 0 { + s.push(common.BigFalse) + } else { + s.push(common.BigTrue) + } +} + +func opAnd(s *stack, data []byte) { + s.push(new(big.Int).And(s.pop(), s.pop())) +} + +func opOr(s *stack, data []byte) { + s.push(new(big.Int).Or(s.pop(), s.pop())) +} + +func opXor(s *stack, data []byte) { + s.push(new(big.Int).Xor(s.pop(), s.pop())) +} + +func opByte(s *stack, data []byte) { + th, val := s.pop(), s.pop() + + res := new(big.Int) + if th.Cmp(big.NewInt(32)) < 0 { + byt := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()])) + + res.Set(byt) + } else { + res.Set(common.BigFalse) + } + + s.push(res) +} + +func opAddmod(s *stack, data []byte) { + x := s.pop() + y := s.pop() + z := s.pop() + + res := new(big.Int) + if z.Cmp(Zero) > 0 { + add := new(big.Int).Add(x, y) + res.Mod(add, z) + res = U256(res) + } + + s.push(res) +} + +func opMulmod(s *stack, data []byte) { + x := s.pop() + y := s.pop() + z := s.pop() + + res := new(big.Int) + if z.Cmp(Zero) > 0 { + res.Mul(x, y) + res.Mod(res, z) + + res = U256(res) + } + + s.push(res) +} diff --git a/core/vm/vm.go b/core/vm/vm.go index 8ad3cd6569..9872f75437 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -9,46 +9,8 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" - "github.com/hashicorp/golang-lru" ) -type operation struct { - fn func(*stack, []byte) - data []byte -} - -func Add(s *stack, data []byte) { - s.push(new(big.Int).Add(s.pop(), s.pop())) -} - -func Push(s *stack, data []byte) { - s.push(common.BytesToBig(data)) -} - -type codeSegments map[uint64]*segment - -var segments *lru.Cache - -func init() { - segments, _ = lru.New(256) -} - -var DisableSegmentation bool - -type segment struct { - cstart, cend, next uint64 - msize uint64 - gas *big.Int - - ssize int - - ops []operation -} - -func (c *segment) String() string { - return fmt.Sprintf("{%d %d %d %v}", c.cstart, c.cend, c.msize, c.gas) -} - // Vm implements VirtualMachine type Vm struct { env Environment @@ -164,48 +126,50 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { for { // Get the current operation location of pc op = context.GetOp(pc) - if isDynamic(op) && csegment != nil && !DisableSegmentation { - // end of segment bound - csegment.cend = ppc - csegment.next = pc - // write out the required required elements on the stack. i.e. the amount of items - // required outside of the segment itself. - // 0: P, 1 - // 1: J, 2 - // 2: D -+ - // 3: P, 2 |- M,0 = M,0 pushes 1. Requires 2. 1 outside bounds of M,0 - // 4: ADD -+ - // 5: J, 2 - csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0))) - csegment = nil - nopay = 0 - } else if csegment == nil && pc != 0 && !DisableSegmentation { - // check if a segment is available to us and set it. - if seg, ok := csegments[pc]; ok { - nopay = seg.cend + if !DisableSegmentation { + if isDynamic(op) && csegment != nil { + // end of segment bound + csegment.cend = ppc + csegment.next = pc + // write out the required required elements on the stack. i.e. the amount of items + // required outside of the segment itself. + // 0: P, 1 + // 1: J, 2 + // 2: D -+ + // 3: P, 2 |- M,0 = M,0 pushes 1. Requires 2. 1 outside bounds of M,0 + // 4: ADD -+ + // 5: J, 2 + csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0))) + csegment = nil + nopay = 0 + } else if csegment == nil && pc != 0 { + // check if a segment is available to us and set it. + if seg, ok := csegments[pc]; ok { + nopay = seg.cend - // make sure there's enough stack items to complete the segment - if err := stack.require(seg.ssize); err != nil { - return context.Return(nil), err + // make sure there's enough stack items to complete the segment + if err := stack.require(seg.ssize); err != nil { + return context.Return(nil), err + } + + // use required gas for this segment + if !context.UseGas(seg.gas) { + context.UseGas(context.Gas) + + return context.Return(nil), OutOfGasError{} + } + + for _, operation := range seg.ops { + operation.fn(stack, operation.data) + } + + pc = seg.next + continue + } else { + // allocate a new segment for tracking information + csegment = &segment{pc, 0, 0, 0, new(big.Int), stack.len(), nil} + csegments[pc] = csegment } - - // use required gas for this segment - if !context.UseGas(seg.gas) { - context.UseGas(context.Gas) - - return context.Return(nil), OutOfGasError{} - } - - for _, operation := range seg.ops { - operation.fn(stack, operation.data) - } - - pc = seg.next - continue - } else { - // allocate a new segment for tracking information - csegment = &segment{pc, 0, 0, 0, new(big.Int), stack.len(), nil} - csegments[pc] = csegment } } @@ -247,7 +211,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { U256(base) - addop(operation{Add, nil}) + addop(operation{opAdd, nil}) // pop result back on the stack stack.push(base) @@ -339,6 +303,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { U256(base) stack.push(base) + + addop(operation{opExp, nil}) case SIGNEXTEND: back := stack.pop() if back.Cmp(big.NewInt(31)) < 0 { @@ -582,7 +548,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) { size := uint64(op - PUSH1 + 1) byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) - addop(operation{Push, byts}) + addop(operation{opPush, byts}) // push value to stack stack.push(common.Bytes2Big(byts))