Skip to content

Commit 3202ca4

Browse files
authored
[CT-1197] add query methods for smart account (#2313)
1 parent e4249ec commit 3202ca4

File tree

6 files changed

+238
-0
lines changed

6 files changed

+238
-0
lines changed

protocol/lib/metrics/metric_keys.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,7 @@ const (
8989

9090
EndBlocker = "end_blocker"
9191
EndBlockerLag = "end_blocker_lag"
92+
93+
// Account plus
94+
MissingRegisteredAuthenticator = "missing_registered_authenticator"
9295
)

protocol/x/accountplus/keeper/authenticators.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import (
66

77
"cosmossdk.io/errors"
88
"cosmossdk.io/store/prefix"
9+
storetypes "cosmossdk.io/store/types"
10+
"github.com/cosmos/cosmos-sdk/telemetry"
911
sdk "github.com/cosmos/cosmos-sdk/types"
12+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1013
gogotypes "github.com/cosmos/gogoproto/types"
14+
"github.com/dydxprotocol/v4-chain/protocol/lib/metrics"
15+
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/authenticator"
1116
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
1217
)
1318

@@ -106,3 +111,124 @@ func (k Keeper) SetNextAuthenticatorId(ctx sdk.Context, authenticatorId uint64)
106111
store := ctx.KVStore(k.storeKey)
107112
store.Set([]byte(types.AuthenticatorIdKeyPrefix), b)
108113
}
114+
115+
// GetSelectedAuthenticatorData gets a single authenticator for the account from the store.
116+
func (k Keeper) GetSelectedAuthenticatorData(
117+
ctx sdk.Context,
118+
account sdk.AccAddress,
119+
selectedAuthenticator uint64,
120+
) (*types.AccountAuthenticator, error) {
121+
store := prefix.NewStore(
122+
ctx.KVStore(k.storeKey),
123+
[]byte(types.AuthenticatorKeyPrefix),
124+
)
125+
bz := store.Get(types.KeyAccountId(account, selectedAuthenticator))
126+
if bz == nil {
127+
return &types.AccountAuthenticator{}, errors.Wrap(
128+
types.ErrAuthenticatorNotFound,
129+
fmt.Sprintf("authenticator %d not found for account %s", selectedAuthenticator, account),
130+
)
131+
}
132+
authenticatorFromStore, err := k.unmarshalAccountAuthenticator(bz)
133+
if err != nil {
134+
return &types.AccountAuthenticator{}, err
135+
}
136+
137+
return authenticatorFromStore, nil
138+
}
139+
140+
// GetInitializedAuthenticatorForAccount returns a single initialized authenticator for the account.
141+
// It fetches the authenticator data from the store, gets the authenticator struct from the manager,
142+
// then calls initialize on the authenticator data
143+
func (k Keeper) GetInitializedAuthenticatorForAccount(
144+
ctx sdk.Context,
145+
account sdk.AccAddress,
146+
selectedAuthenticator uint64,
147+
) (authenticator.InitializedAuthenticator, error) {
148+
// Get the authenticator data from the store
149+
authenticatorFromStore, err := k.GetSelectedAuthenticatorData(ctx, account, selectedAuthenticator)
150+
if err != nil {
151+
return authenticator.InitializedAuthenticator{}, err
152+
}
153+
154+
uninitializedAuthenticator := k.authenticatorManager.GetAuthenticatorByType(authenticatorFromStore.Type)
155+
if uninitializedAuthenticator == nil {
156+
// This should never happen, but if it does, it means that stored authenticator is not registered
157+
// or somehow the registered authenticator was removed / malformed
158+
telemetry.IncrCounter(1, metrics.MissingRegisteredAuthenticator)
159+
k.Logger(ctx).Error(
160+
"account asscoicated authenticator not registered in manager",
161+
"type", authenticatorFromStore.Type,
162+
"id", selectedAuthenticator,
163+
)
164+
165+
return authenticator.InitializedAuthenticator{},
166+
errors.Wrapf(
167+
sdkerrors.ErrLogic,
168+
"authenticator id %d failed to initialize, authenticator type %s not registered in manager",
169+
selectedAuthenticator, authenticatorFromStore.Type,
170+
)
171+
}
172+
// Ensure that initialization of each authenticator works as expected
173+
// NOTE: Always return a concrete authenticator not a pointer, do not modify in place
174+
// NOTE: The authenticator manager returns a struct that is reused
175+
initializedAuthenticator, err := uninitializedAuthenticator.Initialize(authenticatorFromStore.Config)
176+
if err != nil {
177+
return authenticator.InitializedAuthenticator{},
178+
errors.Wrapf(
179+
err,
180+
"authenticator %d with type %s failed to initialize",
181+
selectedAuthenticator, authenticatorFromStore.Type,
182+
)
183+
}
184+
if initializedAuthenticator == nil {
185+
return authenticator.InitializedAuthenticator{},
186+
errors.Wrapf(
187+
types.ErrInitializingAuthenticator,
188+
"authenticator.Initialize returned nil for %d with type %s",
189+
selectedAuthenticator, authenticatorFromStore.Type,
190+
)
191+
}
192+
193+
finalAuthenticator := authenticator.InitializedAuthenticator{
194+
Id: authenticatorFromStore.Id,
195+
Authenticator: initializedAuthenticator,
196+
}
197+
198+
return finalAuthenticator, nil
199+
}
200+
201+
// GetAuthenticatorDataForAccount gets all authenticators AccAddressFromBech32 with an account
202+
// from the store.
203+
func (k Keeper) GetAuthenticatorDataForAccount(
204+
ctx sdk.Context,
205+
account sdk.AccAddress,
206+
) ([]*types.AccountAuthenticator, error) {
207+
authenticators := make([]*types.AccountAuthenticator, 0)
208+
209+
store := prefix.NewStore(
210+
ctx.KVStore(k.storeKey),
211+
[]byte(types.AuthenticatorKeyPrefix),
212+
)
213+
iterator := storetypes.KVStorePrefixIterator(store, []byte(account.String()))
214+
defer iterator.Close()
215+
for ; iterator.Valid(); iterator.Next() {
216+
authenticator, err := k.unmarshalAccountAuthenticator(iterator.Value())
217+
if err != nil {
218+
return nil, err
219+
}
220+
authenticators = append(authenticators, authenticator)
221+
}
222+
223+
return authenticators, nil
224+
}
225+
226+
// unmarshalAccountAuthenticator is used to unmarshal the AccountAuthenticator from the store
227+
func (k Keeper) unmarshalAccountAuthenticator(bz []byte) (*types.AccountAuthenticator, error) {
228+
var accountAuthenticator types.AccountAuthenticator
229+
err := k.cdc.Unmarshal(bz, &accountAuthenticator)
230+
if err != nil {
231+
return &types.AccountAuthenticator{}, errors.Wrap(err, "failed to unmarshal account authenticator")
232+
}
233+
return &accountAuthenticator, nil
234+
}

protocol/x/accountplus/keeper/keeper_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,33 @@ func (s *KeeperTestSuite) TestKeeper_GetAndSetAuthenticatorId() {
135135
authenticatorId = s.tApp.App.AccountPlusKeeper.InitializeOrGetNextAuthenticatorId(ctx)
136136
s.Require().Equal(authenticatorId, uint64(2), "Initialize/Get authenticator id returned incorrect id")
137137
}
138+
139+
func (s *KeeperTestSuite) TestKeeper_GetAuthenticatorDataForAccount() {
140+
ctx := s.Ctx
141+
142+
// Set up account
143+
key := "6cf5103c60c939a5f38e383b52239c5296c968579eec1c68a47d70fbf1d19159"
144+
bz, _ := hex.DecodeString(key)
145+
priv := &secp256k1.PrivKey{Key: bz}
146+
accAddress := sdk.AccAddress(priv.PubKey().Address())
147+
148+
_, err := s.tApp.App.AccountPlusKeeper.AddAuthenticator(
149+
ctx,
150+
accAddress,
151+
"SignatureVerification",
152+
priv.PubKey().Bytes(),
153+
)
154+
s.Require().NoError(err, "Should successfully add a SignatureVerification")
155+
156+
_, err = s.tApp.App.AccountPlusKeeper.AddAuthenticator(
157+
ctx,
158+
accAddress,
159+
"SignatureVerification",
160+
priv.PubKey().Bytes(),
161+
)
162+
s.Require().NoError(err, "Should successfully add a MessageFilter")
163+
164+
authenticators, err := s.tApp.App.AccountPlusKeeper.GetAuthenticatorDataForAccount(ctx, accAddress)
165+
s.Require().NoError(err)
166+
s.Require().Equal(len(authenticators), 2, "Getting authenticators returning incorrect data")
167+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package keeper
2+
3+
import (
4+
"context"
5+
6+
sdk "github.com/cosmos/cosmos-sdk/types"
7+
"google.golang.org/grpc/codes"
8+
"google.golang.org/grpc/status"
9+
10+
"github.com/dydxprotocol/v4-chain/protocol/x/accountplus/types"
11+
)
12+
13+
var _ types.QueryServer = Keeper{}
14+
15+
// GetAuthenticators returns all authenticators for an account.
16+
func (k Keeper) GetAuthenticators(
17+
ctx context.Context,
18+
request *types.GetAuthenticatorsRequest,
19+
) (*types.GetAuthenticatorsResponse, error) {
20+
if request == nil {
21+
return nil, status.Error(codes.InvalidArgument, "empty request")
22+
}
23+
24+
sdkCtx := sdk.UnwrapSDKContext(ctx)
25+
acc, err := sdk.AccAddressFromBech32(request.Account)
26+
if err != nil {
27+
return nil, status.Error(codes.Internal, err.Error())
28+
}
29+
30+
authenticators, err := k.GetAuthenticatorDataForAccount(sdkCtx, acc)
31+
if err != nil {
32+
return nil, status.Error(codes.Internal, err.Error())
33+
}
34+
35+
return &types.GetAuthenticatorsResponse{AccountAuthenticators: authenticators}, nil
36+
}
37+
38+
// GetAuthenticator returns a specific authenticator for an account given its authenticator id.
39+
func (k Keeper) GetAuthenticator(
40+
ctx context.Context,
41+
request *types.GetAuthenticatorRequest,
42+
) (*types.GetAuthenticatorResponse, error) {
43+
if request == nil {
44+
return nil, status.Error(codes.InvalidArgument, "empty request")
45+
}
46+
47+
sdkCtx := sdk.UnwrapSDKContext(ctx)
48+
acc, err := sdk.AccAddressFromBech32(request.Account)
49+
if err != nil {
50+
return nil, status.Error(codes.Internal, err.Error())
51+
}
52+
53+
authenticator, err := k.GetSelectedAuthenticatorData(sdkCtx, acc, request.AuthenticatorId)
54+
if err != nil {
55+
return nil, status.Error(codes.Internal, err.Error())
56+
}
57+
58+
return &types.GetAuthenticatorResponse{AccountAuthenticator: authenticator}, nil
59+
}
60+
61+
// GetParams returns the parameters for the accountplus module.
62+
func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
63+
if req == nil {
64+
return nil, status.Error(codes.InvalidArgument, "invalid request")
65+
}
66+
ctx := sdk.UnwrapSDKContext(goCtx)
67+
68+
return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
69+
}

protocol/x/accountplus/types/codec.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import (
55
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
66
)
77

8+
// AuthenticatorTxOptions
9+
type AuthenticatorTxOptions interface {
10+
GetSelectedAuthenticators() []uint64
11+
}
12+
813
func RegisterCodec(cdc *codec.LegacyAmino) {}
914

1015
func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {

protocol/x/accountplus/types/errors.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ var (
1818
3,
1919
"Authenticator data exceeds maximum length",
2020
)
21+
ErrInitializingAuthenticator = errorsmod.Register(
22+
ModuleName,
23+
4,
24+
"Error initializing authenticator",
25+
)
2126
)

0 commit comments

Comments
 (0)