diff --git a/pkg/smartcontract/manifest/abi.go b/pkg/smartcontract/manifest/abi.go index ee14e25c9..443002ed3 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.Events { + err := a.Events[i].IsValid() + if err != nil { + return err + } + } return nil } diff --git a/pkg/smartcontract/manifest/event.go b/pkg/smartcontract/manifest/event.go index a80b13902..a6fd44349 100644 --- a/pkg/smartcontract/manifest/event.go +++ b/pkg/smartcontract/manifest/event.go @@ -2,6 +2,7 @@ package manifest import ( "errors" + "sort" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -12,6 +13,29 @@ type Event struct { Parameters []Parameter `json:"parameters"` } +// IsValid checks Event consistency and correctness. +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 +} + // ToStackItem converts Event to stackitem.Item. func (e *Event) ToStackItem() stackitem.Item { params := make([]stackitem.Item, len(e.Parameters)) diff --git a/pkg/smartcontract/manifest/event_test.go b/pkg/smartcontract/manifest/event_test.go index 81c63c755..2e50bdd46 100644 --- a/pkg/smartcontract/manifest/event_test.go +++ b/pkg/smartcontract/manifest/event_test.go @@ -9,6 +9,29 @@ import ( "github.com/stretchr/testify/require" ) +func TestEventIsValid(t *testing.T) { + e := Event{} + require.Error(t, e.IsValid()) + + e.Name = "some" + require.NoError(t, e.IsValid()) + + e.Parameters = make([]Parameter, 0) + require.NoError(t, e.IsValid()) + + e.Parameters = append(e.Parameters, NewParameter("p1", smartcontract.BoolType)) + require.NoError(t, e.IsValid()) + + e.Parameters = append(e.Parameters, NewParameter("p2", smartcontract.IntegerType)) + require.NoError(t, e.IsValid()) + + e.Parameters = append(e.Parameters, NewParameter("p3", smartcontract.IntegerType)) + require.NoError(t, e.IsValid()) + + e.Parameters = append(e.Parameters, NewParameter("p1", smartcontract.IntegerType)) + require.Error(t, e.IsValid()) +} + func TestEvent_ToStackItemFromStackItem(t *testing.T) { m := &Event{ Name: "mur", diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index f5d03ca4d..834be1b4e 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -121,10 +121,32 @@ func TestIsValid(t *testing.T) { Parameters: []Parameter{}, }) - t.Run("valid, no groups", func(t *testing.T) { + t.Run("valid, no groups/events", func(t *testing.T) { require.NoError(t, m.IsValid(contractHash)) }) + m.ABI.Events = append(m.ABI.Events, Event{ + Name: "itHappened", + Parameters: []Parameter{}, + }) + + t.Run("valid, with events", func(t *testing.T) { + require.NoError(t, m.IsValid(contractHash)) + }) + + m.ABI.Events = append(m.ABI.Events, Event{ + Name: "itHappened", + Parameters: []Parameter{ + NewParameter("qwerty", smartcontract.IntegerType), + NewParameter("qwerty", smartcontract.IntegerType), + }, + }) + + t.Run("invalid, bad event", func(t *testing.T) { + require.Error(t, m.IsValid(contractHash)) + }) + m.ABI.Events = m.ABI.Events[:1] + t.Run("with groups", func(t *testing.T) { m.Groups = make([]Group, 3) pks := make([]*keys.PrivateKey, 3)