264 lines
8.6 KiB
Go
264 lines
8.6 KiB
Go
package keeper
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
gogotypes "github.com/gogo/protobuf/types"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
|
storetypes "github.com/cosmos/cosmos-sdk/store/types"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/types/address"
|
|
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(sdk.Context, sdk.AccAddress) types.AccountI
|
|
|
|
// Return a new account with the next account number. Does not save the new account to the store.
|
|
NewAccount(sdk.Context, types.AccountI) types.AccountI
|
|
|
|
// Check if an account exists in the store.
|
|
HasAccount(sdk.Context, sdk.AccAddress) bool
|
|
|
|
// Retrieve an account from the store.
|
|
GetAccount(sdk.Context, sdk.AccAddress) types.AccountI
|
|
|
|
// Set an account in the store.
|
|
SetAccount(sdk.Context, types.AccountI)
|
|
|
|
// Remove an account from the store.
|
|
RemoveAccount(sdk.Context, types.AccountI)
|
|
|
|
// Iterate over all accounts, calling the provided function. Stop iteration when it returns true.
|
|
IterateAccounts(sdk.Context, func(types.AccountI) bool)
|
|
|
|
// Fetch the public key of an account at a specified address
|
|
GetPubKey(sdk.Context, sdk.AccAddress) (cryptotypes.PubKey, error)
|
|
|
|
// Fetch the sequence of an account at a specified address.
|
|
GetSequence(sdk.Context, sdk.AccAddress) (uint64, error)
|
|
|
|
// Fetch the next account number, and increment the internal counter.
|
|
GetNextAccountNumber(sdk.Context) uint64
|
|
|
|
// GetModulePermissions fetches per-module account permissions
|
|
GetModulePermissions() map[string]types.PermissionsForAddress
|
|
}
|
|
|
|
// AccountKeeper encodes/decodes accounts using the go-amino (binary)
|
|
// encoding/decoding library.
|
|
type AccountKeeper struct {
|
|
storeKey storetypes.StoreKey
|
|
cdc codec.BinaryCodec
|
|
permAddrs map[string]types.PermissionsForAddress
|
|
|
|
// The prototypical AccountI constructor.
|
|
proto func() types.AccountI
|
|
addressCdc address.Codec
|
|
|
|
// the address capable of executing a MsgUpdateParams message. Typically, this
|
|
// should be the x/gov module account.
|
|
authority string
|
|
}
|
|
|
|
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, storeKey storetypes.StoreKey, proto func() types.AccountI,
|
|
maccPerms map[string][]string, bech32Prefix string, authority string,
|
|
) AccountKeeper {
|
|
permAddrs := make(map[string]types.PermissionsForAddress)
|
|
for name, perms := range maccPerms {
|
|
permAddrs[name] = types.NewPermissionsForAddress(name, perms)
|
|
}
|
|
|
|
bech32Codec := newBech32Codec(bech32Prefix)
|
|
|
|
return AccountKeeper{
|
|
storeKey: storeKey,
|
|
proto: proto,
|
|
cdc: cdc,
|
|
permAddrs: permAddrs,
|
|
addressCdc: bech32Codec,
|
|
authority: authority,
|
|
}
|
|
}
|
|
|
|
// GetAuthority returns the x/auth module's authority.
|
|
func (ak AccountKeeper) GetAuthority() string {
|
|
return ak.authority
|
|
}
|
|
|
|
// Logger returns a module-specific logger.
|
|
func (ak AccountKeeper) Logger(ctx sdk.Context) log.Logger {
|
|
return ctx.Logger().With("module", "x/"+types.ModuleName)
|
|
}
|
|
|
|
// GetPubKey Returns the PubKey of the account at address
|
|
func (ak AccountKeeper) GetPubKey(ctx sdk.Context, addr sdk.AccAddress) (cryptotypes.PubKey, error) {
|
|
acc := ak.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return nil, sdkerrors.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 sdk.Context, addr sdk.AccAddress) (uint64, error) {
|
|
acc := ak.GetAccount(ctx, addr)
|
|
if acc == nil {
|
|
return 0, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "account %s does not exist", addr)
|
|
}
|
|
|
|
return acc.GetSequence(), nil
|
|
}
|
|
|
|
// GetNextAccountNumber 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) GetNextAccountNumber(ctx sdk.Context) uint64 {
|
|
var accNumber uint64
|
|
store := ctx.KVStore(ak.storeKey)
|
|
|
|
bz := store.Get(types.GlobalAccountNumberKey)
|
|
if bz == nil {
|
|
// initialize the account numbers
|
|
accNumber = 0
|
|
} else {
|
|
val := gogotypes.UInt64Value{}
|
|
|
|
err := ak.cdc.Unmarshal(bz, &val)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
accNumber = val.GetValue()
|
|
}
|
|
|
|
bz = ak.cdc.MustMarshal(&gogotypes.UInt64Value{Value: accNumber + 1})
|
|
store.Set(types.GlobalAccountNumberKey, bz)
|
|
|
|
return accNumber
|
|
}
|
|
|
|
// 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 types.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 sdk.Context, moduleName string) (types.ModuleAccountI, []string) {
|
|
addr, perms := ak.GetModuleAddressAndPermissions(moduleName)
|
|
if addr == nil {
|
|
return nil, []string{}
|
|
}
|
|
|
|
acc := ak.GetAccount(ctx, addr)
|
|
if acc != nil {
|
|
macc, ok := acc.(types.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)).(types.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 sdk.Context, moduleName string) types.ModuleAccountI {
|
|
acc, _ := ak.GetModuleAccountAndPermissions(ctx, moduleName)
|
|
return acc
|
|
}
|
|
|
|
// SetModuleAccount sets the module account to the auth account store
|
|
func (ak AccountKeeper) SetModuleAccount(ctx sdk.Context, macc types.ModuleAccountI) {
|
|
ak.SetAccount(ctx, macc)
|
|
}
|
|
|
|
func (ak AccountKeeper) decodeAccount(bz []byte) types.AccountI {
|
|
acc, err := ak.UnmarshalAccount(bz)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return acc
|
|
}
|
|
|
|
// MarshalAccount protobuf serializes an Account interface
|
|
func (ak AccountKeeper) MarshalAccount(accountI types.AccountI) ([]byte, error) { //nolint:interfacer
|
|
return ak.cdc.MarshalInterface(accountI)
|
|
}
|
|
|
|
// UnmarshalAccount returns an Account interface from raw encoded account
|
|
// bytes of a Proto-based Account type
|
|
func (ak AccountKeeper) UnmarshalAccount(bz []byte) (types.AccountI, error) {
|
|
var acc types.AccountI
|
|
return acc, ak.cdc.UnmarshalInterface(bz, &acc)
|
|
}
|
|
|
|
// GetCodec return codec.Codec object used by the keeper
|
|
func (ak AccountKeeper) GetCodec() codec.BinaryCodec { return ak.cdc }
|
|
|
|
// add getter for bech32Prefix
|
|
func (ak AccountKeeper) getBech32Prefix() (string, error) {
|
|
bech32Codec, ok := ak.addressCdc.(bech32Codec)
|
|
if !ok {
|
|
return "", fmt.Errorf("unable cast addressCdc to bech32Codec; expected %T got %T", bech32Codec, ak.addressCdc)
|
|
}
|
|
|
|
return bech32Codec.bech32Prefix, nil
|
|
}
|