Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions pkg/types/query/query.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package query

import (
"encoding/json"
"errors"
"fmt"

"github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives"
)
Expand Down Expand Up @@ -228,3 +230,156 @@ func NewSortBySequence(sortDir SortDirection) SortBySequence {
func (o SortBySequence) GetDirection() SortDirection {
return o.dir
}

const MAX_EXPRESSION_DEPTH = 32

func SerializeExpressions(exprs []Expression) ([]Expression, error) {
serializedExprs := make([]Expression, 0, len(exprs))
for _, expr := range exprs {
serializedExpr, err := serializeExpressionValues(expr, 0)
if err != nil {
return nil, fmt.Errorf("failed to serialize expression: %w", err)
}
serializedExprs = append(serializedExprs, serializedExpr)
}
return serializedExprs, nil
}

func DeserializeExpressions(exprs []Expression) ([]Expression, error) {
deserializedExprs := make([]Expression, 0, len(exprs))
for _, expr := range exprs {
deserializedExpr, err := deserializeExpressionValues(expr, 0)
if err != nil {
return nil, fmt.Errorf("failed to deserialize expression: %w", err)
}
deserializedExprs = append(deserializedExprs, deserializedExpr)
}
return deserializedExprs, nil
}

// the relay wrapper calls getContractEncodedType which defaults to returning a map[string]any
// https://github.com/smartcontractkit/chainlink-common/blob/fe3ec4466fb5adfffd8fc77eef1cef67c4a918cc/pkg/loop/internal/relayer/pluginprovider/contractreader/contract_reader.go#L1033
// in ccipChainReader.ExecutedMessages, it's a primitive

func serializeExpressionValues(expr Expression, depth int) (Expression, error) {
if depth > MAX_EXPRESSION_DEPTH {
return expr, fmt.Errorf("expression depth exceeds maximum allowed depth of %d", MAX_EXPRESSION_DEPTH)
}

resultExpr := expr
var err error

if expr.Primitive != nil {
if comp, ok := expr.Primitive.(*primitives.Comparator); ok {
if comp == nil {
return expr, fmt.Errorf("invalid Expression: Primitive is *primitives.Comparator but pointer is nil")
}
newComp := &primitives.Comparator{
Name: comp.Name,
ValueComparators: make([]primitives.ValueComparator, len(comp.ValueComparators)),
}
for i, vc := range comp.ValueComparators {
var jsonData []byte
jsonData, err = json.Marshal(vc.Value)
if err != nil {
return expr, fmt.Errorf("failed to marshal value in comparator '%s' (type %T): %w", comp.Name, vc.Value, err)
}
newComp.ValueComparators[i] = primitives.ValueComparator{
Value: &jsonData,
Operator: vc.Operator,
}
}
resultExpr.Primitive = newComp
}
}

if !isZeroBoolExpression(expr.BoolExpression) {
originalBoolExpr := expr.BoolExpression
processedSubExprs := make([]Expression, 0, len(originalBoolExpr.Expressions))

for _, subExpr := range originalBoolExpr.Expressions {
var processedSubExpr Expression
processedSubExpr, err = serializeExpressionValues(subExpr, depth+1)
if err != nil {
return expr, fmt.Errorf("failed processing sub-expression within %s: %w", originalBoolExpr.BoolOperator, err)
}
processedSubExprs = append(processedSubExprs, processedSubExpr)
}
resultExpr.BoolExpression = BoolExpression{
Expressions: processedSubExprs,
BoolOperator: originalBoolExpr.BoolOperator,
}
}

return resultExpr, nil
}

func deserializeExpressionValues(expr Expression, depth int) (Expression, error) {
if depth > MAX_EXPRESSION_DEPTH {
return expr, fmt.Errorf("expression depth exceeds maximum allowed depth of %d", MAX_EXPRESSION_DEPTH)
}

resultExpr := expr
var err error

if expr.Primitive != nil {
if comp, ok := expr.Primitive.(*primitives.Comparator); ok {
if comp == nil {
return expr, fmt.Errorf("invalid Expression: Primitive is *primitives.Comparator but pointer is nil")
}
newComp := &primitives.Comparator{
Name: comp.Name,
ValueComparators: make([]primitives.ValueComparator, len(comp.ValueComparators)),
}
for i, vc := range comp.ValueComparators {
if vc.Value == nil {
newComp.ValueComparators[i] = primitives.ValueComparator{
Value: nil,
Operator: vc.Operator,
}
continue
}

jsonData, ok := vc.Value.(*[]byte)
if !ok {
return expr, fmt.Errorf("failed to deserialize value in comparator '%s': expected []byte, got %T", comp.Name, vc.Value)
}

var target uint64
err = json.Unmarshal(*jsonData, &target)
if err != nil {
return expr, fmt.Errorf("failed to unmarshal value '%s' in comparator '%s': %w", string(*jsonData), comp.Name, err)
}
newComp.ValueComparators[i] = primitives.ValueComparator{
Value: target,
Operator: vc.Operator,
}
}
resultExpr.Primitive = newComp
}
}

if !isZeroBoolExpression(expr.BoolExpression) {
originalBoolExpr := expr.BoolExpression
processedSubExprs := make([]Expression, 0, len(originalBoolExpr.Expressions))

for _, subExpr := range originalBoolExpr.Expressions {
var processedSubExpr Expression
processedSubExpr, err = deserializeExpressionValues(subExpr, depth+1)
if err != nil {
return expr, fmt.Errorf("failed processing sub-expression within %s: %w", originalBoolExpr.BoolOperator, err)
}
processedSubExprs = append(processedSubExprs, processedSubExpr)
}
resultExpr.BoolExpression = BoolExpression{
Expressions: processedSubExprs,
BoolOperator: originalBoolExpr.BoolOperator,
}
}

return resultExpr, nil
}

func isZeroBoolExpression(be BoolExpression) bool {
return len(be.Expressions) == 0
}
Loading