Skip to content

Commit

Permalink
feat(accounts): implement account abstraction execution (#18499)
Browse files Browse the repository at this point in the history
Co-authored-by: unknown unknown <unknown@unknown>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 4, 2023
1 parent 0a7567d commit d3b30e9
Show file tree
Hide file tree
Showing 28 changed files with 1,781 additions and 704 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/auth/vesting) [#17810](https://github.com/cosmos/cosmos-sdk/pull/17810) Add the ability to specify a start time for continuous vesting accounts.
* (runtime) [#18475](https://github.com/cosmos/cosmos-sdk/pull/18475) Adds an implementation for core.branch.Service.
* (server) [#18478](https://github.com/cosmos/cosmos-sdk/pull/18478) CMD flag to disable colored logs.
* (baseapp) [#18499](https://github.com/cosmos/cosmos-sdk/pull/18499) Add `MsgRouter` response type from message name function.

### Improvements

Expand Down
536 changes: 316 additions & 220 deletions api/cosmos/accounts/interfaces/account_abstraction/v1/interface.pulsar.go

Large diffs are not rendered by default.

342 changes: 181 additions & 161 deletions api/cosmos/accounts/v1/account_abstraction.pulsar.go

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions baseapp/internal/protocompat/protocompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ var (

type Handler = func(ctx context.Context, request, response protoiface.MessageV1) error

// MakeHybridHandler returns a handler that can handle both gogo and protov2 messages, no matter
// if the handler is a gogo or protov2 handler.
func MakeHybridHandler(cdc codec.BinaryCodec, sd *grpc.ServiceDesc, method grpc.MethodDesc, handler interface{}) (Handler, error) {
methodFullName := protoreflect.FullName(fmt.Sprintf("%s.%s", sd.ServiceName, method.MethodName))
desc, err := gogoproto.HybridResolver.FindDescriptorByName(methodFullName)
Expand Down Expand Up @@ -221,3 +223,17 @@ func RequestFullNameFromMethodDesc(sd *grpc.ServiceDesc, method grpc.MethodDesc)
}
return methodDesc.Input().FullName(), nil
}

// ResponseFullNameFromMethodDesc returns the fully-qualified name of the response message of the provided service's method.
func ResponseFullNameFromMethodDesc(sd *grpc.ServiceDesc, method grpc.MethodDesc) (protoreflect.FullName, error) {
methodFullName := protoreflect.FullName(fmt.Sprintf("%s.%s", sd.ServiceName, method.MethodName))
desc, err := gogoproto.HybridResolver.FindDescriptorByName(methodFullName)
if err != nil {
return "", fmt.Errorf("cannot find method descriptor %s", methodFullName)
}
methodDesc, ok := desc.(protoreflect.MethodDescriptor)
if !ok {
return "", fmt.Errorf("invalid method descriptor %s", methodFullName)
}
return methodDesc.Output().FullName(), nil
}
17 changes: 15 additions & 2 deletions baseapp/msg_service_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type MsgServiceRouter struct {
interfaceRegistry codectypes.InterfaceRegistry
routes map[string]MsgServiceHandler
hybridHandlers map[string]func(ctx context.Context, req, resp protoiface.MessageV1) error
responseByRequest map[string]string
circuitBreaker CircuitBreaker
}

Expand All @@ -39,8 +40,10 @@ var _ gogogrpc.Server = &MsgServiceRouter{}
// NewMsgServiceRouter creates a new MsgServiceRouter.
func NewMsgServiceRouter() *MsgServiceRouter {
return &MsgServiceRouter{
routes: map[string]MsgServiceHandler{},
hybridHandlers: map[string]func(ctx context.Context, req, resp protoiface.MessageV1) error{},
routes: map[string]MsgServiceHandler{},
hybridHandlers: map[string]func(ctx context.Context, req, resp protoiface.MessageV1) error{},
responseByRequest: map[string]string{},
circuitBreaker: nil,
}
}

Expand Down Expand Up @@ -87,16 +90,26 @@ func (msr *MsgServiceRouter) HybridHandlerByMsgName(msgName string) func(ctx con
return msr.hybridHandlers[msgName]
}

func (msr *MsgServiceRouter) ResponseNameByRequestName(msgName string) string {
return msr.responseByRequest[msgName]
}

func (msr *MsgServiceRouter) registerHybridHandler(sd *grpc.ServiceDesc, method grpc.MethodDesc, handler interface{}) error {
inputName, err := protocompat.RequestFullNameFromMethodDesc(sd, method)
if err != nil {
return err
}
outputName, err := protocompat.ResponseFullNameFromMethodDesc(sd, method)
if err != nil {
return err
}
cdc := codec.NewProtoCodec(msr.interfaceRegistry)
hybridHandler, err := protocompat.MakeHybridHandler(cdc, sd, method, handler)
if err != nil {
return err
}
// map input name to output name
msr.responseByRequest[string(inputName)] = string(outputName)
// if circuit breaker is not nil, then we decorate the hybrid handler with the circuit breaker
if msr.circuitBreaker == nil {
msr.hybridHandlers[string(inputName)] = hybridHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import "cosmos/accounts/v1/account_abstraction.proto";
// MsgAuthenticate is a message that an x/account account abstraction implementer
// must handle to authenticate a state transition.
message MsgAuthenticate {
// bundler defines the address of the bundler that sent the operation.
// NOTE: in case the operation was sent directly by the user, this field will reflect
// the user address.
string bundler = 1;
// user_operation is the operation that the user is trying to perform.
// it also contains authentication information.
cosmos.accounts.v1.UserOperation user_operation = 1;
// chain_id defines the network identifier.
string chain_id = 2;
// account_number is the account number of the user_operation.
uint64 account_number = 3;
cosmos.accounts.v1.UserOperation user_operation = 2;
}

// MsgAuthenticateResponse is the response to MsgAuthenticate.
Expand All @@ -27,9 +27,13 @@ message MsgAuthenticateResponse {}
// the bundler payment messages.
// The account must ensure the caller of this message is the x/accounts module itself.
message MsgPayBundler {
// bundler is the address of the bundler.
// NOTE: in case the operation was sent directly by the user, this field will
// reflect the user address.
string bundler = 1;
// bundler_payment_messages are the messages that the operation sender will execute.
// The account can modify the messages as it sees fit.
repeated google.protobuf.Any bundler_payment_messages = 1;
repeated google.protobuf.Any bundler_payment_messages = 2;
}

// MsgPayBundlerResponse is the response to MsgPayBundler.
Expand All @@ -44,9 +48,13 @@ message MsgPayBundlerResponse {
// block certain messages, or modify them.
// The account must ensure the caller of this message is the x/accounts module itself.
message MsgExecute {
// bundler is the address of the bundler.
// NOTE: in case the operation was sent directly by the user, this field will
// reflect the user address.
string bundler = 1;
// execution_messages are the messages that the operation sender will execute.
// The account can modify the messages as it sees fit.
repeated google.protobuf.Any execution_messages = 1;
repeated google.protobuf.Any execution_messages = 2;
}

// MsgExecuteResponse is the response to MsgExecute.
Expand Down
20 changes: 12 additions & 8 deletions proto/cosmos/accounts/v1/account_abstraction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@ message UserOperation {
// authentication_data defines the authentication data associated with the authentication method.
// It is the account implementer duty to assess that the UserOperation is properly signed.
bytes authentication_data = 3;
// sequence defines the sequence number of the account, the authentication method might require this
// to ensure non-replayability.
uint64 sequence = 4;
// authentication_gas_limit expresses the gas limit to be used for the authentication part of the
// UserOperation.
uint64 authentication_gas_limit = 5;
uint64 authentication_gas_limit = 4;
// bundler_payment_messages expresses a list of messages that the account
// executes to pay the bundler for submitting the UserOperation.
// It can be empty if the bundler does not need any form of payment,
Expand All @@ -33,21 +30,22 @@ message UserOperation {
// - NFT payment
// - IBC Token payment.
// - Payment through delegations.
repeated google.protobuf.Any bundler_payment_messages = 6;
repeated google.protobuf.Any bundler_payment_messages = 5;
// bundler_payment_gas_limit defines the gas limit to be used for the bundler payment.
// This ensures that, since the bundler executes a list of UserOperations and there needs to
// be minimal trust between bundler and UserOperation sender, the sender cannot consume
// the whole bundle gas.
uint64 bundler_payment_gas_limit = 7;
uint64 bundler_payment_gas_limit = 6;
// execution_messages expresses a list of messages that the account wants to execute.
// This concretely is the intent of the transaction expressed as a UserOperation.
repeated google.protobuf.Any execution_messages = 8;
repeated google.protobuf.Any execution_messages = 7;
// execution_gas_limit defines the gas limit to be used for the execution of the UserOperation's
// execution messages.
uint64 execution_gas_limit = 9;
uint64 execution_gas_limit = 8;
}

// UserOperationResponse defines the response of a UserOperation.
// If the operation fails the error field will be populated.
message UserOperationResponse {
// authentication_gas_used defines the gas used for the authentication part of the UserOperation.
uint64 authentication_gas_used = 1;
Expand All @@ -60,4 +58,10 @@ message UserOperationResponse {
uint64 execution_gas_used = 4;
// execution_responses defines the responses of the execution messages.
repeated google.protobuf.Any execution_responses = 5;
// error defines the error that occurred during the execution of the UserOperation.
// If the error is not empty, the UserOperation failed.
// Other fields might be populated even if the error is not empty, for example
// if the operation fails after the authentication step, the authentication_gas_used
// field will be populated.
string error = 6;
}
4 changes: 4 additions & 0 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/accounts"
"cosmossdk.io/x/accounts/accountstd"
"cosmossdk.io/x/accounts/testing/account_abstraction"
"cosmossdk.io/x/accounts/testing/counter"
"cosmossdk.io/x/auth"
"cosmossdk.io/x/auth/ante"
Expand Down Expand Up @@ -284,11 +285,14 @@ func NewSimApp(
accountsKeeper, err := accounts.NewKeeper(
runtime.NewKVStoreService(keys[accounts.StoreKey]),
runtime.EventService{},
runtime.BranchService{},
app.AuthKeeper.AddressCodec(),
appCodec.InterfaceRegistry().SigningContext(),
app.MsgServiceRouter(),
app.GRPCQueryRouter(),
accountstd.AddAccount("counter", counter.NewAccount),
accountstd.AddAccount("aa_minimal", account_abstraction.NewMinimalAbstractedAccount),
accountstd.AddAccount("aa_full", account_abstraction.NewFullAbstractedAccount),
)
if err != nil {
panic(err)
Expand Down
Loading

0 comments on commit d3b30e9

Please sign in to comment.