github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/account/util/util.go (about) 1 // Copyright (c) 2018 IoTeX 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package accountutil 7 8 import ( 9 "context" 10 11 "github.com/pkg/errors" 12 13 "github.com/iotexproject/go-pkgs/hash" 14 "github.com/iotexproject/iotex-address/address" 15 16 "github.com/iotexproject/iotex-core/action/protocol" 17 "github.com/iotexproject/iotex-core/state" 18 ) 19 20 // LoadOrCreateAccount either loads an account state or creates an account state 21 func LoadOrCreateAccount(sm protocol.StateManager, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { 22 var ( 23 account = &state.Account{} 24 addrHash = hash.BytesToHash160(addr.Bytes()) 25 ) 26 _, err := sm.State(account, protocol.LegacyKeyOption(addrHash)) 27 switch errors.Cause(err) { 28 case nil: 29 return account, nil 30 case state.ErrStateNotExist: 31 account, err := state.NewAccount(opts...) 32 if err != nil { 33 return nil, errors.Wrapf(err, "failed to create state account for %x", addrHash) 34 } 35 if _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)); err != nil { 36 return nil, errors.Wrapf(err, "failed to put state for account %x", addrHash) 37 } 38 return account, nil 39 default: 40 return nil, err 41 } 42 } 43 44 // LoadAccount loads an account state by address.Address 45 func LoadAccount(sr protocol.StateReader, addr address.Address, opts ...state.AccountCreationOption) (*state.Account, error) { 46 return LoadAccountByHash160(sr, hash.BytesToHash160(addr.Bytes()), opts...) 47 } 48 49 // LoadAccountByHash160 loads an account state by 20-byte address 50 func LoadAccountByHash160(sr protocol.StateReader, addrHash hash.Hash160, opts ...state.AccountCreationOption) (*state.Account, error) { 51 account := &state.Account{} 52 switch _, err := sr.State(account, protocol.LegacyKeyOption(addrHash)); errors.Cause(err) { 53 case state.ErrStateNotExist: 54 return state.NewAccount(opts...) 55 case nil: 56 return account, nil 57 default: 58 return nil, err 59 } 60 } 61 62 // StoreAccount puts updated account state to trie 63 func StoreAccount(sm protocol.StateManager, addr address.Address, account *state.Account) error { 64 addrHash := hash.BytesToHash160(addr.Bytes()) 65 _, err := sm.PutState(account, protocol.LegacyKeyOption(addrHash)) 66 return err 67 } 68 69 // Recorded tests if an account has been actually stored 70 func Recorded(sr protocol.StateReader, addr address.Address) (bool, error) { 71 account := &state.Account{} 72 _, err := sr.State(account, protocol.LegacyKeyOption(hash.BytesToHash160(addr.Bytes()))) 73 switch errors.Cause(err) { 74 case nil: 75 return true, nil 76 case state.ErrStateNotExist: 77 return false, nil 78 } 79 return false, err 80 } 81 82 // AccountState returns the confirmed account state on the chain 83 func AccountState(ctx context.Context, sr protocol.StateReader, addr address.Address) (*state.Account, error) { 84 a, _, err := AccountStateWithHeight(ctx, sr, addr) 85 return a, err 86 } 87 88 // AccountStateWithHeight returns the confirmed account state on the chain with what height the state is read from. 89 func AccountStateWithHeight(ctx context.Context, sr protocol.StateReader, addr address.Address) (*state.Account, uint64, error) { 90 pkHash := hash.BytesToHash160(addr.Bytes()) 91 account := &state.Account{} 92 h, err := sr.State(account, protocol.LegacyKeyOption(pkHash)) 93 switch errors.Cause(err) { 94 case nil: 95 return account, h, nil 96 case state.ErrStateNotExist: 97 tip, err := sr.Height() 98 if err != nil { 99 return nil, 0, err 100 } 101 ctx = protocol.WithBlockCtx(ctx, protocol.BlockCtx{ 102 BlockHeight: tip + 1, 103 }) 104 ctx = protocol.WithFeatureCtx(ctx) 105 var opts []state.AccountCreationOption 106 if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { 107 opts = append(opts, state.LegacyNonceAccountTypeOption()) 108 } 109 account, err = state.NewAccount(opts...) 110 return account, h, err 111 default: 112 return nil, h, errors.Wrapf(err, "error when loading state of %x", pkHash) 113 } 114 }