mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-27 13:58:05 +00:00
4bd5b2812e
* support VM to pass method and arguments to a script. * added support for type assertions in smartcontracts. * added native vm support for print. * moved VM API packages to vm -> API * reverted the native Print opcode in favor of runtime.Log * added support for registering custom interop hooks in the VM. * Updated README * Updated compiler with @OPTIMIZE tags * Moved more tests to VM package. * optimized and refactored compiler and vm API * updated README with new smartcontract apis * bumped version
185 lines
4 KiB
Markdown
185 lines
4 KiB
Markdown
# NEO-GO smart contract compiler
|
|
|
|
The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand.
|
|
|
|
## Currently supported
|
|
|
|
### Go internals
|
|
- type checking
|
|
- multiple assignments
|
|
- global variables
|
|
- types int, string, byte and booleans
|
|
- struct types + method receives
|
|
- functions
|
|
- composite literals `[]int, []string, []byte`
|
|
- basic if statements
|
|
- binary expressions
|
|
- return statements
|
|
- for loops
|
|
- imports
|
|
|
|
### Go builtins
|
|
- len
|
|
- append
|
|
|
|
### VM API (interop layer)
|
|
- storage
|
|
- runtime
|
|
|
|
## Not yet implemented
|
|
- range
|
|
- some parts of the interop layer (VM API)
|
|
|
|
## Not supported
|
|
Due to the limitations of the NEO virtual machine, features listed below will not be supported.
|
|
- channels
|
|
- goroutines
|
|
- multiple returns
|
|
|
|
## Quick start
|
|
|
|
### Compile a smart contract
|
|
|
|
```
|
|
./bin/neo-go contract compile -i mycontract.go
|
|
```
|
|
|
|
By default the filename will be the name of your .go file with the .avm extension, the file will be located in the same directory where your Go contract is. If you want another location for your compiled contract:
|
|
|
|
```
|
|
./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm
|
|
```
|
|
|
|
### Debugging your smart contract
|
|
You can dump the opcodes generated by the compiler with the following command:
|
|
|
|
```
|
|
./bin/neo-go contract opdump -i mycontract.go
|
|
```
|
|
|
|
This will result in something like this:
|
|
|
|
```
|
|
INDEX OPCODE DESC
|
|
0 0x52 OpPush2
|
|
1 0xc5 OpNewArray
|
|
2 0x6b OpToAltStack
|
|
3 0x 0 OpPush0
|
|
4 0x6c OpFromAltStack
|
|
5 0x76 OpDup
|
|
6 0x6b OpToAltStack
|
|
7 0x 0 OpPush0
|
|
8 0x52 OpPush2
|
|
9 0x7a OpRoll
|
|
10 0xc4 OpSetItem
|
|
```
|
|
|
|
### Test invoke a compiled contract
|
|
You can simulate a test invocation of your compiled contract by the VM, to know the total gas cost for example, with the following command:
|
|
|
|
```
|
|
./bin/neo-go contract testinvoke -i mycompiledcontract.avm
|
|
```
|
|
|
|
Will output something like:
|
|
```
|
|
{
|
|
"state": "HALT, BREAK",
|
|
"gas_consumed": "0.006",
|
|
"Stack": [
|
|
{
|
|
"type": "Integer",
|
|
"value": "9"
|
|
}
|
|
]
|
|
}
|
|
|
|
```
|
|
|
|
## 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/api/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/vm/api/runtime"
|
|
"github.com/CityOfZion/neo-go/pkg/vm/api/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.Context()
|
|
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
|
|
1. Make a proper testcase (example testcases can be found in the tests folder)
|
|
2. Create an issue on Github
|
|
3. Make a PR with a reference to the created issue, containing the testcase that proves the bug
|
|
4. Either you fix the bug yourself or wait for patch that solves the problem
|
|
|