forked from TrueCloudLab/neoneo-go
Merge pull request #642 from nspcc-dev/feature/emit
vm, compiler: move Emit* functions to a separate package Closes #449, #534.
This commit is contained in:
commit
9b9adb28c1
16 changed files with 521 additions and 564 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,17 +74,17 @@ func (c *codegen) convertBasicType(t types.TypeAndValue, typ *types.Basic) {
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case types.Int, types.UntypedInt, types.Uint:
|
case types.Int, types.UntypedInt, types.Uint:
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
emitInt(c.prog.BinWriter, val)
|
emit.Int(c.prog.BinWriter, val)
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
val := constant.StringVal(t.Value)
|
val := constant.StringVal(t.Value)
|
||||||
emitString(c.prog.BinWriter, val)
|
emit.String(c.prog.BinWriter, val)
|
||||||
case types.Bool, types.UntypedBool:
|
case types.Bool, types.UntypedBool:
|
||||||
val := constant.BoolVal(t.Value)
|
val := constant.BoolVal(t.Value)
|
||||||
emitBool(c.prog.BinWriter, val)
|
emit.Bool(c.prog.BinWriter, val)
|
||||||
case types.Byte:
|
case types.Byte:
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
b := byte(val)
|
b := byte(val)
|
||||||
emitBytes(c.prog.BinWriter, []byte{b})
|
emit.Bytes(c.prog.BinWriter, []byte{b})
|
||||||
default:
|
default:
|
||||||
c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t)
|
c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t)
|
||||||
return
|
return
|
||||||
|
@ -100,33 +101,33 @@ func (c *codegen) emitLoadLocal(name string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitLoadLocalPos(pos int) {
|
func (c *codegen) emitLoadLocalPos(pos int) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DUPFROMALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.DUPFROMALTSTACK)
|
||||||
emitInt(c.prog.BinWriter, int64(pos))
|
emit.Int(c.prog.BinWriter, int64(pos))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.PICKITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitStoreLocal(pos int) {
|
func (c *codegen) emitStoreLocal(pos int) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DUPFROMALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.DUPFROMALTSTACK)
|
||||||
|
|
||||||
if pos < 0 {
|
if pos < 0 {
|
||||||
c.prog.Err = fmt.Errorf("invalid position to store local: %d", pos)
|
c.prog.Err = fmt.Errorf("invalid position to store local: %d", pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emitInt(c.prog.BinWriter, int64(pos))
|
emit.Int(c.prog.BinWriter, int64(pos))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ROT)
|
emit.Opcode(c.prog.BinWriter, opcode.ROT)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SETITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitLoadField(i int) {
|
func (c *codegen) emitLoadField(i int) {
|
||||||
emitInt(c.prog.BinWriter, int64(i))
|
emit.Int(c.prog.BinWriter, int64(i))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.PICKITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitStoreStructField(i int) {
|
func (c *codegen) emitStoreStructField(i int) {
|
||||||
emitInt(c.prog.BinWriter, int64(i))
|
emit.Int(c.prog.BinWriter, int64(i))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ROT)
|
emit.Opcode(c.prog.BinWriter, opcode.ROT)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SETITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertGlobals traverses the AST and only converts global declarations.
|
// convertGlobals traverses the AST and only converts global declarations.
|
||||||
|
@ -170,9 +171,9 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
|
||||||
|
|
||||||
// All globals copied into the scope of the function need to be added
|
// All globals copied into the scope of the function need to be added
|
||||||
// to the stack size of the function.
|
// to the stack size of the function.
|
||||||
emitInt(c.prog.BinWriter, f.stackSize()+countGlobals(file))
|
emit.Int(c.prog.BinWriter, f.stackSize()+countGlobals(file))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NEWARRAY)
|
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.TOALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.TOALTSTACK)
|
||||||
|
|
||||||
// We need to handle methods, which in Go, is just syntactic sugar.
|
// We need to handle methods, which in Go, is just syntactic sugar.
|
||||||
// The method receiver will be passed in as first argument.
|
// The method receiver will be passed in as first argument.
|
||||||
|
@ -210,9 +211,9 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
|
||||||
|
|
||||||
// If this function returns the void (no return stmt) we will cleanup its junk on the stack.
|
// If this function returns the void (no return stmt) we will cleanup its junk on the stack.
|
||||||
if !hasReturnStmt(decl) {
|
if !hasReturnStmt(decl) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DROP)
|
emit.Opcode(c.prog.BinWriter, opcode.DROP)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.RET)
|
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +259,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.Name == "_" {
|
if t.Name == "_" {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DROP)
|
emit.Opcode(c.prog.BinWriter, opcode.DROP)
|
||||||
} else {
|
} else {
|
||||||
l := c.scope.loadLocal(t.Name)
|
l := c.scope.loadLocal(t.Name)
|
||||||
c.emitStoreLocal(l)
|
c.emitStoreLocal(l)
|
||||||
|
@ -297,8 +298,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
c.emitStoreStructField(index)
|
c.emitStoreStructField(index)
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
c.emitLoadLocal(ind.Name)
|
c.emitLoadLocal(ind.Name)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ROT)
|
emit.Opcode(c.prog.BinWriter, opcode.ROT)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SETITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||||
default:
|
default:
|
||||||
c.prog.Err = fmt.Errorf("unsupported index expression")
|
c.prog.Err = fmt.Errorf("unsupported index expression")
|
||||||
return nil
|
return nil
|
||||||
|
@ -316,9 +317,9 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ast.Walk(c, n.Results[i])
|
ast.Walk(c, n.Results[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DROP) // Cleanup the stack.
|
emit.Opcode(c.prog.BinWriter, opcode.DROP) // Cleanup the stack.
|
||||||
emitOpcode(c.prog.BinWriter, opcode.RET)
|
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case *ast.IfStmt:
|
case *ast.IfStmt:
|
||||||
|
@ -328,13 +329,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
if n.Cond != nil {
|
if n.Cond != nil {
|
||||||
ast.Walk(c, n.Cond)
|
ast.Walk(c, n.Cond)
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lElse))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lElse))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(lIf)
|
c.setLabel(lIf)
|
||||||
ast.Walk(c, n.Body)
|
ast.Walk(c, n.Body)
|
||||||
if n.Else != nil {
|
if n.Else != nil {
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMP, int16(lElseEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(lElseEnd))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(lElse)
|
c.setLabel(lElse)
|
||||||
|
@ -358,13 +359,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
if l := len(cc.List); l != 0 { // if not `default`
|
if l := len(cc.List); l != 0 { // if not `default`
|
||||||
for j := range cc.List {
|
for j := range cc.List {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DUP)
|
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||||
ast.Walk(c, cc.List[j])
|
ast.Walk(c, cc.List[j])
|
||||||
emitOpcode(c.prog.BinWriter, eqOpcode)
|
emit.Opcode(c.prog.BinWriter, eqOpcode)
|
||||||
if j == l-1 {
|
if j == l-1 {
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(lEnd))
|
||||||
} else {
|
} else {
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIF, int16(lStart))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(lStart))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,12 +374,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
for _, stmt := range cc.Body {
|
for _, stmt := range cc.Body {
|
||||||
ast.Walk(c, stmt)
|
ast.Walk(c, stmt)
|
||||||
}
|
}
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMP, int16(switchEnd))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(switchEnd))
|
||||||
c.setLabel(lEnd)
|
c.setLabel(lEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(switchEnd)
|
c.setLabel(switchEnd)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DROP)
|
emit.Opcode(c.prog.BinWriter, opcode.DROP)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -421,8 +422,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
for i := ln - 1; i >= 0; i-- {
|
for i := ln - 1; i >= 0; i-- {
|
||||||
ast.Walk(c, n.Elts[i])
|
ast.Walk(c, n.Elts[i])
|
||||||
}
|
}
|
||||||
emitInt(c.prog.BinWriter, int64(ln))
|
emit.Int(c.prog.BinWriter, int64(ln))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.PACK)
|
emit.Opcode(c.prog.BinWriter, opcode.PACK)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,13 +440,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
case token.LAND:
|
case token.LAND:
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(len(c.l)-1))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(len(c.l)-1))
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case token.LOR:
|
case token.LOR:
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIF, int16(len(c.l)-3))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIF, int16(len(c.l)-3))
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -470,21 +471,21 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
case n.Op == token.ADD:
|
case n.Op == token.ADD:
|
||||||
// VM has separate opcodes for number and string concatenation
|
// VM has separate opcodes for number and string concatenation
|
||||||
if isStringType(tinfo.Type) {
|
if isStringType(tinfo.Type) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.CAT)
|
emit.Opcode(c.prog.BinWriter, opcode.CAT)
|
||||||
} else {
|
} else {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ADD)
|
emit.Opcode(c.prog.BinWriter, opcode.ADD)
|
||||||
}
|
}
|
||||||
case n.Op == token.EQL:
|
case n.Op == token.EQL:
|
||||||
// VM has separate opcodes for number and string equality
|
// VM has separate opcodes for number and string equality
|
||||||
op := c.getEqualityOpcode(n.X)
|
op := c.getEqualityOpcode(n.X)
|
||||||
emitOpcode(c.prog.BinWriter, op)
|
emit.Opcode(c.prog.BinWriter, op)
|
||||||
case n.Op == token.NEQ:
|
case n.Op == token.NEQ:
|
||||||
// VM has separate opcodes for number and string equality
|
// VM has separate opcodes for number and string equality
|
||||||
if isStringType(c.typeInfo.Types[n.X].Type) {
|
if isStringType(c.typeInfo.Types[n.X].Type) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.EQUAL)
|
emit.Opcode(c.prog.BinWriter, opcode.EQUAL)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NOT)
|
emit.Opcode(c.prog.BinWriter, opcode.NOT)
|
||||||
} else {
|
} else {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NUMNOTEQUAL)
|
emit.Opcode(c.prog.BinWriter, opcode.NUMNOTEQUAL)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
c.convertToken(n.Op)
|
c.convertToken(n.Op)
|
||||||
|
@ -540,14 +541,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// Do not swap for builtin functions.
|
// Do not swap for builtin functions.
|
||||||
if !isBuiltin {
|
if !isBuiltin {
|
||||||
if numArgs == 2 {
|
if numArgs == 2 {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SWAP)
|
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
|
||||||
} else if numArgs == 3 {
|
} else if numArgs == 3 {
|
||||||
emitInt(c.prog.BinWriter, 2)
|
emit.Int(c.prog.BinWriter, 2)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.XSWAP)
|
emit.Opcode(c.prog.BinWriter, opcode.XSWAP)
|
||||||
} else {
|
} else {
|
||||||
for i := 1; i < numArgs; i++ {
|
for i := 1; i < numArgs; i++ {
|
||||||
emitInt(c.prog.BinWriter, int64(i))
|
emit.Int(c.prog.BinWriter, int64(i))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ROLL)
|
emit.Opcode(c.prog.BinWriter, opcode.ROLL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -561,7 +562,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
case isSyscall(f):
|
case isSyscall(f):
|
||||||
c.convertSyscall(f.selector.Name, f.name)
|
c.convertSyscall(f.selector.Name, f.name)
|
||||||
default:
|
default:
|
||||||
emitCall(c.prog.BinWriter, opcode.CALL, int16(f.label))
|
emit.Call(c.prog.BinWriter, opcode.CALL, int16(f.label))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -591,11 +592,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
case token.ADD:
|
case token.ADD:
|
||||||
// +10 == 10, no need to do anything in this case
|
// +10 == 10, no need to do anything in this case
|
||||||
case token.SUB:
|
case token.SUB:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NEGATE)
|
emit.Opcode(c.prog.BinWriter, opcode.NEGATE)
|
||||||
case token.NOT:
|
case token.NOT:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NOT)
|
emit.Opcode(c.prog.BinWriter, opcode.NOT)
|
||||||
case token.XOR:
|
case token.XOR:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.INVERT)
|
emit.Opcode(c.prog.BinWriter, opcode.INVERT)
|
||||||
default:
|
default:
|
||||||
c.prog.Err = fmt.Errorf("invalid unary operator: %s", n.Op)
|
c.prog.Err = fmt.Errorf("invalid unary operator: %s", n.Op)
|
||||||
return nil
|
return nil
|
||||||
|
@ -634,7 +635,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ast.Walk(c, n.Index)
|
ast.Walk(c, n.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here
|
emit.Opcode(c.prog.BinWriter, opcode.PICKITEM) // just pickitem here
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -654,7 +655,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
ast.Walk(c, n.Cond)
|
ast.Walk(c, n.Cond)
|
||||||
|
|
||||||
// Jump if the condition is false
|
// Jump if the condition is false
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(fend))
|
emit.Jmp(c.prog.BinWriter, opcode.JMPIFNOT, int16(fend))
|
||||||
|
|
||||||
// Walk body followed by the iterator (post stmt).
|
// Walk body followed by the iterator (post stmt).
|
||||||
ast.Walk(c, n.Body)
|
ast.Walk(c, n.Body)
|
||||||
|
@ -663,7 +664,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump back to condition.
|
// Jump back to condition.
|
||||||
emitJmp(c.prog.BinWriter, opcode.JMP, int16(fstart))
|
emit.Jmp(c.prog.BinWriter, opcode.JMP, int16(fstart))
|
||||||
c.setLabel(fend)
|
c.setLabel(fend)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -721,11 +722,11 @@ func (c *codegen) convertSyscall(api, name string) {
|
||||||
c.prog.Err = fmt.Errorf("unknown VM syscall api: %s", name)
|
c.prog.Err = fmt.Errorf("unknown VM syscall api: %s", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
emitSyscall(c.prog.BinWriter, api)
|
emit.Syscall(c.prog.BinWriter, api)
|
||||||
|
|
||||||
// This NOP instruction is basically not needed, but if we do, we have a
|
// This NOP instruction is basically not needed, but if we do, we have a
|
||||||
// one to one matching avm file with neo-python which is very nice for debugging.
|
// one to one matching avm file with neo-python which is very nice for debugging.
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NOP)
|
emit.Opcode(c.prog.BinWriter, opcode.NOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
|
@ -742,44 +743,44 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
arg := expr.Args[0]
|
arg := expr.Args[0]
|
||||||
typ := c.typeInfo.Types[arg].Type
|
typ := c.typeInfo.Types[arg].Type
|
||||||
if isStringType(typ) {
|
if isStringType(typ) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SIZE)
|
emit.Opcode(c.prog.BinWriter, opcode.SIZE)
|
||||||
} else {
|
} else {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ARRAYSIZE)
|
emit.Opcode(c.prog.BinWriter, opcode.ARRAYSIZE)
|
||||||
}
|
}
|
||||||
case "append":
|
case "append":
|
||||||
arg := expr.Args[0]
|
arg := expr.Args[0]
|
||||||
typ := c.typeInfo.Types[arg].Type
|
typ := c.typeInfo.Types[arg].Type
|
||||||
if isByteArrayType(typ) {
|
if isByteArrayType(typ) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.CAT)
|
emit.Opcode(c.prog.BinWriter, opcode.CAT)
|
||||||
} else {
|
} else {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.OVER)
|
emit.Opcode(c.prog.BinWriter, opcode.OVER)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SWAP)
|
emit.Opcode(c.prog.BinWriter, opcode.SWAP)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.APPEND)
|
emit.Opcode(c.prog.BinWriter, opcode.APPEND)
|
||||||
}
|
}
|
||||||
case "panic":
|
case "panic":
|
||||||
arg := expr.Args[0]
|
arg := expr.Args[0]
|
||||||
if isExprNil(arg) {
|
if isExprNil(arg) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DROP)
|
emit.Opcode(c.prog.BinWriter, opcode.DROP)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.THROW)
|
emit.Opcode(c.prog.BinWriter, opcode.THROW)
|
||||||
} else if isStringType(c.typeInfo.Types[arg].Type) {
|
} else if isStringType(c.typeInfo.Types[arg].Type) {
|
||||||
ast.Walk(c, arg)
|
ast.Walk(c, arg)
|
||||||
emitSyscall(c.prog.BinWriter, "Neo.Runtime.Log")
|
emit.Syscall(c.prog.BinWriter, "Neo.Runtime.Log")
|
||||||
emitOpcode(c.prog.BinWriter, opcode.THROW)
|
emit.Opcode(c.prog.BinWriter, opcode.THROW)
|
||||||
} else {
|
} else {
|
||||||
c.prog.Err = errors.New("panic should have string or nil argument")
|
c.prog.Err = errors.New("panic should have string or nil argument")
|
||||||
}
|
}
|
||||||
case "SHA256":
|
case "SHA256":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SHA256)
|
emit.Opcode(c.prog.BinWriter, opcode.SHA256)
|
||||||
case "SHA1":
|
case "SHA1":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SHA1)
|
emit.Opcode(c.prog.BinWriter, opcode.SHA1)
|
||||||
case "Hash256":
|
case "Hash256":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.HASH256)
|
emit.Opcode(c.prog.BinWriter, opcode.HASH256)
|
||||||
case "Hash160":
|
case "Hash160":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.HASH160)
|
emit.Opcode(c.prog.BinWriter, opcode.HASH160)
|
||||||
case "VerifySignature":
|
case "VerifySignature":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.VERIFY)
|
emit.Opcode(c.prog.BinWriter, opcode.VERIFY)
|
||||||
case "AppCall":
|
case "AppCall":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.APPCALL)
|
emit.Opcode(c.prog.BinWriter, opcode.APPCALL)
|
||||||
buf := c.getByteArray(expr.Args[0])
|
buf := c.getByteArray(expr.Args[0])
|
||||||
if len(buf) != 20 {
|
if len(buf) != 20 {
|
||||||
c.prog.Err = errors.New("invalid script hash")
|
c.prog.Err = errors.New("invalid script hash")
|
||||||
|
@ -787,7 +788,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
|
|
||||||
c.prog.WriteBytes(buf)
|
c.prog.WriteBytes(buf)
|
||||||
case "Equals":
|
case "Equals":
|
||||||
emitOpcode(c.prog.BinWriter, opcode.EQUAL)
|
emit.Opcode(c.prog.BinWriter, opcode.EQUAL)
|
||||||
case "FromAddress":
|
case "FromAddress":
|
||||||
// We can be sure that this is a ast.BasicLit just containing a simple
|
// We can be sure that this is a ast.BasicLit just containing a simple
|
||||||
// address string. Note that the string returned from calling Value will
|
// address string. Note that the string returned from calling Value will
|
||||||
|
@ -800,7 +801,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes := uint160.BytesBE()
|
bytes := uint160.BytesBE()
|
||||||
emitBytes(c.prog.BinWriter, bytes)
|
emit.Bytes(c.prog.BinWriter, bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,17 +836,17 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
buf[i] = byte(val)
|
buf[i] = byte(val)
|
||||||
}
|
}
|
||||||
emitBytes(c.prog.BinWriter, buf)
|
emit.Bytes(c.prog.BinWriter, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NEWMAP)
|
emit.Opcode(c.prog.BinWriter, opcode.NEWMAP)
|
||||||
for i := range lit.Elts {
|
for i := range lit.Elts {
|
||||||
elem := lit.Elts[i].(*ast.KeyValueExpr)
|
elem := lit.Elts[i].(*ast.KeyValueExpr)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DUP)
|
emit.Opcode(c.prog.BinWriter, opcode.DUP)
|
||||||
ast.Walk(c, elem.Key)
|
ast.Walk(c, elem.Key)
|
||||||
ast.Walk(c, elem.Value)
|
ast.Walk(c, elem.Value)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SETITEM)
|
emit.Opcode(c.prog.BinWriter, opcode.SETITEM)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,10 +859,10 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NOP)
|
emit.Opcode(c.prog.BinWriter, opcode.NOP)
|
||||||
emitInt(c.prog.BinWriter, int64(strct.NumFields()))
|
emit.Int(c.prog.BinWriter, int64(strct.NumFields()))
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NEWSTRUCT)
|
emit.Opcode(c.prog.BinWriter, opcode.NEWSTRUCT)
|
||||||
emitOpcode(c.prog.BinWriter, opcode.TOALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.TOALTSTACK)
|
||||||
|
|
||||||
// We need to locally store all the fields, even if they are not initialized.
|
// We need to locally store all the fields, even if they are not initialized.
|
||||||
// We will initialize all fields to their "zero" value.
|
// We will initialize all fields to their "zero" value.
|
||||||
|
@ -894,59 +895,59 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
c.emitLoadConst(typeAndVal)
|
c.emitLoadConst(typeAndVal)
|
||||||
c.emitStoreLocal(i)
|
c.emitStoreLocal(i)
|
||||||
}
|
}
|
||||||
emitOpcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertToken(tok token.Token) {
|
func (c *codegen) convertToken(tok token.Token) {
|
||||||
switch tok {
|
switch tok {
|
||||||
case token.ADD_ASSIGN:
|
case token.ADD_ASSIGN:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ADD)
|
emit.Opcode(c.prog.BinWriter, opcode.ADD)
|
||||||
case token.SUB_ASSIGN:
|
case token.SUB_ASSIGN:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SUB)
|
emit.Opcode(c.prog.BinWriter, opcode.SUB)
|
||||||
case token.MUL_ASSIGN:
|
case token.MUL_ASSIGN:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.MUL)
|
emit.Opcode(c.prog.BinWriter, opcode.MUL)
|
||||||
case token.QUO_ASSIGN:
|
case token.QUO_ASSIGN:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DIV)
|
emit.Opcode(c.prog.BinWriter, opcode.DIV)
|
||||||
case token.REM_ASSIGN:
|
case token.REM_ASSIGN:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.MOD)
|
emit.Opcode(c.prog.BinWriter, opcode.MOD)
|
||||||
case token.ADD:
|
case token.ADD:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.ADD)
|
emit.Opcode(c.prog.BinWriter, opcode.ADD)
|
||||||
case token.SUB:
|
case token.SUB:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SUB)
|
emit.Opcode(c.prog.BinWriter, opcode.SUB)
|
||||||
case token.MUL:
|
case token.MUL:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.MUL)
|
emit.Opcode(c.prog.BinWriter, opcode.MUL)
|
||||||
case token.QUO:
|
case token.QUO:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DIV)
|
emit.Opcode(c.prog.BinWriter, opcode.DIV)
|
||||||
case token.REM:
|
case token.REM:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.MOD)
|
emit.Opcode(c.prog.BinWriter, opcode.MOD)
|
||||||
case token.LSS:
|
case token.LSS:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.LT)
|
emit.Opcode(c.prog.BinWriter, opcode.LT)
|
||||||
case token.LEQ:
|
case token.LEQ:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.LTE)
|
emit.Opcode(c.prog.BinWriter, opcode.LTE)
|
||||||
case token.GTR:
|
case token.GTR:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.GT)
|
emit.Opcode(c.prog.BinWriter, opcode.GT)
|
||||||
case token.GEQ:
|
case token.GEQ:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.GTE)
|
emit.Opcode(c.prog.BinWriter, opcode.GTE)
|
||||||
case token.EQL:
|
case token.EQL:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NUMEQUAL)
|
emit.Opcode(c.prog.BinWriter, opcode.NUMEQUAL)
|
||||||
case token.NEQ:
|
case token.NEQ:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NUMNOTEQUAL)
|
emit.Opcode(c.prog.BinWriter, opcode.NUMNOTEQUAL)
|
||||||
case token.DEC:
|
case token.DEC:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.DEC)
|
emit.Opcode(c.prog.BinWriter, opcode.DEC)
|
||||||
case token.INC:
|
case token.INC:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.INC)
|
emit.Opcode(c.prog.BinWriter, opcode.INC)
|
||||||
case token.NOT:
|
case token.NOT:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.NOT)
|
emit.Opcode(c.prog.BinWriter, opcode.NOT)
|
||||||
case token.AND:
|
case token.AND:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.AND)
|
emit.Opcode(c.prog.BinWriter, opcode.AND)
|
||||||
case token.OR:
|
case token.OR:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.OR)
|
emit.Opcode(c.prog.BinWriter, opcode.OR)
|
||||||
case token.SHL:
|
case token.SHL:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SHL)
|
emit.Opcode(c.prog.BinWriter, opcode.SHL)
|
||||||
case token.SHR:
|
case token.SHR:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.SHR)
|
emit.Opcode(c.prog.BinWriter, opcode.SHR)
|
||||||
case token.XOR:
|
case token.XOR:
|
||||||
emitOpcode(c.prog.BinWriter, opcode.XOR)
|
emit.Opcode(c.prog.BinWriter, opcode.XOR)
|
||||||
default:
|
default:
|
||||||
c.prog.Err = fmt.Errorf("compiler could not convert token: %s", tok)
|
c.prog.Err = fmt.Errorf("compiler could not convert token: %s", tok)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// emit a VM Instruction with data to the given buffer.
|
|
||||||
func emit(w *io.BinWriter, instr opcode.Opcode, b []byte) {
|
|
||||||
emitOpcode(w, instr)
|
|
||||||
w.WriteBytes(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitOpcode emits a single VM Instruction the given buffer.
|
|
||||||
func emitOpcode(w *io.BinWriter, instr opcode.Opcode) {
|
|
||||||
w.WriteBytes([]byte{byte(instr)})
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitBool emits a bool type the given buffer.
|
|
||||||
func emitBool(w *io.BinWriter, ok bool) {
|
|
||||||
if ok {
|
|
||||||
emitOpcode(w, opcode.PUSHT)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
emitOpcode(w, opcode.PUSHF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitInt emits a int type to the given buffer.
|
|
||||||
func emitInt(w *io.BinWriter, i int64) {
|
|
||||||
switch {
|
|
||||||
case i == -1:
|
|
||||||
emitOpcode(w, opcode.PUSHM1)
|
|
||||||
return
|
|
||||||
case i == 0:
|
|
||||||
emitOpcode(w, opcode.PUSHF)
|
|
||||||
return
|
|
||||||
case i > 0 && i < 16:
|
|
||||||
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
|
||||||
emitOpcode(w, val)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bInt := big.NewInt(i)
|
|
||||||
val := vm.IntToBytes(bInt)
|
|
||||||
emitBytes(w, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitString emits a string to the given buffer.
|
|
||||||
func emitString(w *io.BinWriter, s string) {
|
|
||||||
emitBytes(w, []byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitBytes emits a byte array to the given buffer.
|
|
||||||
func emitBytes(w *io.BinWriter, b []byte) {
|
|
||||||
n := len(b)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case n <= int(opcode.PUSHBYTES75):
|
|
||||||
emit(w, opcode.Opcode(n), b)
|
|
||||||
return
|
|
||||||
case n < 0x100:
|
|
||||||
emit(w, opcode.PUSHDATA1, []byte{byte(n)})
|
|
||||||
case n < 0x10000:
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
|
||||||
emit(w, opcode.PUSHDATA2, buf)
|
|
||||||
default:
|
|
||||||
buf := make([]byte, 4)
|
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
|
||||||
emit(w, opcode.PUSHDATA4, buf)
|
|
||||||
if w.Err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteBytes(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitSyscall emits the syscall API to the given buffer.
|
|
||||||
// Syscall API string cannot be 0.
|
|
||||||
func emitSyscall(w *io.BinWriter, api string) {
|
|
||||||
if len(api) == 0 {
|
|
||||||
w.Err = errors.New("syscall api cannot be of length 0")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := make([]byte, len(api)+1)
|
|
||||||
buf[0] = byte(len(api))
|
|
||||||
copy(buf[1:], api)
|
|
||||||
emit(w, opcode.SYSCALL, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitCall emits a call Instruction with label to the given buffer.
|
|
||||||
func emitCall(w *io.BinWriter, instr opcode.Opcode, label int16) {
|
|
||||||
emitJmp(w, instr, label)
|
|
||||||
}
|
|
||||||
|
|
||||||
// emitJmp emits a jump Instruction along with label to the given buffer.
|
|
||||||
func emitJmp(w *io.BinWriter, instr opcode.Opcode, label int16) {
|
|
||||||
if !isInstrJmp(instr) {
|
|
||||||
w.Err = fmt.Errorf("opcode %s is not a jump or call type", instr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
|
||||||
emit(w, instr, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInstrJmp(instr opcode.Opcode) bool {
|
|
||||||
if instr == opcode.JMP || instr == opcode.JMPIFNOT || instr == opcode.JMPIF || instr == opcode.CALL {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
@ -23,6 +22,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/smartcontract/trigger"
|
"github.com/CityOfZion/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -1485,11 +1485,8 @@ func ScriptFromWitness(hash util.Uint160, witness *transaction.Witness) ([]byte,
|
||||||
verification := witness.VerificationScript
|
verification := witness.VerificationScript
|
||||||
|
|
||||||
if len(verification) == 0 {
|
if len(verification) == 0 {
|
||||||
bb := new(bytes.Buffer)
|
bb := io.NewBufBinWriter()
|
||||||
err := vm.EmitAppCall(bb, hash, false)
|
emit.AppCall(bb.BinWriter, hash, false)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
verification = bb.Bytes()
|
verification = bb.Bytes()
|
||||||
} else if h := witness.ScriptHash(); hash != h {
|
} else if h := witness.ScriptHash(); hash != h {
|
||||||
return nil, errors.New("witness hash mismatch")
|
return nil, errors.New("witness hash mismatch")
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -12,7 +11,7 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
errs "github.com/pkg/errors"
|
errs "github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -113,22 +112,12 @@ func GetInvocationScript(tx *transaction.Transaction, wif *keys.WIF) ([]byte, er
|
||||||
func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, error) {
|
func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, error) {
|
||||||
var props smartcontract.PropertyState
|
var props smartcontract.PropertyState
|
||||||
|
|
||||||
script := new(bytes.Buffer)
|
script := io.NewBufBinWriter()
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Description)); err != nil {
|
emit.Bytes(script.BinWriter, []byte(contract.Description))
|
||||||
return nil, err
|
emit.Bytes(script.BinWriter, []byte(contract.Email))
|
||||||
}
|
emit.Bytes(script.BinWriter, []byte(contract.Author))
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Email)); err != nil {
|
emit.Bytes(script.BinWriter, []byte(contract.Version))
|
||||||
return nil, err
|
emit.Bytes(script.BinWriter, []byte(contract.ProjectName))
|
||||||
}
|
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Author)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := vm.EmitBytes(script, []byte(contract.Version)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := vm.EmitBytes(script, []byte(contract.ProjectName)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if contract.HasStorage {
|
if contract.HasStorage {
|
||||||
props |= smartcontract.HasStorage
|
props |= smartcontract.HasStorage
|
||||||
}
|
}
|
||||||
|
@ -138,31 +127,21 @@ func CreateDeploymentScript(avm []byte, contract *ContractDetails) ([]byte, erro
|
||||||
if contract.IsPayable {
|
if contract.IsPayable {
|
||||||
props |= smartcontract.IsPayable
|
props |= smartcontract.IsPayable
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(script, int64(props)); err != nil {
|
emit.Int(script.BinWriter, int64(props))
|
||||||
return nil, err
|
emit.Int(script.BinWriter, int64(contract.ReturnType))
|
||||||
}
|
|
||||||
if err := vm.EmitInt(script, int64(contract.ReturnType)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
params := make([]byte, len(contract.Parameters))
|
params := make([]byte, len(contract.Parameters))
|
||||||
for k := range contract.Parameters {
|
for k := range contract.Parameters {
|
||||||
params[k] = byte(contract.Parameters[k])
|
params[k] = byte(contract.Parameters[k])
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, params); err != nil {
|
emit.Bytes(script.BinWriter, params)
|
||||||
return nil, err
|
emit.Bytes(script.BinWriter, avm)
|
||||||
}
|
emit.Syscall(script.BinWriter, "Neo.Contract.Create")
|
||||||
if err := vm.EmitBytes(script, avm); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := vm.EmitSyscall(script, "Neo.Contract.Create"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// expandArrayIntoScript pushes all FuncParam parameters from the given array
|
// expandArrayIntoScript pushes all FuncParam parameters from the given array
|
||||||
// into the given buffer in reverse order.
|
// into the given buffer in reverse order.
|
||||||
func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
||||||
for j := len(slice) - 1; j >= 0; j-- {
|
for j := len(slice) - 1; j >= 0; j-- {
|
||||||
fp, err := slice[j].GetFuncParam()
|
fp, err := slice[j].GetFuncParam()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,33 +153,25 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, str); err != nil {
|
emit.Bytes(script, str)
|
||||||
return err
|
|
||||||
}
|
|
||||||
case String:
|
case String:
|
||||||
str, err := fp.Value.GetString()
|
str, err := fp.Value.GetString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitString(script, str); err != nil {
|
emit.String(script, str)
|
||||||
return err
|
|
||||||
}
|
|
||||||
case Hash160:
|
case Hash160:
|
||||||
hash, err := fp.Value.GetUint160FromHex()
|
hash, err := fp.Value.GetUint160FromHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, hash.BytesBE()); err != nil {
|
emit.Bytes(script, hash.BytesBE())
|
||||||
return err
|
|
||||||
}
|
|
||||||
case Hash256:
|
case Hash256:
|
||||||
hash, err := fp.Value.GetUint256()
|
hash, err := fp.Value.GetUint256()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, hash.BytesBE()); err != nil {
|
emit.Bytes(script, hash.BytesBE())
|
||||||
return err
|
|
||||||
}
|
|
||||||
case PublicKey:
|
case PublicKey:
|
||||||
str, err := fp.Value.GetString()
|
str, err := fp.Value.GetString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -210,17 +181,13 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitBytes(script, key.Bytes()); err != nil {
|
emit.Bytes(script, key.Bytes())
|
||||||
return err
|
|
||||||
}
|
|
||||||
case Integer:
|
case Integer:
|
||||||
val, err := fp.Value.GetInt()
|
val, err := fp.Value.GetInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := vm.EmitInt(script, int64(val)); err != nil {
|
emit.Int(script, int64(val))
|
||||||
return err
|
|
||||||
}
|
|
||||||
case Boolean:
|
case Boolean:
|
||||||
str, err := fp.Value.GetString()
|
str, err := fp.Value.GetString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -228,14 +195,11 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
}
|
}
|
||||||
switch str {
|
switch str {
|
||||||
case "true":
|
case "true":
|
||||||
err = vm.EmitInt(script, 1)
|
emit.Int(script, 1)
|
||||||
case "false":
|
case "false":
|
||||||
err = vm.EmitInt(script, 0)
|
emit.Int(script, 0)
|
||||||
default:
|
default:
|
||||||
err = errors.New("wrong boolean value")
|
return errors.New("wrong boolean value")
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("parameter type %v is not supported", fp.Type)
|
return fmt.Errorf("parameter type %v is not supported", fp.Type)
|
||||||
|
@ -247,44 +211,32 @@ func expandArrayIntoScript(script *bytes.Buffer, slice []Param) error {
|
||||||
// CreateFunctionInvocationScript creates a script to invoke given contract with
|
// CreateFunctionInvocationScript creates a script to invoke given contract with
|
||||||
// given parameters.
|
// given parameters.
|
||||||
func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byte, error) {
|
func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byte, error) {
|
||||||
script := new(bytes.Buffer)
|
script := io.NewBufBinWriter()
|
||||||
for i := len(params) - 1; i >= 0; i-- {
|
for i := len(params) - 1; i >= 0; i-- {
|
||||||
switch params[i].Type {
|
switch params[i].Type {
|
||||||
case stringT:
|
case stringT:
|
||||||
if err := vm.EmitString(script, params[i].String()); err != nil {
|
emit.String(script.BinWriter, params[i].String())
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case numberT:
|
case numberT:
|
||||||
num, err := params[i].GetInt()
|
num, err := params[i].GetInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitString(script, strconv.Itoa(num)); err != nil {
|
emit.String(script.BinWriter, strconv.Itoa(num))
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
case arrayT:
|
case arrayT:
|
||||||
slice, err := params[i].GetArray()
|
slice, err := params[i].GetArray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = expandArrayIntoScript(script, slice)
|
err = expandArrayIntoScript(script.BinWriter, slice)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = vm.EmitInt(script, int64(len(slice)))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = vm.EmitOpcode(script, opcode.PACK)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
emit.Int(script.BinWriter, int64(len(slice)))
|
||||||
|
emit.Opcode(script.BinWriter, opcode.PACK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vm.EmitAppCall(script, contract, false); err != nil {
|
emit.AppCall(script.BinWriter, contract, false)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,13 +245,11 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
||||||
// expects one array of FuncParams and expands it onto the stack as independent
|
// expects one array of FuncParams and expands it onto the stack as independent
|
||||||
// elements.
|
// elements.
|
||||||
func CreateInvocationScript(contract util.Uint160, funcParams []Param) ([]byte, error) {
|
func CreateInvocationScript(contract util.Uint160, funcParams []Param) ([]byte, error) {
|
||||||
script := new(bytes.Buffer)
|
script := io.NewBufBinWriter()
|
||||||
err := expandArrayIntoScript(script, funcParams)
|
err := expandArrayIntoScript(script.BinWriter, funcParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = vm.EmitAppCall(script, contract, false); err != nil {
|
emit.AppCall(script.BinWriter, contract, false)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,22 +22,14 @@ func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, erro
|
||||||
return nil, fmt.Errorf("public key count %d exceeds maximum of length 1024", len(publicKeys))
|
return nil, fmt.Errorf("public key count %d exceeds maximum of length 1024", len(publicKeys))
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := io.NewBufBinWriter()
|
||||||
if err := vm.EmitInt(buf, int64(m)); err != nil {
|
emit.Int(buf.BinWriter, int64(m))
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Sort(publicKeys)
|
sort.Sort(publicKeys)
|
||||||
for _, pubKey := range publicKeys {
|
for _, pubKey := range publicKeys {
|
||||||
if err := vm.EmitBytes(buf, pubKey.Bytes()); err != nil {
|
emit.Bytes(buf.BinWriter, pubKey.Bytes())
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := vm.EmitOpcode(buf, opcode.CHECKMULTISIG); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
emit.Int(buf.BinWriter, int64(len(publicKeys)))
|
||||||
|
emit.Opcode(buf.BinWriter, opcode.CHECKMULTISIG)
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
151
pkg/vm/emit.go
151
pkg/vm/emit.go
|
@ -1,151 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Emit a VM Instruction with data to the given buffer.
|
|
||||||
func Emit(w *bytes.Buffer, op opcode.Opcode, b []byte) error {
|
|
||||||
if err := w.WriteByte(byte(op)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err := w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitOpcode emits a single VM Instruction the given buffer.
|
|
||||||
func EmitOpcode(w io.ByteWriter, op opcode.Opcode) error {
|
|
||||||
return w.WriteByte(byte(op))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitBool emits a bool type the given buffer.
|
|
||||||
func EmitBool(w io.ByteWriter, ok bool) error {
|
|
||||||
if ok {
|
|
||||||
return EmitOpcode(w, opcode.PUSHT)
|
|
||||||
}
|
|
||||||
return EmitOpcode(w, opcode.PUSHF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitInt emits a int type to the given buffer.
|
|
||||||
func EmitInt(w *bytes.Buffer, i int64) error {
|
|
||||||
if i == -1 {
|
|
||||||
return EmitOpcode(w, opcode.PUSHM1)
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
return EmitOpcode(w, opcode.PUSHF)
|
|
||||||
}
|
|
||||||
if i > 0 && i < 16 {
|
|
||||||
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
|
||||||
return EmitOpcode(w, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
bInt := big.NewInt(i)
|
|
||||||
val := util.ArrayReverse(bInt.Bytes())
|
|
||||||
return EmitBytes(w, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitString emits a string to the given buffer.
|
|
||||||
func EmitString(w *bytes.Buffer, s string) error {
|
|
||||||
return EmitBytes(w, []byte(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitBytes emits a byte array to the given buffer.
|
|
||||||
func EmitBytes(w *bytes.Buffer, b []byte) error {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
n = len(b)
|
|
||||||
)
|
|
||||||
|
|
||||||
if n <= int(opcode.PUSHBYTES75) {
|
|
||||||
return Emit(w, opcode.Opcode(n), b)
|
|
||||||
} else if n < 0x100 {
|
|
||||||
err = Emit(w, opcode.PUSHDATA1, []byte{byte(n)})
|
|
||||||
} else if n < 0x10000 {
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
|
||||||
err = Emit(w, opcode.PUSHDATA2, buf)
|
|
||||||
} else {
|
|
||||||
buf := make([]byte, 4)
|
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
|
||||||
err = Emit(w, opcode.PUSHDATA4, buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitSyscall emits the syscall API to the given buffer.
|
|
||||||
// Syscall API string cannot be 0.
|
|
||||||
func EmitSyscall(w *bytes.Buffer, api string) error {
|
|
||||||
if len(api) == 0 {
|
|
||||||
return errors.New("syscall api cannot be of length 0")
|
|
||||||
}
|
|
||||||
buf := make([]byte, len(api)+1)
|
|
||||||
buf[0] = byte(len(api))
|
|
||||||
copy(buf[1:], api)
|
|
||||||
return Emit(w, opcode.SYSCALL, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitCall emits a call Instruction with label to the given buffer.
|
|
||||||
func EmitCall(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
|
||||||
return EmitJmp(w, op, label)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitJmp emits a jump Instruction along with label to the given buffer.
|
|
||||||
func EmitJmp(w *bytes.Buffer, op opcode.Opcode, label int16) error {
|
|
||||||
if !isInstructionJmp(op) {
|
|
||||||
return fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
|
||||||
}
|
|
||||||
buf := make([]byte, 2)
|
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
|
||||||
return Emit(w, op, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
|
||||||
// emitted instead.
|
|
||||||
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
|
||||||
op := opcode.APPCALL
|
|
||||||
if tailCall {
|
|
||||||
op = opcode.TAILCALL
|
|
||||||
}
|
|
||||||
return Emit(w, op, scriptHash.BytesBE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCallWithOperationAndData emits an appcall with the given operation and data.
|
|
||||||
func EmitAppCallWithOperationAndData(w *bytes.Buffer, scriptHash util.Uint160, operation string, data []byte) error {
|
|
||||||
if err := EmitBytes(w, data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := EmitString(w, operation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return EmitAppCall(w, scriptHash, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EmitAppCallWithOperation emits an appcall with the given operation.
|
|
||||||
func EmitAppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operation string) error {
|
|
||||||
if err := EmitBool(w, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := EmitString(w, operation); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return EmitAppCall(w, scriptHash, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInstructionJmp(op opcode.Opcode) bool {
|
|
||||||
if op == opcode.JMP || op == opcode.JMPIFNOT || op == opcode.JMPIF || op == opcode.CALL {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package vm
|
package emit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
|
@ -1,4 +1,4 @@
|
||||||
package vm
|
package emit
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
140
pkg/vm/emit/emit.go
Normal file
140
pkg/vm/emit/emit.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package emit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Instruction emits a VM Instruction with data to the given buffer.
|
||||||
|
func Instruction(w *io.BinWriter, op opcode.Opcode, b []byte) {
|
||||||
|
w.WriteB(byte(op))
|
||||||
|
w.WriteBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opcode emits a single VM Instruction without arguments to the given buffer.
|
||||||
|
func Opcode(w *io.BinWriter, op opcode.Opcode) {
|
||||||
|
w.WriteB(byte(op))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool emits a bool type the given buffer.
|
||||||
|
func Bool(w *io.BinWriter, ok bool) {
|
||||||
|
if ok {
|
||||||
|
Opcode(w, opcode.PUSHT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Opcode(w, opcode.PUSHF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int emits a int type to the given buffer.
|
||||||
|
func Int(w *io.BinWriter, i int64) {
|
||||||
|
switch {
|
||||||
|
case i == -1:
|
||||||
|
Opcode(w, opcode.PUSHM1)
|
||||||
|
case i == 0:
|
||||||
|
Opcode(w, opcode.PUSHF)
|
||||||
|
case i > 0 && i < 16:
|
||||||
|
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
||||||
|
Opcode(w, val)
|
||||||
|
default:
|
||||||
|
bInt := big.NewInt(i)
|
||||||
|
val := IntToBytes(bInt)
|
||||||
|
Bytes(w, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String emits a string to the given buffer.
|
||||||
|
func String(w *io.BinWriter, s string) {
|
||||||
|
Bytes(w, []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes emits a byte array to the given buffer.
|
||||||
|
func Bytes(w *io.BinWriter, b []byte) {
|
||||||
|
var n = len(b)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case n <= int(opcode.PUSHBYTES75):
|
||||||
|
Instruction(w, opcode.Opcode(n), b)
|
||||||
|
return
|
||||||
|
case n < 0x100:
|
||||||
|
Instruction(w, opcode.PUSHDATA1, []byte{byte(n)})
|
||||||
|
case n < 0x10000:
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||||
|
Instruction(w, opcode.PUSHDATA2, buf)
|
||||||
|
default:
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||||
|
Instruction(w, opcode.PUSHDATA4, buf)
|
||||||
|
}
|
||||||
|
w.WriteBytes(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall emits the syscall API to the given buffer.
|
||||||
|
// Syscall API string cannot be 0.
|
||||||
|
func Syscall(w *io.BinWriter, api string) {
|
||||||
|
if w.Err != nil {
|
||||||
|
return
|
||||||
|
} else if len(api) == 0 {
|
||||||
|
w.Err = errors.New("syscall api cannot be of length 0")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := make([]byte, len(api)+1)
|
||||||
|
buf[0] = byte(len(api))
|
||||||
|
copy(buf[1:], api)
|
||||||
|
Instruction(w, opcode.SYSCALL, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call emits a call Instruction with label to the given buffer.
|
||||||
|
func Call(w *io.BinWriter, op opcode.Opcode, label int16) {
|
||||||
|
Jmp(w, op, label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jmp emits a jump Instruction along with label to the given buffer.
|
||||||
|
func Jmp(w *io.BinWriter, op opcode.Opcode, label int16) {
|
||||||
|
if w.Err != nil {
|
||||||
|
return
|
||||||
|
} else if !isInstructionJmp(op) {
|
||||||
|
w.Err = fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||||
|
Instruction(w, op, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
||||||
|
// emitted instead.
|
||||||
|
func AppCall(w *io.BinWriter, scriptHash util.Uint160, tailCall bool) {
|
||||||
|
op := opcode.APPCALL
|
||||||
|
if tailCall {
|
||||||
|
op = opcode.TAILCALL
|
||||||
|
}
|
||||||
|
Instruction(w, op, scriptHash.BytesBE())
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCallWithOperationAndData emits an appcall with the given operation and data.
|
||||||
|
func AppCallWithOperationAndData(w *io.BinWriter, scriptHash util.Uint160, operation string, data []byte) {
|
||||||
|
Bytes(w, data)
|
||||||
|
String(w, operation)
|
||||||
|
AppCall(w, scriptHash, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppCallWithOperation emits an appcall with the given operation.
|
||||||
|
func AppCallWithOperation(w *io.BinWriter, scriptHash util.Uint160, operation string) {
|
||||||
|
Bool(w, false)
|
||||||
|
String(w, operation)
|
||||||
|
AppCall(w, scriptHash, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInstructionJmp(op opcode.Opcode) bool {
|
||||||
|
if op == opcode.JMP || op == opcode.JMPIFNOT || op == opcode.JMPIF || op == opcode.CALL {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
201
pkg/vm/emit/emit_test.go
Normal file
201
pkg/vm/emit/emit_test.go
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
package emit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmitInt(t *testing.T) {
|
||||||
|
t.Run("minis one", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Int(buf.BinWriter, -1)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.Len(t, result, 1)
|
||||||
|
assert.EqualValues(t, opcode.PUSHM1, result[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("zero", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Int(buf.BinWriter, 0)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.Len(t, result, 1)
|
||||||
|
assert.EqualValues(t, opcode.PUSH0, result[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("1-byte int", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Int(buf.BinWriter, 10)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSH10, result[0])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("2-byte int", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Int(buf.BinWriter, 100)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHBYTES1, result[0])
|
||||||
|
assert.EqualValues(t, 100, result[1])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("4-byte int", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Int(buf.BinWriter, 1000)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHBYTES2, result[0])
|
||||||
|
assert.EqualValues(t, 1000, binary.LittleEndian.Uint16(result[1:3]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSlice(n int) []byte {
|
||||||
|
data := make([]byte, n)
|
||||||
|
for i := range data {
|
||||||
|
data[i] = byte(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
t.Run("small slice", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Bytes(buf.BinWriter, []byte{0, 1, 2, 3})
|
||||||
|
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHBYTES4, result[0])
|
||||||
|
assert.EqualValues(t, []byte{0, 1, 2, 3}, result[1:])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice with len <= 255", func(t *testing.T) {
|
||||||
|
const size = 200
|
||||||
|
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Bytes(buf.BinWriter, getSlice(size))
|
||||||
|
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHDATA1, result[0])
|
||||||
|
assert.EqualValues(t, size, result[1])
|
||||||
|
assert.Equal(t, getSlice(size), result[2:])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice with len <= 65535", func(t *testing.T) {
|
||||||
|
const size = 60000
|
||||||
|
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Bytes(buf.BinWriter, getSlice(size))
|
||||||
|
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHDATA2, result[0])
|
||||||
|
assert.EqualValues(t, size, binary.LittleEndian.Uint16(result[1:3]))
|
||||||
|
assert.Equal(t, getSlice(size), result[3:])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice with len > 65535", func(t *testing.T) {
|
||||||
|
const size = 100000
|
||||||
|
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Bytes(buf.BinWriter, getSlice(size))
|
||||||
|
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, opcode.PUSHDATA4, result[0])
|
||||||
|
assert.EqualValues(t, size, binary.LittleEndian.Uint32(result[1:5]))
|
||||||
|
assert.Equal(t, getSlice(size), result[5:])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmitBool(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Bool(buf.BinWriter, true)
|
||||||
|
Bool(buf.BinWriter, false)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.Equal(t, opcode.Opcode(result[0]), opcode.PUSH1)
|
||||||
|
assert.Equal(t, opcode.Opcode(result[1]), opcode.PUSH0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmitString(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
str := "City Of Zion"
|
||||||
|
String(buf.BinWriter, str)
|
||||||
|
assert.Equal(t, buf.Len(), len(str)+1)
|
||||||
|
assert.Equal(t, buf.Bytes()[1:], []byte(str))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmitSyscall(t *testing.T) {
|
||||||
|
syscalls := []string{
|
||||||
|
"Neo.Runtime.Log",
|
||||||
|
"Neo.Runtime.Notify",
|
||||||
|
"Neo.Runtime.Whatever",
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
for _, syscall := range syscalls {
|
||||||
|
Syscall(buf.BinWriter, syscall)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.Equal(t, opcode.Opcode(result[0]), opcode.SYSCALL)
|
||||||
|
assert.Equal(t, result[1], uint8(len(syscall)))
|
||||||
|
assert.Equal(t, result[2:], []byte(syscall))
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("empty syscall", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Syscall(buf.BinWriter, "")
|
||||||
|
assert.Error(t, buf.Err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("empty syscall after error", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
err := errors.New("first error")
|
||||||
|
|
||||||
|
buf.Err = err
|
||||||
|
Syscall(buf.BinWriter, "")
|
||||||
|
assert.Equal(t, err, buf.Err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJmp(t *testing.T) {
|
||||||
|
const label = 0x23
|
||||||
|
|
||||||
|
t.Run("correct", func(t *testing.T) {
|
||||||
|
ops := []opcode.Opcode{opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL}
|
||||||
|
for i := range ops {
|
||||||
|
t.Run(ops[i].String(), func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Jmp(buf.BinWriter, ops[i], label)
|
||||||
|
assert.NoError(t, buf.Err)
|
||||||
|
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.EqualValues(t, ops[i], result[0])
|
||||||
|
assert.EqualValues(t, 0x23, binary.LittleEndian.Uint16(result[1:]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not a jump instruction", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Jmp(buf.BinWriter, opcode.ABS, label)
|
||||||
|
assert.Error(t, buf.Err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("not a jump after error", func(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
err := errors.New("first error")
|
||||||
|
|
||||||
|
buf.Err = err
|
||||||
|
Jmp(buf.BinWriter, opcode.ABS, label)
|
||||||
|
assert.Error(t, buf.Err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmitCall(t *testing.T) {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
Call(buf.BinWriter, opcode.JMP, 100)
|
||||||
|
result := buf.Bytes()
|
||||||
|
assert.Equal(t, opcode.Opcode(result[0]), opcode.JMP)
|
||||||
|
label := binary.LittleEndian.Uint16(result[1:3])
|
||||||
|
assert.Equal(t, label, uint16(100))
|
||||||
|
}
|
|
@ -1,65 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEmitInt(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
EmitInt(buf, 10)
|
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH10)
|
|
||||||
buf.Reset()
|
|
||||||
EmitInt(buf, 100)
|
|
||||||
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
|
||||||
assert.Equal(t, buf.Bytes()[1], uint8(100))
|
|
||||||
buf.Reset()
|
|
||||||
EmitInt(buf, 1000)
|
|
||||||
assert.Equal(t, buf.Bytes()[0], uint8(2))
|
|
||||||
assert.Equal(t, buf.Bytes()[1:3], []byte{0xe8, 0x03})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitBool(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
EmitBool(buf, true)
|
|
||||||
EmitBool(buf, false)
|
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.PUSH1)
|
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[1]), opcode.PUSH0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitString(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
str := "City Of Zion"
|
|
||||||
EmitString(buf, str)
|
|
||||||
assert.Equal(t, buf.Len(), len(str)+1)
|
|
||||||
assert.Equal(t, buf.Bytes()[1:], []byte(str))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitSyscall(t *testing.T) {
|
|
||||||
syscalls := []string{
|
|
||||||
"Neo.Runtime.Log",
|
|
||||||
"Neo.Runtime.Notify",
|
|
||||||
"Neo.Runtime.Whatever",
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
for _, syscall := range syscalls {
|
|
||||||
EmitSyscall(buf, syscall)
|
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.SYSCALL)
|
|
||||||
assert.Equal(t, buf.Bytes()[1], uint8(len(syscall)))
|
|
||||||
assert.Equal(t, buf.Bytes()[2:], []byte(syscall))
|
|
||||||
buf.Reset()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmitCall(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
EmitCall(buf, opcode.JMP, 100)
|
|
||||||
assert.Equal(t, opcode.Opcode(buf.Bytes()[0]), opcode.JMP)
|
|
||||||
label := binary.LittleEndian.Uint16(buf.Bytes()[1:3])
|
|
||||||
assert.Equal(t, label, uint16(100))
|
|
||||||
}
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
|
@ -196,7 +198,7 @@ func compareItems(t *testing.T, a, b StackItem) {
|
||||||
case *BigIntegerItem:
|
case *BigIntegerItem:
|
||||||
require.Equal(t, val, ac.value.Int64())
|
require.Equal(t, val, ac.value.Int64())
|
||||||
case *ByteArrayItem:
|
case *ByteArrayItem:
|
||||||
require.Equal(t, val, BytesToInt(ac.value).Int64())
|
require.Equal(t, val, emit.BytesToInt(ac.value).Int64())
|
||||||
case *BoolItem:
|
case *BoolItem:
|
||||||
if ac.value {
|
if ac.value {
|
||||||
require.Equal(t, val, int64(1))
|
require.Equal(t, val, int64(1))
|
||||||
|
|
|
@ -3,6 +3,8 @@ package vm
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/io"
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
|
||||||
w.WriteBool(t.value)
|
w.WriteBool(t.value)
|
||||||
case *BigIntegerItem:
|
case *BigIntegerItem:
|
||||||
w.WriteBytes([]byte{byte(integerT)})
|
w.WriteBytes([]byte{byte(integerT)})
|
||||||
w.WriteVarBytes(IntToBytes(t.value))
|
w.WriteVarBytes(emit.IntToBytes(t.value))
|
||||||
case *InteropItem:
|
case *InteropItem:
|
||||||
w.Err = errors.New("interop item can't be serialized")
|
w.Err = errors.New("interop item can't be serialized")
|
||||||
case *ArrayItem, *StructItem:
|
case *ArrayItem, *StructItem:
|
||||||
|
@ -108,7 +110,7 @@ func DecodeBinaryStackItem(r *io.BinReader) StackItem {
|
||||||
return NewBoolItem(b)
|
return NewBoolItem(b)
|
||||||
case integerT:
|
case integerT:
|
||||||
data := r.ReadVarBytes()
|
data := r.ReadVarBytes()
|
||||||
num := BytesToInt(data)
|
num := emit.BytesToInt(data)
|
||||||
return &BigIntegerItem{
|
return &BigIntegerItem{
|
||||||
value: num,
|
value: num,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stack implementation for the neo-go virtual machine. The stack implements
|
// Stack implementation for the neo-go virtual machine. The stack implements
|
||||||
|
@ -81,7 +83,7 @@ func (e *Element) BigInt() *big.Int {
|
||||||
return big.NewInt(0)
|
return big.NewInt(0)
|
||||||
default:
|
default:
|
||||||
b := t.Value().([]uint8)
|
b := t.Value().([]uint8)
|
||||||
return BytesToInt(b)
|
return emit.BytesToInt(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A StackItem represents the "real" value that is pushed on the stack.
|
// A StackItem represents the "real" value that is pushed on the stack.
|
||||||
|
@ -142,7 +144,7 @@ func NewBigIntegerItem(value int) *BigIntegerItem {
|
||||||
|
|
||||||
// Bytes converts i to a slice of bytes.
|
// Bytes converts i to a slice of bytes.
|
||||||
func (i *BigIntegerItem) Bytes() []byte {
|
func (i *BigIntegerItem) Bytes() []byte {
|
||||||
return IntToBytes(i.value)
|
return emit.IntToBytes(i.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value implements StackItem interface.
|
// Value implements StackItem interface.
|
||||||
|
|
|
@ -8,9 +8,12 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/io"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/emit"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -30,9 +33,9 @@ func TestInteropHook(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
v.RegisterInteropGetter(fooInteropGetter)
|
v.RegisterInteropGetter(fooInteropGetter)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := io.NewBufBinWriter()
|
||||||
EmitSyscall(buf, "foo")
|
emit.Syscall(buf.BinWriter, "foo")
|
||||||
EmitOpcode(buf, opcode.RET)
|
emit.Opcode(buf.BinWriter, opcode.RET)
|
||||||
v.Load(buf.Bytes())
|
v.Load(buf.Bytes())
|
||||||
runVM(t, v)
|
runVM(t, v)
|
||||||
assert.Equal(t, 1, v.estack.Len())
|
assert.Equal(t, 1, v.estack.Len())
|
||||||
|
@ -43,12 +46,12 @@ func TestInteropHookViaID(t *testing.T) {
|
||||||
v := New()
|
v := New()
|
||||||
v.RegisterInteropGetter(fooInteropGetter)
|
v.RegisterInteropGetter(fooInteropGetter)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := io.NewBufBinWriter()
|
||||||
fooid := InteropNameToID([]byte("foo"))
|
fooid := InteropNameToID([]byte("foo"))
|
||||||
var id = make([]byte, 4)
|
var id = make([]byte, 4)
|
||||||
binary.LittleEndian.PutUint32(id, fooid)
|
binary.LittleEndian.PutUint32(id, fooid)
|
||||||
_ = EmitSyscall(buf, string(id))
|
emit.Syscall(buf.BinWriter, string(id))
|
||||||
_ = EmitOpcode(buf, opcode.RET)
|
emit.Opcode(buf.BinWriter, opcode.RET)
|
||||||
v.Load(buf.Bytes())
|
v.Load(buf.Bytes())
|
||||||
runVM(t, v)
|
runVM(t, v)
|
||||||
assert.Equal(t, 1, v.estack.Len())
|
assert.Equal(t, 1, v.estack.Len())
|
||||||
|
@ -130,10 +133,10 @@ func TestBytesToPublicKey(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPushBytes1to75(t *testing.T) {
|
func TestPushBytes1to75(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := io.NewBufBinWriter()
|
||||||
for i := 1; i <= 75; i++ {
|
for i := 1; i <= 75; i++ {
|
||||||
b := randomBytes(i)
|
b := randomBytes(i)
|
||||||
EmitBytes(buf, b)
|
emit.Bytes(buf.BinWriter, b)
|
||||||
vm := load(buf.Bytes())
|
vm := load(buf.Bytes())
|
||||||
err := vm.Step()
|
err := vm.Step()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
Loading…
Reference in a new issue