From 66ceaa6b75c114bbfccd6e96af99aaf680b81218 Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 4 Aug 2020 12:55:36 +0300 Subject: [PATCH] smartcontract: add list of supported standards to manifest Closes #1204 --- cli/smartcontract/smart_contract.go | 12 +++-- pkg/compiler/compiler.go | 7 ++- pkg/compiler/debug.go | 5 +- pkg/core/native/native_nep5.go | 1 + pkg/rpc/client/rpc_test.go | 2 +- pkg/smartcontract/manifest/manifest.go | 53 +++++++++++++-------- pkg/smartcontract/manifest/manifest_test.go | 14 +++--- 7 files changed, 59 insertions(+), 35 deletions(-) diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 48560024c..acec4fe53 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -317,7 +317,9 @@ func initSmartContract(ctx *cli.Context) error { return cli.NewExitError(err, 1) } - m := ProjectConfig{} + m := ProjectConfig{ + SupportedStandards: []string{}, + } b, err := yaml.Marshal(m) if err != nil { return cli.NewExitError(err, 1) @@ -361,6 +363,7 @@ func contractCompile(ctx *cli.Context) error { return err } o.ContractFeatures = conf.GetFeatures() + o.ContractSupportedStandards = conf.SupportedStandards } result, err := compiler.CompileAndSave(src, o) @@ -529,9 +532,10 @@ func testInvokeScript(ctx *cli.Context) error { // ProjectConfig contains project metadata. type ProjectConfig struct { - HasStorage bool - IsPayable bool - Events []manifest.Event + HasStorage bool + IsPayable bool + SupportedStandards []string + Events []manifest.Event } // GetFeatures returns smartcontract features from the config. diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index af37f0250..76d48358f 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -35,8 +35,11 @@ type Options struct { // The name of the output for contract manifest file. ManifestFile string - // Contract metadata. + // Contract features. ContractFeatures smartcontract.PropertyState + + // The list of standards supported by the contract. + ContractSupportedStandards []string } type buildInfo struct { @@ -165,7 +168,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { } if o.ManifestFile != "" { - m, err := di.ConvertToManifest(o.ContractFeatures) + m, err := di.ConvertToManifest(o.ContractFeatures, o.ContractSupportedStandards...) if err != nil { return b, errors.Wrap(err, "failed to convert debug info to manifest") } diff --git a/pkg/compiler/debug.go b/pkg/compiler/debug.go index 95bb7677b..8164c2ccf 100644 --- a/pkg/compiler/debug.go +++ b/pkg/compiler/debug.go @@ -359,7 +359,7 @@ func parsePairJSON(data []byte, sep string) (string, string, error) { // ConvertToManifest converts contract to the manifest.Manifest struct for debugger. // Note: manifest is taken from the external source, however it can be generated ad-hoc. See #1038. -func (di *DebugInfo) ConvertToManifest(fs smartcontract.PropertyState) (*manifest.Manifest, error) { +func (di *DebugInfo) ConvertToManifest(fs smartcontract.PropertyState, supportedStandards ...string) (*manifest.Manifest, error) { var err error if di.MainPkg == "" { return nil, errors.New("no Main method was found") @@ -384,6 +384,9 @@ func (di *DebugInfo) ConvertToManifest(fs smartcontract.PropertyState) (*manifes result := manifest.NewManifest(di.Hash) result.Features = fs + if supportedStandards != nil { + result.SupportedStandards = supportedStandards + } result.ABI = manifest.ABI{ Hash: di.Hash, Methods: methods, diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index 589b4144d..dd64acb02 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -47,6 +47,7 @@ var _ interop.Contract = (*nep5TokenNative)(nil) func newNEP5Native(name string) *nep5TokenNative { n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)} + n.Manifest.SupportedStandards = []string{manifest.NEP5StandardName} desc := newDescriptor("name", smartcontract.StringType) md := newMethodAndPrice(n.Name, 0, smartcontract.NoneFlag) diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index 20e35d979..ecda3e520 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -298,7 +298,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ } return c.GetContractState(hash) }, - serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, + serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`, result: func(c *Client) interface{} { script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==") if err != nil { diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index d3db02bba..1d528eec8 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -8,11 +8,18 @@ import ( "github.com/nspcc-dev/neo-go/pkg/util" ) -// MaxManifestSize is a max length for a valid contract manifest. -const MaxManifestSize = 2048 +const ( + // MaxManifestSize is a max length for a valid contract manifest. + MaxManifestSize = 2048 -// MethodInit is a name for default initialization method. -const MethodInit = "_initialize" + // MethodInit is a name for default initialization method. + MethodInit = "_initialize" + + // NEP5StandardName represents the name of NEP5 smartcontract standard. + NEP5StandardName = "NEP-5" + // NEP10StandardName represents the name of NEP10 smartcontract standard. + NEP10StandardName = "NEP-10" +) // ABI represents a contract application binary interface. type ABI struct { @@ -30,6 +37,8 @@ type Manifest struct { // Features is a set of contract's features. Features smartcontract.PropertyState Permissions []Permission + // SupportedStandards is a list of standards supported by the contract. + SupportedStandards []string // Trusts is a set of hashes to a which contract trusts. Trusts WildUint160s // SafeMethods is a set of names of safe methods. @@ -39,13 +48,14 @@ type Manifest struct { } type manifestAux struct { - ABI *ABI `json:"abi"` - Groups []Group `json:"groups"` - Features map[string]bool `json:"features"` - Permissions []Permission `json:"permissions"` - Trusts *WildUint160s `json:"trusts"` - SafeMethods *WildStrings `json:"safemethods"` - Extra interface{} `json:"extra"` + ABI *ABI `json:"abi"` + Groups []Group `json:"groups"` + Features map[string]bool `json:"features"` + Permissions []Permission `json:"permissions"` + SupportedStandards []string `json:"supportedstandards"` + Trusts *WildUint160s `json:"trusts"` + SafeMethods *WildStrings `json:"safemethods"` + Extra interface{} `json:"extra"` } // NewManifest returns new manifest with necessary fields initialized. @@ -56,8 +66,9 @@ func NewManifest(h util.Uint160) *Manifest { Methods: []Method{}, Events: []Event{}, }, - Groups: []Group{}, - Features: smartcontract.NoProperties, + Groups: []Group{}, + Features: smartcontract.NoProperties, + SupportedStandards: []string{}, } m.Trusts.Restrict() m.SafeMethods.Restrict() @@ -116,13 +127,14 @@ func (m *Manifest) MarshalJSON() ([]byte, error) { features["storage"] = m.Features&smartcontract.HasStorage != 0 features["payable"] = m.Features&smartcontract.IsPayable != 0 aux := &manifestAux{ - ABI: &m.ABI, - Groups: m.Groups, - Features: features, - Permissions: m.Permissions, - Trusts: &m.Trusts, - SafeMethods: &m.SafeMethods, - Extra: m.Extra, + ABI: &m.ABI, + Groups: m.Groups, + Features: features, + Permissions: m.Permissions, + SupportedStandards: m.SupportedStandards, + Trusts: &m.Trusts, + SafeMethods: &m.SafeMethods, + Extra: m.Extra, } return json.Marshal(aux) } @@ -148,6 +160,7 @@ func (m *Manifest) UnmarshalJSON(data []byte) error { m.Groups = aux.Groups m.Permissions = aux.Permissions + m.SupportedStandards = aux.SupportedStandards m.Extra = aux.Extra return nil diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index 3d26a01e3..ce9728f03 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -13,39 +13,39 @@ import ( // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10 func TestManifest_MarshalJSON(t *testing.T) { t.Run("default", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[],"features":{"storage":false,"payable":false},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` m := testUnmarshalMarshalManifest(t, s) require.Equal(t, DefaultManifest(util.Uint160{}), m) }) // this vector is missing from original repo t.Run("features", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":true,"payable":true},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[],"features":{"storage":true,"payable":true},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("permissions", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[],"features":{"storage":false,"payable":false},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"safemethods":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("safe methods", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":["balanceOf"],"extra":null}` + s := `{"groups":[],"features":{"storage":false,"payable":false},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":["balanceOf"],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("trust", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"safemethods":[],"extra":null}` + s := `{"groups":[],"features":{"storage":false,"payable":false},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"safemethods":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("groups", func(t *testing.T) { - s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"supportedstandards":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("extra", func(t *testing.T) { - s := `{"groups":[],"features":{"storage":false,"payable":false},"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":{"key":"value"}}` + s := `{"groups":[],"features":{"storage":false,"payable":false},"supportedstandards":[],"abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":{"key":"value"}}` testUnmarshalMarshalManifest(t, s) }) }