diff --git a/cli/smartcontract/testdata/verify.manifest.json b/cli/smartcontract/testdata/verify.manifest.json index 47bf7b91a..d6363f7f1 100755 --- a/cli/smartcontract/testdata/verify.manifest.json +++ b/cli/smartcontract/testdata/verify.manifest.json @@ -1 +1 @@ -{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null} +{"name":"verify","abi":{"methods":[{"name":"verify","offset":0,"parameters":[],"returntype":"Boolean","safe":false},{"name":"onNEP17Payment","offset":5,"parameters":[{"name":"from","type":"ByteArray"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"Hello world!","parameters":[{"name":"args","type":"Array"}]}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null,"features":{}} diff --git a/cli/smartcontract/testdata/verifyrpc/verify.manifest.json b/cli/smartcontract/testdata/verifyrpc/verify.manifest.json index 8b70f752b..eb243980c 100755 --- a/cli/smartcontract/testdata/verifyrpc/verify.manifest.json +++ b/cli/smartcontract/testdata/verifyrpc/verify.manifest.json @@ -4,6 +4,7 @@ "supportedstandards" : [], "name" : "verify", "trusts" : [], + "features": {}, "permissions" : [ { "methods" : "*", diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index 7d11f025e..641703948 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -712,8 +712,9 @@ func TestSystemRuntimeNotify_HFBasilisk(t *testing.T) { require.NoError(t, err) m := &manifest.Manifest{ - Name: "ctr", - Groups: []manifest.Group{}, + Name: "ctr", + Features: json.RawMessage("{}"), + Groups: []manifest.Group{}, ABI: manifest.ABI{ Methods: []manifest.Method{ { diff --git a/pkg/core/native/management_neotest_test.go b/pkg/core/native/management_neotest_test.go index 7434feb3f..13f91ad29 100644 --- a/pkg/core/native/management_neotest_test.go +++ b/pkg/core/native/management_neotest_test.go @@ -52,8 +52,9 @@ func TestManagement_DeployUpdate_HFBasilisk(t *testing.T) { require.NoError(t, err) m := &manifest.Manifest{ - Name: "ctr", - Groups: []manifest.Group{}, + Name: "ctr", + Features: json.RawMessage("{}"), + Groups: []manifest.Group{}, ABI: manifest.ABI{ Methods: []manifest.Method{ { @@ -88,8 +89,9 @@ func TestManagement_CallInTheSameBlock(t *testing.T) { require.NoError(t, err) m := &manifest.Manifest{ - Name: "ctr", - Groups: []manifest.Group{}, + Name: "ctr", + Features: json.RawMessage("{}"), + Groups: []manifest.Group{}, ABI: manifest.ABI{ Methods: []manifest.Method{ { diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index 3f5ac8417..9414cb5db 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "math" + "strings" ojson "github.com/nspcc-dev/go-ordered-json" "github.com/nspcc-dev/neo-go/pkg/util" @@ -24,6 +25,8 @@ const ( NEP11Payable = "NEP-11-Payable" // NEP17Payable represents the name of contract interface which can receive NEP-17 tokens. NEP17Payable = "NEP-17-Payable" + + emptyFeatures = "{}" ) // Manifest represens contract metadata. @@ -53,7 +56,7 @@ func NewManifest(name string) *Manifest { Methods: []Method{}, Events: []Event{}, }, - Features: json.RawMessage("{}"), + Features: json.RawMessage(emptyFeatures), Groups: []Group{}, Permissions: []Permission{}, SupportedStandards: []string{}, @@ -107,6 +110,16 @@ func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error { if err != nil { return fmt.Errorf("ABI: %w", err) } + + if strings.Map(func(c rune) rune { + switch c { + case ' ', '\n', '\t', '\r': // Strip all JSON whitespace. + return -1 + } + return c + }, string(m.Features)) != emptyFeatures { // empty struct should be left. + return errors.New("invalid features") + } err = Groups(m.Groups).AreValid(hash) if err != nil { return err @@ -235,7 +248,7 @@ func (m *Manifest) FromStackItem(item stackitem.Item) error { if str[2].Type() != stackitem.MapT || str[2].(*stackitem.Map).Len() != 0 { return errors.New("invalid Features stackitem") } - m.Features = json.RawMessage("{}") + m.Features = json.RawMessage(emptyFeatures) if str[3].Type() != stackitem.ArrayT { return errors.New("invalid SupportedStandards stackitem type") } diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index e96c5062a..cd7a26055 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -144,6 +144,24 @@ func TestIsValid(t *testing.T) { require.NoError(t, m.IsValid(contractHash, true)) }) + t.Run("invalid, no features", func(t *testing.T) { + m.Features = nil + require.Error(t, m.IsValid(contractHash, true)) + }) + m.Features = json.RawMessage(emptyFeatures) + + t.Run("invalid, bad features", func(t *testing.T) { + m.Features = json.RawMessage(`{ "v" : true}`) + require.Error(t, m.IsValid(contractHash, true)) + }) + m.Features = json.RawMessage(emptyFeatures) + + t.Run("valid, features with spaces", func(t *testing.T) { + m.Features = json.RawMessage("{ \t\n\r }") + require.NoError(t, m.IsValid(contractHash, true)) + }) + m.Features = json.RawMessage(emptyFeatures) + m.ABI.Events = append(m.ABI.Events, Event{ Name: "itHappened", Parameters: []Parameter{},