From 588a18204fdd1df2e571245b7a1906fe17e932e7 Mon Sep 17 00:00:00 2001 From: Matthew Pendrey Date: Mon, 9 Dec 2024 10:12:55 +0000 Subject: [PATCH] queries validation and query key update --- .../services/relay/evm/read/multieventtype.go | 61 ++++++++- .../relay/evm/read/multieventtype_test.go | 119 ++++++++++++++++++ 2 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 core/services/relay/evm/read/multieventtype_test.go diff --git a/core/services/relay/evm/read/multieventtype.go b/core/services/relay/evm/read/multieventtype.go index da83f444601..443dbb7e355 100644 --- a/core/services/relay/evm/read/multieventtype.go +++ b/core/services/relay/evm/read/multieventtype.go @@ -2,9 +2,11 @@ package read import ( "context" + "errors" "fmt" "iter" "reflect" + "sort" "strconv" "strings" @@ -49,6 +51,10 @@ func MultiEventTypeQuery(ctx context.Context, lp logpoller.LogPoller, eventQueri func multiEventTypeQueryWithoutErrorWrapping(ctx context.Context, lp logpoller.LogPoller, eventQueries []EventQuery, limitAndSort query.LimitAndSort) (iter.Seq2[string, commontypes.Sequence], error) { + if err := validateEventQueries(eventQueries); err != nil { + return nil, fmt.Errorf("error validating event queries: %w", err) + } + for _, eq := range eventQueries { if err := eq.EventBinding.validateBound(eq.Address); err != nil { return nil, err @@ -78,11 +84,7 @@ func multiEventTypeQueryWithoutErrorWrapping(ctx context.Context, lp logpoller.L eventQuery := query.Or(allFilterExpressions...) - queryName := "" - for _, eq := range eventQueries { - queryName += eq.EventBinding.contractName + "-" + eq.Address.String() + "-" + eq.EventBinding.eventName + "-" - } - queryName = strings.TrimSuffix(queryName, "-") + queryName := createQueryName(eventQueries) logs, err := lp.FilteredLogs(ctx, []query.Expression{eventQuery}, limitAndSort, queryName) if err != nil { @@ -97,6 +99,55 @@ func multiEventTypeQueryWithoutErrorWrapping(ctx context.Context, lp logpoller.L return seqIter, nil } +func createQueryName(eventQueries []EventQuery) string { + queryName := "" + contractToEvents := map[string][]string{} + for _, eq := range eventQueries { + contractName := eq.EventBinding.contractName + "-" + eq.Address.String() + + if _, exists := contractToEvents[contractName]; !exists { + contractToEvents[contractName] = []string{} + } + contractToEvents[contractName] = append(contractToEvents[contractName], eq.EventBinding.eventName) + } + + contractNames := make([]string, 0, len(contractToEvents)) + for contractName := range contractToEvents { + contractNames = append(contractNames, contractName) + } + + sort.Strings(contractNames) + + for _, contractName := range contractNames { + queryName += contractName + "-" + for _, event := range contractToEvents[contractName] { + queryName += event + "-" + } + } + + queryName = strings.TrimSuffix(queryName, "-") + return queryName +} + +func validateEventQueries(eventQueries []EventQuery) error { + duplicateCheck := map[common.Hash]EventQuery{} + for _, eq := range eventQueries { + if eq.EventBinding == nil { + return errors.New("event binding is nil") + } + + if eq.SequenceDataType == nil { + return errors.New("sequence data type is nil") + } + + if _, exists := duplicateCheck[eq.EventBinding.hash]; exists { + return fmt.Errorf("duplicate event query for event signature %s", eq.EventBinding.hash) + } + duplicateCheck[eq.EventBinding.hash] = eq + } + return nil +} + func decodeMultiEventTypeLogsIntoSequences(ctx context.Context, logs []logpoller.Log, eventQueries []EventQuery) (iter.Seq2[string, commontypes.Sequence], error) { type sequenceWithKey struct { Key string diff --git a/core/services/relay/evm/read/multieventtype_test.go b/core/services/relay/evm/read/multieventtype_test.go new file mode 100644 index 00000000000..a3fcb6142c4 --- /dev/null +++ b/core/services/relay/evm/read/multieventtype_test.go @@ -0,0 +1,119 @@ +package read + +import ( + "github.com/ethereum/go-ethereum/common" + + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink-common/pkg/types/query" +) + +func TestCreateQueryName(t *testing.T) { + eventQueries := []EventQuery{ + { + Filter: query.KeyFilter{Key: "key1"}, + EventBinding: &EventBinding{ + contractName: "ContractA", + eventName: "EventA", + hash: common.HexToHash("0x1"), + }, + SequenceDataType: "dataType1", + Address: common.HexToAddress("0x123"), + }, + { + Filter: query.KeyFilter{Key: "key2"}, + EventBinding: &EventBinding{ + contractName: "ContractB", + eventName: "EventB", + hash: common.HexToHash("0x2"), + }, + SequenceDataType: "dataType2", + Address: common.HexToAddress("0x456"), + }, + { + Filter: query.KeyFilter{Key: "key1"}, + EventBinding: &EventBinding{ + contractName: "ContractA", + eventName: "EventA1", + hash: common.HexToHash("0x1"), + }, + SequenceDataType: "dataType1", + Address: common.HexToAddress("0x123"), + }, + } + + expectedQueryName := "ContractA-0x0000000000000000000000000000000000000123-EventA-EventA1-ContractB-0x0000000000000000000000000000000000000456-EventB" + queryName := createQueryName(eventQueries) + + assert.Equal(t, expectedQueryName, queryName) +} + +func TestValidateEventQueries(t *testing.T) { + tests := []struct { + name string + eventQueries []EventQuery + expectedError string + }{ + { + name: "valid event queries", + eventQueries: []EventQuery{ + { + EventBinding: &EventBinding{hash: common.HexToHash("0x1")}, + SequenceDataType: "dataType1", + }, + { + EventBinding: &EventBinding{hash: common.HexToHash("0x2")}, + SequenceDataType: "dataType2", + }, + }, + expectedError: "", + }, + { + name: "nil event binding", + eventQueries: []EventQuery{ + { + EventBinding: nil, + SequenceDataType: "dataType1", + }, + }, + expectedError: "event binding is nil", + }, + { + name: "nil sequence data type", + eventQueries: []EventQuery{ + { + EventBinding: &EventBinding{hash: common.HexToHash("0x1")}, + SequenceDataType: nil, + }, + }, + expectedError: "sequence data type is nil", + }, + { + name: "duplicate event query", + eventQueries: []EventQuery{ + { + EventBinding: &EventBinding{hash: common.HexToHash("0x1")}, + SequenceDataType: "dataType1", + }, + { + EventBinding: &EventBinding{hash: common.HexToHash("0x1")}, + SequenceDataType: "dataType2", + }, + }, + expectedError: "duplicate event query for event signature 0x0000000000000000000000000000000000000000000000000000000000000001", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateEventQueries(tt.eventQueries) + if tt.expectedError == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tt.expectedError) + } + }) + } +}