github.com/lino-network/lino@v0.6.11/x/account/manager/manager.go (about)

     1  package manager
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  
     8  	codec "github.com/cosmos/cosmos-sdk/codec"
     9  	sdk "github.com/cosmos/cosmos-sdk/types"
    10  	"github.com/tendermint/tendermint/crypto"
    11  
    12  	"github.com/lino-network/lino/param"
    13  	linotypes "github.com/lino-network/lino/types"
    14  	"github.com/lino-network/lino/utils"
    15  	"github.com/lino-network/lino/x/account/model"
    16  	"github.com/lino-network/lino/x/account/types"
    17  )
    18  
    19  const (
    20  	nSecOfOneHour = 3600
    21  	// HoursPerYear - as defined by a julian year of 365.25 days
    22  	nHourOfOneYear = 8766
    23  
    24  	exportVersion = 2
    25  	importVersion = 2
    26  )
    27  
    28  // AccountManager - account manager
    29  type AccountManager struct {
    30  	storage     model.AccountStorage
    31  	paramHolder param.ParamKeeper
    32  }
    33  
    34  // NewLinoAccount - new account manager
    35  func NewAccountManager(key sdk.StoreKey, holder param.ParamKeeper) AccountManager {
    36  	return AccountManager{
    37  		storage:     model.NewAccountStorage(key),
    38  		paramHolder: holder,
    39  	}
    40  }
    41  
    42  func (am AccountManager) InitGenesis(ctx sdk.Context, total linotypes.Coin, pools []model.Pool) {
    43  	neverInited := false
    44  	func() {
    45  		defer func() {
    46  			if r := recover(); r != nil {
    47  				neverInited = true
    48  			}
    49  		}()
    50  		am.storage.GetSupply(ctx)
    51  	}()
    52  	if !neverInited {
    53  		panic("Account Module Already Inited")
    54  	}
    55  	am.storage.SetSupply(ctx, &model.Supply{
    56  		LastYearTotal:     total,
    57  		Total:             total,
    58  		ChainStartTime:    ctx.BlockTime().Unix(),
    59  		LastInflationTime: ctx.BlockTime().Unix(),
    60  	})
    61  	for _, pool := range pools {
    62  		am.storage.SetPool(ctx, &pool)
    63  	}
    64  }
    65  
    66  func (am AccountManager) GetPool(
    67  	ctx sdk.Context, poolName linotypes.PoolName) (linotypes.Coin, sdk.Error) {
    68  	pool, err := am.storage.GetPool(ctx, poolName)
    69  	if err != nil {
    70  		return linotypes.NewCoinFromInt64(0), err
    71  	}
    72  	return pool.Balance, nil
    73  }
    74  
    75  // MoveCoin move coins from the acc/addr to the acc/addr.
    76  func (am AccountManager) MoveCoin(ctx sdk.Context, sender, receiver linotypes.AccOrAddr, coin linotypes.Coin) sdk.Error {
    77  	if coin.IsNegative() {
    78  		return types.ErrNegativeMoveAmount(coin)
    79  	}
    80  	err := am.minusCoin(ctx, sender, coin)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	return am.addCoin(ctx, receiver, coin)
    85  }
    86  
    87  // MoveFromPool - move coin from pool to an address or user.
    88  func (am AccountManager) MoveFromPool(ctx sdk.Context, poolName linotypes.PoolName, dest linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
    89  	if amount.IsNegative() {
    90  		return types.ErrNegativeMoveAmount(amount)
    91  	}
    92  
    93  	pool, err := am.storage.GetPool(ctx, poolName)
    94  	if err != nil {
    95  		return err
    96  	}
    97  	if !pool.Balance.IsGTE(amount) {
    98  		return types.ErrPoolNotEnough(poolName)
    99  	}
   100  	pool.Balance = pool.Balance.Minus(amount)
   101  	am.storage.SetPool(ctx, pool)
   102  	return am.addCoin(ctx, dest, amount)
   103  }
   104  
   105  // MoveToPool - move coin from an address or account to pool
   106  func (am AccountManager) MoveToPool(ctx sdk.Context, poolName linotypes.PoolName, from linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
   107  	if amount.IsNegative() {
   108  		return types.ErrNegativeMoveAmount(amount)
   109  	}
   110  
   111  	err := am.minusCoin(ctx, from, amount)
   112  	if err != nil {
   113  		return err
   114  	}
   115  	pool, err := am.storage.GetPool(ctx, poolName)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	pool.Balance = pool.Balance.Plus(amount)
   120  	am.storage.SetPool(ctx, pool)
   121  	return nil
   122  }
   123  
   124  // MoveBetweenPools, move coin between pools.
   125  func (am AccountManager) MoveBetweenPools(ctx sdk.Context, from, to linotypes.PoolName, amount linotypes.Coin) sdk.Error {
   126  	if amount.IsNegative() {
   127  		return types.ErrNegativeMoveAmount(amount)
   128  	}
   129  
   130  	fromPool, err := am.storage.GetPool(ctx, from)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	toPool, err := am.storage.GetPool(ctx, to)
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	if !fromPool.Balance.IsGTE(amount) {
   141  		return types.ErrPoolNotEnough(from)
   142  	}
   143  
   144  	fromPool.Balance = fromPool.Balance.Minus(amount)
   145  	toPool.Balance = toPool.Balance.Plus(amount)
   146  	am.storage.SetPool(ctx, fromPool)
   147  	am.storage.SetPool(ctx, toPool)
   148  	return nil
   149  }
   150  
   151  // Mint - distribute the inflation to pools hourly.
   152  func (am AccountManager) Mint(ctx sdk.Context) sdk.Error {
   153  	supply := am.storage.GetSupply(ctx)
   154  	chainStartTime := supply.ChainStartTime
   155  	lastInflation := supply.LastInflationTime
   156  	blockTime := ctx.BlockTime().Unix()
   157  
   158  	nLastInflation := (lastInflation - chainStartTime) / nSecOfOneHour
   159  	nCurrent := (blockTime - chainStartTime) / nSecOfOneHour
   160  
   161  	if nCurrent <= nLastInflation {
   162  		return nil
   163  	}
   164  
   165  	// invarience
   166  	// premise: lastInflation >= chainStartTime
   167  	// nCurrent > nLastInflation ==> blocktime > lastInflation
   168  	// after: lastInflation = blocktime > chainStartTime
   169  	for nth := nLastInflation + 1; nth <= nCurrent; nth++ {
   170  		// mint to pools
   171  		err := am.hourlyMintOn(ctx, supply)
   172  		if err != nil {
   173  			return err
   174  		}
   175  		if nth%nHourOfOneYear == 0 {
   176  			supply.LastYearTotal = supply.Total
   177  		}
   178  	}
   179  
   180  	supply.LastInflationTime = blockTime
   181  	am.storage.SetSupply(ctx, supply)
   182  	return nil
   183  }
   184  
   185  // will change supply after mint and allocate to pools.
   186  func (am AccountManager) hourlyMintOn(ctx sdk.Context, supply *model.Supply) sdk.Error {
   187  	allocation := am.paramHolder.GetGlobalAllocationParam(ctx)
   188  	growthRate := allocation.GlobalGrowthRate
   189  	minted := linotypes.DecToCoin(
   190  		supply.LastYearTotal.ToDec().Mul(growthRate).
   191  			Mul(linotypes.NewDecFromRat(1, nHourOfOneYear)))
   192  	contentCreator := linotypes.DecToCoin(minted.ToDec().Mul(allocation.ContentCreatorAllocation))
   193  	validator := linotypes.DecToCoin(minted.ToDec().Mul(allocation.ValidatorAllocation))
   194  	developer := minted.Minus(contentCreator).Minus(validator)
   195  
   196  	if err := am.mintToPool(ctx, linotypes.InflationConsumptionPool, contentCreator); err != nil {
   197  		return err
   198  	}
   199  	if err := am.mintToPool(ctx, linotypes.InflationValidatorPool, validator); err != nil {
   200  		return err
   201  	}
   202  	if err := am.mintToPool(ctx, linotypes.InflationDeveloperPool, developer); err != nil {
   203  		return err
   204  	}
   205  	supply.Total = supply.Total.Plus(minted)
   206  	return nil
   207  }
   208  
   209  func (am AccountManager) mintToPool(ctx sdk.Context, poolName linotypes.PoolName, amount linotypes.Coin) sdk.Error {
   210  	pool, err := am.storage.GetPool(ctx, poolName)
   211  	if err != nil {
   212  		return err
   213  	}
   214  	pool.Balance = pool.Balance.Plus(amount)
   215  	am.storage.SetPool(ctx, pool)
   216  	return nil
   217  }
   218  
   219  func (am AccountManager) addCoin(ctx sdk.Context, dest linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
   220  	if !dest.IsAddr {
   221  		return am.addCoinToUsername(ctx, dest.AccountKey, amount)
   222  	} else {
   223  		am.addCoinToAddress(ctx, dest.Addr, amount)
   224  	}
   225  	return nil
   226  }
   227  
   228  // addCoinToUsername - add coin to address associated username
   229  func (am AccountManager) addCoinToUsername(ctx sdk.Context, username linotypes.AccountKey, coin linotypes.Coin) sdk.Error {
   230  	accInfo, err := am.storage.GetInfo(ctx, username)
   231  	if err != nil {
   232  		return types.ErrAccountNotFound(username)
   233  	}
   234  	am.addCoinToAddress(ctx, accInfo.Address, coin)
   235  	return nil
   236  }
   237  
   238  // addCoinToAddress - add coin to address associated username
   239  func (am AccountManager) addCoinToAddress(ctx sdk.Context, addr sdk.AccAddress, coin linotypes.Coin) {
   240  	if coin.IsZero() {
   241  		return
   242  	}
   243  	bank, err := am.storage.GetBank(ctx, addr)
   244  	if err != nil {
   245  		// if address is not created, created a new one
   246  		bank = &model.AccountBank{
   247  			Saving: linotypes.NewCoinFromInt64(0),
   248  		}
   249  	}
   250  	bank.Saving = bank.Saving.Plus(coin)
   251  	am.storage.SetBank(ctx, addr, bank)
   252  }
   253  
   254  func (am AccountManager) minusCoin(ctx sdk.Context, from linotypes.AccOrAddr, amount linotypes.Coin) sdk.Error {
   255  	if !from.IsAddr {
   256  		return am.minusCoinFromUsername(ctx, from.AccountKey, amount)
   257  	} else {
   258  		return am.minusCoinFromAddress(ctx, from.Addr, amount)
   259  	}
   260  }
   261  
   262  // minusSavingCoin - minus coin from balance, remove coin day in the tail
   263  func (am AccountManager) minusCoinFromUsername(ctx sdk.Context, username linotypes.AccountKey, coin linotypes.Coin) sdk.Error {
   264  	accInfo, err := am.storage.GetInfo(ctx, username)
   265  	if err != nil {
   266  		return types.ErrAccountNotFound(username)
   267  	}
   268  	return am.minusCoinFromAddress(ctx, accInfo.Address, coin)
   269  }
   270  
   271  // minusCoinFromAddress - minus coin from address
   272  func (am AccountManager) minusCoinFromAddress(ctx sdk.Context, address sdk.AccAddress, coin linotypes.Coin) sdk.Error {
   273  	if coin.IsZero() {
   274  		return nil
   275  	}
   276  	bank, err := am.storage.GetBank(ctx, address)
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	bank.Saving = bank.Saving.Minus(coin)
   282  	if !bank.Saving.IsGTE(am.paramHolder.GetAccountParam(ctx).MinimumBalance) {
   283  		return types.ErrAccountSavingCoinNotEnough()
   284  	}
   285  
   286  	am.storage.SetBank(ctx, address, bank)
   287  	return nil
   288  }
   289  
   290  func (am AccountManager) DoesAccountExist(ctx sdk.Context, username linotypes.AccountKey) bool {
   291  	return am.storage.DoesAccountExist(ctx, username)
   292  }
   293  
   294  // RegisterAccount - register account, deduct fee from referrer address then create a new account
   295  func (am AccountManager) RegisterAccount(ctx sdk.Context, referrer linotypes.AccOrAddr, registerFee linotypes.Coin, username linotypes.AccountKey, signingKey, transactionKey crypto.PubKey) sdk.Error {
   296  	minRegFee := am.paramHolder.GetAccountParam(ctx).RegisterFee
   297  	if minRegFee.IsGT(registerFee) {
   298  		return types.ErrRegisterFeeInsufficient()
   299  	}
   300  
   301  	if err := am.createAccount(ctx, username, signingKey, transactionKey); err != nil {
   302  		return err
   303  	}
   304  
   305  	err := am.MoveToPool(ctx, linotypes.InflationValidatorPool, referrer, minRegFee)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	err = am.MoveCoin(ctx,
   311  		referrer, linotypes.NewAccOrAddrFromAcc(username), registerFee.Minus(minRegFee))
   312  	return err
   313  }
   314  
   315  func (am AccountManager) GenesisAccount(ctx sdk.Context, username linotypes.AccountKey, signingKey, transactionKey crypto.PubKey) sdk.Error {
   316  	return am.createAccount(ctx, username, signingKey, transactionKey)
   317  }
   318  
   319  // CreateAccount - create account, caller should make sure the register fee is valid
   320  func (am AccountManager) createAccount(ctx sdk.Context, username linotypes.AccountKey, signingKey, transactionKey crypto.PubKey) sdk.Error {
   321  	if am.storage.DoesAccountExist(ctx, username) {
   322  		return types.ErrAccountAlreadyExists(username)
   323  	}
   324  
   325  	// get address from tx key
   326  	addr := sdk.AccAddress(transactionKey.Address())
   327  	bank, err := am.storage.GetBank(ctx, addr)
   328  	if err != nil {
   329  		bank = &model.AccountBank{}
   330  	}
   331  	if bank.Username != "" {
   332  		return types.ErrAddressAlreadyTaken(addr.String())
   333  	}
   334  
   335  	// set public key to bank
   336  	bank.Username = username
   337  	bank.PubKey = transactionKey
   338  	am.storage.SetBank(ctx, addr, bank)
   339  
   340  	accountInfo := &model.AccountInfo{
   341  		Username:       username,
   342  		CreatedAt:      ctx.BlockHeader().Time.Unix(),
   343  		TransactionKey: transactionKey,
   344  		SigningKey:     signingKey,
   345  		Address:        addr,
   346  	}
   347  	am.storage.SetInfo(ctx, accountInfo)
   348  	return nil
   349  }
   350  
   351  // UpdateJSONMeta - update user JONS meta data
   352  func (accManager AccountManager) UpdateJSONMeta(
   353  	ctx sdk.Context, username linotypes.AccountKey, jsonMeta string) sdk.Error {
   354  	accountMeta := accManager.storage.GetMeta(ctx, username)
   355  	accountMeta.JSONMeta = jsonMeta
   356  	accManager.storage.SetMeta(ctx, username, accountMeta)
   357  	return nil
   358  }
   359  
   360  // GetTransactionKey - get transaction public key
   361  func (accManager AccountManager) GetTransactionKey(
   362  	ctx sdk.Context, username linotypes.AccountKey) (crypto.PubKey, sdk.Error) {
   363  	accountInfo, err := accManager.storage.GetInfo(ctx, username)
   364  	if err != nil {
   365  		return nil, types.ErrGetTransactionKey(username)
   366  	}
   367  	return accountInfo.TransactionKey, nil
   368  }
   369  
   370  // GetAppKey - get app public key
   371  func (accManager AccountManager) GetSigningKey(
   372  	ctx sdk.Context, username linotypes.AccountKey) (crypto.PubKey, sdk.Error) {
   373  	info, err := accManager.storage.GetInfo(ctx, username)
   374  	if err != nil {
   375  		return nil, types.ErrGetSigningKey(username)
   376  	}
   377  	return info.SigningKey, nil
   378  }
   379  
   380  // GetSavingFromUsername - get user balance
   381  func (accManager AccountManager) GetSavingFromUsername(ctx sdk.Context, username linotypes.AccountKey) (linotypes.Coin, sdk.Error) {
   382  	info, err := accManager.storage.GetInfo(ctx, username)
   383  	if err != nil {
   384  		return linotypes.Coin{}, types.ErrGetSavingFromBank(err)
   385  	}
   386  	bank, err := accManager.storage.GetBank(ctx, info.Address)
   387  	if err != nil {
   388  		return linotypes.Coin{}, types.ErrGetSavingFromBank(err)
   389  	}
   390  	return bank.Saving, nil
   391  }
   392  
   393  // GetSavingFromBank - get user balance
   394  func (accManager AccountManager) GetSavingFromAddress(ctx sdk.Context, address sdk.Address) (linotypes.Coin, sdk.Error) {
   395  	bank, err := accManager.storage.GetBank(ctx, address)
   396  	if err != nil {
   397  		return linotypes.Coin{}, types.ErrGetSavingFromBank(err)
   398  	}
   399  	return bank.Saving, nil
   400  }
   401  
   402  // GetSequence - get user sequence number
   403  func (accManager AccountManager) GetSequence(ctx sdk.Context, address sdk.Address) (uint64, sdk.Error) {
   404  	bank, err := accManager.storage.GetBank(ctx, address)
   405  	if err != nil {
   406  		return 0, types.ErrGetSequence(err)
   407  	}
   408  	return bank.Sequence, nil
   409  }
   410  
   411  // GetAddress - get user bank address
   412  func (accManager AccountManager) GetAddress(ctx sdk.Context, username linotypes.AccountKey) (sdk.AccAddress, sdk.Error) {
   413  	info, err := accManager.storage.GetInfo(ctx, username)
   414  	if err != nil {
   415  		return nil, types.ErrGetAddress(err)
   416  	}
   417  	return info.Address, nil
   418  }
   419  
   420  // GetFrozenMoneyList - get user frozen money list
   421  func (accManager AccountManager) GetFrozenMoneyList(
   422  	ctx sdk.Context, addr sdk.Address) ([]model.FrozenMoney, sdk.Error) {
   423  	bank, err := accManager.storage.GetBank(ctx, addr)
   424  	if err != nil {
   425  		return nil, types.ErrGetFrozenMoneyList(err)
   426  	}
   427  	return bank.FrozenMoneyList, nil
   428  }
   429  
   430  // IncreaseSequenceByOne - increase user sequence number by one
   431  func (accManager AccountManager) IncreaseSequenceByOne(ctx sdk.Context, address sdk.Address) sdk.Error {
   432  	bank, err := accManager.storage.GetBank(ctx, address)
   433  	if err != nil {
   434  		return types.ErrIncreaseSequenceByOne(err)
   435  	}
   436  	bank.Sequence++
   437  	accManager.storage.SetBank(ctx, address, bank)
   438  	return nil
   439  }
   440  
   441  // CheckSigningPubKeyOwner - given a public key, check if it is valid for the user.
   442  func (accManager AccountManager) CheckSigningPubKeyOwner(ctx sdk.Context, me linotypes.AccountKey, signKey crypto.PubKey) (linotypes.AccountKey, sdk.Error) {
   443  	accInfo, err := accManager.storage.GetInfo(ctx, me)
   444  	if err != nil {
   445  		return "", err
   446  	}
   447  	//check signing key for all permissions
   448  	if reflect.DeepEqual(accInfo.SigningKey, signKey) {
   449  		return me, nil
   450  	}
   451  
   452  	// otherwise check tx key
   453  	if reflect.DeepEqual(accInfo.TransactionKey, signKey) {
   454  		return me, nil
   455  	}
   456  
   457  	return "", types.ErrCheckAuthenticatePubKeyOwner(me)
   458  }
   459  
   460  // CheckSigningPubKeyOwnerByAddress - given a public key, check if it is valid for address.
   461  // If tx is already paid then bank can be created.
   462  func (accManager AccountManager) CheckSigningPubKeyOwnerByAddress(
   463  	ctx sdk.Context, address sdk.AccAddress, signKey crypto.PubKey, isPaid bool) sdk.Error {
   464  	bank, err := accManager.storage.GetBank(ctx, address)
   465  	if err != nil {
   466  		if !isPaid || err.Code() != linotypes.CodeAccountBankNotFound {
   467  			return err
   468  		}
   469  		bank = &model.AccountBank{}
   470  	}
   471  
   472  	if bank.PubKey == nil {
   473  		if !bytes.Equal(signKey.Address(), address) {
   474  			return sdk.ErrInvalidPubKey(
   475  				fmt.Sprintf("PubKey does not match Signer address %s", address))
   476  		}
   477  		bank.PubKey = signKey
   478  		accManager.storage.SetBank(ctx, address, bank)
   479  	}
   480  	//check signing key for all permissions
   481  	if !reflect.DeepEqual(bank.PubKey, signKey) {
   482  		return types.ErrCheckAuthenticatePubKeyAddress(address)
   483  	}
   484  
   485  	return nil
   486  }
   487  
   488  // RecoverAccount - reset two public key pairs
   489  func (accManager AccountManager) RecoverAccount(
   490  	ctx sdk.Context, username linotypes.AccountKey, newTransactionPubKey, newSigningKey crypto.PubKey) sdk.Error {
   491  	accInfo, err := accManager.storage.GetInfo(ctx, username)
   492  	if err != nil {
   493  		return err
   494  	}
   495  
   496  	newAddr := sdk.AccAddress(newTransactionPubKey.Address())
   497  	newBank, err := accManager.storage.GetBank(ctx, newAddr)
   498  	if err != nil {
   499  		if err.Code() != types.ErrAccountBankNotFound(newAddr).Code() {
   500  			return err
   501  		}
   502  		newBank = &model.AccountBank{
   503  			Saving: linotypes.NewCoinFromInt64(0),
   504  		}
   505  	}
   506  	if newBank.Username != "" {
   507  		return types.ErrAddressAlreadyTaken(newAddr.String())
   508  	}
   509  
   510  	oldAddr := accInfo.Address
   511  	oldBank, err := accManager.storage.GetBank(ctx, oldAddr)
   512  	if err != nil {
   513  		return err
   514  	}
   515  
   516  	newBank.Username = username
   517  	oldBank.Username = ""
   518  
   519  	newBank.Sequence += oldBank.Sequence
   520  	newBank.Saving = newBank.Saving.Plus(oldBank.Saving)
   521  	newBank.PubKey = newTransactionPubKey
   522  	oldBank.Saving = linotypes.NewCoinFromInt64(0)
   523  
   524  	accInfo.Address = newAddr
   525  	accInfo.SigningKey = newSigningKey
   526  	accInfo.TransactionKey = newTransactionPubKey
   527  
   528  	accParams := accManager.paramHolder.GetAccountParam(ctx)
   529  	newBank.FrozenMoneyList = append(newBank.FrozenMoneyList, oldBank.FrozenMoneyList...)
   530  	if int64(len(newBank.FrozenMoneyList)) >= accParams.MaxNumFrozenMoney {
   531  		return types.ErrFrozenMoneyListTooLong()
   532  	}
   533  
   534  	oldBank.FrozenMoneyList = nil
   535  
   536  	accManager.storage.SetInfo(ctx, accInfo)
   537  	accManager.storage.SetBank(ctx, newAddr, newBank)
   538  	accManager.storage.SetBank(ctx, oldAddr, oldBank)
   539  	return nil
   540  }
   541  
   542  // AddFrozenMoney - add frozen money to user's frozen money list
   543  func (accManager AccountManager) AddFrozenMoney(
   544  	ctx sdk.Context, username linotypes.AccountKey,
   545  	amount linotypes.Coin, start, interval, times int64) sdk.Error {
   546  	info, err := accManager.storage.GetInfo(ctx, username)
   547  	if err != nil {
   548  		return err
   549  	}
   550  	accountBank, err := accManager.storage.GetBank(ctx, info.Address)
   551  	if err != nil {
   552  		return err
   553  	}
   554  	accManager.cleanExpiredFrozenMoney(ctx, accountBank)
   555  	frozenMoney := model.FrozenMoney{
   556  		Amount:   amount,
   557  		StartAt:  start,
   558  		Interval: interval,
   559  		Times:    times,
   560  	}
   561  
   562  	accParams := accManager.paramHolder.GetAccountParam(ctx)
   563  	if int64(len(accountBank.FrozenMoneyList)) >= accParams.MaxNumFrozenMoney {
   564  		return types.ErrFrozenMoneyListTooLong()
   565  	}
   566  
   567  	accountBank.FrozenMoneyList = append(accountBank.FrozenMoneyList, frozenMoney)
   568  	accManager.storage.SetBank(ctx, info.Address, accountBank)
   569  	return nil
   570  }
   571  
   572  func (accManager AccountManager) cleanExpiredFrozenMoney(ctx sdk.Context, bank *model.AccountBank) {
   573  	idx := 0
   574  	for idx < len(bank.FrozenMoneyList) {
   575  		frozenMoney := bank.FrozenMoneyList[idx]
   576  		if ctx.BlockHeader().Time.Unix() > frozenMoney.StartAt+frozenMoney.Interval*frozenMoney.Times {
   577  			bank.FrozenMoneyList = append(bank.FrozenMoneyList[:idx], bank.FrozenMoneyList[idx+1:]...)
   578  			continue
   579  		}
   580  
   581  		idx++
   582  	}
   583  }
   584  
   585  // getter
   586  func (accManager AccountManager) GetInfo(ctx sdk.Context, username linotypes.AccountKey) (*model.AccountInfo, sdk.Error) {
   587  	return accManager.storage.GetInfo(ctx, username)
   588  }
   589  
   590  func (accManager AccountManager) GetBank(ctx sdk.Context, username linotypes.AccountKey) (*model.AccountBank, sdk.Error) {
   591  	info, err := accManager.storage.GetInfo(ctx, username)
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  	return accManager.storage.GetBank(ctx, info.Address)
   596  }
   597  
   598  func (accManager AccountManager) GetBankByAddress(ctx sdk.Context, addr sdk.AccAddress) (*model.AccountBank, sdk.Error) {
   599  	return accManager.storage.GetBank(ctx, addr)
   600  }
   601  
   602  func (accManager AccountManager) GetMeta(ctx sdk.Context, username linotypes.AccountKey) (*model.AccountMeta, sdk.Error) {
   603  	return accManager.storage.GetMeta(ctx, username), nil
   604  }
   605  
   606  func (accManager AccountManager) GetSupply(ctx sdk.Context) model.Supply {
   607  	return *accManager.storage.GetSupply(ctx)
   608  }
   609  
   610  // ExportToFile -
   611  func (am AccountManager) ExportToFile(ctx sdk.Context, cdc *codec.Codec, filepath string) error {
   612  	state := &model.AccountTablesIR{
   613  		Version: exportVersion,
   614  	}
   615  	substores := am.storage.PartialStoreMap(ctx)
   616  
   617  	// export accounts
   618  	substores[string(model.AccountInfoSubstore)].Iterate(func(key []byte, val interface{}) bool {
   619  		acc := val.(*model.AccountInfo)
   620  		state.Accounts = append(state.Accounts, model.AccountIR(*acc))
   621  		return false
   622  	})
   623  
   624  	// export banks
   625  	substores[string(model.AccountBankSubstore)].Iterate(func(key []byte, val interface{}) bool {
   626  		bank := val.(*model.AccountBank)
   627  		addr := key
   628  		frozens := make([]model.FrozenMoneyIR, len(bank.FrozenMoneyList))
   629  		for i, v := range bank.FrozenMoneyList {
   630  			frozens[i] = model.FrozenMoneyIR(v)
   631  		}
   632  		state.Banks = append(state.Banks, model.AccountBankIR{
   633  			Address:         addr,
   634  			Saving:          bank.Saving,
   635  			FrozenMoneyList: frozens,
   636  			PubKey:          bank.PubKey,
   637  			Sequence:        bank.Sequence,
   638  			Username:        bank.Username,
   639  		})
   640  		return false
   641  	})
   642  
   643  	// export metas
   644  	substores[string(model.AccountMetaSubstore)].Iterate(func(key []byte, val interface{}) bool {
   645  		meta := val.(*model.AccountMeta)
   646  		acc := linotypes.AccountKey(key)
   647  		state.Metas = append(state.Metas, model.AccountMetaIR{
   648  			Username: acc,
   649  			JSONMeta: meta.JSONMeta,
   650  		})
   651  		return false
   652  	})
   653  
   654  	// pools
   655  	substores[string(model.AccountPoolSubstore)].Iterate(func(key []byte, val interface{}) bool {
   656  		pool := val.(*model.Pool)
   657  		state.Pools = append(state.Pools, model.PoolIR(*pool))
   658  		return false
   659  	})
   660  
   661  	// supply
   662  	state.Supply = model.SupplyIR(*am.storage.GetSupply(ctx))
   663  
   664  	return utils.Save(filepath, cdc, state)
   665  }
   666  
   667  // ImportFromFile import state from file.
   668  func (am AccountManager) ImportFromFile(ctx sdk.Context, cdc *codec.Codec, filepath string) error {
   669  	rst, err := utils.Load(filepath, cdc, func() interface{} { return &model.AccountTablesIR{} })
   670  	if err != nil {
   671  		return err
   672  	}
   673  	table := rst.(*model.AccountTablesIR)
   674  
   675  	if table.Version != importVersion {
   676  		return fmt.Errorf("unsupported import version: %d", table.Version)
   677  	}
   678  
   679  	banks := make(map[string]int)
   680  
   681  	// import accounts.
   682  	for _, v := range table.Accounts {
   683  		info := model.AccountInfo(v)
   684  		if _, err := am.storage.GetInfo(ctx, v.Username); err != nil {
   685  			am.storage.SetInfo(ctx, &info)
   686  			if banks[string(v.Address)] != 0 {
   687  				panic(fmt.Errorf("used address: %s", v.Address))
   688  			}
   689  			banks[string(v.Address)] = 1
   690  		} else {
   691  			panic(fmt.Errorf("duplicated username: %s", v.Username))
   692  		}
   693  	}
   694  
   695  	// import banks
   696  	for _, v := range table.Banks {
   697  		frozens := make([]model.FrozenMoney, 0)
   698  		for _, f := range v.FrozenMoneyList {
   699  			frozens = append(frozens, model.FrozenMoney(f))
   700  		}
   701  		bank := model.AccountBank{
   702  			Saving:          v.Saving,
   703  			FrozenMoneyList: frozens,
   704  			PubKey:          v.PubKey,
   705  			Sequence:        v.Sequence,
   706  			Username:        v.Username,
   707  		}
   708  		if banks[string(v.Address)] > 1 {
   709  			panic(fmt.Errorf("duplicated address: %+v", v))
   710  		}
   711  		banks[string(v.Address)] = 2
   712  		am.storage.SetBank(ctx, sdk.AccAddress(v.Address), &bank)
   713  	}
   714  
   715  	// import meta
   716  	for _, meta := range table.Metas {
   717  		am.storage.SetMeta(ctx, meta.Username, &model.AccountMeta{
   718  			JSONMeta: meta.JSONMeta,
   719  		})
   720  	}
   721  
   722  	// import pools
   723  	for _, poolir := range table.Pools {
   724  		am.storage.SetPool(ctx, (*model.Pool)(&poolir))
   725  	}
   726  
   727  	// import supply
   728  	am.storage.SetSupply(ctx, (*model.Supply)(&table.Supply))
   729  
   730  	return nil
   731  }