core/vm: native functions

This commit is contained in:
Jeffrey Wilcke 2015-06-25 12:47:37 +02:00
parent 27bf7b6c65
commit f17ae6b256
3 changed files with 344 additions and 93 deletions

View file

@ -129,6 +129,7 @@ func run(ctx *cli.Context) {
fmt.Println() fmt.Println()
if !vm.DisableSegmentation {
vmstart = time.Now() vmstart = time.Now()
ret, e = vmenv.Call( ret, e = vmenv.Call(
sender, sender,
@ -144,6 +145,7 @@ func run(ctx *cli.Context) {
os.Exit(1) os.Exit(1)
} }
fmt.Println("with segmentation", time.Since(vmstart)) fmt.Println("with segmentation", time.Since(vmstart))
}
if ctx.GlobalBool(DumpFlag.Name) { if ctx.GlobalBool(DumpFlag.Name) {
fmt.Println(string(statedb.Dump())) fmt.Println(string(statedb.Dump()))

283
core/vm/native.go Normal file
View file

@ -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)
}

View file

@ -9,46 +9,8 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "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 // Vm implements VirtualMachine
type Vm struct { type Vm struct {
env Environment env Environment
@ -164,7 +126,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
for { for {
// Get the current operation location of pc // Get the current operation location of pc
op = context.GetOp(pc) op = context.GetOp(pc)
if isDynamic(op) && csegment != nil && !DisableSegmentation { if !DisableSegmentation {
if isDynamic(op) && csegment != nil {
// end of segment bound // end of segment bound
csegment.cend = ppc csegment.cend = ppc
csegment.next = pc csegment.next = pc
@ -179,7 +142,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0))) csegment.ssize = int(math.Abs(math.Min(float64(stack.len()-csegment.ssize), 0)))
csegment = nil csegment = nil
nopay = 0 nopay = 0
} else if csegment == nil && pc != 0 && !DisableSegmentation { } else if csegment == nil && pc != 0 {
// check if a segment is available to us and set it. // check if a segment is available to us and set it.
if seg, ok := csegments[pc]; ok { if seg, ok := csegments[pc]; ok {
nopay = seg.cend nopay = seg.cend
@ -208,6 +171,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
csegments[pc] = csegment csegments[pc] = csegment
} }
} }
}
if pc > nopay || pc == 0 || DisableSegmentation { if pc > nopay || pc == 0 || DisableSegmentation {
// calculate the new memory size and gas price for the current executing opcode // calculate the new memory size and gas price for the current executing opcode
@ -247,7 +211,7 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
U256(base) U256(base)
addop(operation{Add, nil}) addop(operation{opAdd, nil})
// pop result back on the stack // pop result back on the stack
stack.push(base) stack.push(base)
@ -339,6 +303,8 @@ func (self *Vm) Run(context *Context, input []byte) (ret []byte, err error) {
U256(base) U256(base)
stack.push(base) stack.push(base)
addop(operation{opExp, nil})
case SIGNEXTEND: case SIGNEXTEND:
back := stack.pop() back := stack.pop()
if back.Cmp(big.NewInt(31)) < 0 { 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) size := uint64(op - PUSH1 + 1)
byts := getData(code, new(big.Int).SetUint64(pc+1), new(big.Int).SetUint64(size)) 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 // push value to stack
stack.push(common.Bytes2Big(byts)) stack.push(common.Bytes2Big(byts))