cosmos-sdk/x/auth/keeper/keeper.go
son trinh 021ab6dcc2
refactor(x/auth): Fix system test (#20531)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2024-06-12 07:02:12 +00:00

356 lines
12 KiB
Go

package keeper
import (
"context"
"errors"
"fmt"
"cosmossdk.io/collections"
"cosmossdk.io/collections/indexes"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/auth/types"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// 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)
// 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.
//
// Deprecated: keep this to avoid breaking api
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 {
appmodule.Environment
addressCodec address.Codec
AccountsModKeeper types.AccountsModKeeper
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]
// only use for upgrade handler
//
// Deprecated: move to accounts module accountNumber
accountNumber collections.Sequence
// Accounts key: AccAddr | value: AccountI | index: AccountsIndex
Accounts *collections.IndexedMap[sdk.AccAddress, sdk.AccountI, AccountsIndexes]
}
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(
env appmodule.Environment, cdc codec.BinaryCodec, proto func() sdk.AccountI, accountsModKeeper types.AccountsModKeeper,
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(env.KVStoreService)
ak := AccountKeeper{
Environment: env,
addressCodec: ac,
bech32Prefix: bech32Prefix,
proto: proto,
cdc: cdc,
AccountsModKeeper: accountsModKeeper,
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)),
}
schema, err := sb.Build()
if err != nil {
panic(err)
}
ak.Schema = schema
return ak
}
// removeLegacyAccountNumberUnsafe is used for migration purpose only. It deletes the sequence in the DB
// and returns the last value used on success.
// Deprecated
func (ak AccountKeeper) removeLegacyAccountNumberUnsafe(ctx context.Context) (uint64, error) {
accNum, err := ak.accountNumber.Peek(ctx)
if err != nil {
return 0, err
}
// Delete DB entry for legacy account number
store := ak.KVStoreService.OpenKVStore(ctx)
err = store.Delete(types.GlobalAccountNumberKey.Bytes())
return accNum, err
}
// GetAuthority returns the x/auth module's authority.
func (ak AccountKeeper) GetAuthority() string {
return ak.authority
}
func (ak AccountKeeper) GetEnvironment() appmodule.Environment {
return ak.Environment
}
// 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
}
// 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
}
// NextAccountNumber returns and increments the global account number counter.
// If the global account number is not set, it initializes it with value 0.
//
// Deprecated: NextAccountNumber is deprecated
func (ak AccountKeeper) NextAccountNumber(ctx context.Context) uint64 {
n, err := ak.AccountsModKeeper.NextAccountNumber(ctx)
if 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
}
func (ak AccountKeeper) NonAtomicMsgsExec(ctx context.Context, signer sdk.AccAddress, msgs []sdk.Msg) ([]*types.NonAtomicExecResult, error) {
msgResponses := make([]*types.NonAtomicExecResult, 0, len(msgs))
for _, msg := range msgs {
if m, ok := msg.(sdk.HasValidateBasic); ok {
if err := m.ValidateBasic(); err != nil {
value := &types.NonAtomicExecResult{Error: err.Error()}
msgResponses = append(msgResponses, value)
continue
}
}
if err := ak.BranchService.Execute(ctx, func(ctx context.Context) error {
result, err := ak.AccountsModKeeper.SendModuleMessageUntyped(ctx, signer, msg)
if err != nil {
// If an error occurs during message execution, append error response
response := &types.NonAtomicExecResult{Resp: nil, Error: err.Error()}
msgResponses = append(msgResponses, response)
} else {
resp, err := codectypes.NewAnyWithValue(result)
if err != nil {
response := &types.NonAtomicExecResult{Resp: nil, Error: err.Error()}
msgResponses = append(msgResponses, response)
}
response := &types.NonAtomicExecResult{Resp: resp, Error: ""}
msgResponses = append(msgResponses, response)
}
return nil
}); err != nil {
return nil, err
}
}
return msgResponses, nil
}
// MigrateAccountNumberUnsafe migrates auth's account number to accounts's account number
// and delete store entry for auth legacy GlobalAccountNumberKey.
//
// Should only use in an upgrade handler for migrating account number.
func MigrateAccountNumberUnsafe(ctx context.Context, ak *AccountKeeper) error {
currentAccNum, err := ak.removeLegacyAccountNumberUnsafe(ctx)
if err != nil {
return fmt.Errorf("failed to migrate account number: %w", err)
}
err = ak.AccountsModKeeper.InitAccountNumberSeqUnsafe(ctx, currentAccNum)
return err
}