mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 03:41:34 +00:00
parent
a73757df66
commit
648563c3e2
19 changed files with 304 additions and 265 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.41.2
|
||||
0.42.0
|
||||
|
|
21
pkg/vm/api/crypto/util.go
Normal file
21
pkg/vm/api/crypto/util.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package crypto
|
||||
|
||||
// SHA1 computes the sha1 hash of b.
|
||||
func SHA1(b []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SHA256 computes the sha256 hash of b.
|
||||
func SHA256(b []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash160 ..
|
||||
func Hash160(b []byte) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash256 ..
|
||||
func Hash256(b []byte) []byte {
|
||||
return nil
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package util
|
||||
|
||||
// Package util provides utility functions that can be used in smart contracts.
|
||||
// These functions are just signatures and provide not internal logic.
|
||||
// Only the compiler knows how to convert them to bytecode.
|
||||
|
||||
// Print is a VM helper function to print/log data.
|
||||
func Print(v interface{}) {}
|
|
@ -26,6 +26,12 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machin
|
|||
- storage
|
||||
- runtime
|
||||
|
||||
### VM utility helper functions
|
||||
- SHA1
|
||||
- SHA256
|
||||
- Hash256
|
||||
- Hash160
|
||||
|
||||
## Not yet implemented
|
||||
- range
|
||||
- some parts of the interop layer (VM API)
|
||||
|
|
|
@ -11,8 +11,12 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// Go language builtin functions.
|
||||
builtinFuncs = []string{"len", "append"}
|
||||
// Go language builtin functions and custom builtin utility functions.
|
||||
builtinFuncs = []string{
|
||||
"len", "append", "SHA256",
|
||||
"SHA1", "Hash256", "Hash160",
|
||||
}
|
||||
|
||||
// VM system calls that have no return value.
|
||||
noRetSyscalls = []string{
|
||||
"Notify", "Log", "Put", "Register", "Delete",
|
||||
|
@ -162,15 +166,23 @@ func analyzeFuncUsage(pkgs map[*types.Package]*loader.PackageInfo) funcUsage {
|
|||
}
|
||||
|
||||
func isBuiltin(expr ast.Expr) bool {
|
||||
if ident, ok := expr.(*ast.Ident); ok {
|
||||
var name string
|
||||
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
name = t.Name
|
||||
case *ast.SelectorExpr:
|
||||
name = t.Sel.Name
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
for _, n := range builtinFuncs {
|
||||
if ident.Name == n {
|
||||
if name == n {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isByteArray(lit *ast.CompositeLit, tInfo *types.Info) bool {
|
||||
|
|
|
@ -421,7 +421,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
if isBuiltin {
|
||||
// Use the ident to check, builtins are not in func scopes.
|
||||
// We can be sure builtins are of type *ast.Ident.
|
||||
c.convertBuiltin(n.Fun.(*ast.Ident).Name, n)
|
||||
c.convertBuiltin(n)
|
||||
} else if isSyscall(f.name) {
|
||||
c.convertSyscall(f.name)
|
||||
} else {
|
||||
|
@ -529,7 +529,15 @@ func (c *codegen) convertSyscall(name string) {
|
|||
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE
|
||||
}
|
||||
|
||||
func (c *codegen) convertBuiltin(name string, expr *ast.CallExpr) {
|
||||
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||
var name string
|
||||
switch t := expr.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
name = t.Name
|
||||
case *ast.SelectorExpr:
|
||||
name = t.Sel.Name
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "len":
|
||||
arg := expr.Args[0]
|
||||
|
@ -541,6 +549,14 @@ func (c *codegen) convertBuiltin(name string, expr *ast.CallExpr) {
|
|||
}
|
||||
case "append":
|
||||
emitOpcode(c.prog, vm.Oappend)
|
||||
case "SHA256":
|
||||
emitOpcode(c.prog, vm.Osha256)
|
||||
case "SHA1":
|
||||
emitOpcode(c.prog, vm.Osha1)
|
||||
case "Hash256":
|
||||
emitOpcode(c.prog, vm.Ohash256)
|
||||
case "Hash160":
|
||||
emitOpcode(c.prog, vm.Ohash160)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package compiler
|
||||
|
||||
var builtinTestCases = []testCase{
|
||||
{
|
||||
"array len",
|
||||
`
|
||||
package foo
|
||||
|
||||
func Main() int {
|
||||
x := []int{0, 1, 2}
|
||||
y := len(x)
|
||||
return y
|
||||
}
|
||||
`,
|
||||
"53c56b52510053c16c766b00527ac46c766b00c361c06c766b51527ac46203006c766b51c3616c7566",
|
||||
},
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
src string
|
||||
result interface{}
|
||||
}
|
||||
|
||||
func TestAllCases(t *testing.T) {
|
||||
// The Go language
|
||||
//testCases = append(testCases, builtinTestCases...)
|
||||
//testCases = append(testCases, arrayTestCases...)
|
||||
//testCases = append(testCases, binaryExprTestCases...)
|
||||
//testCases = append(testCases, functionCallTestCases...)
|
||||
//testCases = append(testCases, boolTestCases...)
|
||||
//testCases = append(testCases, stringTestCases...)
|
||||
//testCases = append(testCases, structTestCases...)
|
||||
//testCases = append(testCases, ifStatementTestCases...)
|
||||
//testCases = append(testCases, customTypeTestCases...)
|
||||
//testCases = append(testCases, constantTestCases...)
|
||||
//testCases = append(testCases, importTestCases...)
|
||||
//testCases = append(testCases, forTestCases...)
|
||||
|
||||
//// Blockchain specific
|
||||
//testCases = append(testCases, storageTestCases...)
|
||||
//testCases = append(testCases, runtimeTestCases...)
|
||||
|
||||
//for _, tc := range testCases {
|
||||
// b, err := compiler.Compile(strings.NewReader(tc.src), &compiler.Options{})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// expectedResult, err := hex.DecodeString(tc.result)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// if bytes.Compare(b, expectedResult) != 0 {
|
||||
// fmt.Println(tc.src)
|
||||
// t.Log(hex.EncodeToString(b))
|
||||
// dumpOpCodeSideBySide(b, expectedResult)
|
||||
// t.Fatalf("compiling %s failed", tc.name)
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
func dumpOpCodeSideBySide(have, want []byte) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||
fmt.Fprintln(w, "INDEX\tHAVE OPCODE\tDESC\tWANT OPCODE\tDESC\tDIFF")
|
||||
|
||||
var b byte
|
||||
for i := 0; i < len(have); i++ {
|
||||
if len(want) <= i {
|
||||
b = 0x00
|
||||
} else {
|
||||
b = want[i]
|
||||
}
|
||||
diff := ""
|
||||
if have[i] != b {
|
||||
diff = "<<"
|
||||
}
|
||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\t0x%2x\t%s\t%s\n",
|
||||
i, have[i], vm.Opcode(have[i]), b, vm.Opcode(b), diff)
|
||||
}
|
||||
w.Flush()
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package compiler
|
||||
|
||||
var customTypeTestCases = []testCase{
|
||||
{
|
||||
"test custom type",
|
||||
`
|
||||
package foo
|
||||
|
||||
type bar int
|
||||
type specialString string
|
||||
|
||||
func Main() specialString {
|
||||
var x bar
|
||||
var str specialString
|
||||
x = 10
|
||||
str = "some short string"
|
||||
if x == 10 {
|
||||
return str
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
`,
|
||||
"55c56b5a6c766b00527ac411736f6d652073686f727420737472696e676c766b51527ac46c766b00c35a9c640f006203006c766b51c3616c7566620300046e6f6e65616c7566",
|
||||
},
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
package compiler
|
||||
|
||||
var ifStatementTestCases = []testCase{
|
||||
{
|
||||
"if statement LT",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x < 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"54c56b5a6c766b00527ac46c766b00c301649f640b0062030051616c756662030000616c7566",
|
||||
},
|
||||
{
|
||||
"if statement GT",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x > 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"54c56b5a6c766b00527ac46c766b00c30164a0640b0062030051616c756662030000616c7566",
|
||||
},
|
||||
{
|
||||
"if statement GTE",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"54c56b5a6c766b00527ac46c766b00c30164a2640b0062030051616c756662030000616c7566",
|
||||
},
|
||||
{
|
||||
"complex if statement with LAND",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 10 && x <= 20 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"54c56b5a6c766b00527ac46c766b00c35aa26416006c766b00c30114a1640b0062030051616c756662030000616c7566",
|
||||
},
|
||||
{
|
||||
"complex if statement with LOR",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 10 || x <= 20 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"54c56b5a6c766b00527ac46c766b00c35aa2630e006c766b00c30114a1640b0062030051616c756662030000616c7566",
|
||||
},
|
||||
{
|
||||
"nested if statements",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x > 10 {
|
||||
if x < 20 {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
"56c56b5a6c766b00527ac46c766b00c35aa0641e006c766b00c301149f640b0062030051616c756662030052616c756662030000616c7566",
|
||||
},
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package compiler
|
||||
|
||||
var importTestCases = []testCase{
|
||||
{
|
||||
"import function",
|
||||
`
|
||||
package somethingelse
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/vm/compiler/tests/foo"
|
||||
|
||||
func Main() int {
|
||||
i := foo.NewBar()
|
||||
return i
|
||||
}
|
||||
`,
|
||||
"52c56b616516006c766b00527ac46203006c766b00c3616c756651c56b6203005a616c7566",
|
||||
},
|
||||
{
|
||||
"import test",
|
||||
`
|
||||
package somethingwedontcareabout
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/vm/compiler/tests/bar"
|
||||
|
||||
func Main() int {
|
||||
b := bar.Bar{
|
||||
X: 4,
|
||||
}
|
||||
return b.Y
|
||||
}
|
||||
`,
|
||||
"52c56b6154c66b546c766b00527ac4006c766b51527ac4006c766b52527ac4006c766b53527ac46c6c766b00527ac46203006c766b00c351c3616c7566",
|
||||
},
|
||||
}
|
|
@ -177,6 +177,18 @@ func TestStringLen(t *testing.T) {
|
|||
eval(t, src, big.NewInt(27))
|
||||
}
|
||||
|
||||
func TestByteArrayLen(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
|
||||
func Main() int {
|
||||
b := []byte{0x00, 0x01, 0x2}
|
||||
return len(b)
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(3))
|
||||
}
|
||||
|
||||
func TestSimpleString(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
|
|
93
pkg/vm/tests/if_test.go
Normal file
93
pkg/vm/tests/if_test.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package vm_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLT(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x < 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(1))
|
||||
}
|
||||
|
||||
func TestGT(t *testing.T) {
|
||||
src := `
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x > 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(0))
|
||||
}
|
||||
|
||||
func TestGTE(t *testing.T) {
|
||||
src := `
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 100 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(0))
|
||||
}
|
||||
|
||||
func TestLAND(t *testing.T) {
|
||||
src := `
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 10 && x <= 20 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(1))
|
||||
}
|
||||
|
||||
func TestLOR(t *testing.T) {
|
||||
src := `
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x >= 10 || x <= 20 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(1))
|
||||
}
|
||||
|
||||
func TestNestedIF(t *testing.T) {
|
||||
src := `
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 10
|
||||
if x > 10 {
|
||||
if x < 20 {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(0))
|
||||
}
|
36
pkg/vm/tests/import_test.go
Normal file
36
pkg/vm/tests/import_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package vm_test
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImportFunction(t *testing.T) {
|
||||
src := `
|
||||
package somethingelse
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/vm/tests/foo"
|
||||
|
||||
func Main() int {
|
||||
i := foo.NewBar()
|
||||
return i
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(10))
|
||||
}
|
||||
|
||||
func TestImportStruct(t *testing.T) {
|
||||
src := `
|
||||
package somethingwedontcareabout
|
||||
|
||||
import "github.com/CityOfZion/neo-go/pkg/vm/tests/bar"
|
||||
|
||||
func Main() int {
|
||||
b := bar.Bar{
|
||||
X: 4,
|
||||
}
|
||||
return b.Y
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(0))
|
||||
}
|
24
pkg/vm/tests/type_test.go
Normal file
24
pkg/vm/tests/type_test.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package vm_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCustomType(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
|
||||
type bar int
|
||||
type specialString string
|
||||
|
||||
func Main() specialString {
|
||||
var x bar
|
||||
var str specialString
|
||||
x = 10
|
||||
str = "some short string"
|
||||
if x == 10 {
|
||||
return str
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
`
|
||||
eval(t, src, []byte("some short string"))
|
||||
}
|
65
pkg/vm/tests/util_test.go
Normal file
65
pkg/vm/tests/util_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package vm_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSHA256(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
||||
)
|
||||
func Main() []byte {
|
||||
src := []byte{0x97}
|
||||
hash := crypto.SHA256(src)
|
||||
return hash
|
||||
}
|
||||
`
|
||||
eval(t, src, []byte{0x2a, 0xa, 0xb7, 0x32, 0xb4, 0xe9, 0xd8, 0x5e, 0xf7, 0xdc, 0x25, 0x30, 0x3b, 0x64, 0xab, 0x52, 0x7c, 0x25, 0xa4, 0xd7, 0x78, 0x15, 0xeb, 0xb5, 0x79, 0xf3, 0x96, 0xec, 0x6c, 0xac, 0xca, 0xd3})
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
||||
)
|
||||
func Main() []byte {
|
||||
src := []byte{0x97}
|
||||
hash := crypto.SHA1(src)
|
||||
return hash
|
||||
}
|
||||
`
|
||||
eval(t, src, []byte{0xfa, 0x13, 0x8a, 0xe3, 0x56, 0xd3, 0x5c, 0x8d, 0x77, 0x8, 0x3c, 0x40, 0x6a, 0x5b, 0xe7, 0x37, 0x45, 0x64, 0x3a, 0xae})
|
||||
}
|
||||
|
||||
func TestHash160(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
||||
)
|
||||
func Main() []byte {
|
||||
src := []byte{0x97}
|
||||
hash := crypto.Hash160(src)
|
||||
return hash
|
||||
}
|
||||
`
|
||||
eval(t, src, []byte{0x5f, 0xa4, 0x1c, 0x76, 0xf7, 0xe8, 0xca, 0x72, 0xb7, 0x18, 0xff, 0x59, 0x22, 0x91, 0xc2, 0x3a, 0x3a, 0xf5, 0x58, 0x6c})
|
||||
}
|
||||
|
||||
func TestHash256(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
||||
)
|
||||
func Main() []byte {
|
||||
src := []byte{0x97}
|
||||
hash := crypto.Hash256(src)
|
||||
return hash
|
||||
}
|
||||
`
|
||||
eval(t, src, []byte{0xc0, 0x85, 0x26, 0xad, 0x17, 0x36, 0x53, 0xee, 0xb8, 0xc7, 0xf4, 0xae, 0x82, 0x8b, 0x6e, 0xa1, 0x84, 0xac, 0x5a, 0x3, 0x8a, 0xf6, 0xc3, 0x68, 0x23, 0xfa, 0x5f, 0x5d, 0xd9, 0x1b, 0x91, 0xa2})
|
||||
}
|
11
pkg/vm/vm.go
11
pkg/vm/vm.go
|
@ -584,11 +584,16 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
|||
|
||||
case Oarraysize:
|
||||
elem := v.estack.Pop()
|
||||
arr, ok := elem.value.Value().([]StackItem)
|
||||
if !ok {
|
||||
// Cause there is no native (byte) item type here, hence we need to check
|
||||
// the type of the item for array size operations.
|
||||
switch t := elem.value.Value().(type) {
|
||||
case []StackItem:
|
||||
v.estack.PushVal(len(t))
|
||||
case []uint8:
|
||||
v.estack.PushVal(len(t))
|
||||
default:
|
||||
panic("ARRAYSIZE: item not of type []StackItem")
|
||||
}
|
||||
v.estack.PushVal(len(arr))
|
||||
|
||||
case Osize:
|
||||
elem := v.estack.Pop()
|
||||
|
|
Loading…
Reference in a new issue