mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 21:26:42 +00:00
core/vm: native functions
This commit is contained in:
parent
27bf7b6c65
commit
f17ae6b256
3 changed files with 344 additions and 93 deletions
|
|
@ -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()))
|
||||
|
|
|
|||
283
core/vm/native.go
Normal file
283
core/vm/native.go
Normal 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)
|
||||
}
|
||||
126
core/vm/vm.go
126
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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue