From 64696459929b4b2b4a24fac620142913189f4283 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 1 Oct 2025 18:08:34 +0200 Subject: [PATCH] Fix ConvertExpressionsFromProto --- pkg/chains/evm/proto_helpers.go | 58 +++-- pkg/chains/evm/proto_helpers_test.go | 303 +++++++++++++++++++++++++ pkg/loop/chain-common/proto_helpers.go | 34 ++- 3 files changed, 374 insertions(+), 21 deletions(-) diff --git a/pkg/chains/evm/proto_helpers.go b/pkg/chains/evm/proto_helpers.go index b59b8ca416..c587b1ca55 100644 --- a/pkg/chains/evm/proto_helpers.go +++ b/pkg/chains/evm/proto_helpers.go @@ -519,12 +519,19 @@ func ConvertHashedValueComparatorsToProto(hashedValueComparators []evmprimitives return protoHashedValueComparators } -func ConvertHashedValueComparatorsFromProto(protoHashedValueComparators []*HashValueComparator) []evmprimitives.HashedValueComparator { +func ConvertHashedValueComparatorsFromProto(protoHashedValueComparators []*HashValueComparator) ([]evmprimitives.HashedValueComparator, error) { hashedValueComparators := make([]evmprimitives.HashedValueComparator, 0, len(protoHashedValueComparators)) for _, protoHvc := range protoHashedValueComparators { + if protoHvc == nil { + return nil, errors.New("hashed value comparator can't be nil") + } values := make([]evmtypes.Hash, 0, len(protoHvc.GetValues())) for _, value := range protoHvc.GetValues() { - values = append(values, evmtypes.Hash(value)) + hashValue, err := ConvertHashFromProto(value) + if err != nil { + return nil, fmt.Errorf("failed to convert hash value: %w", err) + } + values = append(values, hashValue) } hashedValueComparators = append(hashedValueComparators, evmprimitives.HashedValueComparator{ @@ -532,7 +539,7 @@ func ConvertHashedValueComparatorsFromProto(protoHashedValueComparators []*HashV Operator: primitives.ComparisonOperator(protoHvc.GetOperator()), }) } - return hashedValueComparators + return hashedValueComparators, nil } func ConvertExpressionsToProto(expressions []query.Expression) ([]*Expression, error) { @@ -552,7 +559,7 @@ func ConvertExpressionsFromProto(protoExpressions []*Expression) ([]query.Expres for idx, protoExpression := range protoExpressions { expr, err := convertExpressionFromProto(protoExpression) if err != nil { - return nil, fmt.Errorf("err to convert expr idx %d err: %w", idx, err) + return nil, status.Errorf(codes.InvalidArgument, "err to convert expr idx %d err: %s", idx, err.Error()) } expressions = append(expressions, expr) @@ -626,13 +633,17 @@ func convertExpressionToProto(expression query.Expression) (*Expression, error) } func convertExpressionFromProto(protoExpression *Expression) (query.Expression, error) { + if protoExpression == nil { + return query.Expression{}, errors.New("expression can not be nil") + } + switch protoEvaluatedExpr := protoExpression.GetEvaluator().(type) { case *Expression_BooleanExpression: var expressions []query.Expression - for _, expression := range protoEvaluatedExpr.BooleanExpression.GetExpression() { + for idx, expression := range protoEvaluatedExpr.BooleanExpression.GetExpression() { convertedExpression, err := convertExpressionFromProto(expression) if err != nil { - return query.Expression{}, err + return query.Expression{}, fmt.Errorf("failed to convert sub-expression %d: %w", idx, err) } expressions = append(expressions, convertedExpression) } @@ -651,25 +662,44 @@ func convertExpressionFromProto(protoExpression *Expression) (query.Expression, return convertEVMExpressionToProto(protoEvaluatedExpr.Primitive) } default: - return query.Expression{}, status.Errorf(codes.InvalidArgument, "Unknown expression type: %T", protoExpression) + return query.Expression{}, fmt.Errorf("unknown expression type: %T", protoExpression.GetEvaluator()) } } func convertEVMExpressionToProto(protoPrimitive *Primitive) (query.Expression, error) { switch primitive := protoPrimitive.GetPrimitive().(type) { case *Primitive_ContractAddress: - address := evmtypes.Address(primitive.ContractAddress) + address, err := ConvertAddressFromProto(primitive.ContractAddress) + if err != nil { + return query.Expression{}, fmt.Errorf("failed to convert contract address: %w", err) + } return evmprimitives.NewAddressFilter(address), nil case *Primitive_EventSig: - return evmprimitives.NewEventSigFilter(evmtypes.Hash(primitive.EventSig)), nil + hash, err := ConvertHashFromProto(primitive.EventSig) + if err != nil { + return query.Expression{}, fmt.Errorf("failed to convert event sig: %w", err) + } + return evmprimitives.NewEventSigFilter(hash), nil case *Primitive_EventByTopic: - return evmprimitives.NewEventByTopicFilter(primitive.EventByTopic.GetTopic(), - ConvertHashedValueComparatorsFromProto(primitive.EventByTopic.GetHashedValueComparers())), nil + if primitive.EventByTopic == nil { + return query.Expression{}, errors.New("EventByTopic can not be nil") + } + valueCmp, err := ConvertHashedValueComparatorsFromProto(primitive.EventByTopic.GetHashedValueComparers()) + if err != nil { + return query.Expression{}, fmt.Errorf("failed to convert EventByTopic hashed value comparators: %w", err) + } + return evmprimitives.NewEventByTopicFilter(primitive.EventByTopic.GetTopic(), valueCmp), nil case *Primitive_EventByWord: - return evmprimitives.NewEventByWordFilter(int(primitive.EventByWord.GetWordIndex()), - ConvertHashedValueComparatorsFromProto(primitive.EventByWord.GetHashedValueComparers())), nil + if primitive.EventByWord == nil { + return query.Expression{}, errors.New("EventByWord can not be nil") + } + valueCmp, err := ConvertHashedValueComparatorsFromProto(primitive.EventByWord.GetHashedValueComparers()) + if err != nil { + return query.Expression{}, fmt.Errorf("failed to convert EventByWord hashed value comparators: %w", err) + } + return evmprimitives.NewEventByWordFilter(int(primitive.EventByWord.GetWordIndex()), valueCmp), nil default: - return query.Expression{}, status.Errorf(codes.InvalidArgument, "Unknown primitive type: %T", primitive) + return query.Expression{}, fmt.Errorf("unknown primitive type: %T", primitive) } } diff --git a/pkg/chains/evm/proto_helpers_test.go b/pkg/chains/evm/proto_helpers_test.go index 2750d1e655..4713e43cc1 100644 --- a/pkg/chains/evm/proto_helpers_test.go +++ b/pkg/chains/evm/proto_helpers_test.go @@ -6,8 +6,12 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/smartcontractkit/chainlink-common/pkg/chains/evm" + chain_common "github.com/smartcontractkit/chainlink-common/pkg/loop/chain-common" evmtypes "github.com/smartcontractkit/chainlink-common/pkg/types/chains/evm" + "github.com/smartcontractkit/chainlink-common/pkg/types/query" ) func mkBytes(n int, fill byte) []byte { @@ -265,3 +269,302 @@ func TestHashConverters(t *testing.T) { } }) } + +func TestConvertExpressionsFromProto(t *testing.T) { + testCases := []struct { + Name string + In []*evm.Expression + ExpectedResult []query.Expression + ExpectedError string + }{ + { + Name: "empty", + ExpectedResult: []query.Expression{}, + }, + { + Name: "Empty evaluator", + In: []*evm.Expression{ + {}, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: unknown expression type: ", + }, + { + Name: "Empty Expression_Primitive", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{}, + }, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: unknown primitive type: ", + }, + { + Name: "Empty Expression_BooleanExpression", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_BooleanExpression{}, + }, + }, + ExpectedResult: []query.Expression{ + { + BoolExpression: query.BoolExpression{}, + }, + }, + }, + { + Name: "Nested empty Expression", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_BooleanExpression{ + BooleanExpression: &evm.BooleanExpression{ + Expression: []*evm.Expression{ + {}, + }, + }, + }, + }, + }, + ExpectedError: "err to convert expr idx 0 err: failed to convert sub-expression 0: unknown expression type: ", + }, + { + Name: "Empty Evaluator.Primitive", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: nil, + }, + }, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: unknown primitive type: ", + }, + { + Name: "Empty Evaluator.Primitive.Primitive", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{}, + }, + }, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: unknown primitive type: ", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: nil, + }, + }, + }, + }, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: rpc error: code = InvalidArgument desc = primitive can not be nil", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{}, + }, + }, + }, + }, + }, + ExpectedError: "rpc error: code = InvalidArgument desc = err to convert expr idx 0 err: rpc error: code = InvalidArgument desc = unknown primitive type: ", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive.Comparator", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{ + Primitive: &chain_common.Primitive_Comparator{}, + }, + }, + }, + }, + }, + }, + ExpectedError: "comparator can not be nil", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive.Comparator.ValueComparator", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{ + Primitive: &chain_common.Primitive_Comparator{ + Comparator: &chain_common.Comparator{ + ValueComparators: []*chain_common.ValueComparator{nil}, + }, + }, + }, + }, + }, + }, + }, + }, + ExpectedError: "unsupported primitive type: *evm.Primitive_GeneralPrimitive", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive.Block", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{ + Primitive: &chain_common.Primitive_Block{}, + }, + }, + }, + }, + }, + }, + ExpectedError: "Block can not be nil", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive.TxHash", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{ + Primitive: &chain_common.Primitive_TxHash{}, + }, + }, + }, + }, + }, + }, + ExpectedError: "TxHash can not be nil", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.GeneralPrimitive.Primitive.Timestamp", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_GeneralPrimitive{ + GeneralPrimitive: &chain_common.Primitive{ + Primitive: &chain_common.Primitive_Timestamp{}, + }, + }, + }, + }, + }, + }, + ExpectedError: "Timestamp can not be nil", + }, + { + Name: "Invalid Evaluator.Primitive.Primitive.ContractAddress", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_ContractAddress{}, + }, + }, + }, + }, + ExpectedError: "address can't be nil", + }, + { + Name: "Invalid Evaluator.Primitive.Primitive.ContractAddress", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_EventSig{}, + }, + }, + }, + }, + ExpectedError: "failed to convert event sig", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.EventByTopic", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_EventByTopic{}, + }, + }, + }, + }, + ExpectedError: "EventByTopic can not be nil", + }, + { + Name: "Invalid Evaluator.Primitive.Primitive.EventByTopic.HashedValueComparers", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_EventByTopic{ + EventByTopic: &evm.EventByTopic{ + HashedValueComparers: []*evm.HashValueComparator{nil}, + }, + }, + }, + }, + }, + }, + ExpectedError: "failed to convert EventByTopic hashed value comparators: hashed value comparator can't be nil", + }, + { + Name: "Empty Evaluator.Primitive.Primitive.EventByWord", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_EventByWord{ + EventByWord: nil, + }, + }, + }, + }, + }, + ExpectedError: "EventByWord can not be nil", + }, + { + Name: "Invalid Empty Evaluator.Primitive.Primitive.EventByWord.HashedValueComparers", + In: []*evm.Expression{ + { + Evaluator: &evm.Expression_Primitive{ + Primitive: &evm.Primitive{ + Primitive: &evm.Primitive_EventByWord{ + EventByWord: &evm.EventByWord{ + HashedValueComparers: []*evm.HashValueComparator{nil}, + }, + }, + }, + }, + }, + }, + ExpectedError: "failed to convert EventByWord hashed value comparators: hashed value comparator can't be nil", + }, + } + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + got, err := evm.ConvertExpressionsFromProto(tc.In) + if tc.ExpectedError != "" { + require.ErrorContains(t, err, tc.ExpectedError) + return + } + + require.NoError(t, err) + require.Equal(t, tc.ExpectedResult, got) + }) + } +} diff --git a/pkg/loop/chain-common/proto_helpers.go b/pkg/loop/chain-common/proto_helpers.go index efee647584..6a20c2a87d 100644 --- a/pkg/loop/chain-common/proto_helpers.go +++ b/pkg/loop/chain-common/proto_helpers.go @@ -75,16 +75,26 @@ func ConvertExpressionToProto(expression query.Expression, encodeValue ValueEnco } func ConvertPrimitiveFromProto(protoPrimitive *Primitive, encodedTypeGetter func(comparatorName string, forEncoding bool) (any, error)) (query.Expression, error) { + if protoPrimitive == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "primitive can not be nil") + } switch primitive := protoPrimitive.Primitive.(type) { case *Primitive_Comparator: + if primitive.Comparator == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "comparator can not be nil") + } + comparator := primitive.Comparator var valueComparators []primitives.ValueComparator - - for _, pbValueComparator := range primitive.Comparator.ValueComparators { - val, err := encodedTypeGetter(primitive.Comparator.Name, true) + for _, pbValueComparator := range comparator.ValueComparators { + val, err := encodedTypeGetter(comparator.Name, true) if err != nil { return query.Expression{}, err } + if pbValueComparator == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "value comparator can not be nil") + } + if err = codec.DecodeVersionedBytes(val, pbValueComparator.Value); err != nil { return query.Expression{}, err } @@ -95,20 +105,30 @@ func ConvertPrimitiveFromProto(protoPrimitive *Primitive, encodedTypeGetter func Operator: primitives.ComparisonOperator(pbValueComparator.Operator), }) } - return query.Comparator(primitive.Comparator.Name, valueComparators...), nil + return query.Comparator(comparator.Name, valueComparators...), nil case *Primitive_Confidence: confidence, err := ConfidenceFromProto(primitive.Confidence) return query.Confidence(confidence), err case *Primitive_Block: - return query.Block(primitive.Block.BlockNumber, - primitives.ComparisonOperator(primitive.Block.Operator)), nil + if primitive.Block == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "Block can not be nil") + } + block := primitive.Block + return query.Block(block.BlockNumber, primitives.ComparisonOperator(block.Operator)), nil case *Primitive_TxHash: - return query.TxHash(primitive.TxHash.TxHash), nil + if primitive.TxHash == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "TxHash can not be nil") + } + txHash := primitive.TxHash + return query.TxHash(txHash.TxHash), nil case *Primitive_Timestamp: + if primitive.Timestamp == nil { + return query.Expression{}, status.Error(codes.InvalidArgument, "Timestamp can not be nil") + } return query.Timestamp(primitive.Timestamp.Timestamp, primitives.ComparisonOperator(primitive.Timestamp.Operator)), nil