cosmos-sdk/x/auth/mapper.go

184 lines
4.6 KiB
Go

package auth
import (
"fmt"
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
crypto "github.com/tendermint/go-crypto"
)
var globalAccountNumberKey = []byte("globalAccountNumber")
// This AccountMapper encodes/decodes accounts using the
// go-amino (binary) encoding/decoding library.
type AccountMapper struct {
// The (unexposed) key used to access the store from the Context.
key sdk.StoreKey
// The prototypical Account concrete type.
proto Account
// The wire codec for binary encoding/decoding of accounts.
cdc *wire.Codec
}
// NewAccountMapper returns a new sdk.AccountMapper that
// uses go-amino to (binary) encode and decode concrete sdk.Accounts.
// nolint
func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto Account) AccountMapper {
return AccountMapper{
key: key,
proto: proto,
cdc: cdc,
}
}
// Implaements sdk.AccountMapper.
func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) Account {
acc := am.clonePrototype()
acc.SetAddress(addr)
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
return acc
}
// New Account
func (am AccountMapper) NewAccount(ctx sdk.Context, acc Account) Account {
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
return acc
}
// Turn an address to key used to get it from the account store
func AddressStoreKey(addr sdk.Address) []byte {
return append([]byte("account:"), addr.Bytes()...)
}
// Implements sdk.AccountMapper.
func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) Account {
store := ctx.KVStore(am.key)
bz := store.Get(AddressStoreKey(addr))
if bz == nil {
return nil
}
acc := am.decodeAccount(bz)
return acc
}
// Implements sdk.AccountMapper.
func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) {
addr := acc.GetAddress()
store := ctx.KVStore(am.key)
bz := am.encodeAccount(acc)
store.Set(AddressStoreKey(addr), bz)
}
// Implements sdk.AccountMapper.
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
store := ctx.KVStore(am.key)
iter := sdk.KVStorePrefixIterator(store, []byte("account:"))
for {
if !iter.Valid() {
return
}
val := iter.Value()
acc := am.decodeAccount(val)
if process(acc) {
return
}
iter.Next()
}
}
// Returns the PubKey of the account at address
func (am AccountMapper) GetPubKey(ctx sdk.Context, addr sdk.Address) (crypto.PubKey, sdk.Error) {
acc := am.GetAccount(ctx, addr)
if acc == nil {
return nil, sdk.ErrUnknownAddress(addr.String())
}
return acc.GetPubKey(), nil
}
// Returns the Sequence of the account at address
func (am AccountMapper) GetSequence(ctx sdk.Context, addr sdk.Address) (int64, sdk.Error) {
acc := am.GetAccount(ctx, addr)
if acc == nil {
return 0, sdk.ErrUnknownAddress(addr.String())
}
return acc.GetSequence(), nil
}
func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequence int64) sdk.Error {
acc := am.GetAccount(ctx, addr)
if acc == nil {
return sdk.ErrUnknownAddress(addr.String())
}
acc.SetSequence(newSequence)
am.SetAccount(ctx, acc)
return nil
}
// Returns and increments the global account number counter
func (am AccountMapper) GetNextAccountNumber(ctx sdk.Context) int64 {
var accNumber int64
store := ctx.KVStore(am.key)
bz := store.Get(globalAccountNumberKey)
if bz == nil {
accNumber = 0
} else {
err := am.cdc.UnmarshalBinary(bz, &accNumber)
if err != nil {
panic(err)
}
}
bz = am.cdc.MustMarshalBinary(accNumber + 1)
store.Set(globalAccountNumberKey, bz)
return accNumber
}
//----------------------------------------
// misc.
// Creates a new struct (or pointer to struct) from am.proto.
func (am AccountMapper) clonePrototype() Account {
protoRt := reflect.TypeOf(am.proto)
if protoRt.Kind() == reflect.Ptr {
protoCrt := protoRt.Elem()
if protoCrt.Kind() != reflect.Struct {
panic("accountMapper requires a struct proto sdk.Account, or a pointer to one")
}
protoRv := reflect.New(protoCrt)
clone, ok := protoRv.Interface().(Account)
if !ok {
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
}
return clone
}
protoRv := reflect.New(protoRt).Elem()
clone, ok := protoRv.Interface().(Account)
if !ok {
panic(fmt.Sprintf("accountMapper requires a proto sdk.Account, but %v doesn't implement sdk.Account", protoRt))
}
return clone
}
func (am AccountMapper) encodeAccount(acc Account) []byte {
bz, err := am.cdc.MarshalBinaryBare(acc)
if err != nil {
panic(err)
}
return bz
}
func (am AccountMapper) decodeAccount(bz []byte) (acc Account) {
err := am.cdc.UnmarshalBinaryBare(bz, &acc)
if err != nil {
panic(err)
}
return
}