forked from TrueCloudLab/neoneo-go
Compiler update (basic sc ready) (#31)
* refactored structs, the scope is not needed anymore + fix passing struct in func arguments. * implemented byte arrays and added runtime tests * Added sc examples in compiler README + added quick nested if test. * Updated README
This commit is contained in:
parent
de3395fb51
commit
2345858238
19 changed files with 464 additions and 215 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -22,3 +22,6 @@ bin/
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# anthdm todolists
|
||||||
|
/pkg/vm/compiler/todo.md
|
||||||
|
|
12
README.md
12
README.md
|
@ -40,23 +40,21 @@ A complete toolkit for the NEO blockchain, including:
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
This project is still under heavy development. Still working on internal API's and project layout. T
|
|
||||||
his should not take longer than 2 weeks.
|
|
||||||
|
|
||||||
The project will exist out of the following packages:
|
The project will exist out of the following packages:
|
||||||
|
|
||||||
| Package | State | Developer |
|
| Package | State | Developer |
|
||||||
|---------------|---------|--------------------------------------|
|
|---------------|---------|--------------------------------------|
|
||||||
| api | started | [@anthdm](https://github.com/anthdm) |
|
|
||||||
| core | started | [@anthdm](https://github.com/anthdm) |
|
| core | started | [@anthdm](https://github.com/anthdm) |
|
||||||
| network | started | [@anthdm](https://github.com/anthdm) |
|
| network | started | [@anthdm](https://github.com/anthdm) |
|
||||||
| vm | started | [@anthdm](https://github.com/anthdm) |
|
| vm | started | [@anthdm](https://github.com/anthdm) |
|
||||||
| smartcontract | started | [@revett](https://github.com/revett) |
|
| compiler | started | [@anthdm](https://github.com/anthdm) |
|
||||||
|
| client | started | [@revett](https://github.com/revett) |
|
||||||
| cli | started | [@revett](https://github.com/revett) |
|
| cli | started | [@revett](https://github.com/revett) |
|
||||||
|
| wallet | started | [@pawanrawal](https://github.com/pawanrawal) |
|
||||||
|
|
||||||
# Getting Started
|
# Getting Started
|
||||||
|
|
||||||
## Server
|
## Node
|
||||||
|
|
||||||
Install dependencies, this requires [dep](https://github.com/golang/dep):
|
Install dependencies, this requires [dep](https://github.com/golang/dep):
|
||||||
|
|
||||||
|
@ -112,7 +110,7 @@ TODO
|
||||||
```
|
```
|
||||||
|
|
||||||
## Smart Contracts
|
## Smart Contracts
|
||||||
> NOTE: At this moment there is only a small subset of the Go language implemented.
|
> In depth documentation about the neo-go compiler and smart contract examples can be found inside the [compiler package](https://github.com/CityOfZion/neo-go/tree/master/pkg/vm/compiler).
|
||||||
|
|
||||||
### Compile a smart contract
|
### Compile a smart contract
|
||||||
|
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.16.0
|
0.17.0
|
||||||
|
|
|
@ -1,36 +1,42 @@
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/core"
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/types"
|
||||||
|
|
||||||
// T is shorthand for interface{} which allows us to use the function signatures
|
|
||||||
// in a more elegant way.
|
|
||||||
type T interface{}
|
|
||||||
|
|
||||||
// GetTrigger return the current trigger type. The return in this function
|
|
||||||
// doesn't really mather, this is just an interop placeholder.
|
|
||||||
func GetTrigger() T { return 0 }
|
|
||||||
|
|
||||||
// CheckWitness verifies if the invoker is the owner of the contract.
|
// CheckWitness verifies if the invoker is the owner of the contract.
|
||||||
func CheckWitness(hash T) T { return 0 }
|
func CheckWitness(hash []byte) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// GetCurrentBlock returns the current block.
|
// GetCurrentBlock returns the current block.
|
||||||
func GetCurrentBlock() core.Block { return core.Block{} }
|
func GetCurrentBlock() types.Block { return types.Block{} }
|
||||||
|
|
||||||
// GetTime returns the timestamp of the most recent block.
|
// GetTime returns the timestamp of the most recent block.
|
||||||
func GetTime() int { return 0 }
|
func GetTime() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// Notify an event to the VM.
|
// Notify an event to the VM.
|
||||||
func Notify(arg T) T { return 0 }
|
func Notify(arg interface{}) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// Log intructs the VM to log the given message.
|
// Log intructs the VM to log the given message.
|
||||||
func Log(message string) T { return 0 }
|
func Log(message string) int {
|
||||||
|
return 0
|
||||||
// Verification returns the verification trigger type.
|
|
||||||
func Verification() byte {
|
|
||||||
return 0x00
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application returns the application trigger type.
|
// Application returns the application trigger type.
|
||||||
func Application() byte {
|
func Application() byte {
|
||||||
return 0x10
|
return 0x10
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verification returns the verification trigger type.
|
||||||
|
func Verification() byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrigger return the current trigger type. The return in this function
|
||||||
|
// doesn't really mather, this is just an interop placeholder.
|
||||||
|
func GetTrigger() interface{} {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
9
pkg/smartcontract/types/block.go
Normal file
9
pkg/smartcontract/types/block.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
// Block represents a block in the blockchain.
|
||||||
|
type Block struct{}
|
||||||
|
|
||||||
|
// Index returns the height of the block.
|
||||||
|
func (b Block) Index() int {
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand.
|
The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand.
|
||||||
|
|
||||||
> The neo-go compiler is under very active development and will be updated on a weekly basis.
|
***NOTE:*** The neo-go compiler is under very active development and will be updated on a weekly basis. The API is likely going to chance, ***hence do not use this in production environments (mainnet)*** yet.
|
||||||
|
|
||||||
## Usage
|
For help, questions and discussion feel free to join the [City Of Zion discord](https://discordapp.com/invite/R8v48YA) and hop in the #golang channel. Or reach out to me on twitter [@anthdm](https://twitter.com/anthdm)
|
||||||
|
|
||||||
```
|
|
||||||
./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm
|
|
||||||
```
|
|
||||||
|
|
||||||
## Currently supported
|
## Currently supported
|
||||||
|
|
||||||
|
@ -19,7 +15,7 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machin
|
||||||
- types int, string, byte and booleans
|
- types int, string, byte and booleans
|
||||||
- struct types + method receives
|
- struct types + method receives
|
||||||
- functions
|
- functions
|
||||||
- composite literals `[]int, []string`
|
- composite literals `[]int, []string, []byte`
|
||||||
- basic if statements
|
- basic if statements
|
||||||
- binary expressions.
|
- binary expressions.
|
||||||
- return statements
|
- return statements
|
||||||
|
@ -41,6 +37,87 @@ Due to the limitations of the NEO virtual machine, features listed below will no
|
||||||
- goroutines
|
- goroutines
|
||||||
- multiple returns
|
- multiple returns
|
||||||
|
|
||||||
|
## Smart contract examples
|
||||||
|
|
||||||
|
### Check if the invoker of the contract is the owning address
|
||||||
|
|
||||||
|
```Golang
|
||||||
|
package mycontract
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/vm/smartcontract/runtime"
|
||||||
|
|
||||||
|
var owner = []byte{0xaf, 0x12, 0xa8, 0x68, 0x7b, 0x14, 0x94, 0x8b, 0xc4, 0xa0, 0x08, 0x12, 0x8a, 0x55, 0x0a, 0x63, 0x69, 0x5b, 0xc1, 0xa5}
|
||||||
|
|
||||||
|
func Main() bool {
|
||||||
|
isOwner := runtime.CheckWitness(owner)
|
||||||
|
|
||||||
|
if isOwner {
|
||||||
|
runtime.Log("invoker is the owner")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simple token
|
||||||
|
|
||||||
|
```Golang
|
||||||
|
package mytoken
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/smartcontract/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var owner = []byte{0xaf, 0x12, 0xa8, 0x68, 0x7b, 0x14, 0x94, 0x8b, 0xc4, 0xa0, 0x08, 0x12, 0x8a, 0x55, 0x0a, 0x63, 0x69, 0x5b, 0xc1, 0xa5}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
Name string
|
||||||
|
Symbol string
|
||||||
|
TotalSupply int
|
||||||
|
Owner []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Token) AddToCirculation(amount int) bool {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
inCirc := storage.GetInt(ctx, "in_circ")
|
||||||
|
inCirc += amount
|
||||||
|
storage.Put(ctx, "in_circ", inCirc)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func newToken() Token {
|
||||||
|
return Token{
|
||||||
|
Name: "your awesome NEO token",
|
||||||
|
Symbol: "YANT",
|
||||||
|
TotalSupply: 1000,
|
||||||
|
Owner: owner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main(operation string, args []interface{}) bool {
|
||||||
|
token := newToken()
|
||||||
|
trigger := runtime.GetTrigger()
|
||||||
|
|
||||||
|
if trigger == runtime.Verification() {
|
||||||
|
isOwner := runtime.CheckWitness(token.Owner)
|
||||||
|
if isOwner {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if trigger == runtime.Application() {
|
||||||
|
if operation == "mintTokens" {
|
||||||
|
token.AddToCirculation(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## How to report compiler bugs
|
## How to report compiler bugs
|
||||||
1. Make a proper testcase (example testcases can be found in the tests folder)
|
1. Make a proper testcase (example testcases can be found in the tests folder)
|
||||||
2. Create an issue on Github
|
2. Create an issue on Github
|
||||||
|
|
|
@ -9,6 +9,33 @@ import (
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// typeAndValueForField returns a zero initializd typeAndValue or the given type.Var.
|
||||||
|
func typeAndValueForField(fld *types.Var) types.TypeAndValue {
|
||||||
|
switch t := fld.Type().(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
switch t.Kind() {
|
||||||
|
case types.Int:
|
||||||
|
return types.TypeAndValue{
|
||||||
|
Type: t,
|
||||||
|
Value: constant.MakeInt64(0),
|
||||||
|
}
|
||||||
|
case types.String:
|
||||||
|
return types.TypeAndValue{
|
||||||
|
Type: t,
|
||||||
|
Value: constant.MakeString(""),
|
||||||
|
}
|
||||||
|
case types.Bool, types.UntypedBool:
|
||||||
|
return types.TypeAndValue{
|
||||||
|
Type: t,
|
||||||
|
Value: constant.MakeBool(false),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Fatalf("could not initialize struct field %s to zero, type: %s", fld.Name(), t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.TypeAndValue{}
|
||||||
|
}
|
||||||
|
|
||||||
// countGlobals counts the global variables in the program to add
|
// countGlobals counts the global variables in the program to add
|
||||||
// them with the stacksize of the function.
|
// them with the stacksize of the function.
|
||||||
func countGlobals(f *ast.File) (i int64) {
|
func countGlobals(f *ast.File) (i int64) {
|
||||||
|
@ -69,3 +96,59 @@ func resolveEntryPoint(entry string, pkg *loader.PackageInfo) (*ast.FuncDecl, *a
|
||||||
}
|
}
|
||||||
return main, file
|
return main, file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// indexOfStruct will return the index of the given field inside that struct.
|
||||||
|
// If the struct does not contain that field it will return -1.
|
||||||
|
func indexOfStruct(strct *types.Struct, fldName string) int {
|
||||||
|
for i := 0; i < strct.NumFields(); i++ {
|
||||||
|
if strct.Field(i).Name() == fldName {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
type funcUsage map[string]bool
|
||||||
|
|
||||||
|
func (f funcUsage) funcUsed(name string) bool {
|
||||||
|
_, ok := f[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyzeFuncUsage(pkgs map[*types.Package]*loader.PackageInfo) funcUsage {
|
||||||
|
usage := funcUsage{}
|
||||||
|
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
for _, f := range pkg.Files {
|
||||||
|
ast.Inspect(f, func(node ast.Node) bool {
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
switch t := n.Fun.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
usage[t.Name] = true
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
usage[t.Sel.Name] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return usage
|
||||||
|
}
|
||||||
|
|
||||||
|
func isByteArray(lit *ast.CompositeLit, tInfo *types.Info) bool {
|
||||||
|
if len(lit.Elts) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := tInfo.Types[lit.Elts[0]].Type.Underlying()
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
switch t.Kind() {
|
||||||
|
case types.Byte:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -89,13 +89,6 @@ func (c *codegen) emitLoadLocal(name string) {
|
||||||
emitOpcode(c.prog, vm.Opickitem)
|
emitOpcode(c.prog, vm.Opickitem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitLoadStructField(sName, fName string) {
|
|
||||||
strct := c.scope.loadStruct(sName)
|
|
||||||
pos := strct.loadField(fName)
|
|
||||||
emitInt(c.prog, int64(pos))
|
|
||||||
emitOpcode(c.prog, vm.Opickitem)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *codegen) emitStoreLocal(pos int) {
|
func (c *codegen) emitStoreLocal(pos int) {
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
emitOpcode(c.prog, vm.Odup)
|
emitOpcode(c.prog, vm.Odup)
|
||||||
|
@ -111,10 +104,13 @@ func (c *codegen) emitStoreLocal(pos int) {
|
||||||
emitOpcode(c.prog, vm.Osetitem)
|
emitOpcode(c.prog, vm.Osetitem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitStoreStructField(sName, fName string) {
|
func (c *codegen) emitLoadStructField(i int) {
|
||||||
strct := c.scope.loadStruct(sName)
|
emitInt(c.prog, int64(i))
|
||||||
pos := strct.loadField(fName)
|
emitOpcode(c.prog, vm.Opickitem)
|
||||||
emitInt(c.prog, int64(pos))
|
}
|
||||||
|
|
||||||
|
func (c *codegen) emitStoreStructField(i int) {
|
||||||
|
emitInt(c.prog, int64(i))
|
||||||
emitOpcode(c.prog, vm.Orot)
|
emitOpcode(c.prog, vm.Orot)
|
||||||
emitOpcode(c.prog, vm.Osetitem)
|
emitOpcode(c.prog, vm.Osetitem)
|
||||||
}
|
}
|
||||||
|
@ -178,11 +174,11 @@ func (c *codegen) convertFuncDecl(file *ast.File, decl *ast.FuncDecl) {
|
||||||
if decl.Recv != nil {
|
if decl.Recv != nil {
|
||||||
for _, arg := range decl.Recv.List {
|
for _, arg := range decl.Recv.List {
|
||||||
ident := arg.Names[0]
|
ident := arg.Names[0]
|
||||||
t, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct)
|
// Currently only method receives for struct types is supported.
|
||||||
|
_, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal("method receiver is not a struct type")
|
log.Fatal("method receives for non-struct types is not yet supported")
|
||||||
}
|
}
|
||||||
c.scope.newStruct(t)
|
|
||||||
l := c.scope.newLocal(ident.Name)
|
l := c.scope.newLocal(ident.Name)
|
||||||
c.emitStoreLocal(l)
|
c.emitStoreLocal(l)
|
||||||
}
|
}
|
||||||
|
@ -235,12 +231,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch n.Tok {
|
switch n.Tok {
|
||||||
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN:
|
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN:
|
||||||
c.emitLoadLocal(t.Name)
|
c.emitLoadLocal(t.Name)
|
||||||
ast.Walk(c, n.Rhs[0])
|
ast.Walk(c, n.Rhs[0]) // can only add assign to 1 expr on the RHS
|
||||||
c.convertToken(n.Tok)
|
c.convertToken(n.Tok)
|
||||||
l := c.scope.loadLocal(t.Name)
|
l := c.scope.loadLocal(t.Name)
|
||||||
c.emitStoreLocal(l)
|
c.emitStoreLocal(l)
|
||||||
default:
|
default:
|
||||||
ast.Walk(c, n.Rhs[0])
|
ast.Walk(c, n.Rhs[i])
|
||||||
l := c.scope.loadLocal(t.Name)
|
l := c.scope.loadLocal(t.Name)
|
||||||
c.emitStoreLocal(l)
|
c.emitStoreLocal(l)
|
||||||
}
|
}
|
||||||
|
@ -249,8 +245,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch expr := t.X.(type) {
|
switch expr := t.X.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
ast.Walk(c, n.Rhs[i])
|
ast.Walk(c, n.Rhs[i])
|
||||||
|
typ := c.typeInfo.ObjectOf(expr).Type().Underlying()
|
||||||
|
if strct, ok := typ.(*types.Struct); ok {
|
||||||
c.emitLoadLocal(expr.Name) // load the struct
|
c.emitLoadLocal(expr.Name) // load the struct
|
||||||
c.emitStoreStructField(expr.Name, t.Sel.Name) // store the field
|
i := indexOfStruct(strct, t.Sel.Name) // get the index of the field
|
||||||
|
c.emitStoreStructField(i) // store the field
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
log.Fatal("nested selector assigns not supported yet")
|
log.Fatal("nested selector assigns not supported yet")
|
||||||
}
|
}
|
||||||
|
@ -320,6 +320,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
typ = c.typeInfo.ObjectOf(t.Sel).Type().Underlying()
|
typ = c.typeInfo.ObjectOf(t.Sel).Type().Underlying()
|
||||||
default:
|
default:
|
||||||
ln := len(n.Elts)
|
ln := len(n.Elts)
|
||||||
|
// ByteArrays need a different approach then normal arrays.
|
||||||
|
if isByteArray(n, c.typeInfo) {
|
||||||
|
c.convertByteArray(n)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for i := ln - 1; i >= 0; i-- {
|
for i := ln - 1; i >= 0; i-- {
|
||||||
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
|
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
|
||||||
}
|
}
|
||||||
|
@ -430,17 +435,18 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
switch t := n.X.(type) {
|
switch t := n.X.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
typ := c.typeInfo.ObjectOf(t).Type().Underlying()
|
typ := c.typeInfo.ObjectOf(t).Type().Underlying()
|
||||||
switch typ.(type) {
|
if strct, ok := typ.(*types.Struct); ok {
|
||||||
case *types.Struct:
|
|
||||||
c.emitLoadLocal(t.Name) // load the struct
|
c.emitLoadLocal(t.Name) // load the struct
|
||||||
c.emitLoadStructField(t.Name, n.Sel.Name) // load the field
|
i := indexOfStruct(strct, n.Sel.Name)
|
||||||
default:
|
c.emitLoadStructField(i) // load the field
|
||||||
log.Fatal("non struct import selections not yet implemented, please use functions instead")
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Fatal("nested selectors not supported yet")
|
log.Fatal("nested selectors not supported yet")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case *ast.UnaryExpr:
|
||||||
|
// fmt.Println(n)
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -454,24 +460,33 @@ func (c *codegen) convertSyscall(name string) {
|
||||||
emitOpcode(c.prog, vm.Onop)
|
emitOpcode(c.prog, vm.Onop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
|
buf := make([]byte, len(lit.Elts))
|
||||||
|
for i := 0; i < len(lit.Elts); i++ {
|
||||||
|
t := c.typeInfo.Types[lit.Elts[i]]
|
||||||
|
val, _ := constant.Int64Val(t.Value)
|
||||||
|
buf[i] = byte(val)
|
||||||
|
}
|
||||||
|
emitBytes(c.prog, buf)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
// Create a new structScope to initialize and store
|
// Create a new structScope to initialize and store
|
||||||
// the positions of its variables.
|
// the positions of its variables.
|
||||||
t, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct)
|
strct, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatalf("the given literal is not of type struct: %v", lit)
|
log.Fatalf("the given literal is not of type struct: %v", lit)
|
||||||
}
|
}
|
||||||
strct := c.scope.newStruct(t)
|
|
||||||
|
|
||||||
emitOpcode(c.prog, vm.Onop)
|
emitOpcode(c.prog, vm.Onop)
|
||||||
emitInt(c.prog, int64(strct.t.NumFields()))
|
emitInt(c.prog, int64(strct.NumFields()))
|
||||||
emitOpcode(c.prog, vm.Onewstruct)
|
emitOpcode(c.prog, vm.Onewstruct)
|
||||||
emitOpcode(c.prog, vm.Otoaltstack)
|
emitOpcode(c.prog, vm.Otoaltstack)
|
||||||
|
|
||||||
// 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.
|
||||||
for i := 0; i < strct.t.NumFields(); i++ {
|
for i := 0; i < strct.NumFields(); i++ {
|
||||||
sField := strct.t.Field(i)
|
sField := strct.Field(i)
|
||||||
fieldAdded := false
|
fieldAdded := false
|
||||||
|
|
||||||
// Fields initialized by the program.
|
// Fields initialized by the program.
|
||||||
|
@ -481,7 +496,7 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
|
|
||||||
if sField.Name() == fieldName {
|
if sField.Name() == fieldName {
|
||||||
ast.Walk(c, f.Value)
|
ast.Walk(c, f.Value)
|
||||||
pos := strct.loadField(fieldName)
|
pos := indexOfStruct(strct, fieldName)
|
||||||
c.emitStoreLocal(pos)
|
c.emitStoreLocal(pos)
|
||||||
fieldAdded = true
|
fieldAdded = true
|
||||||
break
|
break
|
||||||
|
@ -490,7 +505,9 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
if fieldAdded {
|
if fieldAdded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.emitLoadConst(strct.typeAndValues[sField.Name()])
|
|
||||||
|
typeAndVal := typeAndValueForField(sField)
|
||||||
|
c.emitLoadConst(typeAndVal)
|
||||||
c.emitStoreLocal(i)
|
c.emitStoreLocal(i)
|
||||||
}
|
}
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.Ofromaltstack)
|
||||||
|
@ -552,6 +569,8 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
|
||||||
log.Fatal("could not find func main. did you forgot to declare it?")
|
log.Fatal("could not find func main. did you forgot to declare it?")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
funUsage := analyzeFuncUsage(info.program.AllPackages)
|
||||||
|
|
||||||
// Bring all imported functions into scope
|
// Bring all imported functions into scope
|
||||||
for _, pkg := range info.program.AllPackages {
|
for _, pkg := range info.program.AllPackages {
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
|
@ -565,11 +584,14 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
|
||||||
// Generate the code for the program
|
// Generate the code for the program
|
||||||
for _, pkg := range info.program.AllPackages {
|
for _, pkg := range info.program.AllPackages {
|
||||||
c.typeInfo = &pkg.Info
|
c.typeInfo = &pkg.Info
|
||||||
|
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
switch n := decl.(type) {
|
switch n := decl.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
if n.Name.Name != mainIdent {
|
// Dont convert the function if its not used. This will save alot
|
||||||
|
// of bytecode space.
|
||||||
|
if n.Name.Name != mainIdent && funUsage.funcUsed(n.Name.Name) {
|
||||||
c.convertFuncDecl(f, n)
|
c.convertFuncDecl(f, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/importer"
|
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
|
||||||
"go/types"
|
"go/types"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
@ -73,40 +70,6 @@ type archive struct {
|
||||||
typeInfo *types.Info
|
typeInfo *types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveImports(f *ast.File) (map[string]*archive, error) {
|
|
||||||
packages := map[string]*archive{}
|
|
||||||
for _, imp := range f.Imports {
|
|
||||||
path := strings.Replace(imp.Path.Value, `"`, "", 2)
|
|
||||||
path = filepath.Join(gopath(), "src", path)
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
pkgs, err := parser.ParseDir(fset, path, nil, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, pkg := range pkgs {
|
|
||||||
file := ast.MergePackageFiles(pkg, 0)
|
|
||||||
conf := types.Config{Importer: importer.Default()}
|
|
||||||
typeInfo := &types.Info{
|
|
||||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
|
||||||
Defs: make(map[*ast.Ident]types.Object),
|
|
||||||
Uses: make(map[*ast.Ident]types.Object),
|
|
||||||
Implicits: make(map[ast.Node]types.Object),
|
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
|
||||||
Scopes: make(map[ast.Node]*types.Scope),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Typechecker
|
|
||||||
_, err = conf.Check("", fset, []*ast.File{file}, typeInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
packages[name] = &archive{file, typeInfo}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packages, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompileAndSave will compile and save the file to disk.
|
// CompileAndSave will compile and save the file to disk.
|
||||||
func CompileAndSave(src string, o *Options) error {
|
func CompileAndSave(src string, o *Options) error {
|
||||||
if !strings.HasSuffix(src, ".go") {
|
if !strings.HasSuffix(src, ".go") {
|
||||||
|
|
|
@ -2,8 +2,6 @@ package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A funcScope represents the scope within the function context.
|
// A funcScope represents the scope within the function context.
|
||||||
|
@ -21,9 +19,6 @@ type funcScope struct {
|
||||||
// Local variables
|
// Local variables
|
||||||
locals map[string]int
|
locals map[string]int
|
||||||
|
|
||||||
// A mapping of structs positions with their scope
|
|
||||||
structs map[int]*structScope
|
|
||||||
|
|
||||||
// voidCalls are basically functions that return their value
|
// voidCalls are basically functions that return their value
|
||||||
// into nothing. The stack has their return value but there
|
// into nothing. The stack has their return value but there
|
||||||
// is nothing that consumes it. We need to keep track of
|
// is nothing that consumes it. We need to keep track of
|
||||||
|
@ -42,7 +37,6 @@ func newFuncScope(decl *ast.FuncDecl, label int) *funcScope {
|
||||||
decl: decl,
|
decl: decl,
|
||||||
label: label,
|
label: label,
|
||||||
locals: map[string]int{},
|
locals: map[string]int{},
|
||||||
structs: map[int]*structScope{},
|
|
||||||
voidCalls: map[*ast.CallExpr]bool{},
|
voidCalls: map[*ast.CallExpr]bool{},
|
||||||
i: -1,
|
i: -1,
|
||||||
}
|
}
|
||||||
|
@ -77,7 +71,9 @@ func (c *funcScope) stackSize() int64 {
|
||||||
size := 0
|
size := 0
|
||||||
ast.Inspect(c.decl, func(n ast.Node) bool {
|
ast.Inspect(c.decl, func(n ast.Node) bool {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *ast.AssignStmt, *ast.ReturnStmt, *ast.IfStmt:
|
case *ast.AssignStmt:
|
||||||
|
size += len(n.Rhs)
|
||||||
|
case *ast.ReturnStmt, *ast.IfStmt:
|
||||||
size++
|
size++
|
||||||
// This handles the inline GenDecl like "var x = 2"
|
// This handles the inline GenDecl like "var x = 2"
|
||||||
case *ast.GenDecl:
|
case *ast.GenDecl:
|
||||||
|
@ -99,21 +95,6 @@ func (c *funcScope) stackSize() int64 {
|
||||||
return int64(size + numArgs + len(c.voidCalls))
|
return int64(size + numArgs + len(c.voidCalls))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *funcScope) newStruct(t *types.Struct) *structScope {
|
|
||||||
strct := newStructScope(t)
|
|
||||||
c.structs[len(c.locals)] = strct
|
|
||||||
return strct
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *funcScope) loadStruct(name string) *structScope {
|
|
||||||
l := c.loadLocal(name)
|
|
||||||
strct, ok := c.structs[l]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("could not resolve struct %s", name)
|
|
||||||
}
|
|
||||||
return strct
|
|
||||||
}
|
|
||||||
|
|
||||||
// newLocal creates a new local variable into the scope of the function.
|
// newLocal creates a new local variable into the scope of the function.
|
||||||
func (c *funcScope) newLocal(name string) int {
|
func (c *funcScope) newLocal(name string) int {
|
||||||
c.i++
|
c.i++
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/constant"
|
|
||||||
"go/types"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A structScope holds the positions for it's fields. Struct fields have different
|
|
||||||
// positions then local variables in any scope.
|
|
||||||
type structScope struct {
|
|
||||||
// A pointer to the underlying type.
|
|
||||||
t *types.Struct
|
|
||||||
|
|
||||||
// A mapping of fieldnames identifier and its position.
|
|
||||||
fields map[string]int
|
|
||||||
|
|
||||||
// A mapping of fieldnames and with type and value.
|
|
||||||
// This will be populated in "initFields" to initialize all
|
|
||||||
// structs fields to their zero value.
|
|
||||||
// strings: "" (just a pushf 0x00)
|
|
||||||
// int: 0
|
|
||||||
// bool: false
|
|
||||||
typeAndValues map[string]types.TypeAndValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// newStructScope will create a new structScope with all fields initialized.
|
|
||||||
func newStructScope(t *types.Struct) *structScope {
|
|
||||||
s := &structScope{
|
|
||||||
fields: map[string]int{},
|
|
||||||
typeAndValues: make(map[string]types.TypeAndValue, t.NumFields()),
|
|
||||||
t: t,
|
|
||||||
}
|
|
||||||
s.initFields()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *structScope) initFields() {
|
|
||||||
var tv types.TypeAndValue
|
|
||||||
for i := 0; i < s.t.NumFields(); i++ {
|
|
||||||
f := s.t.Field(i)
|
|
||||||
s.newField(f.Name())
|
|
||||||
|
|
||||||
switch t := f.Type().(type) {
|
|
||||||
case *types.Basic:
|
|
||||||
switch t.Kind() {
|
|
||||||
case types.Int:
|
|
||||||
tv = types.TypeAndValue{
|
|
||||||
Type: t,
|
|
||||||
Value: constant.MakeInt64(0),
|
|
||||||
}
|
|
||||||
case types.String:
|
|
||||||
tv = types.TypeAndValue{
|
|
||||||
Type: t,
|
|
||||||
Value: constant.MakeString(""),
|
|
||||||
}
|
|
||||||
case types.Bool, types.UntypedBool:
|
|
||||||
tv = types.TypeAndValue{
|
|
||||||
Type: t,
|
|
||||||
Value: constant.MakeBool(false),
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Fatalf("could not initialize struct field %s to zero, type: %s", f.Name(), t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.typeAndValues[f.Name()] = tv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *structScope) newField(name string) int {
|
|
||||||
i := len(s.fields)
|
|
||||||
s.fields[name] = i
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *structScope) loadField(name string) int {
|
|
||||||
i, ok := s.fields[name]
|
|
||||||
if !ok {
|
|
||||||
log.Fatalf("could not resolve field %s for struct %v", name, s)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *structScope) initialize(t *types.Struct) {
|
|
||||||
s.t = t
|
|
||||||
for i := 0; i < t.NumFields(); i++ {
|
|
||||||
s.newField(t.Field(i).Name())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -112,4 +112,15 @@ var assignTestCases = []testCase{
|
||||||
`,
|
`,
|
||||||
"52c56b546c766b00527ac46203006c766b00c3616c7566",
|
"52c56b546c766b00527ac46203006c766b00c3616c7566",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"multi assign",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() int {
|
||||||
|
x, y := 1, 2
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b516c766b00527ac4526c766b51527ac46203006c766b00c36c766b51c393616c7566",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,4 +12,46 @@ var boolTestCases = []testCase{
|
||||||
`,
|
`,
|
||||||
"52c56b516c766b00527ac46203006c766b00c3616c7566",
|
"52c56b516c766b00527ac46203006c766b00c3616c7566",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"bool compare",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() int {
|
||||||
|
x := true
|
||||||
|
if x {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"54c56b516c766b00527ac46c766b00c3640b006203005a616c756662030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bool compare verbose",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
func Main() int {
|
||||||
|
x := true
|
||||||
|
if x == true {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"54c56b516c766b00527ac46c766b00c3519c640b006203005a616c756662030000616c7566",
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "bool invert (unary expr)",
|
||||||
|
// `
|
||||||
|
// package foo
|
||||||
|
// func Main() int {
|
||||||
|
// x := true
|
||||||
|
// if !x {
|
||||||
|
// return 10
|
||||||
|
// }
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
// `,
|
||||||
|
// "54c56b516c766b00527ac46c766b00c3630b006203005a616c756662030000616c7566",
|
||||||
|
// },
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ func TestAllCases(t *testing.T) {
|
||||||
|
|
||||||
// Blockchain specific
|
// Blockchain specific
|
||||||
testCases = append(testCases, storageTestCases...)
|
testCases = append(testCases, storageTestCases...)
|
||||||
|
testCases = append(testCases, runtimeTestCases...)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
b, err := compiler.Compile(strings.NewReader(tc.src), &compiler.Options{})
|
b, err := compiler.Compile(strings.NewReader(tc.src), &compiler.Options{})
|
||||||
|
@ -50,6 +51,7 @@ func TestAllCases(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Compare(b, expectedResult) != 0 {
|
if bytes.Compare(b, expectedResult) != 0 {
|
||||||
|
fmt.Println(tc.src)
|
||||||
t.Log(hex.EncodeToString(b))
|
t.Log(hex.EncodeToString(b))
|
||||||
dumpOpCodeSideBySide(b, expectedResult)
|
dumpOpCodeSideBySide(b, expectedResult)
|
||||||
t.Fatalf("compiling %s failed", tc.name)
|
t.Fatalf("compiling %s failed", tc.name)
|
||||||
|
|
|
@ -71,4 +71,21 @@ var ifStatementTestCases = []testCase{
|
||||||
`,
|
`,
|
||||||
"54c56b5a6c766b00527ac46c766b00c35aa2630e006c766b00c30114a1640b0062030051616c756662030000616c7566",
|
"54c56b5a6c766b00527ac46c766b00c35aa2630e006c766b00c30114a1640b0062030051616c756662030000616c7566",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"nested if statements",
|
||||||
|
`
|
||||||
|
package testcase
|
||||||
|
func Main() int {
|
||||||
|
x := 10
|
||||||
|
if x > 10 {
|
||||||
|
if x < 20 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"56c56b5a6c766b00527ac46c766b00c35aa0641e006c766b00c301149f640b0062030051616c756662030052616c756662030000616c7566",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
99
pkg/vm/compiler/tests/runtime_test.go
Normal file
99
pkg/vm/compiler/tests/runtime_test.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
var runtimeTestCases = []testCase{
|
||||||
|
{
|
||||||
|
"Notify test",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() bool {
|
||||||
|
runtime.Notify("hello")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b0568656c6c6f6168124e656f2e52756e74696d652e4e6f746966796162030051616c756652c56b6c766b00527ac462030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Log test",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() bool {
|
||||||
|
runtime.Log("hello you there!")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b1068656c6c6f20796f752074686572652161680f4e656f2e52756e74696d652e4c6f676162030051616c756652c56b6c766b00527ac462030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GetTime test",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
t := runtime.GetTime()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"52c56b6168134e656f2e52756e74696d652e47657454696d65616c766b00527ac46203006c766b00c3616c756651c56b62030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"GetTrigger test",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
trigger := runtime.GetTrigger()
|
||||||
|
if trigger == runtime.Application() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if trigger == runtime.Verification() {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"56c56b6168164e656f2e52756e74696d652e47657454726967676572616c766b00527ac46c766b00c361652c009c640b0062030051616c75666c766b00c3616523009c640b0062030052616c756662030000616c756651c56b6203000110616c756651c56b6203000100616c756651c56b62030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"check witness",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
owner := []byte{0xaf, 0x12, 0xa8, 0x68, 0x7b, 0x14, 0x94, 0x8b, 0xc4, 0xa0, 0x08, 0x12, 0x8a, 0x55, 0x0a, 0x63, 0x69, 0x5b, 0xc1, 0xa5}
|
||||||
|
isOwner := runtime.CheckWitness(owner)
|
||||||
|
if isOwner {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"55c56b14af12a8687b14948bc4a008128a550a63695bc1a56c766b00527ac46c766b00c36168184e656f2e52756e74696d652e436865636b5769746e657373616c766b51527ac46c766b51c3640b0062030051616c756662030000616c756652c56b6c766b00527ac462030000616c7566",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"getCurrentBlock",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/smartcontract/runtime"
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
block := runtime.GetCurrentBlock()
|
||||||
|
runtime.Notify(block)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b61681b4e656f2e52756e74696d652e47657443757272656e74426c6f636b616c766b00527ac46c766b00c36168124e656f2e52756e74696d652e4e6f746966796162030000616c756651c56b62030000616c756652c56b6c766b00527ac462030000616c7566",
|
||||||
|
},
|
||||||
|
}
|
|
@ -15,6 +15,6 @@ var storageTestCases = []testCase{
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
"54c56b6168164e656f2e53746f726167652e476574436f6e74657874616c766b00527ac46c766b00c306616d6f756e7402e803527261680f4e656f2e53746f726167652e507574616c766b00c306616d6f756e747c61680f4e656f2e53746f726167652e476574616c766b51527ac46203006c766b51c3616c756651c56b62030000616c756654c56b6c766b00527ac46c766b51527ac46c766b52527ac462030000616c756653c56b6c766b00527ac46c766b51527ac462030000616c756653c56b6c766b00527ac46c766b51527ac462030000616c756653c56b6c766b00527ac46c766b51527ac462030000616c7566",
|
"54c56b6168164e656f2e53746f726167652e476574436f6e74657874616c766b00527ac46c766b00c306616d6f756e7402e803527261680f4e656f2e53746f726167652e507574616c766b00c306616d6f756e747c61680f4e656f2e53746f726167652e476574616c766b51527ac46203006c766b51c3616c756651c56b62030000616c756654c56b6c766b00527ac46c766b51527ac46c766b52527ac462030000616c756653c56b6c766b00527ac46c766b51527ac462030000616c7566",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,4 +200,29 @@ var structTestCases = []testCase{
|
||||||
`,
|
`,
|
||||||
"51c56b62030061650700616c756651c56b6203006154c66b516c766b00527ac4526c766b51527ac40568656c6c6f6c766b52527ac4006c766b53527ac46c616c7566",
|
"51c56b62030061650700616c756651c56b6203006154c66b516c766b00527ac4526c766b51527ac40568656c6c6f6c766b52527ac4006c766b53527ac46c616c7566",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pass struct as argument",
|
||||||
|
`
|
||||||
|
package foo
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
amount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToAmount(x int, bar Bar) int {
|
||||||
|
bar.amount = bar.amount + x
|
||||||
|
return bar.amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() int {
|
||||||
|
b := Bar{
|
||||||
|
amount: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
x := addToAmount(4, b)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"53c56b6151c66b5a6c766b00527ac46c6c766b00527ac4546c766b00c37c616516006c766b51527ac46203006c766b51c3616c756654c56b6c766b00527ac46c766b51527ac46c766b51c300c36c766b00c3936c766b51c3007bc46203006c766b51c300c3616c7566",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,6 @@ var Syscalls = map[string]string{
|
||||||
"CheckWitness": "Neo.Runtime.CheckWitness",
|
"CheckWitness": "Neo.Runtime.CheckWitness",
|
||||||
"GetCurrentBlock": "Neo.Runtime.GetCurrentBlock",
|
"GetCurrentBlock": "Neo.Runtime.GetCurrentBlock",
|
||||||
"GetTime": "Neo.Runtime.GetTime",
|
"GetTime": "Neo.Runtime.GetTime",
|
||||||
"Notify": "Neo.runtime.Notify",
|
"Notify": "Neo.Runtime.Notify",
|
||||||
"Log": "Neo.Runtime.Log",
|
"Log": "Neo.Runtime.Log",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue