diff --git a/pkg/smartcontract/manifest/abi.go b/pkg/smartcontract/manifest/abi.go index 443002ed3..cffb4a21f 100644 --- a/pkg/smartcontract/manifest/abi.go +++ b/pkg/smartcontract/manifest/abi.go @@ -54,6 +54,12 @@ func (a *ABI) IsValid() error { if len(a.Methods) == 0 { return errors.New("ABI contains no methods") } + for i := range a.Methods { + err := a.Methods[i].IsValid() + if err != nil { + return err + } + } for i := range a.Events { err := a.Events[i].IsValid() if err != nil { diff --git a/pkg/smartcontract/manifest/event.go b/pkg/smartcontract/manifest/event.go index a6fd44349..00a87e988 100644 --- a/pkg/smartcontract/manifest/event.go +++ b/pkg/smartcontract/manifest/event.go @@ -2,7 +2,6 @@ package manifest import ( "errors" - "sort" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -18,22 +17,7 @@ func (e *Event) IsValid() error { if e.Name == "" { return errors.New("empty or absent name") } - if len(e.Parameters) > 1 { - paramNames := make([]string, len(e.Parameters)) - for i := range e.Parameters { - paramNames[i] = e.Parameters[i].Name - } - sort.Strings(paramNames) - for i := range paramNames { - if i == 0 { - continue - } - if paramNames[i] == paramNames[i-1] { - return errors.New("duplicate parameter name") - } - } - } - return nil + return Parameters(e.Parameters).AreValid() } // ToStackItem converts Event to stackitem.Item. diff --git a/pkg/smartcontract/manifest/method.go b/pkg/smartcontract/manifest/method.go index a9bd2ac64..85f512fd0 100644 --- a/pkg/smartcontract/manifest/method.go +++ b/pkg/smartcontract/manifest/method.go @@ -16,6 +16,21 @@ type Method struct { Safe bool `json:"safe"` } +// IsValid checks Method consistency and correctness. +func (m *Method) IsValid() error { + if m.Name == "" { + return errors.New("empty or absent name") + } + if m.Offset < 0 { + return errors.New("negative offset") + } + _, err := smartcontract.ConvertToParamType(int(m.ReturnType)) + if err != nil { + return err + } + return Parameters(m.Parameters).AreValid() +} + // ToStackItem converts Method to stackitem.Item. func (m *Method) ToStackItem() stackitem.Item { params := make([]stackitem.Item, len(m.Parameters)) diff --git a/pkg/smartcontract/manifest/method_test.go b/pkg/smartcontract/manifest/method_test.go index b8460c8ed..2f48eb797 100644 --- a/pkg/smartcontract/manifest/method_test.go +++ b/pkg/smartcontract/manifest/method_test.go @@ -10,6 +10,27 @@ import ( "github.com/stretchr/testify/require" ) +func TestMethodIsValid(t *testing.T) { + m := &Method{} + require.Error(t, m.IsValid()) // No name. + + m.Name = "qwerty" + require.NoError(t, m.IsValid()) + + m.Offset = -100 + require.Error(t, m.IsValid()) + + m.Offset = 100 + m.ReturnType = 0x42 // Invalid type. + require.Error(t, m.IsValid()) + + m.ReturnType = smartcontract.BoolType + require.NoError(t, m.IsValid()) + + m.Parameters = append(m.Parameters, NewParameter("param", smartcontract.BoolType), NewParameter("param", smartcontract.BoolType)) + require.Error(t, m.IsValid()) +} + func TestMethod_ToStackItemFromStackItem(t *testing.T) { m := &Method{ Name: "mur", diff --git a/pkg/smartcontract/manifest/parameter.go b/pkg/smartcontract/manifest/parameter.go index b263b61cd..558d54d92 100644 --- a/pkg/smartcontract/manifest/parameter.go +++ b/pkg/smartcontract/manifest/parameter.go @@ -14,6 +14,9 @@ type Parameter struct { Type smartcontract.ParamType `json:"type"` } +// Parameters is just an array of Parameter. +type Parameters []Parameter + // NewParameter returns new parameter of specified name and type. func NewParameter(name string, typ smartcontract.ParamType) Parameter { return Parameter{ @@ -54,3 +57,24 @@ func (p *Parameter) FromStackItem(item stackitem.Item) error { } return nil } + +// AreValid checks all parameters for validity and consistency. +func (p Parameters) AreValid() error { + if len(p) < 2 { + return nil + } + names := make([]string, len(p)) + for i := range p { + names[i] = p[i].Name + } + sort.Strings(names) + for i := range names { + if i == 0 { + continue + } + if names[i] == names[i-1] { + return errors.New("duplicate parameter name") + } + } + return nil +}