diff --git a/x/logic/keeper/interpreter.go b/x/logic/keeper/interpreter.go index e9421549..4737771c 100644 --- a/x/logic/keeper/interpreter.go +++ b/x/logic/keeper/interpreter.go @@ -105,6 +105,7 @@ func (k Keeper) newInterpreter(ctx context.Context, params types.Params) (*prolo interpreter.WithHooks( whitelistBlacklistHookFn(whitelistPredicates, blacklistPredicates), gasMeterHookFn(sdkctx, params.GetGasPolicy()), + telemetryPredicateCallCounterHookFn(), ), interpreter.WithPredicates(ctx, interpreter.RegistryNames), interpreter.WithBootstrap(ctx, util.NonZeroOrDefault(interpreterParams.GetBootstrap(), bootstrap.Bootstrap())), @@ -135,23 +136,24 @@ func whitelistBlacklistHookFn(whitelist, blacklist []string) engine.HookFunc { return nil } - predicateStringer, ok := operand.(fmt.Stringer) + predicate, ok := stringifyOperand(operand) if !ok { return engine.SyntaxError(operand, env) } - predicate := predicateStringer.String() + if !interpreter.IsRegistered(predicate) { + return nil + } - if interpreter.IsRegistered(predicate) { - if _, found := allowed.Get(predicate); !found { - return engine.PermissionError( - prolog2.AtomOperationExecute, - prolog2.AtomPermissionForbiddenPredicate, - engine.NewAtom(predicate), - env, - ) - } + if _, found := allowed.Get(predicate); !found { + return engine.PermissionError( + prolog2.AtomOperationExecute, + prolog2.AtomPermissionForbiddenPredicate, + engine.NewAtom(predicate), + env, + ) } + return nil } } @@ -166,13 +168,11 @@ func gasMeterHookFn(ctx context.Context, gasPolicy types.GasPolicy) engine.HookF return nil } - operandStringer, ok := operand.(fmt.Stringer) + predicate, ok := stringifyOperand(operand) if !ok { return engine.SyntaxError(operand, env) } - predicate := operandStringer.String() - cost := lookupCost(predicate, lo.CoalesceOrEmpty(gasPolicy.DefaultPredicateCost, defaultPredicateCost), gasPolicy.PredicateCosts) diff --git a/x/logic/keeper/metrics.go b/x/logic/keeper/metrics.go new file mode 100644 index 00000000..2f74fde8 --- /dev/null +++ b/x/logic/keeper/metrics.go @@ -0,0 +1,55 @@ +package keeper + +import ( + "fmt" + + "github.com/axone-protocol/prolog/engine" + "github.com/hashicorp/go-metrics" + + "github.com/cosmos/cosmos-sdk/telemetry" + + "github.com/axone-protocol/axoned/v10/x/logic/interpreter" + "github.com/axone-protocol/axoned/v10/x/logic/types" +) + +var metricsKeys = []string{types.ModuleName, "vm", "predicate"} + +const ( + labelPredicate = "predicate" +) + +func telemetryPredicateCallCounterHookFn() engine.HookFunc { + return func(opcode engine.Opcode, operand engine.Term, _ *engine.Env) error { + if opcode != engine.OpCall { + return nil + } + + predicate, ok := stringifyOperand(operand) + if !ok { + return nil + } + + if !interpreter.IsRegistered(predicate) { + return nil + } + + telemetry.IncrCounterWithLabels( + metricsKeys, + 1, + []metrics.Label{ + telemetry.NewLabel(labelPredicate, predicate), + }, + ) + + return nil + } +} + +// stringifyOperand returns the string representation of the operand if it implements fmt.Stringer. +// It returns an empty string and false if the operand does not have a string representation. +func stringifyOperand(operand engine.Term) (string, bool) { + if stringer, ok := operand.(fmt.Stringer); ok { + return stringer.String(), true + } + return "", false +}