forked from TrueCloudLab/neoneo-go
Merge pull request #292 from nspcc-dev/neo-storm-chpicks
Cherry-picks from neo-storm. Closes #290.
This commit is contained in:
commit
0574bcf1fe
61 changed files with 2351 additions and 801 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -13,6 +13,7 @@
|
||||||
# Added by CoZ developers
|
# Added by CoZ developers
|
||||||
vendor/
|
vendor/
|
||||||
bin/
|
bin/
|
||||||
|
!examples/**/vendor
|
||||||
|
|
||||||
# text editors
|
# text editors
|
||||||
# vscode
|
# vscode
|
||||||
|
@ -32,3 +33,7 @@ chains/
|
||||||
chain/
|
chain/
|
||||||
blockchain/
|
blockchain/
|
||||||
blockchains/
|
blockchains/
|
||||||
|
|
||||||
|
# patch
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
|
52
README.md
52
README.md
|
@ -126,7 +126,57 @@ ApplicationConfiguration:
|
||||||
```
|
```
|
||||||
|
|
||||||
## Writing smart contracts in Go
|
## Writing smart contracts in Go
|
||||||
Golang's development is been moved to a separate repository which you can find here [neo-storm](https://github.com/CityOfZion/neo-storm)
|
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
|
||||||
|
|
||||||
|
```
|
||||||
|
./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 you called the command from. 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 inspect -i mycontract.go
|
||||||
|
```
|
||||||
|
|
||||||
|
This will result in something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
INDEX OPCODE DESC
|
||||||
|
0 0x54 PUSH4
|
||||||
|
1 0xc5 NEWARRAY
|
||||||
|
2 0x6b TOALTSTACK
|
||||||
|
3 0x5a PUSH10
|
||||||
|
4 0x6a DUPFROMALTSTACK
|
||||||
|
5 0x0 PUSH0
|
||||||
|
6 0x52 PUSH2
|
||||||
|
7 0x7a ROLL
|
||||||
|
8 0xc4 SETITEM
|
||||||
|
9 0x6a DUPFROMALTSTACK
|
||||||
|
10 0x0 PUSH0
|
||||||
|
11 0xc3 PICKITEM
|
||||||
|
12 0x5a PUSH10
|
||||||
|
13 0xa2 GTE
|
||||||
|
14 0x64 JMPIFNOT
|
||||||
|
15 0x7 7
|
||||||
|
15 0x0 0
|
||||||
|
17 0x51 PUSH1
|
||||||
|
18 0x6c FROMALTSTACK
|
||||||
|
19 0x75 DROP
|
||||||
|
20 0x66 RET
|
||||||
|
21 0x0 PUSH0
|
||||||
|
22 0x6c FROMALTSTACK
|
||||||
|
23 0x75 DROP
|
||||||
|
24 0x66 RET
|
||||||
|
```
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,38 @@
|
||||||
package smartcontract
|
package smartcontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
errNoInput = "Input file is mandatory and should be passed using -i flag."
|
errNoInput = errors.New("No input file was found, specify an input file with the '--in or -i' flag")
|
||||||
|
errNoSmartContractName = errors.New("No name was provided, specify the '--name or -n' flag")
|
||||||
|
errFileExist = errors.New("A file with given smart-contract name already exists")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// smartContractTmpl is written to a file when used with `init` command.
|
||||||
|
// %s is parsed to be the smartContractName
|
||||||
|
smartContractTmpl = `package %s
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
|
||||||
|
func Main(op string, args []interface{}) {
|
||||||
|
runtime.Notify("Hello world!")
|
||||||
|
}`
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCommand returns a new contract command.
|
// NewCommand returns a new contract command.
|
||||||
|
@ -53,13 +72,28 @@ func NewCommand() cli.Command {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "opdump",
|
Name: "init",
|
||||||
Usage: "dump the opcode of a .go file",
|
Usage: "initialize a new smart-contract in a directory with boiler plate code",
|
||||||
Action: contractDumpOpcode,
|
Action: initSmartContract,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "name, n",
|
||||||
|
Usage: "name of the smart-contract to be initialized",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "skip-details, skip",
|
||||||
|
Usage: "skip filling in the projects and contract details",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "inspect",
|
||||||
|
Usage: "creates a user readable dump of the program instructions",
|
||||||
|
Action: inspect,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "in, i",
|
Name: "in, i",
|
||||||
Usage: "Input file for the smart contract",
|
Usage: "input file of the program",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -67,6 +101,45 @@ func NewCommand() cli.Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initSmartContract initializes a given directory with some boiler plate code.
|
||||||
|
func initSmartContract(ctx *cli.Context) error {
|
||||||
|
contractName := ctx.String("name")
|
||||||
|
if contractName == "" {
|
||||||
|
return cli.NewExitError(errNoSmartContractName, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the file already exists, if yes, exit
|
||||||
|
if _, err := os.Stat(contractName); err == nil {
|
||||||
|
return cli.NewExitError(errFileExist, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
basePath := contractName
|
||||||
|
fileName := "main.go"
|
||||||
|
|
||||||
|
// create base directory
|
||||||
|
if err := os.Mkdir(basePath, os.ModePerm); err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask contract information and write a neo-go.yml file unless the -skip-details flag is set.
|
||||||
|
// TODO: Fix the missing neo-go.yml file with the `init` command when the package manager is in place.
|
||||||
|
if !ctx.Bool("skip-details") {
|
||||||
|
details := parseContractDetails()
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(basePath, "neo-go.yml"), details.toStormFile(), 0644); err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte(fmt.Sprintf(smartContractTmpl, contractName))
|
||||||
|
if err := ioutil.WriteFile(filepath.Join(basePath, fileName), data, 0644); err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully initialized smart contract [%s]\n", contractName)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func contractCompile(ctx *cli.Context) error {
|
func contractCompile(ctx *cli.Context) error {
|
||||||
src := ctx.String("in")
|
src := ctx.String("in")
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
|
@ -99,9 +172,8 @@ func testInvoke(ctx *cli.Context) error {
|
||||||
// For now we will hardcode the endpoint.
|
// For now we will hardcode the endpoint.
|
||||||
// On the long term the internal VM will run the script.
|
// On the long term the internal VM will run the script.
|
||||||
// TODO: remove RPC dependency, hardcoded node.
|
// TODO: remove RPC dependency, hardcoded node.
|
||||||
endpoint := "http://seed5.bridgeprotocol.io:10332"
|
endpoint := "http://node1.ams2.bridgeprotocol.io:10332"
|
||||||
opts := rpc.ClientOptions{}
|
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
||||||
client, err := rpc.NewClient(context.TODO(), endpoint, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
@ -122,12 +194,67 @@ func testInvoke(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func contractDumpOpcode(ctx *cli.Context) error {
|
type ContractDetails struct {
|
||||||
|
Author string
|
||||||
|
Email string
|
||||||
|
Version string
|
||||||
|
ProjectName string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d ContractDetails) toStormFile() []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
buf.WriteString("# NEO-GO specific configuration. Do not modify this unless you know what you are doing!\n")
|
||||||
|
buf.WriteString("neo-go:\n")
|
||||||
|
buf.WriteString(" version: 1.0\n")
|
||||||
|
|
||||||
|
buf.WriteString("\n")
|
||||||
|
|
||||||
|
buf.WriteString("# Project section contains information about your smart contract\n")
|
||||||
|
buf.WriteString("project:\n")
|
||||||
|
buf.WriteString(" author: " + d.Author)
|
||||||
|
buf.WriteString(" email: " + d.Email)
|
||||||
|
buf.WriteString(" version: " + d.Version)
|
||||||
|
buf.WriteString(" name: " + d.ProjectName)
|
||||||
|
buf.WriteString(" description: " + d.Description)
|
||||||
|
|
||||||
|
buf.WriteString("\n")
|
||||||
|
|
||||||
|
buf.WriteString("# Module section contains a list of imported modules\n")
|
||||||
|
buf.WriteString("# This will be automatically managed by the neo-go package manager\n")
|
||||||
|
buf.WriteString("modules: \n")
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseContractDetails() ContractDetails {
|
||||||
|
details := ContractDetails{}
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
fmt.Print("Author: ")
|
||||||
|
details.Author, _ = reader.ReadString('\n')
|
||||||
|
|
||||||
|
fmt.Print("Email: ")
|
||||||
|
details.Email, _ = reader.ReadString('\n')
|
||||||
|
|
||||||
|
fmt.Print("Version: ")
|
||||||
|
details.Version, _ = reader.ReadString('\n')
|
||||||
|
|
||||||
|
fmt.Print("Project name: ")
|
||||||
|
details.ProjectName, _ = reader.ReadString('\n')
|
||||||
|
|
||||||
|
fmt.Print("Description: ")
|
||||||
|
details.Description, _ = reader.ReadString('\n')
|
||||||
|
|
||||||
|
return details
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspect(ctx *cli.Context) error {
|
||||||
src := ctx.String("in")
|
src := ctx.String("in")
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
return cli.NewExitError(errNoInput, 1)
|
return cli.NewExitError(errNoInput, 1)
|
||||||
}
|
}
|
||||||
if err := compiler.DumpOpcode(src); err != nil {
|
if err := compiler.CompileAndInspect(src); err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
523
docs/runtime.md
Normal file
523
docs/runtime.md
Normal file
|
@ -0,0 +1,523 @@
|
||||||
|
# Runtime
|
||||||
|
A brief overview of NEO smart contract API's that can be used in the neo-go framework.
|
||||||
|
|
||||||
|
# Overview
|
||||||
|
1. [Account]()
|
||||||
|
2. [Asset]()
|
||||||
|
3. [Attribute]()
|
||||||
|
4. [Block]()
|
||||||
|
5. [Blockchain]()
|
||||||
|
6. [Contract]()
|
||||||
|
7. [Crypto]()
|
||||||
|
8. [Engine]()
|
||||||
|
9. [Enumerator]()
|
||||||
|
10. [Iterator]()
|
||||||
|
11. [Header]()
|
||||||
|
12. [Input]()
|
||||||
|
13. [Output]()
|
||||||
|
14. [Runtime]()
|
||||||
|
15. [Storage]()
|
||||||
|
16. [Transaction]()
|
||||||
|
17. [Util]()
|
||||||
|
|
||||||
|
## Account
|
||||||
|
#### GetScriptHash
|
||||||
|
```
|
||||||
|
GetScriptHash(a Account) []byte
|
||||||
|
```
|
||||||
|
Returns the script hash of the given account.
|
||||||
|
|
||||||
|
#### GetVotes
|
||||||
|
```
|
||||||
|
GetVotes(a Account) [][]byte
|
||||||
|
```
|
||||||
|
Returns the the votes (a slice of public keys) of the given account.
|
||||||
|
|
||||||
|
#### GetBalance
|
||||||
|
```
|
||||||
|
GetBalance(a Account, assetID []byte) int
|
||||||
|
```
|
||||||
|
Returns the balance of the given asset id for the given account.
|
||||||
|
|
||||||
|
## Asset
|
||||||
|
#### GetAssetID
|
||||||
|
```
|
||||||
|
GetAssetID(a Asset) []byte
|
||||||
|
```
|
||||||
|
Returns the id of the given asset.
|
||||||
|
|
||||||
|
#### GetAmount
|
||||||
|
```
|
||||||
|
GetAmount(a Asset) int
|
||||||
|
```
|
||||||
|
Returns the amount of the given asset id.
|
||||||
|
|
||||||
|
#### GetAvailable
|
||||||
|
```
|
||||||
|
GetAvailable(a Asset) int
|
||||||
|
```
|
||||||
|
Returns the available amount of the given asset.
|
||||||
|
|
||||||
|
#### GetPrecision
|
||||||
|
```
|
||||||
|
GetPrecision(a Asset) byte
|
||||||
|
```
|
||||||
|
Returns the precision of the given Asset.
|
||||||
|
|
||||||
|
#### GetOwner
|
||||||
|
```
|
||||||
|
GetOwner(a Asset) []byte
|
||||||
|
```
|
||||||
|
Returns the owner of the given asset.
|
||||||
|
|
||||||
|
#### GetAdmin
|
||||||
|
```
|
||||||
|
GetAdmin(a Asset) []byte
|
||||||
|
```
|
||||||
|
Returns the admin of the given asset.
|
||||||
|
|
||||||
|
#### GetIssuer
|
||||||
|
```
|
||||||
|
GetIssuer(a Asset) []byte
|
||||||
|
```
|
||||||
|
Returns the issuer of the given asset.
|
||||||
|
|
||||||
|
#### Create
|
||||||
|
```
|
||||||
|
Create(type byte, name string, amount int, precision byte, owner, admin, issuer []byte)
|
||||||
|
```
|
||||||
|
Creates a new asset on the blockchain.
|
||||||
|
|
||||||
|
#### Renew
|
||||||
|
```
|
||||||
|
Renew(asset Asset, years int)
|
||||||
|
```
|
||||||
|
Renews the given asset as long as the given years.
|
||||||
|
|
||||||
|
## Attribute
|
||||||
|
#### GetUsage
|
||||||
|
```
|
||||||
|
GetUsage(attr Attribute) []byte
|
||||||
|
```
|
||||||
|
Returns the usage of the given attribute.
|
||||||
|
|
||||||
|
#### GetData
|
||||||
|
```
|
||||||
|
GetData(attr Attribute) []byte
|
||||||
|
```
|
||||||
|
Returns the data of the given attribute.
|
||||||
|
|
||||||
|
## Block
|
||||||
|
#### GetTransactionCount
|
||||||
|
```
|
||||||
|
GetTransactionCount(b Block) int
|
||||||
|
```
|
||||||
|
Returns the number of transactions that are recorded in the given block.
|
||||||
|
|
||||||
|
#### GetTransactions
|
||||||
|
```
|
||||||
|
GetTransactions(b Block) []transaction.Transaction
|
||||||
|
```
|
||||||
|
Returns a slice of the transactions that are recorded in the given block.
|
||||||
|
|
||||||
|
#### GetTransaction
|
||||||
|
```
|
||||||
|
GetTransaction(b Block, hash []byte) transaction.Transaction
|
||||||
|
```
|
||||||
|
Returns the transaction by the given hash that is recorded in the given block.
|
||||||
|
|
||||||
|
## Blockchain
|
||||||
|
#### GetHeight
|
||||||
|
```
|
||||||
|
GetHeight() int
|
||||||
|
```
|
||||||
|
Returns the current height of the blockchain.
|
||||||
|
|
||||||
|
#### GetHeader
|
||||||
|
```
|
||||||
|
GetHeader(heightOrHash []interface{}) header.Header
|
||||||
|
```
|
||||||
|
Return the header by the given hash or index.
|
||||||
|
|
||||||
|
#### GetBlock
|
||||||
|
```
|
||||||
|
GetBlock(heightOrHash interface{}) block.Block
|
||||||
|
```
|
||||||
|
Returns the block by the given hash or index.
|
||||||
|
|
||||||
|
#### GetTransaction
|
||||||
|
```
|
||||||
|
GetTransaction(hash []byte) transaction.Transaction
|
||||||
|
```
|
||||||
|
Returns a transaction by the given hash.
|
||||||
|
|
||||||
|
#### GetContract
|
||||||
|
```
|
||||||
|
GetContract(scriptHash []byte) contract.Contract
|
||||||
|
```
|
||||||
|
Returns the contract found by the given script hash.
|
||||||
|
|
||||||
|
#### GetAccount
|
||||||
|
```
|
||||||
|
GetAccount(scriptHash []byte) account.Account
|
||||||
|
```
|
||||||
|
Returns the account found by the given script hash.
|
||||||
|
|
||||||
|
#### GetValiditors
|
||||||
|
```
|
||||||
|
GetValidators() [][]byte
|
||||||
|
```
|
||||||
|
Returns a list of validators public keys.
|
||||||
|
|
||||||
|
#### GetAsset
|
||||||
|
```
|
||||||
|
GetAsset(assetID []byte) asset.Asset
|
||||||
|
```
|
||||||
|
Returns the asset found by the given asset id.
|
||||||
|
|
||||||
|
## Contract
|
||||||
|
#### GetScript
|
||||||
|
```
|
||||||
|
GetScript(c Contract) []byte
|
||||||
|
```
|
||||||
|
Return the script of the given contract.
|
||||||
|
|
||||||
|
#### IsPayable
|
||||||
|
```
|
||||||
|
IsPayable(c Contract) bool
|
||||||
|
```
|
||||||
|
Returns whether the given contract is payable.
|
||||||
|
|
||||||
|
#### GetStorageContext
|
||||||
|
```
|
||||||
|
GetStorageContext(c Contract)
|
||||||
|
```
|
||||||
|
Returns the storage context of the given contract.
|
||||||
|
|
||||||
|
#### Create
|
||||||
|
```
|
||||||
|
Create(
|
||||||
|
script []byte,
|
||||||
|
params []interface{},
|
||||||
|
returnType byte,
|
||||||
|
properties interface{},
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
author,
|
||||||
|
email,
|
||||||
|
description string)
|
||||||
|
```
|
||||||
|
Creates a new contract on the blockchain.
|
||||||
|
|
||||||
|
#### Migrate
|
||||||
|
```
|
||||||
|
Migrate(
|
||||||
|
script []byte,
|
||||||
|
params []interface{},
|
||||||
|
returnType byte,
|
||||||
|
properties interface{},
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
author,
|
||||||
|
email,
|
||||||
|
description string)
|
||||||
|
```
|
||||||
|
Migrates a contract on the blockchain.
|
||||||
|
|
||||||
|
#### Destroy
|
||||||
|
```
|
||||||
|
Destroy(c Contract)
|
||||||
|
```
|
||||||
|
Deletes the given contract from the blockchain.
|
||||||
|
|
||||||
|
## Crypto
|
||||||
|
#### SHA1
|
||||||
|
```
|
||||||
|
SHA1(data []byte) []byte
|
||||||
|
```
|
||||||
|
Computes the sha1 hash of the given bytes
|
||||||
|
|
||||||
|
#### SHA256
|
||||||
|
```
|
||||||
|
SHA256(data []byte) []byte
|
||||||
|
```
|
||||||
|
Computes the sha256 hash of the given bytes
|
||||||
|
|
||||||
|
#### Hash256
|
||||||
|
```
|
||||||
|
Hash256(data []byte) []byte
|
||||||
|
```
|
||||||
|
Computes the sha256^2 of the given data.
|
||||||
|
|
||||||
|
#### Hash160
|
||||||
|
```
|
||||||
|
Hash160(data []byte) []byte) []byte
|
||||||
|
```
|
||||||
|
Computes the ripemd160 over the sha256 hash of the given data.
|
||||||
|
|
||||||
|
## Engine
|
||||||
|
#### GetScriptContainer
|
||||||
|
```
|
||||||
|
GetScriptContainer() transaction.Transaction
|
||||||
|
```
|
||||||
|
Returns the transaction that is in the context of the VM execution.
|
||||||
|
|
||||||
|
#### GetExecutingScriptHash
|
||||||
|
```
|
||||||
|
GetExecutingScriptHash() []byte
|
||||||
|
```
|
||||||
|
Returns the script hash of the contract that is currently being executed.
|
||||||
|
|
||||||
|
#### GetCallingScriptHash
|
||||||
|
```
|
||||||
|
GetCallingScriptHash() []byte
|
||||||
|
```
|
||||||
|
Returns the script hash of the contract that has started the execution of the current script.
|
||||||
|
|
||||||
|
#### GetEntryScriptHash
|
||||||
|
```
|
||||||
|
GetEntryScriptHash() []byte
|
||||||
|
```
|
||||||
|
Returns the script hash of the contract that started the execution from the start.
|
||||||
|
|
||||||
|
## Enumerator
|
||||||
|
#### Create
|
||||||
|
```
|
||||||
|
Create(items []inteface{}) Enumerator
|
||||||
|
```
|
||||||
|
Create a enumerator from the given items.
|
||||||
|
|
||||||
|
#### Next
|
||||||
|
```
|
||||||
|
Next(e Enumerator) interface{}
|
||||||
|
```
|
||||||
|
Returns the next item from the given enumerator.
|
||||||
|
|
||||||
|
#### Value
|
||||||
|
```
|
||||||
|
Value(e Enumerator) interface{}
|
||||||
|
```
|
||||||
|
Returns the enumerator value.
|
||||||
|
|
||||||
|
## Iterator
|
||||||
|
#### Create
|
||||||
|
```
|
||||||
|
Create(items []inteface{}) Iterator
|
||||||
|
```
|
||||||
|
Creates an iterator from the given items.
|
||||||
|
|
||||||
|
#### Key
|
||||||
|
```
|
||||||
|
Key(it Iterator) interface{}
|
||||||
|
```
|
||||||
|
Return the key from the given iterator.
|
||||||
|
|
||||||
|
#### Keys
|
||||||
|
```
|
||||||
|
Keys(it Iterator) []interface{}
|
||||||
|
```
|
||||||
|
Returns the iterator's keys
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
```
|
||||||
|
Values(it Iterator) []interface{}
|
||||||
|
```
|
||||||
|
Returns the iterator's values
|
||||||
|
|
||||||
|
## Header
|
||||||
|
#### GetIndex
|
||||||
|
```
|
||||||
|
GetIndex(h Header) int
|
||||||
|
```
|
||||||
|
Returns the height of the given header.
|
||||||
|
|
||||||
|
#### GetHash
|
||||||
|
```
|
||||||
|
GetHash(h Header) []byte
|
||||||
|
```
|
||||||
|
Returns the hash of the given header.
|
||||||
|
|
||||||
|
#### GetPrevHash
|
||||||
|
```
|
||||||
|
GetPrevhash(h Header) []byte
|
||||||
|
```
|
||||||
|
Returns the previous hash of the given header.
|
||||||
|
|
||||||
|
#### GetTimestamp
|
||||||
|
```
|
||||||
|
GetTimestamp(h Header) int
|
||||||
|
```
|
||||||
|
Returns the timestamp of the given header.
|
||||||
|
|
||||||
|
#### GetVersion
|
||||||
|
```
|
||||||
|
GetVersion(h Header) int
|
||||||
|
```
|
||||||
|
Returns the version of the given header.
|
||||||
|
|
||||||
|
#### GetMerkleroot
|
||||||
|
```
|
||||||
|
GetMerkleRoot(h Header) []byte
|
||||||
|
```
|
||||||
|
Returns the merkle root of the given header.
|
||||||
|
|
||||||
|
#### GetConsensusData
|
||||||
|
```
|
||||||
|
GetConsensusData(h Header) int
|
||||||
|
```
|
||||||
|
Returns the consensus data of the given header.
|
||||||
|
|
||||||
|
#### GetNextConsensus
|
||||||
|
```
|
||||||
|
GetNextConsensus(h Header) []byte
|
||||||
|
```
|
||||||
|
Returns the next consensus of the given header.
|
||||||
|
|
||||||
|
## Input
|
||||||
|
#### GetHash
|
||||||
|
```
|
||||||
|
GetHash(in Input) []byte
|
||||||
|
```
|
||||||
|
Returns the hash field of the given input.
|
||||||
|
|
||||||
|
#### GetIndex
|
||||||
|
```
|
||||||
|
GetIndex(in Input) int
|
||||||
|
```
|
||||||
|
Returns the index field of the given input.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
#### GetAssetID
|
||||||
|
```
|
||||||
|
GetAssetId(out Output) []byte
|
||||||
|
```
|
||||||
|
Returns the asset id field of the given output.
|
||||||
|
|
||||||
|
#### GetValue
|
||||||
|
```
|
||||||
|
GetValue(out Output) int
|
||||||
|
```
|
||||||
|
Returns the value field of the given output.
|
||||||
|
|
||||||
|
#### GetScriptHash
|
||||||
|
```
|
||||||
|
GetScriptHash(out Output) []byte
|
||||||
|
```
|
||||||
|
Returns the script hash field of the given output.
|
||||||
|
|
||||||
|
## Runtime
|
||||||
|
#### CheckWitness
|
||||||
|
```
|
||||||
|
CheckWitness(hash []byte) bool
|
||||||
|
```
|
||||||
|
Verifies if the given hash is the hash of the contract owner.
|
||||||
|
|
||||||
|
#### Log
|
||||||
|
```
|
||||||
|
Log(message string)
|
||||||
|
```
|
||||||
|
Logs the given message.
|
||||||
|
|
||||||
|
#### Notify
|
||||||
|
```
|
||||||
|
Notify(args ...interface{}) int
|
||||||
|
```
|
||||||
|
Notify any number of arguments to the VM.
|
||||||
|
|
||||||
|
#### GetTime
|
||||||
|
```
|
||||||
|
GetTime() int
|
||||||
|
```
|
||||||
|
Returns the current time based on the highest block in the chain.
|
||||||
|
|
||||||
|
#### GetTrigger
|
||||||
|
```
|
||||||
|
GetTrigger() byte
|
||||||
|
```
|
||||||
|
Returns the trigger type of the execution.
|
||||||
|
|
||||||
|
#### Serialize
|
||||||
|
```
|
||||||
|
Serialize(item interface{}) []byte
|
||||||
|
```
|
||||||
|
Serialize the given stack item to a slice of bytes.
|
||||||
|
|
||||||
|
#### Deserialize
|
||||||
|
```
|
||||||
|
Deserialize(data []byte) interface{}
|
||||||
|
```
|
||||||
|
Deserializes the given data to a stack item.
|
||||||
|
|
||||||
|
## Storage
|
||||||
|
#### GetContext
|
||||||
|
```
|
||||||
|
GetContext() Context
|
||||||
|
```
|
||||||
|
Returns the current storage context.
|
||||||
|
|
||||||
|
#### Put
|
||||||
|
```
|
||||||
|
Put(ctx Context, key, value []interface{})
|
||||||
|
```
|
||||||
|
Stores the given value at the given key.
|
||||||
|
|
||||||
|
#### Get
|
||||||
|
```
|
||||||
|
Get(ctx Context, key interface{}) interface{}
|
||||||
|
```
|
||||||
|
Returns the value found at the given key.
|
||||||
|
|
||||||
|
#### Delete
|
||||||
|
```
|
||||||
|
Delete(ctx Context, key interface{})
|
||||||
|
```
|
||||||
|
Delete's the given key from storage.
|
||||||
|
|
||||||
|
#### Find
|
||||||
|
```
|
||||||
|
Find(ctx Context, key interface{}) iterator.Iterator
|
||||||
|
```
|
||||||
|
Find returns an iterator key-values that match the given key.
|
||||||
|
|
||||||
|
## Transaction
|
||||||
|
#### GetHash
|
||||||
|
```
|
||||||
|
GetHash(t Transacfion) []byte
|
||||||
|
```
|
||||||
|
Returns the hash for the given transaction.
|
||||||
|
|
||||||
|
#### GetType
|
||||||
|
```
|
||||||
|
GetType(t Transacfion) byte
|
||||||
|
```
|
||||||
|
Returns the type of the given transaction.
|
||||||
|
|
||||||
|
#### GetAttributes
|
||||||
|
```
|
||||||
|
GetAttributes(t Transacfion) []attribute.Attribute
|
||||||
|
```
|
||||||
|
Returns the attributes of the given transaction.
|
||||||
|
|
||||||
|
#### GetReferences
|
||||||
|
```
|
||||||
|
GetReferences(t Transacfion) interface{}
|
||||||
|
```
|
||||||
|
Returns the references of the given transaction.
|
||||||
|
|
||||||
|
#### GetUnspentCoins
|
||||||
|
```
|
||||||
|
GetUnspentCoins(t Transacfion) interface{}
|
||||||
|
```
|
||||||
|
Returns the unspent coins of the given transaction.
|
||||||
|
|
||||||
|
#### GetOutputs
|
||||||
|
```
|
||||||
|
GetOutputs(t Transacfion) []output.Output
|
||||||
|
```
|
||||||
|
Returns the outputs of the given transaction
|
||||||
|
|
||||||
|
#### GetInputs
|
||||||
|
```
|
||||||
|
GetInputs(t Transacfion) []input.Input
|
||||||
|
```
|
||||||
|
Returns the inputs of the given transaction
|
22
examples/engine/engine.go
Normal file
22
examples/engine/engine.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package engine_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/engine"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Main() bool {
|
||||||
|
tx := engine.GetScriptContainer()
|
||||||
|
runtime.Notify(tx)
|
||||||
|
|
||||||
|
callingScriptHash := engine.GetCallingScriptHash()
|
||||||
|
runtime.Notify(callingScriptHash)
|
||||||
|
|
||||||
|
execScriptHash := engine.GetExecutingScriptHash()
|
||||||
|
runtime.Notify(execScriptHash)
|
||||||
|
|
||||||
|
entryScriptHash := engine.GetEntryScriptHash()
|
||||||
|
runtime.Notify(entryScriptHash)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
18
examples/iterator/iterator.go
Normal file
18
examples/iterator/iterator.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package iterator_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/iterator"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Main() bool {
|
||||||
|
iter := storage.Find(storage.GetContext(), []byte("foo"))
|
||||||
|
values := iterator.Values(iter)
|
||||||
|
keys := iterator.Keys(iter)
|
||||||
|
|
||||||
|
runtime.Notify("found storage values", values)
|
||||||
|
runtime.Notify("found storage keys", keys)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
43
examples/runtime/runtime.go
Normal file
43
examples/runtime/runtime.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package runtime_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check if the invoker of the contract is the specified owner
|
||||||
|
var owner = util.FromAddress("Aej1fe4mUgou48Zzup5j8sPrE3973cJ5oz")
|
||||||
|
|
||||||
|
func Main(operation string, args []interface{}) bool {
|
||||||
|
trigger := runtime.GetTrigger()
|
||||||
|
|
||||||
|
// Log owner upon Verification trigger
|
||||||
|
if trigger == runtime.Verification() {
|
||||||
|
if runtime.CheckWitness(owner) {
|
||||||
|
runtime.Log("Verified Owner")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discerns between log and notify for this test
|
||||||
|
if trigger == runtime.Application() {
|
||||||
|
return handleOperation(operation, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleOperation(operation string, args []interface{}) bool {
|
||||||
|
if operation == "log" {
|
||||||
|
message := args[0].(string)
|
||||||
|
runtime.Log(message)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation == "notify" {
|
||||||
|
runtime.Notify(args[0])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
46
examples/storage/storage.go
Normal file
46
examples/storage/storage.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package storage_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Main(operation string, args []interface{}) interface{} {
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
// Puts value at key
|
||||||
|
if operation == "put" {
|
||||||
|
if checkArgs(args, 2) {
|
||||||
|
key := args[0].([]byte)
|
||||||
|
value := args[1].([]byte)
|
||||||
|
storage.Put(ctx, key, value)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the value at passed key
|
||||||
|
if operation == "get" {
|
||||||
|
if checkArgs(args, 1) {
|
||||||
|
key := args[0].([]byte)
|
||||||
|
return storage.Get(ctx, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the value at passed key
|
||||||
|
if operation == "delete" {
|
||||||
|
key := args[0].([]byte)
|
||||||
|
storage.Delete(ctx, key)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: storage.Find()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkArgs(args []interface{}, length int) bool {
|
||||||
|
if len(args) == length {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
Binary file not shown.
|
@ -1,9 +1,9 @@
|
||||||
package tokensale
|
package tokensale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/runtime"
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/storage"
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/util"
|
"github.com/CityOfZion/neo-go/pkg/interop/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
95
examples/token/nep5/nep5.go
Normal file
95
examples/token/nep5/nep5.go
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package nep5
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/engine"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Token holds all token info
|
||||||
|
type Token struct {
|
||||||
|
// Token name
|
||||||
|
Name string
|
||||||
|
// Ticker symbol
|
||||||
|
Symbol string
|
||||||
|
// Amount of decimals
|
||||||
|
Decimals int
|
||||||
|
// Token owner address
|
||||||
|
Owner []byte
|
||||||
|
// Total tokens * multiplier
|
||||||
|
TotalSupply int
|
||||||
|
// Storage key for circulation value
|
||||||
|
CirculationKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSupply gets the token totalSupply value from VM storage
|
||||||
|
func (t Token) GetSupply(ctx storage.Context) interface{} {
|
||||||
|
return storage.Get(ctx, t.CirculationKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceOf gets the token balance of a specific address
|
||||||
|
func (t Token) BalanceOf(ctx storage.Context, hodler []byte) interface{} {
|
||||||
|
return storage.Get(ctx, hodler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer token from one user to another
|
||||||
|
func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int) bool {
|
||||||
|
amountFrom := t.CanTransfer(ctx, from, to, amount)
|
||||||
|
if amountFrom == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if amountFrom == 0 {
|
||||||
|
storage.Delete(ctx, from)
|
||||||
|
}
|
||||||
|
|
||||||
|
if amountFrom > 0 {
|
||||||
|
diff := amountFrom - amount
|
||||||
|
storage.Put(ctx, from, diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
amountTo := storage.Get(ctx, to).(int)
|
||||||
|
totalAmountTo := amountTo + amount
|
||||||
|
storage.Put(ctx, to, totalAmountTo)
|
||||||
|
runtime.Notify("transfer", from, to, amount)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanTransfer returns the amount it can transfer
|
||||||
|
func (t Token) CanTransfer(ctx storage.Context, from []byte, to []byte, amount int) int {
|
||||||
|
if len(to) != 20 && !IsUsableAddress(from) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
amountFrom := storage.Get(ctx, from).(int)
|
||||||
|
if amountFrom < amount {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell Transfer the result is equal - special case since it uses Delete
|
||||||
|
if amountFrom == amount {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// return amountFrom value back to Transfer, reduces extra Get
|
||||||
|
return amountFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUsableAddress checks if the sender is either the correct NEO address or SC address
|
||||||
|
func IsUsableAddress(addr []byte) bool {
|
||||||
|
if len(addr) == 20 {
|
||||||
|
|
||||||
|
if runtime.CheckWitness(addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a smart contract is calling scripthash
|
||||||
|
callingScriptHash := engine.GetCallingScriptHash()
|
||||||
|
if util.Equals(callingScriptHash, addr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
70
examples/token/token.go
Normal file
70
examples/token/token.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package token_contract
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/examples/token/nep5"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
decimals = 8
|
||||||
|
multiplier = 100000000
|
||||||
|
)
|
||||||
|
|
||||||
|
var owner = util.FromAddress("AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y")
|
||||||
|
|
||||||
|
// CreateToken initializes the Token Interface for the Smart Contract to operate with
|
||||||
|
func CreateToken() nep5.Token {
|
||||||
|
return nep5.Token{
|
||||||
|
Name: "Awesome NEO Token",
|
||||||
|
Symbol: "ANT",
|
||||||
|
Decimals: decimals,
|
||||||
|
Owner: owner,
|
||||||
|
TotalSupply: 11000000 * multiplier,
|
||||||
|
CirculationKey: "TokenCirculation",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main function = contract entry
|
||||||
|
func Main(operation string, args []interface{}) interface{} {
|
||||||
|
token := CreateToken()
|
||||||
|
|
||||||
|
if operation == "name" {
|
||||||
|
return token.Name
|
||||||
|
}
|
||||||
|
if operation == "symbol" {
|
||||||
|
return token.Symbol
|
||||||
|
}
|
||||||
|
if operation == "decimals" {
|
||||||
|
return token.Decimals
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following operations need ctx
|
||||||
|
ctx := storage.GetContext()
|
||||||
|
|
||||||
|
if operation == "totalSupply" {
|
||||||
|
return token.GetSupply(ctx)
|
||||||
|
}
|
||||||
|
if operation == "balanceOf" {
|
||||||
|
hodler := args[0].([]byte)
|
||||||
|
return token.BalanceOf(ctx, hodler)
|
||||||
|
}
|
||||||
|
if operation == "transfer" && CheckArgs(args, 3) {
|
||||||
|
from := args[0].([]byte)
|
||||||
|
to := args[1].([]byte)
|
||||||
|
amount := args[2].(int)
|
||||||
|
return token.Transfer(ctx, from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckArgs checks args array against a length indicator
|
||||||
|
func CheckArgs(args []interface{}, length int) bool {
|
||||||
|
if len(args) == length {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
|
@ -35,7 +35,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
|
||||||
NextConsensus: nextConsensus,
|
NextConsensus: nextConsensus,
|
||||||
Script: &transaction.Witness{
|
Script: &transaction.Witness{
|
||||||
InvocationScript: []byte{},
|
InvocationScript: []byte{},
|
||||||
VerificationScript: []byte{byte(vm.Opusht)},
|
VerificationScript: []byte{byte(vm.PUSHT)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
|
||||||
Scripts: []*transaction.Witness{
|
Scripts: []*transaction.Witness{
|
||||||
{
|
{
|
||||||
InvocationScript: []byte{},
|
InvocationScript: []byte{},
|
||||||
VerificationScript: []byte{byte(vm.Opusht)},
|
VerificationScript: []byte{byte(vm.PUSHT)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -97,7 +97,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func governingTokenTX() *transaction.Transaction {
|
func governingTokenTX() *transaction.Transaction {
|
||||||
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opusht)})
|
admin, _ := util.Uint160FromScript([]byte{byte(vm.PUSHT)})
|
||||||
registerTX := &transaction.RegisterTX{
|
registerTX := &transaction.RegisterTX{
|
||||||
AssetType: transaction.GoverningToken,
|
AssetType: transaction.GoverningToken,
|
||||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
|
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
|
||||||
|
@ -120,7 +120,7 @@ func governingTokenTX() *transaction.Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
func utilityTokenTX() *transaction.Transaction {
|
func utilityTokenTX() *transaction.Transaction {
|
||||||
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opushf)})
|
admin, _ := util.Uint160FromScript([]byte{byte(vm.PUSHF)})
|
||||||
registerTX := &transaction.RegisterTX{
|
registerTX := &transaction.RegisterTX{
|
||||||
AssetType: transaction.UtilityToken,
|
AssetType: transaction.UtilityToken,
|
||||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",
|
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",
|
||||||
|
|
23
pkg/interop/account/account.go
Normal file
23
pkg/interop/account/account.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package account
|
||||||
|
|
||||||
|
// Package account provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Account stubs a NEO account type.
|
||||||
|
type Account struct{}
|
||||||
|
|
||||||
|
// GetScripHash returns the script hash of the given account.
|
||||||
|
func GetScriptHash(a Account) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVotes returns the votes of the given account which should be a slice of
|
||||||
|
// public key raw bytes.
|
||||||
|
func GetVotes(a Account) [][]byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBalance returns the balance of for the given account and asset id.
|
||||||
|
func GetBalance(a Account, assetID []byte) int {
|
||||||
|
return 0
|
||||||
|
}
|
53
pkg/interop/asset/asset.go
Normal file
53
pkg/interop/asset/asset.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package asset
|
||||||
|
|
||||||
|
// Package asset provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Asset stubs a NEO asset type.
|
||||||
|
type Asset struct{}
|
||||||
|
|
||||||
|
// GetAssetID returns the id of the given asset.
|
||||||
|
func GetAssetID(a Asset) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAssetType returns the type of the given asset.
|
||||||
|
func GetAssetType(a Asset) byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAmount returns the amount of the given asset.
|
||||||
|
func GetAmount(a Asset) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAvailable returns the available of the given asset.
|
||||||
|
func GetAvailable(a Asset) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrecision returns the precision of the given asset.
|
||||||
|
func GetPrecision(a Asset) byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwner returns the owner of the given asset.
|
||||||
|
func GetOwner(a Asset) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAdmin returns the admin of the given asset.
|
||||||
|
func GetAdmin(a Asset) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssuer returns the issuer of the given asset.
|
||||||
|
func GetIssuer(a Asset) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create registers a new asset on the blockchain.
|
||||||
|
func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) {}
|
||||||
|
|
||||||
|
// Renew renews the existance of an asset by the given years.
|
||||||
|
func Renew(asset Asset, years int) {}
|
17
pkg/interop/attribute/attribute.go
Normal file
17
pkg/interop/attribute/attribute.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package attribute
|
||||||
|
|
||||||
|
// Package attribute provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Attribute stubs a NEO transaction attribute type.
|
||||||
|
type Attribute struct{}
|
||||||
|
|
||||||
|
// GetUsage returns the usage of the given attribute.
|
||||||
|
func GetUsage(attr Attribute) byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetData returns the data of the given attribute.
|
||||||
|
func GetData(attr Attribute) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
25
pkg/interop/block/block.go
Normal file
25
pkg/interop/block/block.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package block
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/interop/transaction"
|
||||||
|
|
||||||
|
// Package block provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Block stubs a NEO block type.
|
||||||
|
type Block struct{}
|
||||||
|
|
||||||
|
// GetTransactionCount return the number of recorded transactions in the given block.
|
||||||
|
func GetTransactionCount(b Block) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransactions returns a slice of transactions recorded in the given block.
|
||||||
|
func GetTransactions(b Block) []transaction.Transaction {
|
||||||
|
return []transaction.Transaction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransaction returns a transaction from the given a block hash of the
|
||||||
|
// transaction.
|
||||||
|
func GetTransaction(b Block, hash []byte) transaction.Transaction {
|
||||||
|
return transaction.Transaction{}
|
||||||
|
}
|
53
pkg/interop/blockchain/blockchain.go
Normal file
53
pkg/interop/blockchain/blockchain.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/account"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/asset"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/block"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/header"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/transaction"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package blockchain provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// GetHeight returns the height of te block recorded in the current execution scope.
|
||||||
|
func GetHeight() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader returns the header found by the given hash or index.
|
||||||
|
func GetHeader(heightOrHash interface{}) header.Header {
|
||||||
|
return header.Header{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlock returns the block found by the given hash or index.
|
||||||
|
func GetBlock(heightOrHash interface{}) block.Block {
|
||||||
|
return block.Block{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransaction returns the transaction found by the given hash.
|
||||||
|
func GetTransaction(hash []byte) transaction.Transaction {
|
||||||
|
return transaction.Transaction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContract returns the contract found by the given script hash.
|
||||||
|
func GetContract(scriptHash []byte) contract.Contract {
|
||||||
|
return contract.Contract{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccount returns the account found by the given script hash.
|
||||||
|
func GetAccount(scriptHash []byte) account.Account {
|
||||||
|
return account.Account{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValidators returns a slice of validator addresses.
|
||||||
|
func GetValidators() [][]byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAsset returns the asset found by the given asset id.
|
||||||
|
func GetAsset(assetID []byte) asset.Asset {
|
||||||
|
return asset.Asset{}
|
||||||
|
}
|
55
pkg/interop/contract/contract.go
Normal file
55
pkg/interop/contract/contract.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package contract
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
|
||||||
|
// Package contract provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Contract stubs a NEO contract type.
|
||||||
|
type Contract struct{}
|
||||||
|
|
||||||
|
// GetScript returns the script of the given contract.
|
||||||
|
func GetScript(c Contract) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPayable returns whether the given contract is payable.
|
||||||
|
func IsPayable(c Contract) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorageContext returns the storage context for the given contract.
|
||||||
|
func GetStorageContext(c Contract) storage.Context {
|
||||||
|
return storage.Context{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates a new contract.
|
||||||
|
// @FIXME What is the type of the returnType here?
|
||||||
|
func Create(
|
||||||
|
script []byte,
|
||||||
|
params []interface{},
|
||||||
|
returnType byte,
|
||||||
|
properties interface{},
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
author,
|
||||||
|
email,
|
||||||
|
description string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate migrates a new contract.
|
||||||
|
// @FIXME What is the type of the returnType here?
|
||||||
|
func Migrate(
|
||||||
|
script []byte,
|
||||||
|
params []interface{},
|
||||||
|
returnType byte,
|
||||||
|
properties interface{},
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
author,
|
||||||
|
email,
|
||||||
|
description string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy deletes a contract that is registered on the blockchain.
|
||||||
|
func Destroy(c Contract) {}
|
|
@ -1,5 +1,8 @@
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
// Package crypto provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
// SHA1 computes the sha1 hash of b.
|
// SHA1 computes the sha1 hash of b.
|
||||||
func SHA1(b []byte) []byte {
|
func SHA1(b []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
|
@ -10,12 +13,12 @@ func SHA256(b []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash160 ..
|
// Hash160 computes the sha256 + ripemd160 of b.
|
||||||
func Hash160(b []byte) []byte {
|
func Hash160(b []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash256 ..
|
// Hash256 computes the sha256^2 hash of b.
|
||||||
func Hash256(b []byte) []byte {
|
func Hash256(b []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
29
pkg/interop/engine/engine.go
Normal file
29
pkg/interop/engine/engine.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/interop/transaction"
|
||||||
|
|
||||||
|
// Package engine provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// GetScriptContainer returns the transaction that is in the execution context.
|
||||||
|
func GetScriptContainer() transaction.Transaction {
|
||||||
|
return transaction.Transaction{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExecutingScriptHash returns the script hash of the contract that is
|
||||||
|
// currently being executed.
|
||||||
|
func GetExecutingScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCallingScriptHash returns the script hash of the contract that started
|
||||||
|
// the execution of the current script.
|
||||||
|
func GetCallingScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntryScriptHash returns the script hash of the contract that started the
|
||||||
|
// execution from the start.
|
||||||
|
func GetEntryScriptHash() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
29
pkg/interop/enumerator/enumerator.go
Normal file
29
pkg/interop/enumerator/enumerator.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package enumerator
|
||||||
|
|
||||||
|
// Package enumerator provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// TODO: Check enumerator use cases and add them to the examples folder.
|
||||||
|
|
||||||
|
// Enumerator stubs a NEO enumerator type.
|
||||||
|
type Enumerator struct{}
|
||||||
|
|
||||||
|
// Create creates a new enumerator from the given items.
|
||||||
|
func Create(items []interface{}) Enumerator {
|
||||||
|
return Enumerator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next item in the iteration.
|
||||||
|
func Next(e Enumerator) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the enumerator value.
|
||||||
|
func Value(e Enumerator) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concat concats the 2 given enumerators.
|
||||||
|
func Concat(a, b Enumerator) Enumerator {
|
||||||
|
return Enumerator{}
|
||||||
|
}
|
47
pkg/interop/header/header.go
Normal file
47
pkg/interop/header/header.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package header
|
||||||
|
|
||||||
|
// Package header provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Header stubs a NEO block header type.
|
||||||
|
type Header struct{}
|
||||||
|
|
||||||
|
// GetIndex returns the index of the given header.
|
||||||
|
func GetIndex(h Header) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHash returns the hash of the given header.
|
||||||
|
func GetHash(h Header) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrevHash returns the previous hash of the given header.
|
||||||
|
func GetPrevHash(h Header) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimestamp returns the timestamp of the given header.
|
||||||
|
func GetTimestamp(h Header) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVersion returns the version of the given header.
|
||||||
|
func GetVersion(h Header) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMerkleRoot returns the merkle root of the given header.
|
||||||
|
func GetMerkleRoot(h Header) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConsensusData returns the consensus data of the given header.
|
||||||
|
func GetConsensusData(h Header) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextConsensus returns the next consensus of the given header.
|
||||||
|
func GetNextConsensus(h Header) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
17
pkg/interop/input/input.go
Normal file
17
pkg/interop/input/input.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package input
|
||||||
|
|
||||||
|
// Package input provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Input stubs the input of a NEO transaction.
|
||||||
|
type Input struct{}
|
||||||
|
|
||||||
|
// GetHash returns the hash of the given input.
|
||||||
|
func GetHash(in Input) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndex returns the index of the given input.
|
||||||
|
func GetIndex(in Input) int {
|
||||||
|
return 0
|
||||||
|
}
|
28
pkg/interop/iterator/iterator.go
Normal file
28
pkg/interop/iterator/iterator.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package iterator
|
||||||
|
|
||||||
|
// Package iterator provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Iterator stubs a NEO iterator object type.
|
||||||
|
type Iterator struct{}
|
||||||
|
|
||||||
|
// Create creates an iterator from the given items.
|
||||||
|
func Create(items []interface{}) Iterator {
|
||||||
|
return Iterator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Better description for this.
|
||||||
|
// Key returns the iterator key.
|
||||||
|
func Key(it Iterator) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the iterator keys.
|
||||||
|
func Keys(it Iterator) []interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns the iterator values.
|
||||||
|
func Values(it Iterator) []interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
22
pkg/interop/output/output.go
Normal file
22
pkg/interop/output/output.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package output
|
||||||
|
|
||||||
|
// Package output provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Output stubs the output of a NEO transaction.
|
||||||
|
type Output struct{}
|
||||||
|
|
||||||
|
// GetAssetID returns the asset id of the given output.
|
||||||
|
func GetAssetID(out Output) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetValue returns the value of the given output.
|
||||||
|
func GetValue(out Output) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetScriptHash returns the script hash of the given output.
|
||||||
|
func GetScriptHash(out Output) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
48
pkg/interop/runtime/runtime.go
Normal file
48
pkg/interop/runtime/runtime.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// Package runtime provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// CheckWitness verifies if the given hash is the invoker of the contract.
|
||||||
|
func CheckWitness(hash []byte) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log instucts the VM to log the given message.
|
||||||
|
func Log(message string) {}
|
||||||
|
|
||||||
|
// Notify an event to the VM.
|
||||||
|
func Notify(arg ...interface{}) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTime returns the timestamp of the most recent block.
|
||||||
|
func GetTime() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrigger returns the smart contract invoke trigger which can be either
|
||||||
|
// verification or application.
|
||||||
|
func GetTrigger() byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application returns the Application trigger type
|
||||||
|
func Application() byte {
|
||||||
|
return 0x10
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification returns the Verification trigger type
|
||||||
|
func Verification() byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize serializes and item into a bytearray.
|
||||||
|
func Serialize(item interface{}) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize an item from a bytearray.
|
||||||
|
func Deserialize(b []byte) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
24
pkg/interop/storage/storage.go
Normal file
24
pkg/interop/storage/storage.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import "github.com/CityOfZion/neo-go/pkg/interop/iterator"
|
||||||
|
|
||||||
|
// Package storage provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Context represents the storage context
|
||||||
|
type Context struct{}
|
||||||
|
|
||||||
|
// GetContext returns the storage context
|
||||||
|
func GetContext() Context { return Context{} }
|
||||||
|
|
||||||
|
// Put value at given key
|
||||||
|
func Put(ctx Context, key interface{}, value interface{}) {}
|
||||||
|
|
||||||
|
// Get value matching given key
|
||||||
|
func Get(ctx Context, key interface{}) interface{} { return 0 }
|
||||||
|
|
||||||
|
// Delete key value pair from storage
|
||||||
|
func Delete(ctx Context, key interface{}) {}
|
||||||
|
|
||||||
|
// Find returns an iterator.Iterator over the keys that matched the given key.
|
||||||
|
func Find(ctx Context, key interface{}) iterator.Iterator { return iterator.Iterator{} }
|
50
pkg/interop/transaction/transaction.go
Normal file
50
pkg/interop/transaction/transaction.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package transaction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/attribute"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/input"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/interop/output"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Package transaction provides function signatures that can be used inside
|
||||||
|
// smart contracts that are written in the neo-go framework.
|
||||||
|
|
||||||
|
// Transaction stubs a NEO transaction type.
|
||||||
|
type Transaction struct{}
|
||||||
|
|
||||||
|
// GetHash returns the hash of the given transaction.
|
||||||
|
func GetHash(t Transaction) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetType returns the type of the given transaction.
|
||||||
|
func GetType(t Transaction) byte {
|
||||||
|
return 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttributes returns a slice of attributes for the given transaction.
|
||||||
|
func GetAttributes(t Transaction) []attribute.Attribute {
|
||||||
|
return []attribute.Attribute{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: What is the correct return type for this?
|
||||||
|
// GetReferences returns a slice of references for the given transaction.
|
||||||
|
func GetReferences(t Transaction) []interface{} {
|
||||||
|
return []interface{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: What is the correct return type for this?
|
||||||
|
// GetUnspentCoins returns the unspent coins for the given transaction.
|
||||||
|
func GetUnspentCoins(t Transaction) interface{} {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInputs returns the inputs of the given transaction.
|
||||||
|
func GetInputs(t Transaction) []input.Input {
|
||||||
|
return []input.Input{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOutputs returns the outputs of the given transaction.
|
||||||
|
func GetOutputs(t Transaction) []output.Output {
|
||||||
|
return []output.Output{}
|
||||||
|
}
|
12
pkg/interop/util/util.go
Normal file
12
pkg/interop/util/util.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
// FromAddress is an utility function that converts an NEO address to its hash.
|
||||||
|
func FromAddress(address string) []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares a with b and will return true whether a and b
|
||||||
|
// are equal.
|
||||||
|
func Equals(a, b interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ func CreateMultiSigRedeemScript(m int, publicKeys crypto.PublicKeys) ([]byte, er
|
||||||
if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil {
|
if err := vm.EmitInt(buf, int64(len(publicKeys))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := vm.EmitOpcode(buf, vm.Ocheckmultisig); err != nil {
|
if err := vm.EmitOpcode(buf, vm.CHECKMULTISIG); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
|
||||||
|
|
||||||
buf := bytes.NewBuffer(out)
|
buf := bytes.NewBuffer(out)
|
||||||
b, _ := buf.ReadByte()
|
b, _ := buf.ReadByte()
|
||||||
assert.Equal(t, vm.Opush3, vm.Opcode(b))
|
assert.Equal(t, vm.PUSH3, vm.Instruction(b))
|
||||||
|
|
||||||
for i := 0; i < len(validators); i++ {
|
for i := 0; i < len(validators); i++ {
|
||||||
b, err := util.ReadVarBytes(buf)
|
b, err := util.ReadVarBytes(buf)
|
||||||
|
@ -35,7 +35,7 @@ func TestCreateMultiSigRedeemScript(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b, _ = buf.ReadByte()
|
b, _ = buf.ReadByte()
|
||||||
assert.Equal(t, vm.Opush3, vm.Opcode(b))
|
assert.Equal(t, vm.PUSH3, vm.Instruction(b))
|
||||||
b, _ = buf.ReadByte()
|
b, _ = buf.ReadByte()
|
||||||
assert.Equal(t, vm.Ocheckmultisig, vm.Opcode(b))
|
assert.Equal(t, vm.CHECKMULTISIG, vm.Instruction(b))
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ You can invoke smart contracts with arguments. Take the following ***roll the di
|
||||||
```
|
```
|
||||||
package rollthedice
|
package rollthedice
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/vm/api/runtime"
|
import "github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
|
|
||||||
func Main(method string, args []interface{}) int {
|
func Main(method string, args []interface{}) int {
|
||||||
if method == "rollDice" {
|
if method == "rollDice" {
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package asset
|
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/core"
|
|
||||||
|
|
||||||
// GetAssetID returns the id of the given asset.
|
|
||||||
func GetAssetID(asset *core.AssetState) []byte { return nil }
|
|
||||||
|
|
||||||
// TODO: Verify if we need to return a uint8 here.
|
|
||||||
// GetAssetType returns the type of the given asset.
|
|
||||||
func GetAssetType(asset *core.AssetState) uint8 { return 0x00 }
|
|
||||||
|
|
||||||
// GetAmount returns the amount of the given asset.
|
|
||||||
func GetAmount(asset *core.AssetState) uint64 { return 0 }
|
|
||||||
|
|
||||||
// GetAvailable returns the available amount of the given asset.
|
|
||||||
func GetAvailable(asset *core.AssetState) uint64 { return 0 }
|
|
||||||
|
|
||||||
// GetPrecision returns the precision the given asset.
|
|
||||||
func GetPrecision(asset *core.AssetState) uint8 { return 0 }
|
|
||||||
|
|
||||||
// GetOwner returns the owner the given asset.
|
|
||||||
func GetOwner(asset *core.AssetState) []byte { return nil }
|
|
||||||
|
|
||||||
// GetIssuer returns the issuer the given asset.
|
|
||||||
func GetIssuer(asset *core.AssetState) []byte { return nil }
|
|
||||||
|
|
||||||
// Create a new asset specified by the given parameters.
|
|
||||||
func Create(typ uint8, name string, amount uint64, owner, admin, issuer []byte) {}
|
|
||||||
|
|
||||||
// Renew the given asset for the given x years.
|
|
||||||
func Renew(asset *core.AssetState, years uint32) {}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package block
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetTransactionCount returns the number of transactions that are recorded in
|
|
||||||
// the given block.
|
|
||||||
func GetTransactionCount(block *core.Block) int { return 0 }
|
|
||||||
|
|
||||||
// GetTransactions returns a list of transactions that are recorded in this block.
|
|
||||||
func GetTransactions(block *core.Block) []*transaction.Transaction { return nil }
|
|
||||||
|
|
||||||
// GetIndex returns the index of the given block.
|
|
||||||
func GetIndex(block *core.Block) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the hash of the given block.
|
|
||||||
func GetHash(block *core.Block) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the version of the given block.
|
|
||||||
func GetVersion(block *core.Block) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the previous hash of the given block.
|
|
||||||
func GetPrevHash(block *core.Block) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the merkle root of the given block.
|
|
||||||
func GetMerkleRoot(block *core.Block) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the timestamp of the given block.
|
|
||||||
func GetTimestamp(block *core.Block) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the next validator address of the given block.
|
|
||||||
func GetNextConsensus(block *core.Block) []byte { return nil }
|
|
||||||
|
|
||||||
// GetConsensusData returns the consensus data of the given block.
|
|
||||||
func GetConsensusData(block *core.Block) uint64 { return 0 }
|
|
||||||
|
|
||||||
// GetTransaction returns a specific transaction that is recorded in the given block
|
|
||||||
// by the given index.
|
|
||||||
func GetTransaction(block *core.Block, index int) *transaction.Transaction { return nil }
|
|
|
@ -1,24 +0,0 @@
|
||||||
package header
|
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/core"
|
|
||||||
|
|
||||||
// GetIndex returns the index of the given header.
|
|
||||||
func GetIndex(header *core.Header) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the hash of the given header.
|
|
||||||
func GetHash(header *core.Header) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the version of the given header.
|
|
||||||
func GetVersion(header *core.Header) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the previous hash of the given header.
|
|
||||||
func GetPrevHash(header *core.Header) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the merkle root of the given header.
|
|
||||||
func GetMerkleRoot(header *core.Header) []byte { return nil }
|
|
||||||
|
|
||||||
// GetHash returns the timestamp of the given header.
|
|
||||||
func GetTimestamp(header *core.Header) uint32 { return 0 }
|
|
||||||
|
|
||||||
// GetHash returns the next validator address of the given header.
|
|
||||||
func GetNextConsensus(header *core.Header) []byte { return nil }
|
|
|
@ -1,45 +0,0 @@
|
||||||
package runtime
|
|
||||||
|
|
||||||
// CheckWitness verifies if the invoker is the owner of the contract.
|
|
||||||
func CheckWitness(hash []byte) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTime returns the timestamp of the most recent block.
|
|
||||||
func GetTime() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify an event to the VM.
|
|
||||||
func Notify(arg interface{}) int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log instructs the VM to log the given message.
|
|
||||||
func Log(message string) {}
|
|
||||||
|
|
||||||
// Application returns the application trigger type.
|
|
||||||
func Application() byte {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize serializes and item into a bytearray.
|
|
||||||
func Serialize(item interface{}) []byte {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize an item from a bytearray.
|
|
||||||
func Deserialize(b []byte) interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
// Context represents the storage context.
|
|
||||||
type Context interface{}
|
|
||||||
|
|
||||||
// GetContext returns the storage context.
|
|
||||||
func GetContext() interface{} { return nil }
|
|
||||||
|
|
||||||
// Put stores a value in to the storage.
|
|
||||||
func Put(ctx interface{}, key interface{}, value interface{}) {}
|
|
||||||
|
|
||||||
// Get returns the value from the storage.
|
|
||||||
func Get(ctx interface{}, key interface{}) interface{} { return 0 }
|
|
||||||
|
|
||||||
// Delete removes a stored key value pair.
|
|
||||||
func Delete(ctx interface{}, key interface{}) {}
|
|
||||||
|
|
||||||
// Find entries somewhat matching the given key.
|
|
||||||
func Find(ctx interface{}, key interface{}) interface{} { return 0 }
|
|
|
@ -1,27 +0,0 @@
|
||||||
package transaction
|
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/core/transaction"
|
|
||||||
|
|
||||||
// GetType returns the type of the given transaction.
|
|
||||||
// TODO: Double check if the type returned should be of type uint8.
|
|
||||||
func GetType(tx *transaction.Transaction) uint8 { return 0x00 }
|
|
||||||
|
|
||||||
// GetTXHash returns the hash of the given transaction.
|
|
||||||
func GetTXHash(tx *transaction.Transaction) []byte { return nil }
|
|
||||||
|
|
||||||
// GetAttributes returns the attributes of the given transaction.
|
|
||||||
func GetAttributes(tx *transaction.Transaction) []*transaction.Attribute { return nil }
|
|
||||||
|
|
||||||
// GetInputs returns the inputs of the given transaction.
|
|
||||||
func GetInputs(tx *transaction.Transaction) []*transaction.Input { return nil }
|
|
||||||
|
|
||||||
// GetOutputs returns the outputs of the given transaction.
|
|
||||||
func GetOutputs(tx *transaction.Transaction) []*transaction.Output { return nil }
|
|
||||||
|
|
||||||
// TODO: What does this return as data type?
|
|
||||||
// GetReferences returns the outputs of the given transaction.
|
|
||||||
// func GetReferences(tx *transaction.Transaction) { }
|
|
||||||
|
|
||||||
// TODO: What does this return as data type?
|
|
||||||
// GetUnspentCoins returns the unspent coins of the given transaction.
|
|
||||||
// func GetUnspentCoins(tx *transaction.Transaction) { }
|
|
|
@ -1,9 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package util
|
|
||||||
|
|
||||||
// FromAddress returns the underlying bytes from the given address string.
|
|
||||||
func FromAddress(address string) []byte {
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -68,24 +68,39 @@ By default the filename will be the name of your .go file with the .avm extensio
|
||||||
You can dump the opcodes generated by the compiler with the following command:
|
You can dump the opcodes generated by the compiler with the following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
./bin/neo-go contract opdump -i mycontract.go
|
./bin/neo-go contract inspect -i mycontract.go
|
||||||
```
|
```
|
||||||
|
|
||||||
This will result in something like this:
|
This will result in something like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
INDEX OPCODE DESC
|
INDEX OPCODE DESC
|
||||||
0 0x52 OpPush2
|
0 0x54 PUSH4
|
||||||
1 0xc5 OpNewArray
|
1 0xc5 NEWARRAY
|
||||||
2 0x6b OpToAltStack
|
2 0x6b TOALTSTACK
|
||||||
3 0x 0 OpPush0
|
3 0x1 PUSHBYTES1
|
||||||
4 0x6c OpFromAltStack
|
3 0x2a *
|
||||||
5 0x76 OpDup
|
5 0x6a DUPFROMALTSTACK
|
||||||
6 0x6b OpToAltStack
|
6 0x0 PUSH0
|
||||||
7 0x 0 OpPush0
|
7 0x52 PUSH2
|
||||||
8 0x52 OpPush2
|
8 0x7a ROLL
|
||||||
9 0x7a OpRoll
|
9 0xc4 SETITEM
|
||||||
10 0xc4 OpSetItem
|
10 0x6a DUPFROMALTSTACK
|
||||||
|
11 0x0 PUSH0
|
||||||
|
12 0xc3 PICKITEM
|
||||||
|
13 0x5a PUSH10
|
||||||
|
14 0xa2 GTE
|
||||||
|
15 0x64 JMPIFNOT
|
||||||
|
16 0x7 7
|
||||||
|
16 0x0 0
|
||||||
|
18 0x51 PUSH1
|
||||||
|
19 0x6c FROMALTSTACK
|
||||||
|
20 0x75 DROP
|
||||||
|
21 0x66 RET
|
||||||
|
22 0x0 PUSH0
|
||||||
|
23 0x6c FROMALTSTACK
|
||||||
|
24 0x75 DROP
|
||||||
|
25 0x66 RET
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test invoke a compiled contract
|
### Test invoke a compiled contract
|
||||||
|
@ -118,8 +133,8 @@ Will output something like:
|
||||||
package mycontract
|
package mycontract
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/runtime"
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/util"
|
"github.com/CityOfZion/neo-go/pkg/interop/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg")
|
var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg")
|
||||||
|
@ -142,8 +157,8 @@ func Main() bool {
|
||||||
package mytoken
|
package mytoken
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/runtime"
|
"github.com/CityOfZion/neo-go/pkg/interop/runtime"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/storage"
|
"github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg")
|
var owner = util.FromAddress("AJX1jGfj3qPBbpAKjY527nPbnrnvSx9nCg")
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"go/types"
|
"go/types"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,14 +13,8 @@ var (
|
||||||
// Go language builtin functions and custom builtin utility functions.
|
// Go language builtin functions and custom builtin utility functions.
|
||||||
builtinFuncs = []string{
|
builtinFuncs = []string{
|
||||||
"len", "append", "SHA256",
|
"len", "append", "SHA256",
|
||||||
"SHA1", "Hash256", "Hash160", "FromAddress",
|
"SHA1", "Hash256", "Hash160",
|
||||||
}
|
"FromAddress", "Equals",
|
||||||
|
|
||||||
// VM system calls that have no return value.
|
|
||||||
noRetSyscalls = []string{
|
|
||||||
"Notify", "Log", "Put", "Register", "Delete",
|
|
||||||
"SetVotes", "ContractDestroy", "MerkleRoot", "Hash",
|
|
||||||
"PrevHash", "GetHeader",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -202,20 +195,13 @@ func isByteArray(lit *ast.CompositeLit, tInfo *types.Info) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSyscall(name string) bool {
|
func isSyscall(fun *funcScope) bool {
|
||||||
_, ok := vm.Syscalls[name]
|
if fun.selector == nil {
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// isNoRetSyscall checks if the syscall has a return value.
|
|
||||||
func isNoRetSyscall(name string) bool {
|
|
||||||
for _, s := range noRetSyscalls {
|
|
||||||
if s == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
_, ok := syscalls[fun.selector.Name][fun.name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func isStringType(t types.Type) bool {
|
func isStringType(t types.Type) bool {
|
||||||
return t.String() == "string"
|
return t.String() == "string"
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"log"
|
"log"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto"
|
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||||
|
@ -57,7 +59,7 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) {
|
||||||
switch typ := t.Type.Underlying().(type) {
|
switch typ := t.Type.Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case types.Int, types.UntypedInt:
|
case types.Int, types.UntypedInt, types.Uint:
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
emitInt(c.prog, val)
|
emitInt(c.prog, val)
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
|
@ -87,18 +89,13 @@ func (c *codegen) emitLoadLocal(name string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitLoadLocalPos(pos int) {
|
func (c *codegen) emitLoadLocalPos(pos int) {
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.DUPFROMALTSTACK)
|
||||||
emitOpcode(c.prog, vm.Odup)
|
|
||||||
emitOpcode(c.prog, vm.Otoaltstack)
|
|
||||||
|
|
||||||
emitInt(c.prog, int64(pos))
|
emitInt(c.prog, int64(pos))
|
||||||
emitOpcode(c.prog, vm.Opickitem)
|
emitOpcode(c.prog, vm.PICKITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitStoreLocal(pos int) {
|
func (c *codegen) emitStoreLocal(pos int) {
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.DUPFROMALTSTACK)
|
||||||
emitOpcode(c.prog, vm.Odup)
|
|
||||||
emitOpcode(c.prog, vm.Otoaltstack)
|
|
||||||
|
|
||||||
if pos < 0 {
|
if pos < 0 {
|
||||||
log.Fatalf("invalid position to store local: %d", pos)
|
log.Fatalf("invalid position to store local: %d", pos)
|
||||||
|
@ -106,19 +103,19 @@ func (c *codegen) emitStoreLocal(pos int) {
|
||||||
|
|
||||||
emitInt(c.prog, int64(pos))
|
emitInt(c.prog, int64(pos))
|
||||||
emitInt(c.prog, 2)
|
emitInt(c.prog, 2)
|
||||||
emitOpcode(c.prog, vm.Oroll)
|
emitOpcode(c.prog, vm.ROLL)
|
||||||
emitOpcode(c.prog, vm.Osetitem)
|
emitOpcode(c.prog, vm.SETITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitLoadField(i int) {
|
func (c *codegen) emitLoadField(i int) {
|
||||||
emitInt(c.prog, int64(i))
|
emitInt(c.prog, int64(i))
|
||||||
emitOpcode(c.prog, vm.Opickitem)
|
emitOpcode(c.prog, vm.PICKITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) emitStoreStructField(i int) {
|
func (c *codegen) emitStoreStructField(i int) {
|
||||||
emitInt(c.prog, int64(i))
|
emitInt(c.prog, int64(i))
|
||||||
emitOpcode(c.prog, vm.Orot)
|
emitOpcode(c.prog, vm.ROT)
|
||||||
emitOpcode(c.prog, vm.Osetitem)
|
emitOpcode(c.prog, vm.SETITEM)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertGlobals will traverse the AST and only convert global declarations.
|
// convertGlobals will traverse the AST and only convert global declarations.
|
||||||
|
@ -144,6 +141,10 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
|
||||||
|
|
||||||
f, ok = c.funcs[decl.Name.Name]
|
f, ok = c.funcs[decl.Name.Name]
|
||||||
if ok {
|
if ok {
|
||||||
|
// If this function is a syscall we will not convert it to bytecode.
|
||||||
|
if isSyscall(f) {
|
||||||
|
return
|
||||||
|
}
|
||||||
c.setLabel(f.label)
|
c.setLabel(f.label)
|
||||||
} else {
|
} else {
|
||||||
f = c.newFunc(decl)
|
f = c.newFunc(decl)
|
||||||
|
@ -155,8 +156,8 @@ 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, f.stackSize()+countGlobals(file))
|
emitInt(c.prog, f.stackSize()+countGlobals(file))
|
||||||
emitOpcode(c.prog, vm.Onewarray)
|
emitOpcode(c.prog, vm.NEWARRAY)
|
||||||
emitOpcode(c.prog, vm.Otoaltstack)
|
emitOpcode(c.prog, vm.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.
|
||||||
|
@ -185,7 +186,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
|
||||||
}
|
}
|
||||||
// Load in all the global variables in to the scope of the function.
|
// Load in all the global variables in to the scope of the function.
|
||||||
// This is not necessary for syscalls.
|
// This is not necessary for syscalls.
|
||||||
if !isSyscall(f.name) {
|
if !isSyscall(f) {
|
||||||
c.convertGlobals(file)
|
c.convertGlobals(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,9 +194,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, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.FROMALTSTACK)
|
||||||
emitOpcode(c.prog, vm.Odrop)
|
emitOpcode(c.prog, vm.DROP)
|
||||||
emitOpcode(c.prog, vm.Oret)
|
emitOpcode(c.prog, vm.RET)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +250,21 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
default:
|
default:
|
||||||
log.Fatal("nested selector assigns not supported yet")
|
log.Fatal("nested selector assigns not supported yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assignments to index expressions.
|
||||||
|
// slice[0] = 10
|
||||||
|
case *ast.IndexExpr:
|
||||||
|
ast.Walk(c, n.Rhs[i])
|
||||||
|
name := t.X.(*ast.Ident).Name
|
||||||
|
c.emitLoadLocal(name)
|
||||||
|
// For now storm only supports basic index operations. Hence we
|
||||||
|
// cast this to an *ast.BasicLit (1, 2 , 3)
|
||||||
|
indexStr := t.Index.(*ast.BasicLit).Value
|
||||||
|
index, err := strconv.Atoi(indexStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to convert slice index to integer")
|
||||||
|
}
|
||||||
|
c.emitStoreStructField(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -258,42 +274,39 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
log.Fatal("multiple returns not supported.")
|
log.Fatal("multiple returns not supported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// @OPTIMIZE: We could skip these 3 instructions for each return statement.
|
|
||||||
// To be backwards compatible we will put them them in.
|
|
||||||
// See issue #65 (https://github.com/CityOfZion/neo-go/issues/65)
|
|
||||||
l := c.newLabel()
|
l := c.newLabel()
|
||||||
emitJmp(c.prog, vm.Ojmp, int16(l))
|
|
||||||
c.setLabel(l)
|
c.setLabel(l)
|
||||||
|
|
||||||
if len(n.Results) > 0 {
|
if len(n.Results) > 0 {
|
||||||
ast.Walk(c, n.Results[0])
|
ast.Walk(c, n.Results[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE
|
emitOpcode(c.prog, vm.FROMALTSTACK)
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.DROP) // Cleanup the stack.
|
||||||
emitOpcode(c.prog, vm.Odrop) // Cleanup the stack.
|
emitOpcode(c.prog, vm.RET)
|
||||||
emitOpcode(c.prog, vm.Oret)
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case *ast.IfStmt:
|
case *ast.IfStmt:
|
||||||
lIf := c.newLabel()
|
lIf := c.newLabel()
|
||||||
lElse := c.newLabel()
|
lElse := c.newLabel()
|
||||||
|
lElseEnd := c.newLabel()
|
||||||
|
|
||||||
if n.Cond != nil {
|
if n.Cond != nil {
|
||||||
ast.Walk(c, n.Cond)
|
ast.Walk(c, n.Cond)
|
||||||
emitJmp(c.prog, vm.Ojmpifnot, int16(lElse))
|
emitJmp(c.prog, vm.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 {
|
||||||
// TODO: handle else statements.
|
emitJmp(c.prog, vm.JMP, int16(lElseEnd))
|
||||||
// emitJmp(c.prog, vm.Ojmp, int16(lEnd))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.setLabel(lElse)
|
c.setLabel(lElse)
|
||||||
if n.Else != nil {
|
if n.Else != nil {
|
||||||
ast.Walk(c, n.Else)
|
ast.Walk(c, n.Else)
|
||||||
}
|
}
|
||||||
|
c.setLabel(lElseEnd)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case *ast.BasicLit:
|
case *ast.BasicLit:
|
||||||
|
@ -327,7 +340,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
|
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
|
||||||
}
|
}
|
||||||
emitInt(c.prog, int64(ln))
|
emitInt(c.prog, int64(ln))
|
||||||
emitOpcode(c.prog, vm.Opack)
|
emitOpcode(c.prog, vm.PACK)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,13 +355,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, vm.Ojmpifnot, int16(len(c.l)-1))
|
emitJmp(c.prog, vm.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, vm.Ojmpif, int16(len(c.l)-2))
|
emitJmp(c.prog, vm.JMPIF, int16(len(c.l) - 3))
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -360,14 +373,33 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// example:
|
// example:
|
||||||
// const x = 10
|
// const x = 10
|
||||||
// x + 2 will results into 12
|
// x + 2 will results into 12
|
||||||
if tinfo := c.typeInfo.Types[n]; tinfo.Value != nil {
|
tinfo := c.typeInfo.Types[n]
|
||||||
|
if tinfo.Value != nil {
|
||||||
c.emitLoadConst(tinfo)
|
c.emitLoadConst(tinfo)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
ast.Walk(c, n.Y)
|
ast.Walk(c, n.Y)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case n.Op == token.ADD:
|
||||||
|
// VM has separate opcodes for number and string concatenation
|
||||||
|
if isStringType(tinfo.Type) {
|
||||||
|
emitOpcode(c.prog, vm.CAT)
|
||||||
|
} else {
|
||||||
|
emitOpcode(c.prog, vm.ADD)
|
||||||
|
}
|
||||||
|
case n.Op == token.EQL:
|
||||||
|
// VM has separate opcodes for number and string equality
|
||||||
|
if isStringType(tinfo.Type) {
|
||||||
|
emitOpcode(c.prog, vm.EQUAL)
|
||||||
|
} else {
|
||||||
|
emitOpcode(c.prog, vm.NUMEQUAL)
|
||||||
|
}
|
||||||
|
default:
|
||||||
c.convertToken(n.Op)
|
c.convertToken(n.Op)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +426,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// Dont forget to add 1 extra argument when its a method.
|
// Dont forget to add 1 extra argument when its a method.
|
||||||
numArgs++
|
numArgs++
|
||||||
}
|
}
|
||||||
|
|
||||||
f, ok = c.funcs[fun.Sel.Name]
|
f, ok = c.funcs[fun.Sel.Name]
|
||||||
|
// @FIXME this could cause runtime errors.
|
||||||
|
f.selector = fun.X.(*ast.Ident)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatalf("could not resolve function %s", fun.Sel.Name)
|
log.Fatalf("could not resolve function %s", fun.Sel.Name)
|
||||||
}
|
}
|
||||||
|
@ -414,18 +449,17 @@ 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, vm.Oswap)
|
emitOpcode(c.prog, vm.SWAP)
|
||||||
}
|
} else if numArgs == 3 {
|
||||||
if numArgs == 3 {
|
|
||||||
emitInt(c.prog, 2)
|
emitInt(c.prog, 2)
|
||||||
emitOpcode(c.prog, vm.Oxswap)
|
emitOpcode(c.prog, vm.XSWAP)
|
||||||
|
} else {
|
||||||
|
for i := 1; i < numArgs; i++ {
|
||||||
|
emitInt(c.prog, int64(i))
|
||||||
|
emitOpcode(c.prog, vm.ROLL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// c# compiler adds a NOP (0x61) before every function call. Dont think its relevant
|
|
||||||
// and we could easily removed it, but to be consistent with the original compiler I
|
|
||||||
// will put them in. ^^
|
|
||||||
emitOpcode(c.prog, vm.Onop)
|
|
||||||
|
|
||||||
// Check builtin first to avoid nil pointer on funcScope!
|
// Check builtin first to avoid nil pointer on funcScope!
|
||||||
switch {
|
switch {
|
||||||
|
@ -433,17 +467,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// Use the ident to check, builtins are not in func scopes.
|
// Use the ident to check, builtins are not in func scopes.
|
||||||
// We can be sure builtins are of type *ast.Ident.
|
// We can be sure builtins are of type *ast.Ident.
|
||||||
c.convertBuiltin(n)
|
c.convertBuiltin(n)
|
||||||
case isSyscall(f.name):
|
case isSyscall(f):
|
||||||
c.convertSyscall(f.name)
|
c.convertSyscall(f.selector.Name, f.name)
|
||||||
default:
|
default:
|
||||||
emitCall(c.prog, vm.Ocall, int16(f.label))
|
emitCall(c.prog, vm.CALL, int16(f.label))
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are not assigning this function to a variable we need to drop
|
|
||||||
// (cleanup) the top stack item. It's not a void but you get the point \o/.
|
|
||||||
if _, ok := c.scope.voidCalls[n]; ok {
|
|
||||||
emitOpcode(c.prog, vm.Odrop)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
|
@ -462,7 +491,22 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
|
|
||||||
case *ast.UnaryExpr:
|
case *ast.UnaryExpr:
|
||||||
ast.Walk(c, n.X)
|
ast.Walk(c, n.X)
|
||||||
c.convertToken(n.Op)
|
// From https://golang.org/ref/spec#Operators
|
||||||
|
// there can be only following unary operators
|
||||||
|
// "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
|
||||||
|
// of which last three are not used in SC
|
||||||
|
switch n.Op {
|
||||||
|
case token.ADD:
|
||||||
|
// +10 == 10, no need to do anything in this case
|
||||||
|
case token.SUB:
|
||||||
|
emitOpcode(c.prog, vm.NEGATE)
|
||||||
|
case token.NOT:
|
||||||
|
emitOpcode(c.prog, vm.NOT)
|
||||||
|
case token.XOR:
|
||||||
|
emitOpcode(c.prog, vm.INVERT)
|
||||||
|
default:
|
||||||
|
log.Fatalf("invalid unary operator: %s", n.Op)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case *ast.IncDecStmt:
|
case *ast.IncDecStmt:
|
||||||
|
@ -490,7 +534,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
c.emitLoadField(int(val))
|
c.emitLoadField(int(val))
|
||||||
default:
|
default:
|
||||||
ast.Walk(c, n.Index)
|
ast.Walk(c, n.Index)
|
||||||
emitOpcode(c.prog, vm.Opickitem) // just pickitem here
|
emitOpcode(c.prog, vm.PICKITEM) // just pickitem here
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -508,14 +552,14 @@ 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, vm.Ojmpifnot, int16(fend))
|
emitJmp(c.prog, vm.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)
|
||||||
ast.Walk(c, n.Post)
|
ast.Walk(c, n.Post)
|
||||||
|
|
||||||
// Jump back to condition.
|
// Jump back to condition.
|
||||||
emitJmp(c.prog, vm.Ojmp, int16(fstart))
|
emitJmp(c.prog, vm.JMP, int16(fstart))
|
||||||
c.setLabel(fend)
|
c.setLabel(fend)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -531,13 +575,16 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertSyscall(name string) {
|
func (c *codegen) convertSyscall(api, name string) {
|
||||||
api, ok := vm.Syscalls[name]
|
api, ok := syscalls[api][name]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatalf("unknown VM syscall api: %s", name)
|
log.Fatalf("unknown VM syscall api: %s", name)
|
||||||
}
|
}
|
||||||
emitSyscall(c.prog, api)
|
emitSyscall(c.prog, api)
|
||||||
emitOpcode(c.prog, vm.Onop) // @OPTIMIZE
|
|
||||||
|
// 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.
|
||||||
|
emitOpcode(c.prog, vm.NOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
|
@ -554,20 +601,22 @@ 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, vm.Osize)
|
emitOpcode(c.prog, vm.SIZE)
|
||||||
} else {
|
} else {
|
||||||
emitOpcode(c.prog, vm.Oarraysize)
|
emitOpcode(c.prog, vm.ARRAYSIZE)
|
||||||
}
|
}
|
||||||
case "append":
|
case "append":
|
||||||
emitOpcode(c.prog, vm.Oappend)
|
emitOpcode(c.prog, vm.APPEND)
|
||||||
case "SHA256":
|
case "SHA256":
|
||||||
emitOpcode(c.prog, vm.Osha256)
|
emitOpcode(c.prog, vm.SHA256)
|
||||||
case "SHA1":
|
case "SHA1":
|
||||||
emitOpcode(c.prog, vm.Osha1)
|
emitOpcode(c.prog, vm.SHA1)
|
||||||
case "Hash256":
|
case "Hash256":
|
||||||
emitOpcode(c.prog, vm.Ohash256)
|
emitOpcode(c.prog, vm.HASH256)
|
||||||
case "Hash160":
|
case "Hash160":
|
||||||
emitOpcode(c.prog, vm.Ohash160)
|
emitOpcode(c.prog, vm.HASH160)
|
||||||
|
case "Equals":
|
||||||
|
emitOpcode(c.prog, vm.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
|
||||||
|
@ -601,10 +650,10 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
log.Fatalf("the given literal is not of type struct: %v", lit)
|
log.Fatalf("the given literal is not of type struct: %v", lit)
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOpcode(c.prog, vm.Onop)
|
emitOpcode(c.prog, vm.NOP)
|
||||||
emitInt(c.prog, int64(strct.NumFields()))
|
emitInt(c.prog, int64(strct.NumFields()))
|
||||||
emitOpcode(c.prog, vm.Onewstruct)
|
emitOpcode(c.prog, vm.NEWSTRUCT)
|
||||||
emitOpcode(c.prog, vm.Otoaltstack)
|
emitOpcode(c.prog, vm.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.
|
||||||
|
@ -633,45 +682,58 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
|
||||||
c.emitLoadConst(typeAndVal)
|
c.emitLoadConst(typeAndVal)
|
||||||
c.emitStoreLocal(i)
|
c.emitStoreLocal(i)
|
||||||
}
|
}
|
||||||
emitOpcode(c.prog, vm.Ofromaltstack)
|
emitOpcode(c.prog, vm.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, vm.Oadd)
|
emitOpcode(c.prog, vm.ADD)
|
||||||
case token.SUB_ASSIGN:
|
case token.SUB_ASSIGN:
|
||||||
emitOpcode(c.prog, vm.Osub)
|
emitOpcode(c.prog, vm.SUB)
|
||||||
case token.MUL_ASSIGN:
|
case token.MUL_ASSIGN:
|
||||||
emitOpcode(c.prog, vm.Omul)
|
emitOpcode(c.prog, vm.MUL)
|
||||||
case token.QUO_ASSIGN:
|
case token.QUO_ASSIGN:
|
||||||
emitOpcode(c.prog, vm.Odiv)
|
emitOpcode(c.prog, vm.DIV)
|
||||||
case token.ADD:
|
case token.ADD:
|
||||||
emitOpcode(c.prog, vm.Oadd)
|
emitOpcode(c.prog, vm.ADD)
|
||||||
case token.SUB:
|
case token.SUB:
|
||||||
emitOpcode(c.prog, vm.Osub)
|
emitOpcode(c.prog, vm.SUB)
|
||||||
case token.MUL:
|
case token.MUL:
|
||||||
emitOpcode(c.prog, vm.Omul)
|
emitOpcode(c.prog, vm.MUL)
|
||||||
case token.QUO:
|
case token.QUO:
|
||||||
emitOpcode(c.prog, vm.Odiv)
|
emitOpcode(c.prog, vm.DIV)
|
||||||
case token.LSS:
|
case token.LSS:
|
||||||
emitOpcode(c.prog, vm.Olt)
|
emitOpcode(c.prog, vm.LT)
|
||||||
case token.LEQ:
|
case token.LEQ:
|
||||||
emitOpcode(c.prog, vm.Olte)
|
emitOpcode(c.prog, vm.LTE)
|
||||||
case token.GTR:
|
case token.GTR:
|
||||||
emitOpcode(c.prog, vm.Ogt)
|
emitOpcode(c.prog, vm.GT)
|
||||||
case token.GEQ:
|
case token.GEQ:
|
||||||
emitOpcode(c.prog, vm.Ogte)
|
emitOpcode(c.prog, vm.GTE)
|
||||||
case token.EQL:
|
case token.EQL:
|
||||||
emitOpcode(c.prog, vm.Onumequal)
|
// TODO: this is wrong (and the next one also is), see issue #294
|
||||||
|
// Changing it EQUAL is not that big of an improvement, so we're
|
||||||
|
// using NUMEQUAL for now
|
||||||
|
emitOpcode(c.prog, vm.NUMEQUAL)
|
||||||
case token.NEQ:
|
case token.NEQ:
|
||||||
emitOpcode(c.prog, vm.Onumnotequal)
|
emitOpcode(c.prog, vm.NUMNOTEQUAL)
|
||||||
case token.DEC:
|
case token.DEC:
|
||||||
emitOpcode(c.prog, vm.Odec)
|
emitOpcode(c.prog, vm.DEC)
|
||||||
case token.INC:
|
case token.INC:
|
||||||
emitOpcode(c.prog, vm.Oinc)
|
emitOpcode(c.prog, vm.INC)
|
||||||
case token.NOT:
|
case token.NOT:
|
||||||
emitOpcode(c.prog, vm.Onot)
|
emitOpcode(c.prog, vm.NOT)
|
||||||
|
case token.AND:
|
||||||
|
emitOpcode(c.prog, vm.AND)
|
||||||
|
case token.OR:
|
||||||
|
emitOpcode(c.prog, vm.OR)
|
||||||
|
case token.SHL:
|
||||||
|
emitOpcode(c.prog, vm.SHL)
|
||||||
|
case token.SHR:
|
||||||
|
emitOpcode(c.prog, vm.SHR)
|
||||||
|
case token.XOR:
|
||||||
|
emitOpcode(c.prog, vm.XOR)
|
||||||
default:
|
default:
|
||||||
log.Fatalf("compiler could not convert token: %s", tok)
|
log.Fatalf("compiler could not convert token: %s", tok)
|
||||||
}
|
}
|
||||||
|
@ -712,8 +774,16 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
|
||||||
// convert the entry point first
|
// convert the entry point first
|
||||||
c.convertFuncDecl(mainFile, main)
|
c.convertFuncDecl(mainFile, main)
|
||||||
|
|
||||||
|
// sort map keys to generate code deterministically
|
||||||
|
keys := make([]*types.Package, 0, len(info.program.AllPackages))
|
||||||
|
for p := range info.program.AllPackages {
|
||||||
|
keys = append(keys, p)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool { return keys[i].Path() < keys[j].Path() })
|
||||||
|
|
||||||
// Generate the code for the program
|
// Generate the code for the program
|
||||||
for _, pkg := range info.program.AllPackages {
|
for _, k := range keys {
|
||||||
|
pkg := info.program.AllPackages[k]
|
||||||
c.typeInfo = &pkg.Info
|
c.typeInfo = &pkg.Info
|
||||||
|
|
||||||
for _, f := range pkg.Files {
|
for _, f := range pkg.Files {
|
||||||
|
@ -750,8 +820,8 @@ func (c *codegen) writeJumps() {
|
||||||
b := c.prog.Bytes()
|
b := c.prog.Bytes()
|
||||||
for i, op := range b {
|
for i, op := range b {
|
||||||
j := i + 1
|
j := i + 1
|
||||||
switch vm.Opcode(op) {
|
switch vm.Instruction(op) {
|
||||||
case vm.Ojmp, vm.Ojmpifnot, vm.Ojmpif, vm.Ocall:
|
case vm.JMP, vm.JMPIFNOT, vm.JMPIF, vm.CALL:
|
||||||
index := int16(binary.LittleEndian.Uint16(b[j : j+2]))
|
index := int16(binary.LittleEndian.Uint16(b[j : j+2]))
|
||||||
if int(index) > len(c.l) || int(index) < 0 {
|
if int(index) > len(c.l) || int(index) < 0 {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -90,16 +90,15 @@ func CompileAndSave(src string, o *Options) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error while trying to compile smart contract file: %v", err)
|
return fmt.Errorf("Error while trying to compile smart contract file: %v", err)
|
||||||
}
|
}
|
||||||
if o.Debug {
|
|
||||||
log.Println(hex.EncodeToString(b))
|
log.Println(hex.EncodeToString(b))
|
||||||
}
|
|
||||||
|
|
||||||
out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext)
|
out := fmt.Sprintf("%s.%s", o.Outfile, o.Ext)
|
||||||
return ioutil.WriteFile(out, b, os.ModePerm)
|
return ioutil.WriteFile(out, b, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpOpcode compiles the program and dumps the opcode in a user friendly format.
|
// CompileAndInspect compiles the program and dumps the opcode in a user friendly format.
|
||||||
func DumpOpcode(src string) error {
|
func CompileAndInspect(src string) error {
|
||||||
b, err := ioutil.ReadFile(src)
|
b, err := ioutil.ReadFile(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -111,8 +110,24 @@ func DumpOpcode(src string) error {
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
|
||||||
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
fmt.Fprintln(w, "INDEX\tOPCODE\tDESC\t")
|
||||||
for i := 0; i < len(b); i++ {
|
for i := 0; i <= len(b)-1; {
|
||||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\t\n", i, b[i], vm.Opcode(b[i]))
|
instr := vm.Instruction(b[i])
|
||||||
|
if instr >= vm.PUSHBYTES1 && instr <= vm.PUSHBYTES75 {
|
||||||
|
fmt.Fprintf(w, "%d\t0x%x\t%s\t\n", i, b[i], fmt.Sprintf("PUSHBYTES%d", int(instr)))
|
||||||
|
for x := 0; x < int(instr); x++ {
|
||||||
|
fmt.Fprintf(w, "%d\t0x%x\t%s\t\n", i, b[i+1+x], string(b[i+1+x]))
|
||||||
|
}
|
||||||
|
i += int(instr) + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%d\t0x%x\t%s\t\n", i, b[i], instr)
|
||||||
|
i++
|
||||||
|
if instr == vm.JMP || instr == vm.JMPIF || instr == vm.JMPIFNOT || instr == vm.CALL {
|
||||||
|
for x := 0; x < 2; x++ {
|
||||||
|
fmt.Fprintf(w, "%d\t0x%x\t%d\t\n", i, b[i + x], b[i + x])
|
||||||
|
}
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
return nil
|
return nil
|
||||||
|
|
57
pkg/vm/compiler/compiler_test.go
Normal file
57
pkg/vm/compiler/compiler_test.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/vm/compiler"
|
||||||
|
)
|
||||||
|
|
||||||
|
const examplePath = "../../../examples"
|
||||||
|
|
||||||
|
func TestExamplesFolder(t *testing.T) {
|
||||||
|
infos, err := ioutil.ReadDir(examplePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range infos {
|
||||||
|
infos, err := ioutil.ReadDir(path.Join(examplePath, info.Name()))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(infos) == 0 {
|
||||||
|
t.Fatal("detected smart contract folder with no contract in it")
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := filterFilename(infos)
|
||||||
|
targetPath := path.Join(examplePath, info.Name(), filename)
|
||||||
|
if err := compileFile(targetPath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterFilename(infos []os.FileInfo) string {
|
||||||
|
for _, info := range infos {
|
||||||
|
if !info.IsDir() {
|
||||||
|
return info.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileFile(src string) error {
|
||||||
|
o := compiler.Options{
|
||||||
|
Outfile: "tmp/contract.avm",
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = compiler.Compile(file, &o)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -12,34 +12,34 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func emit(w *bytes.Buffer, op vm.Opcode, b []byte) error {
|
func emit(w *bytes.Buffer, instr vm.Instruction, b []byte) error {
|
||||||
if err := w.WriteByte(byte(op)); err != nil {
|
if err := w.WriteByte(byte(instr)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err := w.Write(b)
|
_, err := w.Write(b)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitOpcode(w io.ByteWriter, op vm.Opcode) error {
|
func emitOpcode(w io.ByteWriter, instr vm.Instruction) error {
|
||||||
return w.WriteByte(byte(op))
|
return w.WriteByte(byte(instr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitBool(w io.ByteWriter, ok bool) error {
|
func emitBool(w io.ByteWriter, ok bool) error {
|
||||||
if ok {
|
if ok {
|
||||||
return emitOpcode(w, vm.Opusht)
|
return emitOpcode(w, vm.PUSHT)
|
||||||
}
|
}
|
||||||
return emitOpcode(w, vm.Opushf)
|
return emitOpcode(w, vm.PUSHF)
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitInt(w *bytes.Buffer, i int64) error {
|
func emitInt(w *bytes.Buffer, i int64) error {
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
return emitOpcode(w, vm.Opushm1)
|
return emitOpcode(w, vm.PUSHM1)
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return emitOpcode(w, vm.Opushf)
|
return emitOpcode(w, vm.PUSHF)
|
||||||
}
|
}
|
||||||
if i > 0 && i < 16 {
|
if i > 0 && i < 16 {
|
||||||
val := vm.Opcode(int(vm.Opush1) - 1 + int(i))
|
val := vm.Instruction(int(vm.PUSH1) - 1 + int(i))
|
||||||
return emitOpcode(w, val)
|
return emitOpcode(w, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,18 +59,18 @@ func emitBytes(w *bytes.Buffer, b []byte) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case n <= int(vm.Opushbytes75):
|
case n <= int(vm.PUSHBYTES75):
|
||||||
return emit(w, vm.Opcode(n), b)
|
return emit(w, vm.Instruction(n), b)
|
||||||
case n < 0x100:
|
case n < 0x100:
|
||||||
err = emit(w, vm.Opushdata1, []byte{byte(n)})
|
err = emit(w, vm.PUSHDATA1, []byte{byte(n)})
|
||||||
case n < 0x10000:
|
case n < 0x10000:
|
||||||
buf := make([]byte, 2)
|
buf := make([]byte, 2)
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||||
err = emit(w, vm.Opushdata2, buf)
|
err = emit(w, vm.PUSHDATA2, buf)
|
||||||
default:
|
default:
|
||||||
buf := make([]byte, 4)
|
buf := make([]byte, 4)
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||||
err = emit(w, vm.Opushdata4, buf)
|
err = emit(w, vm.PUSHDATA4, buf)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -86,24 +86,24 @@ func emitSyscall(w *bytes.Buffer, api string) error {
|
||||||
buf := make([]byte, len(api)+1)
|
buf := make([]byte, len(api)+1)
|
||||||
buf[0] = byte(len(api))
|
buf[0] = byte(len(api))
|
||||||
copy(buf[1:], []byte(api))
|
copy(buf[1:], []byte(api))
|
||||||
return emit(w, vm.Osyscall, buf)
|
return emit(w, vm.SYSCALL, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitCall(w *bytes.Buffer, op vm.Opcode, label int16) error {
|
func emitCall(w *bytes.Buffer, instr vm.Instruction, label int16) error {
|
||||||
return emitJmp(w, op, label)
|
return emitJmp(w, instr, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
func emitJmp(w *bytes.Buffer, op vm.Opcode, label int16) error {
|
func emitJmp(w *bytes.Buffer, instr vm.Instruction, label int16) error {
|
||||||
if !isOpcodeJmp(op) {
|
if !isInstrJmp(instr) {
|
||||||
return fmt.Errorf("opcode %s is not a jump or call type", op)
|
return fmt.Errorf("opcode %s is not a jump or call type", instr)
|
||||||
}
|
}
|
||||||
buf := make([]byte, 2)
|
buf := make([]byte, 2)
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(label))
|
binary.LittleEndian.PutUint16(buf, uint16(label))
|
||||||
return emit(w, op, buf)
|
return emit(w, instr, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOpcodeJmp(op vm.Opcode) bool {
|
func isInstrJmp(instr vm.Instruction) bool {
|
||||||
if op == vm.Ojmp || op == vm.Ojmpifnot || op == vm.Ojmpif || op == vm.Ocall {
|
if instr == vm.JMP || instr == vm.JMPIFNOT || instr == vm.JMPIF || instr == vm.CALL {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -10,6 +10,10 @@ type funcScope struct {
|
||||||
// identifier of the function.
|
// identifier of the function.
|
||||||
name string
|
name string
|
||||||
|
|
||||||
|
// Selector of the function if there is any. Only functions imported
|
||||||
|
// from other packages should have a selector.
|
||||||
|
selector *ast.Ident
|
||||||
|
|
||||||
// The declaration of the function in the AST. Nil if this scope is not a function.
|
// The declaration of the function in the AST. Nil if this scope is not a function.
|
||||||
decl *ast.FuncDecl
|
decl *ast.FuncDecl
|
||||||
|
|
||||||
|
|
91
pkg/vm/compiler/syscall.go
Normal file
91
pkg/vm/compiler/syscall.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
var syscalls = map[string]map[string]string{
|
||||||
|
"storage": {
|
||||||
|
"GetContext": "Neo.Storage.GetContext",
|
||||||
|
"Put": "Neo.Storage.Put",
|
||||||
|
"Get": "Neo.Storage.Get",
|
||||||
|
"Delete": "Neo.Storage.Delete",
|
||||||
|
"Find": "Neo.Storage.Find",
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"GetTrigger": "Neo.Runtime.GetTrigger",
|
||||||
|
"CheckWitness": "Neo.Runtime.CheckWitness",
|
||||||
|
"Notify": "Neo.Runtime.Notify",
|
||||||
|
"Log": "Neo.Runtime.Log",
|
||||||
|
"GetTime": "Neo.Runtime.GetTime",
|
||||||
|
"Serialize": "Neo.Runtime.Serialize",
|
||||||
|
"Deserialize": "Neo.Runtime.Deserialize",
|
||||||
|
},
|
||||||
|
"blockchain": {
|
||||||
|
"GetHeight": "Neo.Blockchain.GetHeight",
|
||||||
|
"GetHeader": "Neo.Blockchain.GetHeader",
|
||||||
|
"GetBlock": "Neo.Blockchain.GetBlock",
|
||||||
|
"GetTransaction": "Neo.Blockchain.GetTransaction",
|
||||||
|
"GetContract": "Neo.Blockchain.GetContract",
|
||||||
|
"GetAccount": "Neo.Blockchain.GetAccount",
|
||||||
|
"GetValidators": "Neo.Blockchain.GetValidators",
|
||||||
|
"GetAsset": "Neo.Blockchain.GetAsset",
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"GetIndex": "Neo.Header.GetIndex",
|
||||||
|
"GetHash": "Neo.Header.GetHash",
|
||||||
|
"GetPrevHash": "Neo.Header.GetPrevHash",
|
||||||
|
"GetTimestamp": "Neo.Header.GetTimestamp",
|
||||||
|
"GetVersion": "Neo.Header.GetVersion",
|
||||||
|
"GetMerkleRoot": "Neo.Header.GetMerkleRoot",
|
||||||
|
"GetConsensusData": "Neo.Header.GetConsensusData",
|
||||||
|
"GetNextConsensus": "Neo.Header.GetNextConsensus",
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"GetTransactionCount": "Neo.Block.GetTransactionCount",
|
||||||
|
"GetTransactions": "Neo.Block.GetTransactions",
|
||||||
|
"GetTransaction": "Neo.Block.GetTransaction",
|
||||||
|
},
|
||||||
|
"transaction": {
|
||||||
|
"GetHash": "Neo.Transaction.GetHash",
|
||||||
|
"GetType": "Neo.Transaction.GetType",
|
||||||
|
"GetAttributes": "Neo.Transaction.GetAttributes",
|
||||||
|
"GetInputs": "Neo.Transaction.GetInputs",
|
||||||
|
"GetOutputs": "Neo.Transaction.GetOutputs",
|
||||||
|
"GetReferences": "Neo.Transaction.GetReferences",
|
||||||
|
"GetUnspentCoins": "Neo.Transaction.GetUnspentCoins",
|
||||||
|
"GetScript": "Neo.Transaction.GetScript",
|
||||||
|
},
|
||||||
|
"asset": {
|
||||||
|
"GetAssetID": "Neo.Asset.GetAssetID",
|
||||||
|
"GetAssetType": "Neo.Asset.GetAssetType",
|
||||||
|
"GetAmount": "Neo.Asset.GetAmount",
|
||||||
|
"Create": "Neo.Asset.Create",
|
||||||
|
"Renew": "Neo.Asset.Renew",
|
||||||
|
},
|
||||||
|
"contract": {
|
||||||
|
"GetScript": "Neo.Contract.GetScript",
|
||||||
|
"IsPayable": "Neo.Contract.IsPayable",
|
||||||
|
"Create": "Neo.Contract.Create",
|
||||||
|
"Destroy": "Neo.Contract.Destroy",
|
||||||
|
"Migrate": "Neo.Contract.Migrate",
|
||||||
|
"GetStorageContext": "Neo.Contract.GetStorageContext",
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"GetHash": "Neo.Input.GetHash",
|
||||||
|
"GetIndex": "Neo.Input.GetIndex",
|
||||||
|
},
|
||||||
|
"output": {
|
||||||
|
"GetAssetID": "Neo.Output.GetAssetID",
|
||||||
|
"GetValue": "Neo.Output.GetValue",
|
||||||
|
"GetScriptHash": "Neo.Output.GetScriptHash",
|
||||||
|
},
|
||||||
|
"engine": {
|
||||||
|
"GetScriptContainer": "System.ExecutionEngine.GetScriptContainer",
|
||||||
|
"GetCallingScriptHash": "System.ExecutionEngine.GetCallingScriptHash",
|
||||||
|
"GetEntryScriptHash": "System.ExecutionEngine.GetEntryScriptHash",
|
||||||
|
"GetExecutingScriptHash": "System.ExecutionEngine.GetExecutingScriptHash",
|
||||||
|
},
|
||||||
|
"iterator": {
|
||||||
|
"Create": "Neo.Iterator.Create",
|
||||||
|
"Key": "Neo.Iterator.Key",
|
||||||
|
"Keys": "Neo.Iterator.Keys",
|
||||||
|
"Values": "Neo.Iterator.Values",
|
||||||
|
},
|
||||||
|
}
|
|
@ -26,12 +26,12 @@ func NewContext(b []byte) *Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next return the next instruction to execute.
|
// Next return the next instruction to execute.
|
||||||
func (c *Context) Next() Opcode {
|
func (c *Context) Next() Instruction {
|
||||||
c.ip++
|
c.ip++
|
||||||
if c.ip >= len(c.prog) {
|
if c.ip >= len(c.prog) {
|
||||||
return Oret
|
return RET
|
||||||
}
|
}
|
||||||
return Opcode(c.prog[c.ip])
|
return Instruction(c.prog[c.ip])
|
||||||
}
|
}
|
||||||
|
|
||||||
// IP returns the absolute instruction without taking 0 into account.
|
// IP returns the absolute instruction without taking 0 into account.
|
||||||
|
@ -47,11 +47,11 @@ func (c *Context) LenInstr() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrInstr returns the current instruction and opcode.
|
// CurrInstr returns the current instruction and opcode.
|
||||||
func (c *Context) CurrInstr() (int, Opcode) {
|
func (c *Context) CurrInstr() (int, Instruction) {
|
||||||
if c.ip < 0 {
|
if c.ip < 0 {
|
||||||
return c.ip, Opcode(0x00)
|
return c.ip, Instruction(0x00)
|
||||||
}
|
}
|
||||||
return c.ip, Opcode(c.prog[c.ip])
|
return c.ip, Instruction(c.prog[c.ip])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns an new exact copy of c.
|
// Copy returns an new exact copy of c.
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Emit a VM Opcode with data to the given buffer.
|
// Emit a VM Instruction with data to the given buffer.
|
||||||
func Emit(w *bytes.Buffer, op Opcode, b []byte) error {
|
func Emit(w *bytes.Buffer, op Instruction, b []byte) error {
|
||||||
if err := w.WriteByte(byte(op)); err != nil {
|
if err := w.WriteByte(byte(op)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -20,29 +20,29 @@ func Emit(w *bytes.Buffer, op Opcode, b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitOpcode emits a single VM Opcode the given buffer.
|
// EmitOpcode emits a single VM Instruction the given buffer.
|
||||||
func EmitOpcode(w io.ByteWriter, op Opcode) error {
|
func EmitOpcode(w io.ByteWriter, op Instruction) error {
|
||||||
return w.WriteByte(byte(op))
|
return w.WriteByte(byte(op))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitBool emits a bool type the given buffer.
|
// EmitBool emits a bool type the given buffer.
|
||||||
func EmitBool(w io.ByteWriter, ok bool) error {
|
func EmitBool(w io.ByteWriter, ok bool) error {
|
||||||
if ok {
|
if ok {
|
||||||
return EmitOpcode(w, Opusht)
|
return EmitOpcode(w, PUSHT)
|
||||||
}
|
}
|
||||||
return EmitOpcode(w, Opushf)
|
return EmitOpcode(w, PUSHF)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitInt emits a int type to the given buffer.
|
// EmitInt emits a int type to the given buffer.
|
||||||
func EmitInt(w *bytes.Buffer, i int64) error {
|
func EmitInt(w *bytes.Buffer, i int64) error {
|
||||||
if i == -1 {
|
if i == -1 {
|
||||||
return EmitOpcode(w, Opushm1)
|
return EmitOpcode(w, PUSHM1)
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return EmitOpcode(w, Opushf)
|
return EmitOpcode(w, PUSHF)
|
||||||
}
|
}
|
||||||
if i > 0 && i < 16 {
|
if i > 0 && i < 16 {
|
||||||
val := Opcode(int(Opush1) - 1 + int(i))
|
val := Instruction(int(PUSH1) - 1 + int(i))
|
||||||
return EmitOpcode(w, val)
|
return EmitOpcode(w, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,18 +63,18 @@ func EmitBytes(w *bytes.Buffer, b []byte) error {
|
||||||
n = len(b)
|
n = len(b)
|
||||||
)
|
)
|
||||||
|
|
||||||
if n <= int(Opushbytes75) {
|
if n <= int(PUSHBYTES75) {
|
||||||
return Emit(w, Opcode(n), b)
|
return Emit(w, Instruction(n), b)
|
||||||
} else if n < 0x100 {
|
} else if n < 0x100 {
|
||||||
err = Emit(w, Opushdata1, []byte{byte(n)})
|
err = Emit(w, PUSHDATA1, []byte{byte(n)})
|
||||||
} else if n < 0x10000 {
|
} else if n < 0x10000 {
|
||||||
buf := make([]byte, 2)
|
buf := make([]byte, 2)
|
||||||
binary.LittleEndian.PutUint16(buf, uint16(n))
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
||||||
err = Emit(w, Opushdata2, buf)
|
err = Emit(w, PUSHDATA2, buf)
|
||||||
} else {
|
} else {
|
||||||
buf := make([]byte, 4)
|
buf := make([]byte, 4)
|
||||||
binary.LittleEndian.PutUint32(buf, uint32(n))
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
||||||
err = Emit(w, Opushdata4, buf)
|
err = Emit(w, PUSHDATA4, buf)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -92,17 +92,17 @@ func EmitSyscall(w *bytes.Buffer, api string) error {
|
||||||
buf := make([]byte, len(api)+1)
|
buf := make([]byte, len(api)+1)
|
||||||
buf[0] = byte(len(api))
|
buf[0] = byte(len(api))
|
||||||
copy(buf[1:], []byte(api))
|
copy(buf[1:], []byte(api))
|
||||||
return Emit(w, Osyscall, buf)
|
return Emit(w, SYSCALL, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitCall emits a call Opcode with label to the given buffer.
|
// EmitCall emits a call Instruction with label to the given buffer.
|
||||||
func EmitCall(w *bytes.Buffer, op Opcode, label int16) error {
|
func EmitCall(w *bytes.Buffer, op Instruction, label int16) error {
|
||||||
return EmitJmp(w, op, label)
|
return EmitJmp(w, op, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmitJmp emits a jump Opcode along with label to the given buffer.
|
// EmitJmp emits a jump Instruction along with label to the given buffer.
|
||||||
func EmitJmp(w *bytes.Buffer, op Opcode, label int16) error {
|
func EmitJmp(w *bytes.Buffer, op Instruction, label int16) error {
|
||||||
if !isOpcodeJmp(op) {
|
if !isInstructionJmp(op) {
|
||||||
return fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
return fmt.Errorf("opcode %s is not a jump or call type", op.String())
|
||||||
}
|
}
|
||||||
buf := make([]byte, 2)
|
buf := make([]byte, 2)
|
||||||
|
@ -113,9 +113,9 @@ func EmitJmp(w *bytes.Buffer, op Opcode, label int16) error {
|
||||||
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
// EmitAppCall emits an appcall, if tailCall is true, tailCall opcode will be
|
||||||
// emitted instead.
|
// emitted instead.
|
||||||
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
func EmitAppCall(w *bytes.Buffer, scriptHash util.Uint160, tailCall bool) error {
|
||||||
op := Oappcall
|
op := APPCALL
|
||||||
if tailCall {
|
if tailCall {
|
||||||
op = Otailcall
|
op = TAILCALL
|
||||||
}
|
}
|
||||||
return Emit(w, op, scriptHash.Bytes())
|
return Emit(w, op, scriptHash.Bytes())
|
||||||
}
|
}
|
||||||
|
@ -142,8 +142,8 @@ func EmitAppCallWithOperation(w *bytes.Buffer, scriptHash util.Uint160, operatio
|
||||||
return EmitAppCall(w, scriptHash, false)
|
return EmitAppCall(w, scriptHash, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOpcodeJmp(op Opcode) bool {
|
func isInstructionJmp(op Instruction) bool {
|
||||||
if op == Ojmp || op == Ojmpifnot || op == Ojmpif || op == Ocall {
|
if op == JMP || op == JMPIFNOT || op == JMPIF || op == CALL {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func TestEmitInt(t *testing.T) {
|
func TestEmitInt(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitInt(buf, 10)
|
EmitInt(buf, 10)
|
||||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush10)
|
assert.Equal(t, Instruction(buf.Bytes()[0]), PUSH10)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
EmitInt(buf, 100)
|
EmitInt(buf, 100)
|
||||||
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
assert.Equal(t, buf.Bytes()[0], uint8(1))
|
||||||
|
@ -26,8 +26,8 @@ func TestEmitBool(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitBool(buf, true)
|
EmitBool(buf, true)
|
||||||
EmitBool(buf, false)
|
EmitBool(buf, false)
|
||||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Opush1)
|
assert.Equal(t, Instruction(buf.Bytes()[0]), PUSH1)
|
||||||
assert.Equal(t, Opcode(buf.Bytes()[1]), Opush0)
|
assert.Equal(t, Instruction(buf.Bytes()[1]), PUSH0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmitString(t *testing.T) {
|
func TestEmitString(t *testing.T) {
|
||||||
|
@ -48,7 +48,7 @@ func TestEmitSyscall(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
for _, syscall := range syscalls {
|
for _, syscall := range syscalls {
|
||||||
EmitSyscall(buf, syscall)
|
EmitSyscall(buf, syscall)
|
||||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Osyscall)
|
assert.Equal(t, Instruction(buf.Bytes()[0]), SYSCALL)
|
||||||
assert.Equal(t, buf.Bytes()[1], uint8(len(syscall)))
|
assert.Equal(t, buf.Bytes()[1], uint8(len(syscall)))
|
||||||
assert.Equal(t, buf.Bytes()[2:], []byte(syscall))
|
assert.Equal(t, buf.Bytes()[2:], []byte(syscall))
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
|
@ -57,8 +57,8 @@ func TestEmitSyscall(t *testing.T) {
|
||||||
|
|
||||||
func TestEmitCall(t *testing.T) {
|
func TestEmitCall(t *testing.T) {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitCall(buf, Ojmp, 100)
|
EmitCall(buf, JMP, 100)
|
||||||
assert.Equal(t, Opcode(buf.Bytes()[0]), Ojmp)
|
assert.Equal(t, Instruction(buf.Bytes()[0]), JMP)
|
||||||
label := binary.LittleEndian.Uint16(buf.Bytes()[1:3])
|
label := binary.LittleEndian.Uint16(buf.Bytes()[1:3])
|
||||||
assert.Equal(t, label, uint16(100))
|
assert.Equal(t, label, uint16(100))
|
||||||
}
|
}
|
||||||
|
|
118
pkg/vm/instruction_string.go
Normal file
118
pkg/vm/instruction_string.go
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
// Code generated by "stringer -type=Instruction"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
const _Instruction_name = "PUSH0PUSHBYTES1PUSHBYTES75PUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPIFJMPIFNOTCALLRETAPPCALLSYSCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKXDROPXSWAPXTUCKDEPTHDROPDUPNIPOVERPICKROLLROTSWAPTUCKCATSUBSTRLEFTRIGHTSIZEINVERTANDORXOREQUALINCDECSIGNNEGATEABSNOTNZADDSUBMULDIVMODSHLSHRBOOLANDBOOLORNUMEQUALNUMNOTEQUALLTGTLTEGTEMINMAXWITHINSHA1SHA256HASH160HASH256CHECKSIGCHECKMULTISIGARRAYSIZEPACKUNPACKPICKITEMSETITEMNEWARRAYNEWSTRUCTAPPENDREVERSEREMOVETHROWTHROWIFNOT"
|
||||||
|
|
||||||
|
var _Instruction_map = map[Instruction]string{
|
||||||
|
0: _Instruction_name[0:5],
|
||||||
|
1: _Instruction_name[5:15],
|
||||||
|
75: _Instruction_name[15:26],
|
||||||
|
76: _Instruction_name[26:35],
|
||||||
|
77: _Instruction_name[35:44],
|
||||||
|
78: _Instruction_name[44:53],
|
||||||
|
79: _Instruction_name[53:59],
|
||||||
|
81: _Instruction_name[59:64],
|
||||||
|
82: _Instruction_name[64:69],
|
||||||
|
83: _Instruction_name[69:74],
|
||||||
|
84: _Instruction_name[74:79],
|
||||||
|
85: _Instruction_name[79:84],
|
||||||
|
86: _Instruction_name[84:89],
|
||||||
|
87: _Instruction_name[89:94],
|
||||||
|
88: _Instruction_name[94:99],
|
||||||
|
89: _Instruction_name[99:104],
|
||||||
|
90: _Instruction_name[104:110],
|
||||||
|
91: _Instruction_name[110:116],
|
||||||
|
92: _Instruction_name[116:122],
|
||||||
|
93: _Instruction_name[122:128],
|
||||||
|
94: _Instruction_name[128:134],
|
||||||
|
95: _Instruction_name[134:140],
|
||||||
|
96: _Instruction_name[140:146],
|
||||||
|
97: _Instruction_name[146:149],
|
||||||
|
98: _Instruction_name[149:152],
|
||||||
|
99: _Instruction_name[152:157],
|
||||||
|
100: _Instruction_name[157:165],
|
||||||
|
101: _Instruction_name[165:169],
|
||||||
|
102: _Instruction_name[169:172],
|
||||||
|
103: _Instruction_name[172:179],
|
||||||
|
104: _Instruction_name[179:186],
|
||||||
|
105: _Instruction_name[186:194],
|
||||||
|
106: _Instruction_name[194:209],
|
||||||
|
107: _Instruction_name[209:219],
|
||||||
|
108: _Instruction_name[219:231],
|
||||||
|
109: _Instruction_name[231:236],
|
||||||
|
114: _Instruction_name[236:241],
|
||||||
|
115: _Instruction_name[241:246],
|
||||||
|
116: _Instruction_name[246:251],
|
||||||
|
117: _Instruction_name[251:255],
|
||||||
|
118: _Instruction_name[255:258],
|
||||||
|
119: _Instruction_name[258:261],
|
||||||
|
120: _Instruction_name[261:265],
|
||||||
|
121: _Instruction_name[265:269],
|
||||||
|
122: _Instruction_name[269:273],
|
||||||
|
123: _Instruction_name[273:276],
|
||||||
|
124: _Instruction_name[276:280],
|
||||||
|
125: _Instruction_name[280:284],
|
||||||
|
126: _Instruction_name[284:287],
|
||||||
|
127: _Instruction_name[287:293],
|
||||||
|
128: _Instruction_name[293:297],
|
||||||
|
129: _Instruction_name[297:302],
|
||||||
|
130: _Instruction_name[302:306],
|
||||||
|
131: _Instruction_name[306:312],
|
||||||
|
132: _Instruction_name[312:315],
|
||||||
|
133: _Instruction_name[315:317],
|
||||||
|
134: _Instruction_name[317:320],
|
||||||
|
135: _Instruction_name[320:325],
|
||||||
|
139: _Instruction_name[325:328],
|
||||||
|
140: _Instruction_name[328:331],
|
||||||
|
141: _Instruction_name[331:335],
|
||||||
|
143: _Instruction_name[335:341],
|
||||||
|
144: _Instruction_name[341:344],
|
||||||
|
145: _Instruction_name[344:347],
|
||||||
|
146: _Instruction_name[347:349],
|
||||||
|
147: _Instruction_name[349:352],
|
||||||
|
148: _Instruction_name[352:355],
|
||||||
|
149: _Instruction_name[355:358],
|
||||||
|
150: _Instruction_name[358:361],
|
||||||
|
151: _Instruction_name[361:364],
|
||||||
|
152: _Instruction_name[364:367],
|
||||||
|
153: _Instruction_name[367:370],
|
||||||
|
154: _Instruction_name[370:377],
|
||||||
|
155: _Instruction_name[377:383],
|
||||||
|
156: _Instruction_name[383:391],
|
||||||
|
158: _Instruction_name[391:402],
|
||||||
|
159: _Instruction_name[402:404],
|
||||||
|
160: _Instruction_name[404:406],
|
||||||
|
161: _Instruction_name[406:409],
|
||||||
|
162: _Instruction_name[409:412],
|
||||||
|
163: _Instruction_name[412:415],
|
||||||
|
164: _Instruction_name[415:418],
|
||||||
|
165: _Instruction_name[418:424],
|
||||||
|
167: _Instruction_name[424:428],
|
||||||
|
168: _Instruction_name[428:434],
|
||||||
|
169: _Instruction_name[434:441],
|
||||||
|
170: _Instruction_name[441:448],
|
||||||
|
172: _Instruction_name[448:456],
|
||||||
|
174: _Instruction_name[456:469],
|
||||||
|
192: _Instruction_name[469:478],
|
||||||
|
193: _Instruction_name[478:482],
|
||||||
|
194: _Instruction_name[482:488],
|
||||||
|
195: _Instruction_name[488:496],
|
||||||
|
196: _Instruction_name[496:503],
|
||||||
|
197: _Instruction_name[503:511],
|
||||||
|
198: _Instruction_name[511:520],
|
||||||
|
200: _Instruction_name[520:526],
|
||||||
|
201: _Instruction_name[526:533],
|
||||||
|
202: _Instruction_name[533:539],
|
||||||
|
240: _Instruction_name[539:544],
|
||||||
|
241: _Instruction_name[544:554],
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Instruction) String() string {
|
||||||
|
if str, ok := _Instruction_map[i]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return "Instruction(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
130
pkg/vm/instructions.go
Normal file
130
pkg/vm/instructions.go
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
//go:generate stringer -type=Instruction
|
||||||
|
|
||||||
|
// Instruction represents an single operation for the NEO virtual machine.
|
||||||
|
type Instruction byte
|
||||||
|
|
||||||
|
// Viable list of supported instruction constants.
|
||||||
|
const (
|
||||||
|
// Constants
|
||||||
|
PUSH0 Instruction = 0x00
|
||||||
|
PUSHF Instruction = PUSH0
|
||||||
|
PUSHBYTES1 Instruction = 0x01
|
||||||
|
PUSHBYTES75 Instruction = 0x4B
|
||||||
|
PUSHDATA1 Instruction = 0x4C
|
||||||
|
PUSHDATA2 Instruction = 0x4D
|
||||||
|
PUSHDATA4 Instruction = 0x4E
|
||||||
|
PUSHM1 Instruction = 0x4F
|
||||||
|
PUSH1 Instruction = 0x51
|
||||||
|
PUSHT Instruction = PUSH1
|
||||||
|
PUSH2 Instruction = 0x52
|
||||||
|
PUSH3 Instruction = 0x53
|
||||||
|
PUSH4 Instruction = 0x54
|
||||||
|
PUSH5 Instruction = 0x55
|
||||||
|
PUSH6 Instruction = 0x56
|
||||||
|
PUSH7 Instruction = 0x57
|
||||||
|
PUSH8 Instruction = 0x58
|
||||||
|
PUSH9 Instruction = 0x59
|
||||||
|
PUSH10 Instruction = 0x5A
|
||||||
|
PUSH11 Instruction = 0x5B
|
||||||
|
PUSH12 Instruction = 0x5C
|
||||||
|
PUSH13 Instruction = 0x5D
|
||||||
|
PUSH14 Instruction = 0x5E
|
||||||
|
PUSH15 Instruction = 0x5F
|
||||||
|
PUSH16 Instruction = 0x60
|
||||||
|
|
||||||
|
// Flow control
|
||||||
|
NOP Instruction = 0x61
|
||||||
|
JMP Instruction = 0x62
|
||||||
|
JMPIF Instruction = 0x63
|
||||||
|
JMPIFNOT Instruction = 0x64
|
||||||
|
CALL Instruction = 0x65
|
||||||
|
RET Instruction = 0x66
|
||||||
|
APPCALL Instruction = 0x67
|
||||||
|
SYSCALL Instruction = 0x68
|
||||||
|
TAILCALL Instruction = 0x69
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
DUPFROMALTSTACK Instruction = 0x6A
|
||||||
|
TOALTSTACK Instruction = 0x6B
|
||||||
|
FROMALTSTACK Instruction = 0x6C
|
||||||
|
XDROP Instruction = 0x6D
|
||||||
|
XSWAP Instruction = 0x72
|
||||||
|
XTUCK Instruction = 0x73
|
||||||
|
DEPTH Instruction = 0x74
|
||||||
|
DROP Instruction = 0x75
|
||||||
|
DUP Instruction = 0x76
|
||||||
|
NIP Instruction = 0x77
|
||||||
|
OVER Instruction = 0x78
|
||||||
|
PICK Instruction = 0x79
|
||||||
|
ROLL Instruction = 0x7A
|
||||||
|
ROT Instruction = 0x7B
|
||||||
|
SWAP Instruction = 0x7C
|
||||||
|
TUCK Instruction = 0x7D
|
||||||
|
|
||||||
|
// Splice
|
||||||
|
CAT Instruction = 0x7E
|
||||||
|
SUBSTR Instruction = 0x7F
|
||||||
|
LEFT Instruction = 0x80
|
||||||
|
RIGHT Instruction = 0x81
|
||||||
|
SIZE Instruction = 0x82
|
||||||
|
|
||||||
|
// Bitwise logic
|
||||||
|
INVERT Instruction = 0x83
|
||||||
|
AND Instruction = 0x84
|
||||||
|
OR Instruction = 0x85
|
||||||
|
XOR Instruction = 0x86
|
||||||
|
EQUAL Instruction = 0x87
|
||||||
|
|
||||||
|
// Arithmetic
|
||||||
|
INC Instruction = 0x8B
|
||||||
|
DEC Instruction = 0x8C
|
||||||
|
SIGN Instruction = 0x8D
|
||||||
|
NEGATE Instruction = 0x8F
|
||||||
|
ABS Instruction = 0x90
|
||||||
|
NOT Instruction = 0x91
|
||||||
|
NZ Instruction = 0x92
|
||||||
|
ADD Instruction = 0x93
|
||||||
|
SUB Instruction = 0x94
|
||||||
|
MUL Instruction = 0x95
|
||||||
|
DIV Instruction = 0x96
|
||||||
|
MOD Instruction = 0x97
|
||||||
|
SHL Instruction = 0x98
|
||||||
|
SHR Instruction = 0x99
|
||||||
|
BOOLAND Instruction = 0x9A
|
||||||
|
BOOLOR Instruction = 0x9B
|
||||||
|
NUMEQUAL Instruction = 0x9C
|
||||||
|
NUMNOTEQUAL Instruction = 0x9E
|
||||||
|
LT Instruction = 0x9F
|
||||||
|
GT Instruction = 0xA0
|
||||||
|
LTE Instruction = 0xA1
|
||||||
|
GTE Instruction = 0xA2
|
||||||
|
MIN Instruction = 0xA3
|
||||||
|
MAX Instruction = 0xA4
|
||||||
|
WITHIN Instruction = 0xA5
|
||||||
|
|
||||||
|
// Crypto
|
||||||
|
SHA1 Instruction = 0xA7
|
||||||
|
SHA256 Instruction = 0xA8
|
||||||
|
HASH160 Instruction = 0xA9
|
||||||
|
HASH256 Instruction = 0xAA
|
||||||
|
CHECKSIG Instruction = 0xAC
|
||||||
|
CHECKMULTISIG Instruction = 0xAE
|
||||||
|
|
||||||
|
// Array
|
||||||
|
ARRAYSIZE Instruction = 0xC0
|
||||||
|
PACK Instruction = 0xC1
|
||||||
|
UNPACK Instruction = 0xC2
|
||||||
|
PICKITEM Instruction = 0xC3
|
||||||
|
SETITEM Instruction = 0xC4
|
||||||
|
NEWARRAY Instruction = 0xC5
|
||||||
|
NEWSTRUCT Instruction = 0xC6
|
||||||
|
APPEND Instruction = 0xC8
|
||||||
|
REVERSE Instruction = 0xC9
|
||||||
|
REMOVE Instruction = 0xCA
|
||||||
|
|
||||||
|
// Exceptions
|
||||||
|
THROW Instruction = 0xF0
|
||||||
|
THROWIFNOT Instruction = 0xF1
|
||||||
|
)
|
131
pkg/vm/opcode.go
131
pkg/vm/opcode.go
|
@ -1,131 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
//go:generate stringer -type=Opcode
|
|
||||||
|
|
||||||
// Opcode is an single operational instruction for the GO NEO virtual machine.
|
|
||||||
type Opcode byte
|
|
||||||
|
|
||||||
// List of supported opcodes.
|
|
||||||
const (
|
|
||||||
// Constants
|
|
||||||
Opush0 Opcode = 0x00 // An empty array of bytes is pushed onto the stack.
|
|
||||||
Opushf = Opush0
|
|
||||||
Opushbytes1 Opcode = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack
|
|
||||||
Opushbytes75 Opcode = 0x4B
|
|
||||||
Opushdata1 Opcode = 0x4C // The next byte contains the number of bytes to be pushed onto the stack.
|
|
||||||
Opushdata2 Opcode = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack.
|
|
||||||
Opushdata4 Opcode = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack.
|
|
||||||
Opushm1 Opcode = 0x4F // The number -1 is pushed onto the stack.
|
|
||||||
Opush1 Opcode = 0x51
|
|
||||||
Opusht = Opush1
|
|
||||||
Opush2 Opcode = 0x52 // The number 2 is pushed onto the stack.
|
|
||||||
Opush3 Opcode = 0x53 // The number 3 is pushed onto the stack.
|
|
||||||
Opush4 Opcode = 0x54 // The number 4 is pushed onto the stack.
|
|
||||||
Opush5 Opcode = 0x55 // The number 5 is pushed onto the stack.
|
|
||||||
Opush6 Opcode = 0x56 // The number 6 is pushed onto the stack.
|
|
||||||
Opush7 Opcode = 0x57 // The number 7 is pushed onto the stack.
|
|
||||||
Opush8 Opcode = 0x58 // The number 8 is pushed onto the stack.
|
|
||||||
Opush9 Opcode = 0x59 // The number 9 is pushed onto the stack.
|
|
||||||
Opush10 Opcode = 0x5A // The number 10 is pushed onto the stack.
|
|
||||||
Opush11 Opcode = 0x5B // The number 11 is pushed onto the stack.
|
|
||||||
Opush12 Opcode = 0x5C // The number 12 is pushed onto the stack.
|
|
||||||
Opush13 Opcode = 0x5D // The number 13 is pushed onto the stack.
|
|
||||||
Opush14 Opcode = 0x5E // The number 14 is pushed onto the stack.
|
|
||||||
Opush15 Opcode = 0x5F // The number 15 is pushed onto the stack.
|
|
||||||
Opush16 Opcode = 0x60 // The number 16 is pushed onto the stack.
|
|
||||||
|
|
||||||
// Flow control
|
|
||||||
Onop Opcode = 0x61 // No operation.
|
|
||||||
Ojmp Opcode = 0x62
|
|
||||||
Ojmpif Opcode = 0x63
|
|
||||||
Ojmpifnot Opcode = 0x64
|
|
||||||
Ocall Opcode = 0x65
|
|
||||||
Oret Opcode = 0x66
|
|
||||||
Oappcall Opcode = 0x67
|
|
||||||
Osyscall Opcode = 0x68
|
|
||||||
Otailcall Opcode = 0x69
|
|
||||||
|
|
||||||
// The stack
|
|
||||||
Odupfromaltstack Opcode = 0x6A
|
|
||||||
Otoaltstack Opcode = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack.
|
|
||||||
Ofromaltstack Opcode = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack.
|
|
||||||
Oxdrop Opcode = 0x6D
|
|
||||||
Oxswap Opcode = 0x72
|
|
||||||
Oxtuck Opcode = 0x73
|
|
||||||
Odepth Opcode = 0x74 // Puts the number of stack items onto the stack.
|
|
||||||
Odrop Opcode = 0x75 // Removes the top stack item.
|
|
||||||
Odup Opcode = 0x76 // Duplicates the top stack item.
|
|
||||||
Onip Opcode = 0x77 // Removes the second-to-top stack item.
|
|
||||||
Oover Opcode = 0x78 // Copies the second-to-top stack item to the top.
|
|
||||||
Opick Opcode = 0x79 // The item n back in the stack is copied to the top.
|
|
||||||
Oroll Opcode = 0x7A // The item n back in the stack is moved to the top.
|
|
||||||
Orot Opcode = 0x7B // The top three items on the stack are rotated to the left.
|
|
||||||
Oswap Opcode = 0x7C // The top two items on the stack are swapped.
|
|
||||||
Otuck Opcode = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item.
|
|
||||||
|
|
||||||
// Splice
|
|
||||||
Ocat Opcode = 0x7E // Concatenates two strings.
|
|
||||||
Osubstr Opcode = 0x7F // Returns a section of a string.
|
|
||||||
Oleft Opcode = 0x80 // Keeps only characters left of the specified point in a string.
|
|
||||||
Oright Opcode = 0x81 // Keeps only characters right of the specified point in a string.
|
|
||||||
Osize Opcode = 0x82 // Returns the length of the input string.
|
|
||||||
|
|
||||||
// Bitwise logic
|
|
||||||
Oinvert Opcode = 0x83 // Flips all of the bits in the input.
|
|
||||||
Oand Opcode = 0x84 // Boolean and between each bit in the inputs.
|
|
||||||
Oor Opcode = 0x85 // Boolean or between each bit in the inputs.
|
|
||||||
Oxor Opcode = 0x86 // Boolean exclusive or between each bit in the inputs.
|
|
||||||
Oequal Opcode = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise.
|
|
||||||
|
|
||||||
// Arithmetic
|
|
||||||
// Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output.
|
|
||||||
Oinc Opcode = 0x8B // 1 is added to the input.
|
|
||||||
Odec Opcode = 0x8C // 1 is subtracted from the input.
|
|
||||||
Osign Opcode = 0x8D
|
|
||||||
Onegate Opcode = 0x8F // The sign of the input is flipped.
|
|
||||||
Oabs Opcode = 0x90 // The input is made positive.
|
|
||||||
Onot Opcode = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
|
||||||
Onz Opcode = 0x92 // Returns 0 if the input is 0. 1 otherwise.
|
|
||||||
Oadd Opcode = 0x93 // a is added to b.
|
|
||||||
Osub Opcode = 0x94 // b is subtracted from a.
|
|
||||||
Omul Opcode = 0x95 // a is multiplied by b.
|
|
||||||
Odiv Opcode = 0x96 // a is divided by b.
|
|
||||||
Omod Opcode = 0x97 // Returns the remainder after dividing a by b.
|
|
||||||
Oshl Opcode = 0x98 // Shifts a left b bits, preserving sign.
|
|
||||||
Oshr Opcode = 0x99 // Shifts a right b bits, preserving sign.
|
|
||||||
Obooland Opcode = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0.
|
|
||||||
Oboolor Opcode = 0x9B // If a or b is not 0, the output is 1. Otherwise 0.
|
|
||||||
Onumequal Opcode = 0x9C // Returns 1 if the numbers are equal, 0 otherwise.
|
|
||||||
Onumnotequal Opcode = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise.
|
|
||||||
Olt Opcode = 0x9F // Returns 1 if a is less than b, 0 otherwise.
|
|
||||||
Ogt Opcode = 0xA0 // Returns 1 if a is greater than b, 0 otherwise.
|
|
||||||
Olte Opcode = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise.
|
|
||||||
Ogte Opcode = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise.
|
|
||||||
Omin Opcode = 0xA3 // Returns the smaller of a and b.
|
|
||||||
Omax Opcode = 0xA4 // Returns the larger of a and b.
|
|
||||||
Owithin Opcode = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
|
||||||
|
|
||||||
// Crypto
|
|
||||||
Osha1 Opcode = 0xA7 // The input is hashed using SHA-1.
|
|
||||||
Osha256 Opcode = 0xA8 // The input is hashed using SHA-256.
|
|
||||||
Ohash160 Opcode = 0xA9
|
|
||||||
Ohash256 Opcode = 0xAA
|
|
||||||
Ochecksig Opcode = 0xAC
|
|
||||||
Ocheckmultisig Opcode = 0xAE
|
|
||||||
|
|
||||||
// array
|
|
||||||
Oarraysize Opcode = 0xC0
|
|
||||||
Opack Opcode = 0xC1
|
|
||||||
Ounpack Opcode = 0xC2
|
|
||||||
Opickitem Opcode = 0xC3
|
|
||||||
Osetitem Opcode = 0xC4
|
|
||||||
Onewarray Opcode = 0xC5 // Pops size from stack and creates a new array with that size, and pushes the array into the stack
|
|
||||||
Onewstruct Opcode = 0xC6
|
|
||||||
Oappend Opcode = 0xC8
|
|
||||||
Oreverse Opcode = 0xC9
|
|
||||||
Oremove Opcode = 0xCA
|
|
||||||
|
|
||||||
// exceptions
|
|
||||||
Othrow Opcode = 0xF0
|
|
||||||
Othrowifnot Opcode = 0xF1
|
|
||||||
)
|
|
|
@ -1,118 +0,0 @@
|
||||||
// Code generated by "stringer -type=Opcode"; DO NOT EDIT.
|
|
||||||
|
|
||||||
package vm
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
const _Opcode_name = "Opush0Opushbytes1Opushbytes75Opushdata1Opushdata2Opushdata4Opushm1Opush1Opush2Opush3Opush4Opush5Opush6Opush7Opush8Opush9Opush10Opush11Opush12Opush13Opush14Opush15Opush16OnopOjmpOjmpifOjmpifnotOcallOretOappcallOsyscallOtailcallOdupfromaltstackOtoaltstackOfromaltstackOxdropOxswapOxtuckOdepthOdropOdupOnipOoverOpickOrollOrotOswapOtuckOcatOsubstrOleftOrightOsizeOinvertOandOorOxorOequalOincOdecOsignOnegateOabsOnotOnzOaddOsubOmulOdivOmodOshlOshrOboolandOboolorOnumequalOnumnotequalOltOgtOlteOgteOminOmaxOwithinOsha1Osha256Ohash160Ohash256OchecksigOcheckmultisigOarraysizeOpackOunpackOpickitemOsetitemOnewarrayOnewstructOappendOreverseOremoveOthrowOthrowifnot"
|
|
||||||
|
|
||||||
var _Opcode_map = map[Opcode]string{
|
|
||||||
0: _Opcode_name[0:6],
|
|
||||||
1: _Opcode_name[6:17],
|
|
||||||
75: _Opcode_name[17:29],
|
|
||||||
76: _Opcode_name[29:39],
|
|
||||||
77: _Opcode_name[39:49],
|
|
||||||
78: _Opcode_name[49:59],
|
|
||||||
79: _Opcode_name[59:66],
|
|
||||||
81: _Opcode_name[66:72],
|
|
||||||
82: _Opcode_name[72:78],
|
|
||||||
83: _Opcode_name[78:84],
|
|
||||||
84: _Opcode_name[84:90],
|
|
||||||
85: _Opcode_name[90:96],
|
|
||||||
86: _Opcode_name[96:102],
|
|
||||||
87: _Opcode_name[102:108],
|
|
||||||
88: _Opcode_name[108:114],
|
|
||||||
89: _Opcode_name[114:120],
|
|
||||||
90: _Opcode_name[120:127],
|
|
||||||
91: _Opcode_name[127:134],
|
|
||||||
92: _Opcode_name[134:141],
|
|
||||||
93: _Opcode_name[141:148],
|
|
||||||
94: _Opcode_name[148:155],
|
|
||||||
95: _Opcode_name[155:162],
|
|
||||||
96: _Opcode_name[162:169],
|
|
||||||
97: _Opcode_name[169:173],
|
|
||||||
98: _Opcode_name[173:177],
|
|
||||||
99: _Opcode_name[177:183],
|
|
||||||
100: _Opcode_name[183:192],
|
|
||||||
101: _Opcode_name[192:197],
|
|
||||||
102: _Opcode_name[197:201],
|
|
||||||
103: _Opcode_name[201:209],
|
|
||||||
104: _Opcode_name[209:217],
|
|
||||||
105: _Opcode_name[217:226],
|
|
||||||
106: _Opcode_name[226:242],
|
|
||||||
107: _Opcode_name[242:253],
|
|
||||||
108: _Opcode_name[253:266],
|
|
||||||
109: _Opcode_name[266:272],
|
|
||||||
114: _Opcode_name[272:278],
|
|
||||||
115: _Opcode_name[278:284],
|
|
||||||
116: _Opcode_name[284:290],
|
|
||||||
117: _Opcode_name[290:295],
|
|
||||||
118: _Opcode_name[295:299],
|
|
||||||
119: _Opcode_name[299:303],
|
|
||||||
120: _Opcode_name[303:308],
|
|
||||||
121: _Opcode_name[308:313],
|
|
||||||
122: _Opcode_name[313:318],
|
|
||||||
123: _Opcode_name[318:322],
|
|
||||||
124: _Opcode_name[322:327],
|
|
||||||
125: _Opcode_name[327:332],
|
|
||||||
126: _Opcode_name[332:336],
|
|
||||||
127: _Opcode_name[336:343],
|
|
||||||
128: _Opcode_name[343:348],
|
|
||||||
129: _Opcode_name[348:354],
|
|
||||||
130: _Opcode_name[354:359],
|
|
||||||
131: _Opcode_name[359:366],
|
|
||||||
132: _Opcode_name[366:370],
|
|
||||||
133: _Opcode_name[370:373],
|
|
||||||
134: _Opcode_name[373:377],
|
|
||||||
135: _Opcode_name[377:383],
|
|
||||||
139: _Opcode_name[383:387],
|
|
||||||
140: _Opcode_name[387:391],
|
|
||||||
141: _Opcode_name[391:396],
|
|
||||||
143: _Opcode_name[396:403],
|
|
||||||
144: _Opcode_name[403:407],
|
|
||||||
145: _Opcode_name[407:411],
|
|
||||||
146: _Opcode_name[411:414],
|
|
||||||
147: _Opcode_name[414:418],
|
|
||||||
148: _Opcode_name[418:422],
|
|
||||||
149: _Opcode_name[422:426],
|
|
||||||
150: _Opcode_name[426:430],
|
|
||||||
151: _Opcode_name[430:434],
|
|
||||||
152: _Opcode_name[434:438],
|
|
||||||
153: _Opcode_name[438:442],
|
|
||||||
154: _Opcode_name[442:450],
|
|
||||||
155: _Opcode_name[450:457],
|
|
||||||
156: _Opcode_name[457:466],
|
|
||||||
158: _Opcode_name[466:478],
|
|
||||||
159: _Opcode_name[478:481],
|
|
||||||
160: _Opcode_name[481:484],
|
|
||||||
161: _Opcode_name[484:488],
|
|
||||||
162: _Opcode_name[488:492],
|
|
||||||
163: _Opcode_name[492:496],
|
|
||||||
164: _Opcode_name[496:500],
|
|
||||||
165: _Opcode_name[500:507],
|
|
||||||
167: _Opcode_name[507:512],
|
|
||||||
168: _Opcode_name[512:519],
|
|
||||||
169: _Opcode_name[519:527],
|
|
||||||
170: _Opcode_name[527:535],
|
|
||||||
172: _Opcode_name[535:544],
|
|
||||||
174: _Opcode_name[544:558],
|
|
||||||
192: _Opcode_name[558:568],
|
|
||||||
193: _Opcode_name[568:573],
|
|
||||||
194: _Opcode_name[573:580],
|
|
||||||
195: _Opcode_name[580:589],
|
|
||||||
196: _Opcode_name[589:597],
|
|
||||||
197: _Opcode_name[597:606],
|
|
||||||
198: _Opcode_name[606:616],
|
|
||||||
200: _Opcode_name[616:623],
|
|
||||||
201: _Opcode_name[623:631],
|
|
||||||
202: _Opcode_name[631:638],
|
|
||||||
240: _Opcode_name[638:644],
|
|
||||||
241: _Opcode_name[644:655],
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i Opcode) String() string {
|
|
||||||
if str, ok := _Opcode_map[i]; ok {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
return "Opcode(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package vm
|
|
||||||
|
|
||||||
// Syscalls are a mapping between the syscall function name
|
|
||||||
// and the registered VM interop API.
|
|
||||||
var Syscalls = map[string]string{
|
|
||||||
// Storage API
|
|
||||||
"GetContext": "Neo.Storage.GetContext",
|
|
||||||
"Put": "Neo.Storage.Put",
|
|
||||||
"Get": "Neo.Storage.Get",
|
|
||||||
"Delete": "Neo.Storage.Delete",
|
|
||||||
|
|
||||||
// Runtime API
|
|
||||||
"GetTrigger": "Neo.Runtime.GetTrigger",
|
|
||||||
"CheckWitness": "Neo.Runtime.CheckWitness",
|
|
||||||
"GetCurrentBlock": "Neo.Runtime.GetCurrentBlock",
|
|
||||||
"GetTime": "Neo.Runtime.GetTime",
|
|
||||||
"Notify": "Neo.Runtime.Notify",
|
|
||||||
"Log": "Neo.Runtime.Log",
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ func TestStoragePutGet(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
import "github.com/CityOfZion/neo-go/pkg/vm/api/storage"
|
import "github.com/CityOfZion/neo-go/pkg/interop/storage"
|
||||||
|
|
||||||
func Main() string {
|
func Main() string {
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
|
|
|
@ -8,7 +8,7 @@ func TestSHA256(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foo
|
package foo
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
"github.com/CityOfZion/neo-go/pkg/interop/crypto"
|
||||||
)
|
)
|
||||||
func Main() []byte {
|
func Main() []byte {
|
||||||
src := []byte{0x97}
|
src := []byte{0x97}
|
||||||
|
@ -23,7 +23,7 @@ func TestSHA1(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foo
|
package foo
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
"github.com/CityOfZion/neo-go/pkg/interop/crypto"
|
||||||
)
|
)
|
||||||
func Main() []byte {
|
func Main() []byte {
|
||||||
src := []byte{0x97}
|
src := []byte{0x97}
|
||||||
|
@ -38,7 +38,7 @@ func TestHash160(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foo
|
package foo
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
"github.com/CityOfZion/neo-go/pkg/interop/crypto"
|
||||||
)
|
)
|
||||||
func Main() []byte {
|
func Main() []byte {
|
||||||
src := []byte{0x97}
|
src := []byte{0x97}
|
||||||
|
@ -53,7 +53,7 @@ func TestHash256(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foo
|
package foo
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm/api/crypto"
|
"github.com/CityOfZion/neo-go/pkg/interop/crypto"
|
||||||
)
|
)
|
||||||
func Main() []byte {
|
func Main() []byte {
|
||||||
src := []byte{0x97}
|
src := []byte{0x97}
|
||||||
|
|
164
pkg/vm/vm.go
164
pkg/vm/vm.go
|
@ -104,7 +104,7 @@ func (v *VM) PrintOps() {
|
||||||
} else {
|
} else {
|
||||||
cursor = ""
|
cursor = ""
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Opcode(prog[i]).String(), cursor)
|
fmt.Fprintf(w, "%d\t0x%2x\t%s\t%s\n", i, prog[i], Instruction(prog[i]).String(), cursor)
|
||||||
|
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
@ -228,7 +228,7 @@ func (v *VM) Step() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute performs an instruction cycle in the VM. Acting on the instruction (opcode).
|
// execute performs an instruction cycle in the VM. Acting on the instruction (opcode).
|
||||||
func (v *VM) execute(ctx *Context, op Opcode) {
|
func (v *VM) execute(ctx *Context, op Instruction) {
|
||||||
// Instead of polluting the whole VM logic with error handling, we will recover
|
// Instead of polluting the whole VM logic with error handling, we will recover
|
||||||
// each panic at a central point, putting the VM in a fault state.
|
// each panic at a central point, putting the VM in a fault state.
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -239,57 +239,57 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if op >= Opushbytes1 && op <= Opushbytes75 {
|
if op >= PUSHBYTES1 && op <= PUSHBYTES75 {
|
||||||
b := ctx.readBytes(int(op))
|
b := ctx.readBytes(int(op))
|
||||||
v.estack.PushVal(b)
|
v.estack.PushVal(b)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch op {
|
switch op {
|
||||||
case Opushm1, Opush1, Opush2, Opush3, Opush4, Opush5,
|
case PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5,
|
||||||
Opush6, Opush7, Opush8, Opush9, Opush10, Opush11,
|
PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11,
|
||||||
Opush12, Opush13, Opush14, Opush15, Opush16:
|
PUSH12, PUSH13, PUSH14, PUSH15, PUSH16:
|
||||||
val := int(op) - int(Opush1) + 1
|
val := int(op) - int(PUSH1) + 1
|
||||||
v.estack.PushVal(val)
|
v.estack.PushVal(val)
|
||||||
|
|
||||||
case Opush0:
|
case PUSH0:
|
||||||
v.estack.PushVal(0)
|
v.estack.PushVal(0)
|
||||||
|
|
||||||
case Opushdata1:
|
case PUSHDATA1:
|
||||||
n := ctx.readByte()
|
n := ctx.readByte()
|
||||||
b := ctx.readBytes(int(n))
|
b := ctx.readBytes(int(n))
|
||||||
v.estack.PushVal(b)
|
v.estack.PushVal(b)
|
||||||
|
|
||||||
case Opushdata2:
|
case PUSHDATA2:
|
||||||
n := ctx.readUint16()
|
n := ctx.readUint16()
|
||||||
b := ctx.readBytes(int(n))
|
b := ctx.readBytes(int(n))
|
||||||
v.estack.PushVal(b)
|
v.estack.PushVal(b)
|
||||||
|
|
||||||
case Opushdata4:
|
case PUSHDATA4:
|
||||||
n := ctx.readUint32()
|
n := ctx.readUint32()
|
||||||
b := ctx.readBytes(int(n))
|
b := ctx.readBytes(int(n))
|
||||||
v.estack.PushVal(b)
|
v.estack.PushVal(b)
|
||||||
|
|
||||||
// Stack operations.
|
// Stack operations.
|
||||||
case Otoaltstack:
|
case TOALTSTACK:
|
||||||
v.astack.Push(v.estack.Pop())
|
v.astack.Push(v.estack.Pop())
|
||||||
|
|
||||||
case Ofromaltstack:
|
case FROMALTSTACK:
|
||||||
v.estack.Push(v.astack.Pop())
|
v.estack.Push(v.astack.Pop())
|
||||||
|
|
||||||
case Odupfromaltstack:
|
case DUPFROMALTSTACK:
|
||||||
v.estack.Push(v.astack.Dup(0))
|
v.estack.Push(v.astack.Dup(0))
|
||||||
|
|
||||||
case Odup:
|
case DUP:
|
||||||
v.estack.Push(v.estack.Dup(0))
|
v.estack.Push(v.estack.Dup(0))
|
||||||
|
|
||||||
case Oswap:
|
case SWAP:
|
||||||
a := v.estack.Pop()
|
a := v.estack.Pop()
|
||||||
b := v.estack.Pop()
|
b := v.estack.Pop()
|
||||||
v.estack.Push(a)
|
v.estack.Push(a)
|
||||||
v.estack.Push(b)
|
v.estack.Push(b)
|
||||||
|
|
||||||
case Oxswap:
|
case XSWAP:
|
||||||
n := int(v.estack.Pop().BigInt().Int64())
|
n := int(v.estack.Pop().BigInt().Int64())
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
panic("XSWAP: invalid length")
|
panic("XSWAP: invalid length")
|
||||||
|
@ -305,7 +305,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
b.value = aval
|
b.value = aval
|
||||||
}
|
}
|
||||||
|
|
||||||
case Otuck:
|
case TUCK:
|
||||||
n := int(v.estack.Pop().BigInt().Int64())
|
n := int(v.estack.Pop().BigInt().Int64())
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
panic("OTUCK: invalid length")
|
panic("OTUCK: invalid length")
|
||||||
|
@ -313,7 +313,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
|
|
||||||
v.estack.InsertAt(v.estack.Peek(0), n)
|
v.estack.InsertAt(v.estack.Peek(0), n)
|
||||||
|
|
||||||
case Orot:
|
case ROT:
|
||||||
c := v.estack.Pop()
|
c := v.estack.Pop()
|
||||||
b := v.estack.Pop()
|
b := v.estack.Pop()
|
||||||
a := v.estack.Pop()
|
a := v.estack.Pop()
|
||||||
|
@ -322,21 +322,21 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
v.estack.Push(c)
|
v.estack.Push(c)
|
||||||
v.estack.Push(a)
|
v.estack.Push(a)
|
||||||
|
|
||||||
case Odepth:
|
case DEPTH:
|
||||||
v.estack.PushVal(v.estack.Len())
|
v.estack.PushVal(v.estack.Len())
|
||||||
|
|
||||||
case Onip:
|
case NIP:
|
||||||
elem := v.estack.Pop()
|
elem := v.estack.Pop()
|
||||||
_ = v.estack.Pop()
|
_ = v.estack.Pop()
|
||||||
v.estack.Push(elem)
|
v.estack.Push(elem)
|
||||||
|
|
||||||
case Oover:
|
case OVER:
|
||||||
b := v.estack.Pop()
|
b := v.estack.Pop()
|
||||||
a := v.estack.Peek(0)
|
a := v.estack.Peek(0)
|
||||||
v.estack.Push(b)
|
v.estack.Push(b)
|
||||||
v.estack.Push(a)
|
v.estack.Push(a)
|
||||||
|
|
||||||
case Oroll:
|
case ROLL:
|
||||||
n := int(v.estack.Pop().BigInt().Int64())
|
n := int(v.estack.Pop().BigInt().Int64())
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
panic("negative stack item returned")
|
panic("negative stack item returned")
|
||||||
|
@ -345,105 +345,105 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
v.estack.Push(v.estack.RemoveAt(n))
|
v.estack.Push(v.estack.RemoveAt(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
case Odrop:
|
case DROP:
|
||||||
v.estack.Pop()
|
v.estack.Pop()
|
||||||
|
|
||||||
case Oequal:
|
case EQUAL:
|
||||||
panic("TODO EQUAL")
|
panic("TODO EQUAL")
|
||||||
|
|
||||||
// Bit operations.
|
// Bit operations.
|
||||||
case Oand:
|
case AND:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).And(b, a))
|
v.estack.PushVal(new(big.Int).And(b, a))
|
||||||
|
|
||||||
case Oor:
|
case OR:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Or(b, a))
|
v.estack.PushVal(new(big.Int).Or(b, a))
|
||||||
|
|
||||||
case Oxor:
|
case XOR:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Xor(b, a))
|
v.estack.PushVal(new(big.Int).Xor(b, a))
|
||||||
|
|
||||||
// Numeric operations.
|
// Numeric operations.
|
||||||
case Oadd:
|
case ADD:
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Add(a, b))
|
v.estack.PushVal(new(big.Int).Add(a, b))
|
||||||
|
|
||||||
case Osub:
|
case SUB:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Sub(a, b))
|
v.estack.PushVal(new(big.Int).Sub(a, b))
|
||||||
|
|
||||||
case Odiv:
|
case DIV:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Div(a, b))
|
v.estack.PushVal(new(big.Int).Div(a, b))
|
||||||
|
|
||||||
case Omul:
|
case MUL:
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Mul(a, b))
|
v.estack.PushVal(new(big.Int).Mul(a, b))
|
||||||
|
|
||||||
case Omod:
|
case MOD:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Mod(a, b))
|
v.estack.PushVal(new(big.Int).Mod(a, b))
|
||||||
|
|
||||||
case Oshl:
|
case SHL:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64())))
|
v.estack.PushVal(new(big.Int).Lsh(a, uint(b.Int64())))
|
||||||
|
|
||||||
case Oshr:
|
case SHR:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64())))
|
v.estack.PushVal(new(big.Int).Rsh(a, uint(b.Int64())))
|
||||||
|
|
||||||
case Obooland:
|
case BOOLAND:
|
||||||
b := v.estack.Pop().Bool()
|
b := v.estack.Pop().Bool()
|
||||||
a := v.estack.Pop().Bool()
|
a := v.estack.Pop().Bool()
|
||||||
v.estack.PushVal(a && b)
|
v.estack.PushVal(a && b)
|
||||||
|
|
||||||
case Oboolor:
|
case BOOLOR:
|
||||||
b := v.estack.Pop().Bool()
|
b := v.estack.Pop().Bool()
|
||||||
a := v.estack.Pop().Bool()
|
a := v.estack.Pop().Bool()
|
||||||
v.estack.PushVal(a || b)
|
v.estack.PushVal(a || b)
|
||||||
|
|
||||||
case Onumequal:
|
case NUMEQUAL:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) == 0)
|
v.estack.PushVal(a.Cmp(b) == 0)
|
||||||
|
|
||||||
case Onumnotequal:
|
case NUMNOTEQUAL:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) != 0)
|
v.estack.PushVal(a.Cmp(b) != 0)
|
||||||
|
|
||||||
case Olt:
|
case LT:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) == -1)
|
v.estack.PushVal(a.Cmp(b) == -1)
|
||||||
|
|
||||||
case Ogt:
|
case GT:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) == 1)
|
v.estack.PushVal(a.Cmp(b) == 1)
|
||||||
|
|
||||||
case Olte:
|
case LTE:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) <= 0)
|
v.estack.PushVal(a.Cmp(b) <= 0)
|
||||||
|
|
||||||
case Ogte:
|
case GTE:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(b) >= 0)
|
v.estack.PushVal(a.Cmp(b) >= 0)
|
||||||
|
|
||||||
case Omin:
|
case MIN:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
val := a
|
val := a
|
||||||
|
@ -452,7 +452,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
}
|
}
|
||||||
v.estack.PushVal(val)
|
v.estack.PushVal(val)
|
||||||
|
|
||||||
case Omax:
|
case MAX:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
val := a
|
val := a
|
||||||
|
@ -461,52 +461,52 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
}
|
}
|
||||||
v.estack.PushVal(val)
|
v.estack.PushVal(val)
|
||||||
|
|
||||||
case Owithin:
|
case WITHIN:
|
||||||
b := v.estack.Pop().BigInt()
|
b := v.estack.Pop().BigInt()
|
||||||
a := v.estack.Pop().BigInt()
|
a := v.estack.Pop().BigInt()
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1)
|
v.estack.PushVal(a.Cmp(x) <= 0 && x.Cmp(b) == -1)
|
||||||
|
|
||||||
case Oinc:
|
case INC:
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Add(x, big.NewInt(1)))
|
v.estack.PushVal(new(big.Int).Add(x, big.NewInt(1)))
|
||||||
|
|
||||||
case Odec:
|
case DEC:
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(new(big.Int).Sub(x, big.NewInt(1)))
|
v.estack.PushVal(new(big.Int).Sub(x, big.NewInt(1)))
|
||||||
|
|
||||||
case Osign:
|
case SIGN:
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(x.Sign())
|
v.estack.PushVal(x.Sign())
|
||||||
|
|
||||||
case Onegate:
|
case NEGATE:
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(x.Neg(x))
|
v.estack.PushVal(x.Neg(x))
|
||||||
|
|
||||||
case Oabs:
|
case ABS:
|
||||||
x := v.estack.Pop().BigInt()
|
x := v.estack.Pop().BigInt()
|
||||||
v.estack.PushVal(x.Abs(x))
|
v.estack.PushVal(x.Abs(x))
|
||||||
|
|
||||||
case Onot:
|
case NOT:
|
||||||
x := v.estack.Pop().Bool()
|
x := v.estack.Pop().Bool()
|
||||||
v.estack.PushVal(!x)
|
v.estack.PushVal(!x)
|
||||||
|
|
||||||
case Onz:
|
case NZ:
|
||||||
panic("todo NZ")
|
panic("todo NZ")
|
||||||
// x := v.estack.Pop().BigInt()
|
// x := v.estack.Pop().BigInt()
|
||||||
|
|
||||||
// Object operations.
|
// Object operations.
|
||||||
case Onewarray:
|
case NEWARRAY:
|
||||||
n := v.estack.Pop().BigInt().Int64()
|
n := v.estack.Pop().BigInt().Int64()
|
||||||
items := make([]StackItem, n)
|
items := make([]StackItem, n)
|
||||||
v.estack.PushVal(&ArrayItem{items})
|
v.estack.PushVal(&ArrayItem{items})
|
||||||
|
|
||||||
case Onewstruct:
|
case NEWSTRUCT:
|
||||||
n := v.estack.Pop().BigInt().Int64()
|
n := v.estack.Pop().BigInt().Int64()
|
||||||
items := make([]StackItem, n)
|
items := make([]StackItem, n)
|
||||||
v.estack.PushVal(&StructItem{items})
|
v.estack.PushVal(&StructItem{items})
|
||||||
|
|
||||||
case Oappend:
|
case APPEND:
|
||||||
itemElem := v.estack.Pop()
|
itemElem := v.estack.Pop()
|
||||||
arrElem := v.estack.Pop()
|
arrElem := v.estack.Pop()
|
||||||
|
|
||||||
|
@ -522,11 +522,11 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic("APPEND: not of underlying type Array")
|
panic("APPEND: not of underlying type Array")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Oreverse:
|
case REVERSE:
|
||||||
|
|
||||||
case Oremove:
|
case REMOVE:
|
||||||
|
|
||||||
case Opack:
|
case PACK:
|
||||||
n := int(v.estack.Pop().BigInt().Int64())
|
n := int(v.estack.Pop().BigInt().Int64())
|
||||||
if n < 0 || n > v.estack.Len() {
|
if n < 0 || n > v.estack.Len() {
|
||||||
panic("OPACK: invalid length")
|
panic("OPACK: invalid length")
|
||||||
|
@ -539,10 +539,10 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
|
|
||||||
v.estack.PushVal(items)
|
v.estack.PushVal(items)
|
||||||
|
|
||||||
case Ounpack:
|
case UNPACK:
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
|
|
||||||
case Opickitem:
|
case PICKITEM:
|
||||||
var (
|
var (
|
||||||
key = v.estack.Pop()
|
key = v.estack.Pop()
|
||||||
obj = v.estack.Pop()
|
obj = v.estack.Pop()
|
||||||
|
@ -562,7 +562,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic("PICKITEM: unknown type")
|
panic("PICKITEM: unknown type")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Osetitem:
|
case SETITEM:
|
||||||
var (
|
var (
|
||||||
item = v.estack.Pop().value
|
item = v.estack.Pop().value
|
||||||
key = v.estack.Pop()
|
key = v.estack.Pop()
|
||||||
|
@ -582,7 +582,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
||||||
}
|
}
|
||||||
|
|
||||||
case Oarraysize:
|
case ARRAYSIZE:
|
||||||
elem := v.estack.Pop()
|
elem := v.estack.Pop()
|
||||||
// Cause there is no native (byte) item type here, hence we need to check
|
// Cause there is no native (byte) item type here, hence we need to check
|
||||||
// the type of the item for array size operations.
|
// the type of the item for array size operations.
|
||||||
|
@ -595,7 +595,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic("ARRAYSIZE: item not of type []StackItem")
|
panic("ARRAYSIZE: item not of type []StackItem")
|
||||||
}
|
}
|
||||||
|
|
||||||
case Osize:
|
case SIZE:
|
||||||
elem := v.estack.Pop()
|
elem := v.estack.Pop()
|
||||||
arr, ok := elem.value.Value().([]uint8)
|
arr, ok := elem.value.Value().([]uint8)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -603,7 +603,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
}
|
}
|
||||||
v.estack.PushVal(len(arr))
|
v.estack.PushVal(len(arr))
|
||||||
|
|
||||||
case Ojmp, Ojmpif, Ojmpifnot:
|
case JMP, JMPIF, JMPIFNOT:
|
||||||
var (
|
var (
|
||||||
rOffset = int16(ctx.readUint16())
|
rOffset = int16(ctx.readUint16())
|
||||||
offset = ctx.ip + int(rOffset) - 3 // sizeOf(int16 + uint8)
|
offset = ctx.ip + int(rOffset) - 3 // sizeOf(int16 + uint8)
|
||||||
|
@ -612,9 +612,9 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip))
|
panic(fmt.Sprintf("JMP: invalid offset %d ip at %d", offset, ctx.ip))
|
||||||
}
|
}
|
||||||
cond := true
|
cond := true
|
||||||
if op > Ojmp {
|
if op > JMP {
|
||||||
cond = v.estack.Pop().Bool()
|
cond = v.estack.Pop().Bool()
|
||||||
if op == Ojmpifnot {
|
if op == JMPIFNOT {
|
||||||
cond = !cond
|
cond = !cond
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,12 +622,12 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
ctx.ip = offset
|
ctx.ip = offset
|
||||||
}
|
}
|
||||||
|
|
||||||
case Ocall:
|
case CALL:
|
||||||
v.istack.PushVal(ctx.Copy())
|
v.istack.PushVal(ctx.Copy())
|
||||||
ctx.ip += 2
|
ctx.ip += 2
|
||||||
v.execute(v.Context(), Ojmp)
|
v.execute(v.Context(), JMP)
|
||||||
|
|
||||||
case Osyscall:
|
case SYSCALL:
|
||||||
api := ctx.readVarBytes()
|
api := ctx.readVarBytes()
|
||||||
ifunc, ok := v.interop[string(api)]
|
ifunc, ok := v.interop[string(api)]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -637,7 +637,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic(fmt.Sprintf("failed to invoke syscall: %s", err))
|
panic(fmt.Sprintf("failed to invoke syscall: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
case Oappcall, Otailcall:
|
case APPCALL, TAILCALL:
|
||||||
if len(v.scripts) == 0 {
|
if len(v.scripts) == 0 {
|
||||||
panic("script table is empty")
|
panic("script table is empty")
|
||||||
}
|
}
|
||||||
|
@ -652,32 +652,32 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
panic("could not find script")
|
panic("could not find script")
|
||||||
}
|
}
|
||||||
|
|
||||||
if op == Otailcall {
|
if op == TAILCALL {
|
||||||
_ = v.istack.Pop()
|
_ = v.istack.Pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
v.LoadScript(script)
|
v.LoadScript(script)
|
||||||
|
|
||||||
case Oret:
|
case RET:
|
||||||
_ = v.istack.Pop()
|
_ = v.istack.Pop()
|
||||||
if v.istack.Len() == 0 {
|
if v.istack.Len() == 0 {
|
||||||
v.state = haltState
|
v.state = haltState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cryptographic operations.
|
// Cryptographic operations.
|
||||||
case Osha1:
|
case SHA1:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
sha := sha1.New()
|
sha := sha1.New()
|
||||||
sha.Write(b)
|
sha.Write(b)
|
||||||
v.estack.PushVal(sha.Sum(nil))
|
v.estack.PushVal(sha.Sum(nil))
|
||||||
|
|
||||||
case Osha256:
|
case SHA256:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
sha := sha256.New()
|
sha := sha256.New()
|
||||||
sha.Write(b)
|
sha.Write(b)
|
||||||
v.estack.PushVal(sha.Sum(nil))
|
v.estack.PushVal(sha.Sum(nil))
|
||||||
|
|
||||||
case Ohash160:
|
case HASH160:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
sha := sha256.New()
|
sha := sha256.New()
|
||||||
sha.Write(b)
|
sha.Write(b)
|
||||||
|
@ -686,7 +686,7 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
ripemd.Write(h)
|
ripemd.Write(h)
|
||||||
v.estack.PushVal(ripemd.Sum(nil))
|
v.estack.PushVal(ripemd.Sum(nil))
|
||||||
|
|
||||||
case Ohash256:
|
case HASH256:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
sha := sha256.New()
|
sha := sha256.New()
|
||||||
sha.Write(b)
|
sha.Write(b)
|
||||||
|
@ -695,19 +695,19 @@ func (v *VM) execute(ctx *Context, op Opcode) {
|
||||||
sha.Write(h)
|
sha.Write(h)
|
||||||
v.estack.PushVal(sha.Sum(nil))
|
v.estack.PushVal(sha.Sum(nil))
|
||||||
|
|
||||||
case Ochecksig:
|
case CHECKSIG:
|
||||||
// pubkey := v.estack.Pop().Bytes()
|
// pubkey := v.estack.Pop().Bytes()
|
||||||
// sig := v.estack.Pop().Bytes()
|
// sig := v.estack.Pop().Bytes()
|
||||||
|
|
||||||
case Ocheckmultisig:
|
case CHECKMULTISIG:
|
||||||
|
|
||||||
case Onop:
|
case NOP:
|
||||||
// unlucky ^^
|
// unlucky ^^
|
||||||
|
|
||||||
case Othrow:
|
case THROW:
|
||||||
panic("THROW")
|
panic("THROW")
|
||||||
|
|
||||||
case Othrowifnot:
|
case THROWIFNOT:
|
||||||
if !v.estack.Pop().Bool() {
|
if !v.estack.Pop().Bool() {
|
||||||
panic("THROWIFNOT")
|
panic("THROWIFNOT")
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func TestInteropHook(t *testing.T) {
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
EmitSyscall(buf, "foo")
|
EmitSyscall(buf, "foo")
|
||||||
EmitOpcode(buf, Oret)
|
EmitOpcode(buf, RET)
|
||||||
v.Load(buf.Bytes())
|
v.Load(buf.Bytes())
|
||||||
v.Run()
|
v.Run()
|
||||||
assert.Equal(t, 1, v.estack.Len())
|
assert.Equal(t, 1, v.estack.Len())
|
||||||
|
@ -51,7 +51,7 @@ func TestPushBytes1to75(t *testing.T) {
|
||||||
assert.IsType(t, elem.Bytes(), b)
|
assert.IsType(t, elem.Bytes(), b)
|
||||||
assert.Equal(t, 0, vm.estack.Len())
|
assert.Equal(t, 0, vm.estack.Len())
|
||||||
|
|
||||||
vm.execute(nil, Oret)
|
vm.execute(nil, RET)
|
||||||
|
|
||||||
assert.Equal(t, 0, vm.astack.Len())
|
assert.Equal(t, 0, vm.astack.Len())
|
||||||
assert.Equal(t, 0, vm.istack.Len())
|
assert.Equal(t, 0, vm.istack.Len())
|
||||||
|
@ -61,7 +61,7 @@ func TestPushBytes1to75(t *testing.T) {
|
||||||
|
|
||||||
func TestPushm1to16(t *testing.T) {
|
func TestPushm1to16(t *testing.T) {
|
||||||
var prog []byte
|
var prog []byte
|
||||||
for i := int(Opushm1); i <= int(Opush16); i++ {
|
for i := int(PUSHM1); i <= int(PUSH16); i++ {
|
||||||
if i == 80 {
|
if i == 80 {
|
||||||
continue // opcode layout we got here.
|
continue // opcode layout we got here.
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func TestPushm1to16(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
for i := int(Opushm1); i <= int(Opush16); i++ {
|
for i := int(PUSHM1); i <= int(PUSH16); i++ {
|
||||||
if i == 80 {
|
if i == 80 {
|
||||||
continue // nice opcode layout we got here.
|
continue // nice opcode layout we got here.
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func TestPushm1to16(t *testing.T) {
|
||||||
|
|
||||||
elem := vm.estack.Pop()
|
elem := vm.estack.Pop()
|
||||||
assert.IsType(t, &BigIntegerItem{}, elem.value)
|
assert.IsType(t, &BigIntegerItem{}, elem.value)
|
||||||
val := i - int(Opush1) + 1
|
val := i - int(PUSH1) + 1
|
||||||
assert.Equal(t, elem.BigInt().Int64(), int64(val))
|
assert.Equal(t, elem.BigInt().Int64(), int64(val))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func TestPushData4(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
func TestAdd(t *testing.T) {
|
||||||
prog := makeProgram(Oadd)
|
prog := makeProgram(ADD)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(4)
|
vm.estack.PushVal(4)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -104,7 +104,7 @@ func TestAdd(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMul(t *testing.T) {
|
func TestMul(t *testing.T) {
|
||||||
prog := makeProgram(Omul)
|
prog := makeProgram(MUL)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(4)
|
vm.estack.PushVal(4)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -113,7 +113,7 @@ func TestMul(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiv(t *testing.T) {
|
func TestDiv(t *testing.T) {
|
||||||
prog := makeProgram(Odiv)
|
prog := makeProgram(DIV)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(4)
|
vm.estack.PushVal(4)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -122,7 +122,7 @@ func TestDiv(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSub(t *testing.T) {
|
func TestSub(t *testing.T) {
|
||||||
prog := makeProgram(Osub)
|
prog := makeProgram(SUB)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(4)
|
vm.estack.PushVal(4)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -131,7 +131,7 @@ func TestSub(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLT(t *testing.T) {
|
func TestLT(t *testing.T) {
|
||||||
prog := makeProgram(Olt)
|
prog := makeProgram(LT)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(4)
|
vm.estack.PushVal(4)
|
||||||
vm.estack.PushVal(3)
|
vm.estack.PushVal(3)
|
||||||
|
@ -140,7 +140,7 @@ func TestLT(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLTE(t *testing.T) {
|
func TestLTE(t *testing.T) {
|
||||||
prog := makeProgram(Olte)
|
prog := makeProgram(LTE)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
vm.estack.PushVal(3)
|
vm.estack.PushVal(3)
|
||||||
|
@ -149,7 +149,7 @@ func TestLTE(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGT(t *testing.T) {
|
func TestGT(t *testing.T) {
|
||||||
prog := makeProgram(Ogt)
|
prog := makeProgram(GT)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(9)
|
vm.estack.PushVal(9)
|
||||||
vm.estack.PushVal(3)
|
vm.estack.PushVal(3)
|
||||||
|
@ -159,7 +159,7 @@ func TestGT(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGTE(t *testing.T) {
|
func TestGTE(t *testing.T) {
|
||||||
prog := makeProgram(Ogte)
|
prog := makeProgram(GTE)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(3)
|
vm.estack.PushVal(3)
|
||||||
vm.estack.PushVal(3)
|
vm.estack.PushVal(3)
|
||||||
|
@ -168,7 +168,7 @@ func TestGTE(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDepth(t *testing.T) {
|
func TestDepth(t *testing.T) {
|
||||||
prog := makeProgram(Odepth)
|
prog := makeProgram(DEPTH)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -178,7 +178,7 @@ func TestDepth(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumEqual(t *testing.T) {
|
func TestNumEqual(t *testing.T) {
|
||||||
prog := makeProgram(Onumequal)
|
prog := makeProgram(NUMEQUAL)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -187,7 +187,7 @@ func TestNumEqual(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumNotEqual(t *testing.T) {
|
func TestNumNotEqual(t *testing.T) {
|
||||||
prog := makeProgram(Onumnotequal)
|
prog := makeProgram(NUMNOTEQUAL)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
@ -196,7 +196,7 @@ func TestNumNotEqual(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestINC(t *testing.T) {
|
func TestINC(t *testing.T) {
|
||||||
prog := makeProgram(Oinc)
|
prog := makeProgram(INC)
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.estack.PushVal(1)
|
vm.estack.PushVal(1)
|
||||||
vm.Run()
|
vm.Run()
|
||||||
|
@ -204,13 +204,13 @@ func TestINC(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppCall(t *testing.T) {
|
func TestAppCall(t *testing.T) {
|
||||||
prog := []byte{byte(Oappcall)}
|
prog := []byte{byte(APPCALL)}
|
||||||
hash := util.Uint160{}
|
hash := util.Uint160{}
|
||||||
prog = append(prog, hash.Bytes()...)
|
prog = append(prog, hash.Bytes()...)
|
||||||
prog = append(prog, byte(Oret))
|
prog = append(prog, byte(RET))
|
||||||
|
|
||||||
vm := load(prog)
|
vm := load(prog)
|
||||||
vm.scripts[hash] = makeProgram(Odepth)
|
vm.scripts[hash] = makeProgram(DEPTH)
|
||||||
vm.estack.PushVal(2)
|
vm.estack.PushVal(2)
|
||||||
|
|
||||||
vm.Run()
|
vm.Run()
|
||||||
|
@ -231,12 +231,12 @@ func TestSimpleCall(t *testing.T) {
|
||||||
assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64()))
|
assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeProgram(opcodes ...Opcode) []byte {
|
func makeProgram(opcodes ...Instruction) []byte {
|
||||||
prog := make([]byte, len(opcodes)+1) // Oret
|
prog := make([]byte, len(opcodes)+1) // RET
|
||||||
for i := 0; i < len(opcodes); i++ {
|
for i := 0; i < len(opcodes); i++ {
|
||||||
prog[i] = byte(opcodes[i])
|
prog[i] = byte(opcodes[i])
|
||||||
}
|
}
|
||||||
prog[len(prog)-1] = byte(Oret)
|
prog[len(prog)-1] = byte(RET)
|
||||||
return prog
|
return prog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue