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  }