From 25f1db6de090eff2c34c579f2a286bd9c0a089c7 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Tue, 24 Nov 2020 13:38:24 +0300 Subject: [PATCH] compiler: check supported standards Check that emitted manifest complies with supported standards. This can be made a separate flag. --- cli/contract_test.go | 51 +++++++++++++++++++++++++++++ cli/smartcontract/smart_contract.go | 6 ++++ examples/token-sale/token_sale.go | 19 ++++++----- examples/token-sale/token_sale.yml | 10 +++++- examples/token/token.go | 4 +-- examples/token/token.yml | 4 +-- pkg/compiler/compiler.go | 10 ++++++ 7 files changed, 90 insertions(+), 14 deletions(-) diff --git a/cli/contract_test.go b/cli/contract_test.go index 002274a9f..a9b391e91 100644 --- a/cli/contract_test.go +++ b/cli/contract_test.go @@ -104,3 +104,54 @@ func TestComlileAndInvokeFunction(t *testing.T) { require.Equal(t, []byte("on update|sub update"), res.Stack[0].Value()) }) } + +func TestCompileExamples(t *testing.T) { + const examplePath = "../examples" + infos, err := ioutil.ReadDir(examplePath) + require.NoError(t, err) + + // For proper nef generation. + config.Version = "0.90.0-test" + + tmpDir := os.TempDir() + + e := newExecutor(t, false) + defer e.Close(t) + + for _, info := range infos { + t.Run(info.Name(), func(t *testing.T) { + infos, err := ioutil.ReadDir(path.Join(examplePath, info.Name())) + require.NoError(t, err) + require.False(t, len(infos) == 0, "detected smart contract folder with no contract in it") + + outPath := path.Join(tmpDir, info.Name()+".nef") + manifestPath := path.Join(tmpDir, info.Name()+".manifest.json") + defer func() { + os.Remove(outPath) + os.Remove(manifestPath) + }() + + cfgName := filterFilename(infos, ".yml") + opts := []string{ + "neo-go", "contract", "compile", + "--in", path.Join(examplePath, info.Name()), + "--out", outPath, + "--manifest", manifestPath, + "--config", path.Join(examplePath, info.Name(), cfgName), + } + e.Run(t, opts...) + }) + } +} + +func filterFilename(infos []os.FileInfo, ext string) string { + for _, info := range infos { + if !info.IsDir() { + name := info.Name() + if strings.HasSuffix(name, ext) { + return name + } + } + } + return "" +} diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 4b92ca465..bb6095175 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -150,6 +150,10 @@ func NewCommands() []cli.Command { Name: "config, c", Usage: "Configuration input file (*.yml)", }, + cli.BoolFlag{ + Name: "no-standards", + Usage: "do not check compliance with supported standards", + }, }, }, { @@ -398,6 +402,8 @@ func contractCompile(ctx *cli.Context) error { DebugInfo: debugFile, ManifestFile: manifestFile, + + NoStandardCheck: ctx.Bool("no-standards"), } if len(confFile) != 0 { diff --git a/examples/token-sale/token_sale.go b/examples/token-sale/token_sale.go index e48510516..d75cf32ad 100644 --- a/examples/token-sale/token_sale.go +++ b/examples/token-sale/token_sale.go @@ -1,6 +1,7 @@ package tokensale import ( + "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/util" @@ -134,39 +135,39 @@ func checkOwnerWitness() bool { } // Decimals returns the token decimals -func Decimals() interface{} { +func Decimals() int { if trigger != runtime.Application { - return false + panic("invalid trigger") } return token.Decimals } // Symbol returns the token symbol -func Symbol() interface{} { +func Symbol() string { if trigger != runtime.Application { - return false + panic("invalid trigger") } return token.Symbol } // TotalSupply returns the token total supply value -func TotalSupply() interface{} { +func TotalSupply() int { if trigger != runtime.Application { - return false + panic("invalid trigger") } return getIntFromDB(ctx, token.CirculationKey) } // BalanceOf returns the amount of token on the specified address -func BalanceOf(holder []byte) interface{} { +func BalanceOf(holder interop.Hash160) int { if trigger != runtime.Application { - return false + panic("invalid trigger") } return getIntFromDB(ctx, holder) } // Transfer transfers specified amount of token from one user to another -func Transfer(from, to []byte, amount int) bool { +func Transfer(from, to interop.Hash160, amount int, _ interface{}) bool { if trigger != runtime.Application { return false } diff --git a/examples/token-sale/token_sale.yml b/examples/token-sale/token_sale.yml index 2d6181dca..bda6ba182 100644 --- a/examples/token-sale/token_sale.yml +++ b/examples/token-sale/token_sale.yml @@ -1,3 +1,11 @@ name: "My awesome token" supportedstandards: ["NEP-17"] -events: [] +events: +- name: Transfer + parameters: + - name: from + type: Hash160 + - name: to + type: Hash160 + - name: amount + type: Integer diff --git a/examples/token/token.go b/examples/token/token.go index 3252964ad..f0a7eb09c 100644 --- a/examples/token/token.go +++ b/examples/token/token.go @@ -48,7 +48,7 @@ func TotalSupply() int { } // BalanceOf returns the amount of token on the specified address -func BalanceOf(holder interop.Hash160) interface{} { +func BalanceOf(holder interop.Hash160) int { return token.BalanceOf(ctx, holder) } @@ -58,6 +58,6 @@ func Transfer(from interop.Hash160, to interop.Hash160, amount int, data interfa } // Mint initial supply of tokens -func Mint(to []byte) bool { +func Mint(to interop.Hash160) bool { return token.Mint(ctx, to) } diff --git a/examples/token/token.yml b/examples/token/token.yml index cea35644b..32fd428b5 100644 --- a/examples/token/token.yml +++ b/examples/token/token.yml @@ -4,8 +4,8 @@ events: - name: Transfer parameters: - name: from - type: ByteString + type: Hash160 - name: to - type: ByteString + type: Hash160 - name: amount type: Integer diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index cc7ce5f7f..bae128f81 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -14,6 +14,7 @@ import ( "strings" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "golang.org/x/tools/go/loader" ) @@ -34,6 +35,10 @@ type Options struct { // The name of the output for contract manifest file. ManifestFile string + // NoStandardCheck specifies if supported standards compliance needs to be checked. + // This setting has effect only if manifest is emitted. + NoStandardCheck bool + // Name is contract's name to be written to manifest. Name string @@ -214,6 +219,11 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if err != nil { return b, fmt.Errorf("failed to convert debug info to manifest: %w", err) } + if !o.NoStandardCheck { + if err := standard.Check(m, o.ContractSupportedStandards...); err != nil { + return b, err + } + } mData, err := json.Marshal(m) if err != nil { return b, fmt.Errorf("failed to marshal manifest to JSON: %w", err)