mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
feature/1220 origin handle exists method (#1241)
* feat: add docs and CI workflow for publishing to onsonr.dev * (refactor): Move hway,motr executables to their own repos * feat: simplify devnet and testnet configurations * refactor: update import path for didcrypto package * docs(networks): Add README with project overview, architecture, and community links * refactor: Move network configurations to deploy directory * build: update golang version to 1.23 * refactor: move logger interface to appropriate package * refactor: Move devnet configuration to networks/devnet * chore: improve release process with date variable * (chore): Move Crypto Library * refactor: improve code structure and readability in DID module * feat: integrate Trunk CI checks * ci: optimize CI workflow by removing redundant build jobs --------- Co-authored-by: Darp Alakun <i@prad.nu>
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/sonr/internal/transaction"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoInitHandler = errors.New("no init handler")
|
||||
errNoExecuteHandler = errors.New("account does not accept messages")
|
||||
errInvalidMessage = errors.New("invalid message")
|
||||
)
|
||||
|
||||
// NewInitBuilder creates a new InitBuilder instance.
|
||||
func NewInitBuilder() *InitBuilder {
|
||||
return &InitBuilder{}
|
||||
}
|
||||
|
||||
// InitBuilder defines a smart account's initialisation handler builder.
|
||||
type InitBuilder struct {
|
||||
// handler is the handler function that will be called when the smart account is initialized.
|
||||
// Although the function here is defined to take an any, the smart account will work
|
||||
// with a typed version of it.
|
||||
handler func(ctx context.Context, initRequest transaction.Msg) (initResponse transaction.Msg, err error)
|
||||
|
||||
// schema is the schema of the message that will be passed to the handler function.
|
||||
schema HandlerSchema
|
||||
}
|
||||
|
||||
// makeHandler returns the handler function that will be called when the smart account is initialized.
|
||||
// It returns an error if no handler was registered.
|
||||
func (i *InitBuilder) makeHandler() (func(ctx context.Context, initRequest transaction.Msg) (initResponse transaction.Msg, err error), error) {
|
||||
if i.handler == nil {
|
||||
return nil, errNoInitHandler
|
||||
}
|
||||
return i.handler, nil
|
||||
}
|
||||
|
||||
// NewExecuteBuilder creates a new ExecuteBuilder instance.
|
||||
func NewExecuteBuilder() *ExecuteBuilder {
|
||||
return &ExecuteBuilder{
|
||||
handlers: make(map[string]func(ctx context.Context, executeRequest transaction.Msg) (executeResponse transaction.Msg, err error)),
|
||||
handlersSchema: make(map[string]HandlerSchema),
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteBuilder defines a smart account's execution router, it will be used to map an execution message
|
||||
// to a handler function for a specific account.
|
||||
type ExecuteBuilder struct {
|
||||
// handlers is a map of handler functions that will be called when the smart account is executed.
|
||||
handlers map[string]func(ctx context.Context, executeRequest transaction.Msg) (executeResponse transaction.Msg, err error)
|
||||
|
||||
// handlersSchema is a map of schemas for the messages that will be passed to the handler functions
|
||||
// and the messages that will be returned by the handler functions.
|
||||
handlersSchema map[string]HandlerSchema
|
||||
|
||||
// err is the error that occurred before building the handler function.
|
||||
err error
|
||||
}
|
||||
|
||||
func (r *ExecuteBuilder) makeHandler() (func(ctx context.Context, executeRequest transaction.Msg) (executeResponse transaction.Msg, err error), error) {
|
||||
// if no handler is registered it's fine, it means the account will not be accepting execution or query messages.
|
||||
if len(r.handlers) == 0 {
|
||||
return func(ctx context.Context, _ transaction.Msg) (_ transaction.Msg, err error) {
|
||||
return nil, errNoExecuteHandler
|
||||
}, nil
|
||||
}
|
||||
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
|
||||
// build the real execution handler
|
||||
return func(ctx context.Context, executeRequest transaction.Msg) (executeResponse transaction.Msg, err error) {
|
||||
messageName := MessageName(executeRequest)
|
||||
handler, ok := r.handlers[messageName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: no handler for message %s", errInvalidMessage, messageName)
|
||||
}
|
||||
return handler(ctx, executeRequest)
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewQueryBuilder creates a new QueryBuilder instance.
|
||||
func NewQueryBuilder() *QueryBuilder {
|
||||
return &QueryBuilder{
|
||||
er: NewExecuteBuilder(),
|
||||
}
|
||||
}
|
||||
|
||||
// QueryBuilder defines a smart account's query router, it will be used to map a query message
|
||||
// to a handler function for a specific account.
|
||||
type QueryBuilder struct {
|
||||
// er is the ExecuteBuilder, since there's no difference between the execution and query handlers API.
|
||||
er *ExecuteBuilder
|
||||
}
|
||||
|
||||
func (r *QueryBuilder) makeHandler() (func(ctx context.Context, queryRequest transaction.Msg) (queryResponse transaction.Msg, err error), error) {
|
||||
return r.er.makeHandler()
|
||||
}
|
||||
|
||||
// IsRoutingError returns true if the error is a routing error,
|
||||
// which typically occurs when a message cannot be matched to a handler.
|
||||
func IsRoutingError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
return errors.Is(err, errInvalidMessage)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
"cosmossdk.io/core/store"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/onsonr/sonr/internal/prefixstore"
|
||||
"github.com/onsonr/sonr/internal/transaction"
|
||||
)
|
||||
|
||||
var AccountStatePrefix = collections.NewPrefix(255)
|
||||
|
||||
type (
|
||||
ModuleExecFunc = func(ctx context.Context, sender []byte, msg transaction.Msg) (transaction.Msg, error)
|
||||
ModuleQueryFunc = func(ctx context.Context, queryReq transaction.Msg) (transaction.Msg, 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.
|
||||
funds sdk.Coins // funds reports the coins sent alongside the request.
|
||||
parentContext context.Context // parentContext that was used to build the account context.
|
||||
moduleExec ModuleExecFunc // moduleExec is a function that executes a module message, when the resp type is unknown.
|
||||
moduleQuery ModuleQueryFunc // moduleQuery is a function that queries a module.
|
||||
}
|
||||
|
||||
func addCtx(ctx context.Context, value contextValue) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, value)
|
||||
}
|
||||
|
||||
func getCtx(ctx context.Context) contextValue {
|
||||
return ctx.Value(contextKey{}).(contextValue)
|
||||
}
|
||||
|
||||
// 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,
|
||||
accNumber uint64,
|
||||
accountAddr []byte,
|
||||
sender []byte,
|
||||
funds sdk.Coins,
|
||||
moduleExec ModuleExecFunc,
|
||||
moduleQuery ModuleQueryFunc,
|
||||
) context.Context {
|
||||
return addCtx(ctx, contextValue{
|
||||
store: makeAccountStore(ctx, storeSvc, accNumber),
|
||||
sender: sender,
|
||||
whoami: accountAddr,
|
||||
funds: funds,
|
||||
parentContext: ctx,
|
||||
moduleExec: moduleExec,
|
||||
moduleQuery: moduleQuery,
|
||||
})
|
||||
}
|
||||
|
||||
func SetSender(ctx context.Context, sender []byte) context.Context {
|
||||
v := getCtx(ctx)
|
||||
v.sender = sender
|
||||
return addCtx(v.parentContext, v)
|
||||
}
|
||||
|
||||
// makeAccountStore creates the prefixed store for the account.
|
||||
// It uses the number of the account, this gives constant size
|
||||
// bytes prefixes for the account state.
|
||||
func makeAccountStore(ctx context.Context, storeSvc store.KVStoreService, accNum uint64) store.KVStore {
|
||||
prefix := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(prefix, accNum)
|
||||
return prefixstore.New(storeSvc.OpenKVStore(ctx), append(AccountStatePrefix, prefix...))
|
||||
}
|
||||
|
||||
// ExecModule can be used to execute a message towards a module, when the response type is unknown.
|
||||
func ExecModule(ctx context.Context, msg transaction.Msg) (transaction.Msg, error) {
|
||||
// get sender
|
||||
v := getCtx(ctx)
|
||||
|
||||
resp, err := v.moduleExec(v.parentContext, v.whoami, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// QueryModule can be used by an account to execute a module query.
|
||||
func QueryModule(ctx context.Context, req transaction.Msg) (transaction.Msg, 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 := getCtx(ctx)
|
||||
resp, err := v.moduleQuery(v.parentContext, req)
|
||||
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 getCtx(ctx).store }
|
||||
|
||||
// Sender returns the address of the entity invoking the account action.
|
||||
func Sender(ctx context.Context) []byte {
|
||||
return getCtx(ctx).sender
|
||||
}
|
||||
|
||||
// Whoami returns the address of the account being invoked.
|
||||
func Whoami(ctx context.Context) []byte {
|
||||
return getCtx(ctx).whoami
|
||||
}
|
||||
|
||||
// Funds returns the funds associated with the execution context.
|
||||
func Funds(ctx context.Context) sdk.Coins { return getCtx(ctx).funds }
|
||||
@@ -0,0 +1,66 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/gogoproto/proto"
|
||||
|
||||
"github.com/onsonr/sonr/internal/transaction"
|
||||
)
|
||||
|
||||
// ProtoMsgG is a generic interface for protobuf messages.
|
||||
type ProtoMsgG[T any] interface {
|
||||
*T
|
||||
transaction.Msg
|
||||
}
|
||||
|
||||
type Any = codectypes.Any
|
||||
|
||||
func FindMessageByName(name string) (transaction.Msg, error) {
|
||||
typ := proto.MessageType(name)
|
||||
if typ == nil {
|
||||
return nil, fmt.Errorf("no message type found for %s", name)
|
||||
}
|
||||
return reflect.New(typ.Elem()).Interface().(transaction.Msg), nil
|
||||
}
|
||||
|
||||
func MessageName(msg transaction.Msg) string {
|
||||
return proto.MessageName(msg)
|
||||
}
|
||||
|
||||
// PackAny packs a proto message into an anypb.Any.
|
||||
func PackAny(msg transaction.Msg) (*Any, error) {
|
||||
return codectypes.NewAnyWithValue(msg)
|
||||
}
|
||||
|
||||
// UnpackAny unpacks an anypb.Any into a proto message.
|
||||
func UnpackAny[T any, PT ProtoMsgG[T]](anyPB *Any) (PT, error) {
|
||||
to := new(T)
|
||||
return to, UnpackAnyTo(anyPB, PT(to))
|
||||
}
|
||||
|
||||
func UnpackAnyTo(anyPB *Any, to transaction.Msg) error {
|
||||
return proto.Unmarshal(anyPB.Value, to)
|
||||
}
|
||||
|
||||
func UnpackAnyRaw(anyPB *Any) (proto.Message, error) {
|
||||
split := strings.Split(anyPB.TypeUrl, "/")
|
||||
name := split[len(split)-1]
|
||||
typ := proto.MessageType(name)
|
||||
if typ == nil {
|
||||
return nil, fmt.Errorf("no message type found for %s", name)
|
||||
}
|
||||
to := reflect.New(typ.Elem()).Interface().(proto.Message)
|
||||
return to, UnpackAnyTo(anyPB, to)
|
||||
}
|
||||
|
||||
func Merge(a, b transaction.Msg) {
|
||||
proto.Merge(a, b)
|
||||
}
|
||||
|
||||
func Equal(a, b transaction.Msg) bool {
|
||||
return proto.Equal(a, b)
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"cosmossdk.io/collections"
|
||||
"cosmossdk.io/core/address"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
gogoproto "github.com/cosmos/gogoproto/proto"
|
||||
|
||||
"github.com/onsonr/sonr/internal/appmodule"
|
||||
"github.com/onsonr/sonr/internal/transaction"
|
||||
)
|
||||
|
||||
// Dependencies are passed to the constructor of a smart account.
|
||||
type Dependencies struct {
|
||||
SchemaBuilder *collections.SchemaBuilder
|
||||
AddressCodec address.Codec
|
||||
Environment appmodule.Environment
|
||||
LegacyStateCodec interface {
|
||||
Marshal(gogoproto.Message) ([]byte, error)
|
||||
Unmarshal([]byte, gogoproto.Message) error
|
||||
}
|
||||
}
|
||||
|
||||
// AccountCreatorFunc is a function that creates an account.
|
||||
type AccountCreatorFunc = func(deps Dependencies) (string, Account, error)
|
||||
|
||||
// MakeAccountsMap creates a map of account names to account implementations
|
||||
// from a list of account creator functions.
|
||||
func MakeAccountsMap(
|
||||
cdc codec.Codec,
|
||||
addressCodec address.Codec,
|
||||
env appmodule.Environment,
|
||||
accounts []AccountCreatorFunc,
|
||||
) (map[string]Implementation, error) {
|
||||
accountsMap := make(map[string]Implementation, len(accounts))
|
||||
for _, makeAccount := range accounts {
|
||||
stateSchemaBuilder := collections.NewSchemaBuilderFromAccessor(openKVStore)
|
||||
deps := Dependencies{
|
||||
SchemaBuilder: stateSchemaBuilder,
|
||||
AddressCodec: addressCodec,
|
||||
Environment: env,
|
||||
LegacyStateCodec: cdc,
|
||||
}
|
||||
name, accountInterface, err := makeAccount(deps)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create account %s: %w", name, err)
|
||||
}
|
||||
if _, ok := accountsMap[name]; ok {
|
||||
return nil, fmt.Errorf("account %s is already registered", name)
|
||||
}
|
||||
impl, err := newImplementation(stateSchemaBuilder, accountInterface)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create implementation for account %s: %w", name, err)
|
||||
}
|
||||
accountsMap[name] = impl
|
||||
}
|
||||
|
||||
return accountsMap, nil
|
||||
}
|
||||
|
||||
// newImplementation creates a new Implementation instance given an Account implementer.
|
||||
func newImplementation(schemaBuilder *collections.SchemaBuilder, account Account) (Implementation, error) {
|
||||
// make init handler
|
||||
ir := NewInitBuilder()
|
||||
account.RegisterInitHandler(ir)
|
||||
initHandler, err := ir.makeHandler()
|
||||
if err != nil {
|
||||
return Implementation{}, err
|
||||
}
|
||||
|
||||
// make execute handler
|
||||
er := NewExecuteBuilder()
|
||||
account.RegisterExecuteHandlers(er)
|
||||
executeHandler, err := er.makeHandler()
|
||||
if err != nil {
|
||||
return Implementation{}, err
|
||||
}
|
||||
|
||||
// make query handler
|
||||
qr := NewQueryBuilder()
|
||||
account.RegisterQueryHandlers(qr)
|
||||
queryHandler, err := qr.makeHandler()
|
||||
if err != nil {
|
||||
return Implementation{}, err
|
||||
}
|
||||
|
||||
// build schema
|
||||
schema, err := schemaBuilder.Build()
|
||||
if err != nil {
|
||||
return Implementation{}, err
|
||||
}
|
||||
return Implementation{
|
||||
Init: initHandler,
|
||||
Execute: executeHandler,
|
||||
Query: queryHandler,
|
||||
CollectionsSchema: schema,
|
||||
InitHandlerSchema: ir.schema,
|
||||
QueryHandlersSchema: qr.er.handlersSchema,
|
||||
ExecuteHandlersSchema: er.handlersSchema,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Implementation wraps an Account implementer in order to provide a concrete
|
||||
// and non-generic implementation usable by the x/accounts module.
|
||||
type Implementation struct {
|
||||
// Init defines the initialisation handler for the smart account.
|
||||
Init func(ctx context.Context, msg transaction.Msg) (resp transaction.Msg, err error)
|
||||
// Execute defines the execution handler for the smart account.
|
||||
Execute func(ctx context.Context, msg transaction.Msg) (resp transaction.Msg, err error)
|
||||
// Query defines the query handler for the smart account.
|
||||
Query func(ctx context.Context, msg transaction.Msg) (resp transaction.Msg, err error)
|
||||
// CollectionsSchema represents the state schema.
|
||||
CollectionsSchema collections.Schema
|
||||
// InitHandlerSchema represents the init handler schema.
|
||||
InitHandlerSchema HandlerSchema
|
||||
// QueryHandlersSchema is the schema of the query handlers.
|
||||
QueryHandlersSchema map[string]HandlerSchema
|
||||
// ExecuteHandlersSchema is the schema of the execute handlers.
|
||||
ExecuteHandlersSchema map[string]HandlerSchema
|
||||
}
|
||||
|
||||
// HasExec returns true if the account can execute the given msg.
|
||||
func (i Implementation) HasExec(m transaction.Msg) bool {
|
||||
_, ok := i.ExecuteHandlersSchema[MessageName(m)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasQuery returns true if the account can execute the given request.
|
||||
func (i Implementation) HasQuery(m transaction.Msg) bool {
|
||||
_, ok := i.QueryHandlersSchema[MessageName(m)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasInit returns true if the account uses the provided init message.
|
||||
func (i Implementation) HasInit(m transaction.Msg) bool {
|
||||
return i.InitHandlerSchema.RequestSchema.Name == MessageName(m)
|
||||
}
|
||||
|
||||
// MessageSchema defines the schema of a message.
|
||||
// A message can also define a state schema.
|
||||
type MessageSchema struct {
|
||||
// Name identifies the message name, this must be queryable from some reflection service.
|
||||
Name string
|
||||
// New is used to create a new message instance for the schema.
|
||||
New func() transaction.Msg
|
||||
}
|
||||
|
||||
// HandlerSchema defines the schema of a handler.
|
||||
type HandlerSchema struct {
|
||||
// RequestSchema defines the schema of the request.
|
||||
RequestSchema MessageSchema
|
||||
// ResponseSchema defines the schema of the response.
|
||||
ResponseSchema MessageSchema
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package accounts
|
||||
|
||||
// Account defines a smart account interface.
|
||||
type Account interface {
|
||||
// RegisterInitHandler allows the smart account to register an initialisation handler, using
|
||||
// the provided InitBuilder. The handler will be called when the smart account is initialized
|
||||
// (deployed).
|
||||
RegisterInitHandler(builder *InitBuilder)
|
||||
|
||||
// RegisterExecuteHandlers allows the smart account to register execution handlers.
|
||||
// The smart account might also decide to not register any execution handler.
|
||||
RegisterExecuteHandlers(builder *ExecuteBuilder)
|
||||
|
||||
// RegisterQueryHandlers allows the smart account to register query handlers. The smart account
|
||||
// might also decide to not register any query handler.
|
||||
RegisterQueryHandlers(builder *QueryBuilder)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/onsonr/sonr/internal/transaction"
|
||||
)
|
||||
|
||||
// RegisterInitHandler registers an initialisation handler for a smart account that uses protobuf.
|
||||
func RegisterInitHandler[
|
||||
Req any, ProtoReq ProtoMsgG[Req], Resp any, ProtoResp ProtoMsgG[Resp],
|
||||
](router *InitBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
|
||||
) {
|
||||
reqName := MessageName(ProtoReq(new(Req)))
|
||||
|
||||
router.handler = func(ctx context.Context, initRequest transaction.Msg) (initResponse transaction.Msg, err error) {
|
||||
concrete, ok := initRequest.(ProtoReq)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: wanted %s, got %T", errInvalidMessage, reqName, initRequest)
|
||||
}
|
||||
return handler(ctx, concrete)
|
||||
}
|
||||
|
||||
router.schema = HandlerSchema{
|
||||
RequestSchema: *NewProtoMessageSchema[Req, ProtoReq](),
|
||||
ResponseSchema: *NewProtoMessageSchema[Resp, ProtoResp](),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterExecuteHandler registers an execution handler for a smart account that uses protobuf.
|
||||
func RegisterExecuteHandler[
|
||||
Req any, ProtoReq ProtoMsgG[Req], Resp any, ProtoResp ProtoMsgG[Resp],
|
||||
](router *ExecuteBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
|
||||
) {
|
||||
reqName := MessageName(ProtoReq(new(Req)))
|
||||
// check if not registered already
|
||||
if _, ok := router.handlers[reqName]; ok {
|
||||
router.err = fmt.Errorf("handler already registered for message %s", reqName)
|
||||
return
|
||||
}
|
||||
|
||||
router.handlers[reqName] = func(ctx context.Context, executeRequest transaction.Msg) (executeResponse transaction.Msg, err error) {
|
||||
concrete, ok := executeRequest.(ProtoReq)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: wanted %s, got %T", errInvalidMessage, reqName, executeRequest)
|
||||
}
|
||||
return handler(ctx, concrete)
|
||||
}
|
||||
|
||||
router.handlersSchema[reqName] = HandlerSchema{
|
||||
RequestSchema: *NewProtoMessageSchema[Req, ProtoReq](),
|
||||
ResponseSchema: *NewProtoMessageSchema[Resp, ProtoResp](),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterQueryHandler registers a query handler for a smart account that uses protobuf.
|
||||
func RegisterQueryHandler[
|
||||
Req any, ProtoReq ProtoMsgG[Req], Resp any, ProtoResp ProtoMsgG[Resp],
|
||||
](router *QueryBuilder, handler func(ctx context.Context, req ProtoReq) (ProtoResp, error),
|
||||
) {
|
||||
RegisterExecuteHandler(router.er, handler)
|
||||
}
|
||||
|
||||
func NewProtoMessageSchema[T any, PT ProtoMsgG[T]]() *MessageSchema {
|
||||
msg := PT(new(T))
|
||||
if _, ok := (interface{}(msg)).(proto.Message); ok {
|
||||
panic("protov2 messages are not supported")
|
||||
}
|
||||
return &MessageSchema{
|
||||
Name: MessageName(msg),
|
||||
New: func() transaction.Msg {
|
||||
return PT(new(T))
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user