From 4eb6b478cd9ab1b3d4268028611abfa86a3381e2 Mon Sep 17 00:00:00 2001 From: Benjamin DENEUX Date: Mon, 6 Mar 2023 15:40:40 +0100 Subject: [PATCH] feat(logic): call wasm contract from fileSystem --- app/app.go | 3 +- app/wasm/query.go | 2 +- x/logic/interpreter/fs/fs.go | 93 +++++++++++++++++++++++++++++- x/logic/interpreter/interpreter.go | 2 +- x/logic/keeper/interpreter.go | 2 + x/logic/predicate/wasm.go | 36 ++++++++++++ x/logic/types/context.go | 2 + x/logic/wasm/query.go | 4 +- 8 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 x/logic/predicate/wasm.go diff --git a/app/app.go b/app/app.go index de5eb671..1439b67b 100644 --- a/app/app.go +++ b/app/app.go @@ -567,7 +567,7 @@ func New( wasmOpts = append(wasmOpts, wasmkeeper.WithVMCacheMetrics(prometheus.DefaultRegisterer)) } - wasmOpts = append(wasmOpts, wasmkeeper.WithQueryPlugins(okp4wasm.CustomQueryPlugins(app.LogicKeeper))) + wasmOpts = append(wasmOpts, wasmkeeper.WithQueryPlugins(okp4wasm.CustomQueryPlugins(&app.LogicKeeper))) // The last arguments can contain custom message handlers, and custom query handlers, // if we want to allow any custom callbacks @@ -591,7 +591,6 @@ func New( availableCapabilities, wasmOpts..., ) - app.LogicKeeper.WasmKeeper = app.WasmKeeper govRouter := govv1beta1.NewRouter() diff --git a/app/wasm/query.go b/app/wasm/query.go index 1e571e8a..068a27ca 100644 --- a/app/wasm/query.go +++ b/app/wasm/query.go @@ -19,7 +19,7 @@ type customQuery struct { // CustomQueryPlugins creates a wasm QueryPlugins containing the custom querier managing wasm contracts queries to the // logic module. -func CustomQueryPlugins(logicKeeper logickeeper.Keeper) *wasmkeeper.QueryPlugins { +func CustomQueryPlugins(logicKeeper *logickeeper.Keeper) *wasmkeeper.QueryPlugins { return &wasmkeeper.QueryPlugins{ Custom: makeCustomQuerier( logicwasm.MakeLogicQuerier(logicKeeper), diff --git a/x/logic/interpreter/fs/fs.go b/x/logic/interpreter/fs/fs.go index 1a0d1427..21cbc1b6 100644 --- a/x/logic/interpreter/fs/fs.go +++ b/x/logic/interpreter/fs/fs.go @@ -1,24 +1,113 @@ package fs import ( + goctx "context" + "encoding/base64" + "encoding/json" "fmt" "io/fs" + "time" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/okp4/okp4d/x/logic/types" ) type FileSystem struct { + ctx goctx.Context wasmKeeper types.WasmKeeper } // New return a new FileSystem object that will handle all virtual file on the interpreter. // File can be provided from different sources like CosmWasm cw-storage smart contract. -func New(keeper types.WasmKeeper) FileSystem { +func New(ctx goctx.Context, keeper types.WasmKeeper) FileSystem { return FileSystem{ + ctx: ctx, wasmKeeper: keeper, } } func (f FileSystem) Open(name string) (fs.File, error) { - return nil, fmt.Errorf("not implemented") + data, err := f.ReadFile(name) + return Object(data), err +} + +// ReadFile reads the named file and returns its contents. +// A successful call returns a nil error, not io.EOF. +// (Because ReadFi le reads the whole file, the expected EOF +// from the final Read is not treated as an error to be reported.) +// +// The caller is permitted to modify the returned byte slice. +// This method should return a copy of the underlying data. +func (f FileSystem) ReadFile(name string) ([]byte, error) { + sdkCtx := sdk.UnwrapSDKContext(f.ctx) + + req := []byte(fmt.Sprintf("{\"object_data\":{\"id\": \"%s\"}}", name)) + contractAddr, err := sdk.AccAddressFromBech32("okp415ekvz3qdter33mdnk98v8whv5qdr53yusksnfgc08xd26fpdn3ts8gddht") + if err != nil { + return nil, err + } + + data, err := f.wasmKeeper.QuerySmart(sdkCtx, contractAddr, req) + if err != nil { + return nil, err + } + var program string + err = json.Unmarshal(data, &program) + if err != nil { + return nil, err + } + + decoded, err := base64.StdEncoding.DecodeString(program) + return decoded, err +} + +type Object []byte + +type ObjectInfo struct { + name string + size int64 +} + +func From(object Object) ObjectInfo { + return ObjectInfo{ + name: "contract", + size: int64(len(object)), + } +} + +func (o ObjectInfo) Name() string { + return o.name +} + +func (o ObjectInfo) Size() int64 { + return o.size +} + +func (o ObjectInfo) Mode() fs.FileMode { + return fs.ModeIrregular +} + +func (o ObjectInfo) ModTime() time.Time { + return time.Now() +} + +func (o ObjectInfo) IsDir() bool { + return false +} + +func (o ObjectInfo) Sys() any { + return nil +} + +func (o Object) Stat() (fs.FileInfo, error) { + return From(o), nil +} + +func (o Object) Read(bytes []byte) (int, error) { + //TODO implement me + panic("implement me") +} + +func (o Object) Close() error { + return nil } diff --git a/x/logic/interpreter/interpreter.go b/x/logic/interpreter/interpreter.go index 920509e6..78bba085 100644 --- a/x/logic/interpreter/interpreter.go +++ b/x/logic/interpreter/interpreter.go @@ -25,7 +25,7 @@ func New( wasmKeeper types.WasmKeeper, ) (*prolog.Interpreter, error) { var i prolog.Interpreter - i.FS = fs.New(wasmKeeper) + i.FS = fs.New(ctx, wasmKeeper) for _, o := range predicates { if err := Register(&i, o, meter); err != nil { diff --git a/x/logic/keeper/interpreter.go b/x/logic/keeper/interpreter.go index 003e31d9..5ebc7619 100644 --- a/x/logic/keeper/interpreter.go +++ b/x/logic/keeper/interpreter.go @@ -23,6 +23,8 @@ func (k Keeper) enhanceContext(ctx goctx.Context) goctx.Context { sdkCtx = sdkCtx.WithValue(types.AuthKeeperContextKey, k.authKeeper) sdkCtx = sdkCtx.WithValue(types.BankKeeperContextKey, k.bankKeeper) + sdkCtx = sdkCtx.WithValue(types.WasmKeeperContextKey, k.WasmKeeper) + return sdkCtx } diff --git a/x/logic/predicate/wasm.go b/x/logic/predicate/wasm.go new file mode 100644 index 00000000..10437fd2 --- /dev/null +++ b/x/logic/predicate/wasm.go @@ -0,0 +1,36 @@ +package predicate + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/ichiban/prolog/engine" + "github.com/okp4/okp4d/x/logic/types" + "github.com/okp4/okp4d/x/logic/util" +) + +func QueryWasm(vm *engine.VM, contractAddr 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(fmt.Errorf("query_wasm/1: %w", err)) + } + wasmKeeper := sdkContext.Value(types.WasmKeeperContextKey).(types.WasmKeeper) + addr, err := getBech32(env, contractAddr) + if err != nil { + return engine.Error(fmt.Errorf("query_wasm/1: %w", err)) + } + + req := []byte("{\"ask\":{\"query\": \"query_wasm('okp410gnd30r45k9658jm7hzxvp8ehz4ptf33tqjnaepwkunev6kax5ks3mnvmf').\"}}") + if !json.Valid(req) { + return engine.Error(fmt.Errorf("query_wasm/1: wasm query must be a valid json")) + } + res, err := wasmKeeper.QuerySmart(sdkContext, addr, req) + if err != nil { + return engine.Error(fmt.Errorf("query_wasm/1: %w", err)) + } + fmt.Printf("result %w", string(res)) + return engine.Unify(vm, contractAddr, engine.Integer(sdkContext.BlockHeight()), cont, env) + }) +} diff --git a/x/logic/types/context.go b/x/logic/types/context.go index 780ae714..985b075f 100644 --- a/x/logic/types/context.go +++ b/x/logic/types/context.go @@ -8,4 +8,6 @@ const ( AuthKeeperContextKey = ContextKey("authKeeper") // BankKeeperContextKey is the context key for the bank keeper. BankKeeperContextKey = ContextKey("bankKeeper") + // WasmKeeperContextKey is the context key for the wasm keeper. + WasmKeeperContextKey = ContextKey("wasmKeeper") ) diff --git a/x/logic/wasm/query.go b/x/logic/wasm/query.go index ddd4209a..01c801c6 100644 --- a/x/logic/wasm/query.go +++ b/x/logic/wasm/query.go @@ -11,11 +11,11 @@ import ( // LogicQuerier ease the bridge between the logic module with the wasm CustomQuerier to allow wasm contracts to query // the logic module. type LogicQuerier struct { - k keeper.Keeper + k *keeper.Keeper } // MakeLogicQuerier creates a new LogicQuerier based on the logic keeper. -func MakeLogicQuerier(keeper keeper.Keeper) LogicQuerier { +func MakeLogicQuerier(keeper *keeper.Keeper) LogicQuerier { return LogicQuerier{ k: keeper, }