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 }