forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeeper.go
356 lines (294 loc) · 12.1 KB
/
keeper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
package keeper
import (
"context"
"errors"
"fmt"
"time"
gogotypes "github.com/cosmos/gogoproto/types"
"cosmossdk.io/collections"
"cosmossdk.io/collections/indexes"
"cosmossdk.io/core/address"
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
// AccountKeeperI is the interface contract that x/auth's keeper implements.
type AccountKeeperI interface {
// Return a new account with the next account number and the specified address. Does not save the new account to the store.
NewAccountWithAddress(context.Context, sdk.AccAddress) sdk.AccountI
// Return a new account with the next account number. Does not save the new account to the store.
NewAccount(context.Context, sdk.AccountI) sdk.AccountI
// Check if an account exists in the store.
HasAccount(context.Context, sdk.AccAddress) bool
// Retrieve an account from the store.
GetAccount(context.Context, sdk.AccAddress) sdk.AccountI
// Set an account in the store.
SetAccount(context.Context, sdk.AccountI)
// Remove an account from the store.
RemoveAccount(context.Context, sdk.AccountI)
// Iterate over all accounts, calling the provided function. Stop iteration when it returns true.
IterateAccounts(context.Context, func(sdk.AccountI) bool)
// Fetch the public key of an account at a specified address
GetPubKey(context.Context, sdk.AccAddress) (cryptotypes.PubKey, error)
// Fetch the sequence of an account at a specified address.
GetSequence(context.Context, sdk.AccAddress) (uint64, error)
// Fetch the next account number, and increment the internal counter.
NextAccountNumber(context.Context) uint64
// GetModulePermissions fetches per-module account permissions
GetModulePermissions() map[string]types.PermissionsForAddress
// AddressCodec returns the account address codec.
AddressCodec() address.Codec
}
func NewAccountIndexes(sb *collections.SchemaBuilder) AccountsIndexes {
return AccountsIndexes{
Number: indexes.NewUnique(
sb, types.AccountNumberStoreKeyPrefix, "account_by_number", collections.Uint64Key, sdk.AccAddressKey,
func(_ sdk.AccAddress, v sdk.AccountI) (uint64, error) {
return v.GetAccountNumber(), nil
},
),
}
}
type AccountsIndexes struct {
// Number is a unique index that indexes accounts by their account number.
Number *indexes.Unique[uint64, sdk.AccAddress, sdk.AccountI]
}
func (a AccountsIndexes) IndexesList() []collections.Index[sdk.AccAddress, sdk.AccountI] {
return []collections.Index[sdk.AccAddress, sdk.AccountI]{
a.Number,
}
}
// AccountKeeper encodes/decodes accounts using the go-amino (binary)
// encoding/decoding library.
type AccountKeeper struct {
addressCodec address.Codec
storeService store.KVStoreService
cdc codec.BinaryCodec
permAddrs map[string]types.PermissionsForAddress
bech32Prefix string
// The prototypical AccountI constructor.
proto func() sdk.AccountI
// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string
// State
Schema collections.Schema
Params collections.Item[types.Params]
AccountNumber collections.Sequence
Accounts *collections.IndexedMap[sdk.AccAddress, sdk.AccountI, AccountsIndexes]
UnorderedNonces collections.KeySet[collections.Pair[int64, []byte]]
}
var _ AccountKeeperI = &AccountKeeper{}
// NewAccountKeeper returns a new AccountKeeperI that uses go-amino to
// (binary) encode and decode concrete sdk.Accounts.
// `maccPerms` is a map that takes accounts' addresses as keys, and their respective permissions as values. This map is used to construct
// types.PermissionsForAddress and is used in keeper.ValidatePermissions. Permissions are plain strings,
// and don't have to fit into any predefined structure. This auth module does not use account permissions internally, though other modules
// may use auth.Keeper to access the accounts permissions map.
func NewAccountKeeper(
cdc codec.BinaryCodec, storeService store.KVStoreService, proto func() sdk.AccountI,
maccPerms map[string][]string, ac address.Codec, bech32Prefix, authority string,
) AccountKeeper {
permAddrs := make(map[string]types.PermissionsForAddress)
for name, perms := range maccPerms {
permAddrs[name] = types.NewPermissionsForAddress(name, perms)
}
sb := collections.NewSchemaBuilder(storeService)
ak := AccountKeeper{
addressCodec: ac,
bech32Prefix: bech32Prefix,
storeService: storeService,
proto: proto,
cdc: cdc,
permAddrs: permAddrs,
authority: authority,
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
AccountNumber: collections.NewSequence(sb, types.GlobalAccountNumberKey, "account_number"),
Accounts: collections.NewIndexedMap(sb, types.AddressStoreKeyPrefix, "accounts", sdk.AccAddressKey, codec.CollInterfaceValue[sdk.AccountI](cdc), NewAccountIndexes(sb)),
UnorderedNonces: collections.NewKeySet(sb, types.UnorderedNoncesKey, "unordered_nonces", collections.PairKeyCodec(collections.Int64Key, collections.BytesKey)),
}
schema, err := sb.Build()
if err != nil {
panic(err)
}
ak.Schema = schema
return ak
}
// GetAuthority returns the x/auth module's authority.
func (ak AccountKeeper) GetAuthority() string {
return ak.authority
}
// AddressCodec returns the x/auth account address codec.
// x/auth is tied to bech32 encoded user accounts
func (ak AccountKeeper) AddressCodec() address.Codec {
return ak.addressCodec
}
// Logger returns a module-specific logger.
func (ak AccountKeeper) Logger(ctx context.Context) log.Logger {
return sdk.UnwrapSDKContext(ctx).Logger().With("module", "x/"+types.ModuleName)
}
// GetPubKey Returns the PubKey of the account at address
func (ak AccountKeeper) GetPubKey(ctx context.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) {
acc := ak.GetAccount(ctx, addr)
if acc == nil {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
}
return acc.GetPubKey(), nil
}
// GetSequence Returns the Sequence of the account at address
func (ak AccountKeeper) GetSequence(ctx context.Context, addr sdk.AccAddress) (uint64, error) {
acc := ak.GetAccount(ctx, addr)
if acc == nil {
return 0, errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
}
return acc.GetSequence(), nil
}
func (ak AccountKeeper) getAccountNumberLegacy(ctx context.Context) (uint64, error) {
store := ak.storeService.OpenKVStore(ctx)
b, err := store.Get(types.LegacyGlobalAccountNumberKey)
if err != nil {
return 0, fmt.Errorf("failed to get legacy account number: %w", err)
}
v := new(gogotypes.UInt64Value)
if err := v.Unmarshal(b); err != nil {
return 0, fmt.Errorf("failed to unmarshal legacy account number: %w", err)
}
return v.Value, nil
}
// NextAccountNumber returns and increments the global account number counter.
// If the global account number is not set, it initializes it with value 0.
func (ak AccountKeeper) NextAccountNumber(ctx context.Context) uint64 {
n, err := collections.Item[uint64](ak.AccountNumber).Get(ctx)
if err != nil && errors.Is(err, collections.ErrNotFound) {
// this won't happen in the tip of production network,
// but can happen when query historical states,
// fallback to old key for backward-compatibility.
// for more info, see https://github.com/cosmos/cosmos-sdk/issues/23741
n, err = ak.getAccountNumberLegacy(ctx)
}
if err != nil {
panic(err)
}
if err := ak.AccountNumber.Set(ctx, n+1); err != nil {
panic(err)
}
return n
}
// GetModulePermissions fetches per-module account permissions.
func (ak AccountKeeper) GetModulePermissions() map[string]types.PermissionsForAddress {
return ak.permAddrs
}
// ValidatePermissions validates that the module account has been granted
// permissions within its set of allowed permissions.
func (ak AccountKeeper) ValidatePermissions(macc sdk.ModuleAccountI) error {
permAddr := ak.permAddrs[macc.GetName()]
for _, perm := range macc.GetPermissions() {
if !permAddr.HasPermission(perm) {
return fmt.Errorf("invalid module permission %s", perm)
}
}
return nil
}
// GetModuleAddress returns an address based on the module name
func (ak AccountKeeper) GetModuleAddress(moduleName string) sdk.AccAddress {
permAddr, ok := ak.permAddrs[moduleName]
if !ok {
return nil
}
return permAddr.GetAddress()
}
// GetModuleAddressAndPermissions returns an address and permissions based on the module name
func (ak AccountKeeper) GetModuleAddressAndPermissions(moduleName string) (addr sdk.AccAddress, permissions []string) {
permAddr, ok := ak.permAddrs[moduleName]
if !ok {
return addr, permissions
}
return permAddr.GetAddress(), permAddr.GetPermissions()
}
// GetModuleAccountAndPermissions gets the module account from the auth account store and its
// registered permissions
func (ak AccountKeeper) GetModuleAccountAndPermissions(ctx context.Context, moduleName string) (sdk.ModuleAccountI, []string) {
addr, perms := ak.GetModuleAddressAndPermissions(moduleName)
if addr == nil {
return nil, []string{}
}
acc := ak.GetAccount(ctx, addr)
if acc != nil {
macc, ok := acc.(sdk.ModuleAccountI)
if !ok {
panic("account is not a module account")
}
return macc, perms
}
// create a new module account
macc := types.NewEmptyModuleAccount(moduleName, perms...)
maccI := (ak.NewAccount(ctx, macc)).(sdk.ModuleAccountI) // set the account number
ak.SetModuleAccount(ctx, maccI)
return maccI, perms
}
// GetModuleAccount gets the module account from the auth account store, if the account does not
// exist in the AccountKeeper, then it is created.
func (ak AccountKeeper) GetModuleAccount(ctx context.Context, moduleName string) sdk.ModuleAccountI {
acc, _ := ak.GetModuleAccountAndPermissions(ctx, moduleName)
return acc
}
// SetModuleAccount sets the module account to the auth account store
func (ak AccountKeeper) SetModuleAccount(ctx context.Context, macc sdk.ModuleAccountI) {
ak.SetAccount(ctx, macc)
}
// add getter for bech32Prefix
func (ak AccountKeeper) getBech32Prefix() (string, error) {
return ak.bech32Prefix, nil
}
// GetParams gets the auth module's parameters.
func (ak AccountKeeper) GetParams(ctx context.Context) (params types.Params) {
params, err := ak.Params.Get(ctx)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
panic(err)
}
return params
}
// -------------------------------------
// Unordered Nonce management methods
// -------------------------------------
// ContainsUnorderedNonce reports whether the sender has used this timeout already.
func (ak AccountKeeper) ContainsUnorderedNonce(ctx sdk.Context, sender []byte, timeout time.Time) (bool, error) {
return ak.UnorderedNonces.Has(ctx, collections.Join(timeout.UnixNano(), sender))
}
// TryAddUnorderedNonce tries to add a new unordered nonce for the sender.
// If the sender already has an entry with the provided timeout, an error is returned.
func (ak AccountKeeper) TryAddUnorderedNonce(ctx sdk.Context, sender []byte, timeout time.Time) error {
alreadyHas, err := ak.ContainsUnorderedNonce(ctx, sender, timeout)
if err != nil {
return fmt.Errorf("failed to check unordered nonce in storage: %w", err)
}
if alreadyHas {
return fmt.Errorf("sender %s has already used timeout %d", sdk.AccAddress(sender).String(), timeout.UnixNano())
}
return ak.UnorderedNonces.Set(ctx, collections.Join(timeout.UnixNano(), sender))
}
// RemoveExpiredUnorderedNonces removes all unordered nonces that have a timeout value before
// the current block time.
func (ak AccountKeeper) RemoveExpiredUnorderedNonces(ctx sdk.Context) error {
blkTime := ctx.BlockTime().UnixNano()
it, err := ak.UnorderedNonces.Iterate(ctx, collections.NewPrefixUntilPairRange[int64, []byte](blkTime))
if err != nil {
return err
}
defer it.Close()
keys, err := it.Keys()
if err != nil {
return err
}
for _, key := range keys {
if err := ak.UnorderedNonces.Remove(ctx, key); err != nil {
return err
}
}
return nil
}