Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

predicate extension wip #418

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ func New(
app.AccountKeeper,
app.BankKeeper,
app.provideFS,
app.WasmKeeper,
)

wasmDir := filepath.Join(homePath, "wasm")
Expand Down
1 change: 1 addition & 0 deletions proto/logic/v1beta2/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ message QueryServiceAskRequest {
string program = 1 [(gogoproto.moretags) = "yaml:\"program\",omitempty"];
// query is the query string to be executed.
string query = 2 [(gogoproto.moretags) = "yaml:\"query\",omitempty"];
repeated string exts = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

// QueryServiceAskResponse is response type for the QueryService/Ask RPC method.
Expand Down
2 changes: 1 addition & 1 deletion x/logic/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Option func(*prolog.Interpreter) error

// WithPredicates configures the interpreter to register the specified predicates.
// The predicates names must be present in the registry, otherwise the function will return an error.
func WithPredicates(_ goctx.Context, predicates Predicates, meter sdk.GasMeter) Option {
func WithPredicates(_ goctx.Context, registry map[string]any, predicates Predicates, meter sdk.GasMeter) Option {
return func(i *prolog.Interpreter) error {
for predicate, cost := range predicates {
if err := Register(i, predicate, cost, meter); err != nil {
Expand Down
9 changes: 9 additions & 0 deletions x/logic/interpreter/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ var registry = map[string]any{
"read_string/3": predicate.ReadString,
}

func NewRegistry() map[string]any {
r := make(map[string]any)
for k, v := range registry {
r[k] = v
}

return r
}

// RegistryNames is the list of the predicate names in the Registry.
var RegistryNames = func() []string {
names := make([]string, 0, len(registry))
Expand Down
4 changes: 3 additions & 1 deletion x/logic/keeper/grpc_query_ask.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@
return k.execute(
sdkCtx,
req.Program,
req.Query)
req.Query,
req.Extensions,

Check failure on line 47 in x/logic/keeper/grpc_query_ask.go

View workflow job for this annotation

GitHub Actions / build-go

req.Extensions undefined (type *"github.com/okp4/okp4d/x/logic/types".QueryServiceAskRequest has no field or method Extensions)

Check failure on line 47 in x/logic/keeper/grpc_query_ask.go

View workflow job for this annotation

GitHub Actions / lint-go

req.Extensions undefined (type *"github.com/okp4/okp4d/x/logic/types".QueryServiceAskRequest has no field or method Extensions) (typecheck)

Check failure on line 47 in x/logic/keeper/grpc_query_ask.go

View workflow job for this annotation

GitHub Actions / test-blockchain

req.Extensions undefined (type *"github.com/okp4/okp4d/x/logic/types".QueryServiceAskRequest has no field or method Extensions)

Check failure on line 47 in x/logic/keeper/grpc_query_ask.go

View workflow job for this annotation

GitHub Actions / test-go

req.Extensions undefined (type *"github.com/okp4/okp4d/x/logic/types".QueryServiceAskRequest has no field or method Extensions)
)
}

// withGasMeter returns a new context with a gas meter that has the given limit.
Expand Down
2 changes: 2 additions & 0 deletions x/logic/keeper/grpc_query_ask_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func TestGRPCAsk(t *testing.T) {
accountKeeper := logictestutil.NewMockAccountKeeper(ctrl)
bankKeeper := logictestutil.NewMockBankKeeper(ctrl)
fsProvider := logictestutil.NewMockFS(ctrl)
wasmKeeper := logictestutil.NewMockWasmKeeper(ctrl)

logicKeeper := keeper.NewKeeper(
encCfg.Codec,
Expand All @@ -108,6 +109,7 @@ func TestGRPCAsk(t *testing.T) {
func(ctx gocontext.Context) fs.FS {
return fsProvider
},
wasmKeeper,
)
err := logicKeeper.SetParams(testCtx.Ctx, types.DefaultParams())

Expand Down
2 changes: 2 additions & 0 deletions x/logic/keeper/grpc_query_params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestGRPCParams(t *testing.T) {
accountKeeper := logictestutil.NewMockAccountKeeper(ctrl)
bankKeeper := logictestutil.NewMockBankKeeper(ctrl)
fsProvider := logictestutil.NewMockFS(ctrl)
wasmKeeper := logictestutil.NewMockWasmKeeper(ctrl)

logicKeeper := keeper.NewKeeper(
encCfg.Codec,
Expand All @@ -72,6 +73,7 @@ func TestGRPCParams(t *testing.T) {
func(ctx gocontext.Context) fs.FS {
return fsProvider
},
wasmKeeper,
)

Convey("and given params to the keeper", func() {
Expand Down
40 changes: 36 additions & 4 deletions x/logic/keeper/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
goctx "context"
"encoding/json"
"math"

"github.com/ichiban/prolog"
Expand All @@ -15,6 +16,7 @@
"github.com/okp4/okp4d/x/logic/fs"
"github.com/okp4/okp4d/x/logic/interpreter"
"github.com/okp4/okp4d/x/logic/interpreter/bootstrap"
"github.com/okp4/okp4d/x/logic/predicate"
"github.com/okp4/okp4d/x/logic/meter"
"github.com/okp4/okp4d/x/logic/types"
"github.com/okp4/okp4d/x/logic/util"
Expand All @@ -37,11 +39,34 @@
return sdkCtx
}

func (k Keeper) execute(ctx goctx.Context, program, query string) (*types.QueryServiceAskResponse, error) {
func (k Keeper) execute(ctx goctx.Context, program, query string, exts []sdk.AccAddress) (*types.QueryServiceAskResponse, error) {
ctx = k.enhanceContext(ctx)
sdkCtx := sdk.UnwrapSDKContext(ctx)

i, userOutputBuffer, err := k.newInterpreter(ctx)

manifests := make([]types.ExtensionManifest, 0)

Check failure on line 47 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / build-go

undefined: types.ExtensionManifest

Check failure on line 47 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.ExtensionManifest

Check failure on line 47 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.ExtensionManifest

Check failure on line 47 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-blockchain

undefined: types.ExtensionManifest

Check failure on line 47 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-go

undefined: types.ExtensionManifest
manifestMsg := types.PrologQueryMsg {

Check failure on line 48 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / build-go

undefined: types.PrologQueryMsg

Check failure on line 48 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.PrologQueryMsg

Check failure on line 48 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.PrologQueryMsg

Check failure on line 48 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-blockchain

undefined: types.PrologQueryMsg

Check failure on line 48 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-go

undefined: types.PrologQueryMsg
PrologExtensionManifest: &types.PrologExtensionManifest {},

Check failure on line 49 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / build-go

undefined: types.PrologExtensionManifest

Check failure on line 49 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.PrologExtensionManifest

Check failure on line 49 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-blockchain

undefined: types.PrologExtensionManifest

Check failure on line 49 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-go

undefined: types.PrologExtensionManifest
}
manifestMsgBz, err := json.Marshal(manifestMsg)
if err != nil {
return nil, errorsmod.Wrapf(types.Internal, "error marshalling manifest request: %v", err.Error())
}
for _, ext := range exts {
resbz, err := k.wasmKeeper.QuerySmart(sdkCtx, ext, manifestMsgBz)
if err != nil {
return nil, errorsmod.Wrapf(types.InvalidArgument, "error querying extension manifest: %v", err.Error())
}

var manifestRes types.PrologQueryResponse

Check failure on line 61 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / build-go

undefined: types.PrologQueryResponse

Check failure on line 61 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: types.PrologQueryResponse

Check failure on line 61 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-blockchain

undefined: types.PrologQueryResponse

Check failure on line 61 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-go

undefined: types.PrologQueryResponse
if err := json.Unmarshal(resbz, &manifestRes); err != nil {
return nil, errorsmod.Wrapf(types.Internal, "error unmarshalling manifest response: %v", err.Error())
}

manifests = append(manifests, *manifestRes.PrologExtensionManifest.Manifests...)
}

i, userOutputBuffer, err := k.newInterpreter(ctx, manifests)
if err != nil {
return nil, errorsmod.Wrapf(types.Internal, "error creating interpreter: %v", err.Error())
}
Expand Down Expand Up @@ -111,7 +136,7 @@
}

// newInterpreter creates a new interpreter properly configured.
func (k Keeper) newInterpreter(ctx goctx.Context) (*prolog.Interpreter, *util.BoundedBuffer, error) {
func (k Keeper) newInterpreter(ctx goctx.Context, manifests []ExtensionManifest) (*prolog.Interpreter, *util.BoundedBuffer, error) {

Check failure on line 139 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / build-go

undefined: ExtensionManifest

Check failure on line 139 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / lint-go

undefined: ExtensionManifest

Check failure on line 139 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-blockchain

undefined: ExtensionManifest

Check failure on line 139 in x/logic/keeper/interpreter.go

View workflow job for this annotation

GitHub Actions / test-go

undefined: ExtensionManifest
sdkctx := sdk.UnwrapSDKContext(ctx)
params := k.GetParams(sdkctx)

Expand All @@ -136,6 +161,13 @@
},
interpreter.Predicates{})

extendedRegistry := interpreter.NewRegistry()

for _, manifest := range manifests {
predicates[manifest.Name] = manifest.Cost
extendedRegistry[manifest.Name] = predicate.NewWasmExtension(manifest.ContractAddress, manifest.Name)
}

whitelistUrls := lo.Map(
util.NonZeroOrDefault(interpreterParams.VirtualFilesFilter.Whitelist, []string{}),
util.Indexed(util.ParseURLMust))
Expand All @@ -144,7 +176,7 @@
util.Indexed(util.ParseURLMust))

options := []interpreter.Option{
interpreter.WithPredicates(ctx, predicates, gasMeter),
interpreter.WithPredicates(ctx, extendedRegistry, predicates, gasMeter),
interpreter.WithBootstrap(ctx, util.NonZeroOrDefault(interpreterParams.GetBootstrap(), bootstrap.Bootstrap())),
interpreter.WithFS(fs.NewFilteredFS(whitelistUrls, blacklistUrls, k.fsProvider(ctx))),
}
Expand Down
3 changes: 3 additions & 0 deletions x/logic/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type (
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
fsProvider FSProvider
wasmKeeper types.WasmKeeper
}
)

Expand All @@ -38,6 +39,7 @@ func NewKeeper(
authKeeper types.AccountKeeper,
bankKeeper types.BankKeeper,
fsProvider FSProvider,
wasmKeeper types.WasmKeeper,
) *Keeper {
// ensure gov module account is set and is not nil
if err := sdk.VerifyAddressFormat(authority); err != nil {
Expand All @@ -52,6 +54,7 @@ func NewKeeper(
authKeeper: authKeeper,
bankKeeper: bankKeeper,
fsProvider: fsProvider,
wasmKeeper: wasmKeeper,
}
}

Expand Down
2 changes: 2 additions & 0 deletions x/logic/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func TestUpdateParams(t *testing.T) {
accountKeeper := logictestutil.NewMockAccountKeeper(ctrl)
bankKeeper := logictestutil.NewMockBankKeeper(ctrl)
fsProvider := logictestutil.NewMockFS(ctrl)
wasmKeeper := logictestutil.NewMockWasmKeeper(ctrl)

logicKeeper := keeper.NewKeeper(
encCfg.Codec,
Expand All @@ -69,6 +70,7 @@ func TestUpdateParams(t *testing.T) {
func(ctx gocontext.Context) fs.FS {
return fsProvider
},
wasmKeeper,
)

msgServer := keeper.NewMsgServerImpl(*logicKeeper)
Expand Down
156 changes: 156 additions & 0 deletions x/logic/predicate/wasm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package predicate

import (
"fmt"
"context"
"encoding/json"
"strings"


"github.com/ichiban/prolog/engine"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/okp4/okp4d/x/logic/util"
"github.com/okp4/okp4d/x/logic/types"
)

func NewWasmExtension(contractAddress sdk.AccAddress, name string) any {
parts := strings.Split(name, "/")
if len(parts) != 2 {
return nil
}

arity := parts[1]
switch arity {
case "1":
return func(vm *engine.VM, arg0 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery(name, vm, contractAddress, []engine.Term{arg0}, cont, env)
}
case "2":
return func(vm *engine.VM, arg0, arg1 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery(name, vm, contractAddress, []engine.Term{arg0, arg1}, cont, env)
}
case "3":
return func(vm *engine.VM, arg0, arg1, arg2 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery(name, vm, contractAddress, []engine.Term{arg0, arg1, arg2}, cont, env)
}
default:
return nil
}
}

type PrologQueryMsg struct {
PrologExtensionManifest *PrologExtensionManifest `json:"prolog_extension_manifest,omitempty"`
}

type PrologExtensionManifest struct {
PredicateName string `json:"predicate_name"`
Args []string `json:"args"`
}

type PrologQueryResult struct {
Solve *PrologSolveResult `json:"solve"`
}

type PrologSolveResult struct {
Solutions [][]string `json:"solutions"`
// Continuation *SolveContinuation `json:"continuation"`
}

func solvePredicate(ctx sdk.Context, wasm types.WasmKeeper, contractAddr sdk.AccAddress, predicateName string, termArgs []engine.Term) ([][]engine.Term, error) {
args := make([]string, len(termArgs))
for i, arg := range termArgs {
switch arg := arg.(type) {
case engine.Atom:
args[i] = arg.String()
case engine.Variable:
args[i] = ""
}
}

msg := PrologQueryMsg {
PrologExtensionManifest: &PrologExtensionManifest {
PredicateName: predicateName,
Args: args,
},
}
bz, err := json.Marshal(msg)

resbz, err := wasm.QuerySmart(ctx, contractAddr, bz)
if err != nil {
return nil, err
}

var res PrologQueryResult
err = json.Unmarshal(resbz, &res)
if err != nil {
return nil, err
}

solutions := make([][]engine.Term, len(res.Solve.Solutions))
for i, solution := range res.Solve.Solutions {
solutions[i] = make([]engine.Term, len(solution))
for j, atom := range solution {
arg := termArgs[j]
switch arg := arg.(type) {
case engine.Atom:
if arg.String() != atom {
return nil, fmt.Errorf("unexpected atom: %s", atom)
}
solutions[i][j] = engine.NewAtom(atom)
case engine.Variable:
solutions[i][j] = engine.NewAtom(atom) // will be unified in CosmwasmQuery
}
}
}

return solutions, nil
}

func CosmWasmQuery(predicate string, vm *engine.VM, contractAddress sdk.AccAddress, args []engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return engine.Delay(func(ctx context.Context) *engine.Promise {
sdkContext, err := util.UnwrapSDKContext(ctx)
if err != nil {
return engine.Error(err)
}
wasmKeeper := sdkContext.Value(types.CosmWasmKeeperContextKey).(types.WasmKeeper)

solutions, err := solvePredicate(sdkContext, wasmKeeper, contractAddress, predicate, args)
if err != nil {
return engine.Error(fmt.Errorf("%s: %w", predicate, err))
}

promises := make([]func(ctx context.Context) *engine.Promise, len(solutions))
for i, solution := range solutions {
promise := func(ctx context.Context) *engine.Promise {
return engine.Unify(
vm,
Tuple(solution...),
Tuple(args[2:]...),
cont,
env,
)
}
promises[i] = promise
}

return engine.Delay(promises...)
})
}
/*
func CosmWasmQuery3(vm *engine.VM, contractAddress engine.Term, predicateName engine.Term, arg0 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery("cosmwasm_query/3", vm, contractAddress, predicateName, []engine.Term{arg0}, cont, env)
}

func CosmWasmQuery4(vm *engine.VM, contractAddress engine.Term, predicateName engine.Term, arg0, arg1 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery("cosmwasm_query/4", vm, contractAddress, predicateName, []engine.Term{arg0, arg1}, cont, env)
}

func CosmWasmQuery5(vm *engine.VM, contractAddress engine.Term, predicateName engine.Term, arg0, arg1, arg2 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery("cosmwasm_query/5", vm, contractAddress, predicateName, []engine.Term{arg0, arg1, arg2}, cont, env)
}

func CosmWasmQuery6(vm *engine.VM, contractAddress engine.Term, predicateName engine.Term, arg0, arg1, arg2, arg3 engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
return CosmWasmQuery("cosmwasm_query/6", vm, contractAddress, predicateName, []engine.Term{arg0, arg1, arg2, arg3}, cont, env)
}
*/
2 changes: 2 additions & 0 deletions x/logic/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ const (
AuthKeeperContextKey = ContextKey("authKeeper")
// BankKeeperContextKey is the context key for the bank keeper.
BankKeeperContextKey = ContextKey("bankKeeper")

CosmWasmKeeperContextKey = ContextKey("cosmWasmKeeper")
)
Loading