Skip to content

Commit

Permalink
[CT-1197] add query methods for smart account (#2313)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayy04 authored Sep 24, 2024
1 parent e4249ec commit 3202ca4
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 0 deletions.
3 changes: 3 additions & 0 deletions protocol/lib/metrics/metric_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,7 @@ const (

EndBlocker = "end_blocker"
EndBlockerLag = "end_blocker_lag"

// Account plus
MissingRegisteredAuthenticator = "missing_registered_authenticator"
)
126 changes: 126 additions & 0 deletions protocol/x/accountplus/keeper/authenticators.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import (

"cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
gogotypes "github.com/cosmos/gogoproto/types"
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/authenticator"
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
)

Expand Down Expand Up @@ -106,3 +111,124 @@ func (k Keeper) SetNextAuthenticatorId(ctx sdk.Context, authenticatorId uint64)
store := ctx.KVStore(k.storeKey)
store.Set([]byte(types.AuthenticatorIdKeyPrefix), b)
}

// GetSelectedAuthenticatorData gets a single authenticator for the account from the store.
func (k Keeper) GetSelectedAuthenticatorData(
ctx sdk.Context,
account sdk.AccAddress,
selectedAuthenticator uint64,
) (*types.AccountAuthenticator, error) {
store := prefix.NewStore(
ctx.KVStore(k.storeKey),
[]byte(types.AuthenticatorKeyPrefix),
)
bz := store.Get(types.KeyAccountId(account, selectedAuthenticator))
if bz == nil {
return &types.AccountAuthenticator{}, errors.Wrap(
types.ErrAuthenticatorNotFound,
fmt.Sprintf("authenticator %d not found for account %s", selectedAuthenticator, account),
)
}
authenticatorFromStore, err := k.unmarshalAccountAuthenticator(bz)
if err != nil {
return &types.AccountAuthenticator{}, err
}

return authenticatorFromStore, nil
}

// GetInitializedAuthenticatorForAccount returns a single initialized authenticator for the account.
// It fetches the authenticator data from the store, gets the authenticator struct from the manager,
// then calls initialize on the authenticator data
func (k Keeper) GetInitializedAuthenticatorForAccount(
ctx sdk.Context,
account sdk.AccAddress,
selectedAuthenticator uint64,
) (authenticator.InitializedAuthenticator, error) {
// Get the authenticator data from the store
authenticatorFromStore, err := k.GetSelectedAuthenticatorData(ctx, account, selectedAuthenticator)
if err != nil {
return authenticator.InitializedAuthenticator{}, err
}

uninitializedAuthenticator := k.authenticatorManager.GetAuthenticatorByType(authenticatorFromStore.Type)
if uninitializedAuthenticator == nil {
// This should never happen, but if it does, it means that stored authenticator is not registered
// or somehow the registered authenticator was removed / malformed
telemetry.IncrCounter(1, metrics.MissingRegisteredAuthenticator)
k.Logger(ctx).Error(
"account asscoicated authenticator not registered in manager",
"type", authenticatorFromStore.Type,
"id", selectedAuthenticator,
)

return authenticator.InitializedAuthenticator{},
errors.Wrapf(
sdkerrors.ErrLogic,
"authenticator id %d failed to initialize, authenticator type %s not registered in manager",
selectedAuthenticator, authenticatorFromStore.Type,
)
}
// Ensure that initialization of each authenticator works as expected
// NOTE: Always return a concrete authenticator not a pointer, do not modify in place
// NOTE: The authenticator manager returns a struct that is reused
initializedAuthenticator, err := uninitializedAuthenticator.Initialize(authenticatorFromStore.Config)
if err != nil {
return authenticator.InitializedAuthenticator{},
errors.Wrapf(
err,
"authenticator %d with type %s failed to initialize",
selectedAuthenticator, authenticatorFromStore.Type,
)
}
if initializedAuthenticator == nil {
return authenticator.InitializedAuthenticator{},
errors.Wrapf(
types.ErrInitializingAuthenticator,
"authenticator.Initialize returned nil for %d with type %s",
selectedAuthenticator, authenticatorFromStore.Type,
)
}

finalAuthenticator := authenticator.InitializedAuthenticator{
Id: authenticatorFromStore.Id,
Authenticator: initializedAuthenticator,
}

return finalAuthenticator, nil
}

// GetAuthenticatorDataForAccount gets all authenticators AccAddressFromBech32 with an account
// from the store.
func (k Keeper) GetAuthenticatorDataForAccount(
ctx sdk.Context,
account sdk.AccAddress,
) ([]*types.AccountAuthenticator, error) {
authenticators := make([]*types.AccountAuthenticator, 0)

store := prefix.NewStore(
ctx.KVStore(k.storeKey),
[]byte(types.AuthenticatorKeyPrefix),
)
iterator := storetypes.KVStorePrefixIterator(store, []byte(account.String()))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
authenticator, err := k.unmarshalAccountAuthenticator(iterator.Value())
if err != nil {
return nil, err
}
authenticators = append(authenticators, authenticator)
}

return authenticators, nil
}

// unmarshalAccountAuthenticator is used to unmarshal the AccountAuthenticator from the store
func (k Keeper) unmarshalAccountAuthenticator(bz []byte) (*types.AccountAuthenticator, error) {
var accountAuthenticator types.AccountAuthenticator
err := k.cdc.Unmarshal(bz, &accountAuthenticator)
if err != nil {
return &types.AccountAuthenticator{}, errors.Wrap(err, "failed to unmarshal account authenticator")
}
return &accountAuthenticator, nil
}
30 changes: 30 additions & 0 deletions protocol/x/accountplus/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,33 @@ func (s *KeeperTestSuite) TestKeeper_GetAndSetAuthenticatorId() {
authenticatorId = s.tApp.App.AccountPlusKeeper.InitializeOrGetNextAuthenticatorId(ctx)
s.Require().Equal(authenticatorId, uint64(2), "Initialize/Get authenticator id returned incorrect id")
}

func (s *KeeperTestSuite) TestKeeper_GetAuthenticatorDataForAccount() {
ctx := s.Ctx

// Set up account
key := "6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159"
bz, _ := hex.DecodeString(key)
priv := &secp256k1.PrivKey{Key: bz}
accAddress := sdk.AccAddress(priv.PubKey().Address())

_, err := s.tApp.App.AccountPlusKeeper.AddAuthenticator(
ctx,
accAddress,
"SignatureVerification",
priv.PubKey().Bytes(),
)
s.Require().NoError(err, "Should successfully add a SignatureVerification")

_, err = s.tApp.App.AccountPlusKeeper.AddAuthenticator(
ctx,
accAddress,
"SignatureVerification",
priv.PubKey().Bytes(),
)
s.Require().NoError(err, "Should successfully add a MessageFilter")

authenticators, err := s.tApp.App.AccountPlusKeeper.GetAuthenticatorDataForAccount(ctx, accAddress)
s.Require().NoError(err)
s.Require().Equal(len(authenticators), 2, "Getting authenticators returning incorrect data")
}
69 changes: 69 additions & 0 deletions protocol/x/accountplus/keeper/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package keeper

import (
"context"

sdk "github.com/cosmos/cosmos-sdk/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
)

var _ types.QueryServer = Keeper{}

// GetAuthenticators returns all authenticators for an account.
func (k Keeper) GetAuthenticators(
ctx context.Context,
request *types.GetAuthenticatorsRequest,
) (*types.GetAuthenticatorsResponse, error) {
if request == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
acc, err := sdk.AccAddressFromBech32(request.Account)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

authenticators, err := k.GetAuthenticatorDataForAccount(sdkCtx, acc)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.GetAuthenticatorsResponse{AccountAuthenticators: authenticators}, nil
}

// GetAuthenticator returns a specific authenticator for an account given its authenticator id.
func (k Keeper) GetAuthenticator(
ctx context.Context,
request *types.GetAuthenticatorRequest,
) (*types.GetAuthenticatorResponse, error) {
if request == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
acc, err := sdk.AccAddressFromBech32(request.Account)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

authenticator, err := k.GetSelectedAuthenticatorData(sdkCtx, acc, request.AuthenticatorId)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.GetAuthenticatorResponse{AccountAuthenticator: authenticator}, nil
}

// GetParams returns the parameters for the accountplus module.
func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
ctx := sdk.UnwrapSDKContext(goCtx)

return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
}
5 changes: 5 additions & 0 deletions protocol/x/accountplus/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import (
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
)

// AuthenticatorTxOptions
type AuthenticatorTxOptions interface {
GetSelectedAuthenticators() []uint64
}

func RegisterCodec(cdc *codec.LegacyAmino) {}

func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
Expand Down
5 changes: 5 additions & 0 deletions protocol/x/accountplus/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ var (
3,
"Authenticator data exceeds maximum length",
)
ErrInitializingAuthenticator = errorsmod.Register(
ModuleName,
4,
"Error initializing authenticator",
)
)

0 comments on commit 3202ca4

Please sign in to comment.