cosmos-sdk/x/accounts/internal/implementation/context.go
testinginprod 112f6cbdae
feat(accounts): use gogoproto API instead of protov2. (#18653)
Co-authored-by: unknown unknown <unknown@unknown>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2023-12-08 13:17:48 +00:00

113 lines
4.1 KiB
Go

package implementation
import (
"context"
"cosmossdk.io/collections"
"cosmossdk.io/core/store"
"cosmossdk.io/x/accounts/internal/prefixstore"
)
var AccountStatePrefix = collections.NewPrefix(255)
type (
ModuleExecUntypedFunc = func(ctx context.Context, sender []byte, msg ProtoMsg) (ProtoMsg, error)
ModuleExecFunc = func(ctx context.Context, sender []byte, msg, msgResp ProtoMsg) error
ModuleQueryFunc = func(ctx context.Context, queryReq, queryResp ProtoMsg) error
)
type contextKey struct{}
type contextValue struct {
store store.KVStore // store is the prefixed store for the account.
sender []byte // sender is the address of the entity invoking the account action.
whoami []byte // whoami is the address of the account being invoked.
originalContext context.Context // originalContext that was used to build the account context.
moduleExec ModuleExecFunc // moduleExec is a function that executes a module message, when the resp type is known.
moduleExecUntyped ModuleExecUntypedFunc // moduleExecUntyped is a function that executes a module message, when the resp type is unknown.
moduleQuery ModuleQueryFunc // moduleQuery is a function that queries a module.
}
// MakeAccountContext creates a new account execution context given:
// storeSvc: which fetches the x/accounts module store.
// accountAddr: the address of the account being invoked, which is used to give the
// account a prefixed storage.
// sender: the address of entity invoking the account action.
// moduleExec: a function that executes a module message.
// moduleQuery: a function that queries a module.
func MakeAccountContext(
ctx context.Context,
storeSvc store.KVStoreService,
accountAddr,
sender []byte,
moduleExec ModuleExecFunc,
moduleExecUntyped ModuleExecUntypedFunc,
moduleQuery ModuleQueryFunc,
) context.Context {
return context.WithValue(ctx, contextKey{}, contextValue{
store: prefixstore.New(storeSvc.OpenKVStore(ctx), append(AccountStatePrefix, accountAddr...)),
sender: sender,
whoami: accountAddr,
originalContext: ctx,
moduleExecUntyped: moduleExecUntyped,
moduleExec: moduleExec,
moduleQuery: moduleQuery,
})
}
// ExecModuleUntyped can be used to execute a message towards a module, when the response type is unknown.
func ExecModuleUntyped(ctx context.Context, msg ProtoMsg) (ProtoMsg, error) {
// get sender
v := ctx.Value(contextKey{}).(contextValue)
resp, err := v.moduleExecUntyped(v.originalContext, v.whoami, msg)
if err != nil {
return nil, err
}
return resp, nil
}
// ExecModule can be used to execute a message towards a module.
func ExecModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG[Req]](ctx context.Context, msg ReqProto) (RespProto, error) {
// get sender
v := ctx.Value(contextKey{}).(contextValue)
// execute module, unwrapping the original context.
resp := RespProto(new(Resp))
err := v.moduleExec(v.originalContext, v.whoami, msg, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// QueryModule can be used by an account to execute a module query.
func QueryModule[Resp any, RespProto ProtoMsgG[Resp], Req any, ReqProto ProtoMsgG[Req]](ctx context.Context, req ReqProto) (RespProto, error) {
// we do not need to check the sender in a query because it is not a state transition.
// we also unwrap the original context.
v := ctx.Value(contextKey{}).(contextValue)
resp := RespProto(new(Resp))
err := v.moduleQuery(v.originalContext, req, resp)
if err != nil {
return nil, err
}
return resp, nil
}
// OpenKVStore returns the prefixed store for the account given the context.
func OpenKVStore(ctx context.Context) store.KVStore {
return ctx.Value(contextKey{}).(contextValue).store
}
// Sender returns the address of the entity invoking the account action.
func Sender(ctx context.Context) []byte {
return ctx.Value(contextKey{}).(contextValue).sender
}
// Whoami returns the address of the account being invoked.
func Whoami(ctx context.Context) []byte {
return ctx.Value(contextKey{}).(contextValue).whoami
}