github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/types/genesis.go (about)

     1  package types
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"sort"
     7  
     8  	proto "github.com/cosmos/gogoproto/proto"
     9  
    10  	"github.com/cosmos/cosmos-sdk/codec"
    11  	"github.com/cosmos/cosmos-sdk/codec/types"
    12  	sdk "github.com/cosmos/cosmos-sdk/types"
    13  	"github.com/cosmos/cosmos-sdk/types/module"
    14  )
    15  
    16  var _ types.UnpackInterfacesMessage = GenesisState{}
    17  
    18  // RandomGenesisAccountsFn defines the function required to generate custom account types
    19  type RandomGenesisAccountsFn func(simState *module.SimulationState) GenesisAccounts
    20  
    21  // NewGenesisState - Create a new genesis state
    22  func NewGenesisState(params Params, accounts GenesisAccounts) *GenesisState {
    23  	genAccounts, err := PackAccounts(accounts)
    24  	if err != nil {
    25  		panic(err)
    26  	}
    27  	return &GenesisState{
    28  		Params:   params,
    29  		Accounts: genAccounts,
    30  	}
    31  }
    32  
    33  // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
    34  func (g GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error {
    35  	for _, any := range g.Accounts {
    36  		var account GenesisAccount
    37  		err := unpacker.UnpackAny(any, &account)
    38  		if err != nil {
    39  			return err
    40  		}
    41  	}
    42  	return nil
    43  }
    44  
    45  // DefaultGenesisState - Return a default genesis state
    46  func DefaultGenesisState() *GenesisState {
    47  	return NewGenesisState(DefaultParams(), GenesisAccounts{})
    48  }
    49  
    50  // GetGenesisStateFromAppState returns x/auth GenesisState given raw application
    51  // genesis state.
    52  func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) GenesisState {
    53  	var genesisState GenesisState
    54  
    55  	if appState[ModuleName] != nil {
    56  		cdc.MustUnmarshalJSON(appState[ModuleName], &genesisState)
    57  	}
    58  
    59  	return genesisState
    60  }
    61  
    62  // ValidateGenesis performs basic validation of auth genesis data returning an
    63  // error for any failed validation criteria.
    64  func ValidateGenesis(data GenesisState) error {
    65  	if err := data.Params.Validate(); err != nil {
    66  		return err
    67  	}
    68  
    69  	genAccs, err := UnpackAccounts(data.Accounts)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	return ValidateGenAccounts(genAccs)
    75  }
    76  
    77  // SanitizeGenesisAccounts sorts accounts and coin sets.
    78  func SanitizeGenesisAccounts(genAccs GenesisAccounts) GenesisAccounts {
    79  	// Make sure there aren't any duplicated account numbers by fixing the duplicates with the lowest unused values.
    80  	// seenAccNum = easy lookup for used account numbers.
    81  	seenAccNum := map[uint64]bool{}
    82  	// dupAccNum = a map of account number to accounts with duplicate account numbers (excluding the 1st one seen).
    83  	dupAccNum := map[uint64]GenesisAccounts{}
    84  	for _, acc := range genAccs {
    85  		num := acc.GetAccountNumber()
    86  		if !seenAccNum[num] {
    87  			seenAccNum[num] = true
    88  		} else {
    89  			dupAccNum[num] = append(dupAccNum[num], acc)
    90  		}
    91  	}
    92  
    93  	// dupAccNums a sorted list of the account numbers with duplicates.
    94  	var dupAccNums []uint64
    95  	for num := range dupAccNum {
    96  		dupAccNums = append(dupAccNums, num)
    97  	}
    98  	sort.Slice(dupAccNums, func(i, j int) bool {
    99  		return dupAccNums[i] < dupAccNums[j]
   100  	})
   101  
   102  	// Change the account number of the duplicated ones to the first unused value.
   103  	globalNum := uint64(0)
   104  	for _, dupNum := range dupAccNums {
   105  		accs := dupAccNum[dupNum]
   106  		for _, acc := range accs {
   107  			for seenAccNum[globalNum] {
   108  				globalNum++
   109  			}
   110  			if err := acc.SetAccountNumber(globalNum); err != nil {
   111  				panic(err)
   112  			}
   113  			seenAccNum[globalNum] = true
   114  		}
   115  	}
   116  
   117  	// Then sort them all by account number.
   118  	sort.Slice(genAccs, func(i, j int) bool {
   119  		return genAccs[i].GetAccountNumber() < genAccs[j].GetAccountNumber()
   120  	})
   121  	return genAccs
   122  }
   123  
   124  // ValidateGenAccounts validates an array of GenesisAccounts and checks for duplicates
   125  func ValidateGenAccounts(accounts GenesisAccounts) error {
   126  	addrMap := make(map[string]bool, len(accounts))
   127  
   128  	for _, acc := range accounts {
   129  		// check for duplicated accounts
   130  		addrStr := acc.GetAddress().String()
   131  		if _, ok := addrMap[addrStr]; ok {
   132  			return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr)
   133  		}
   134  
   135  		addrMap[addrStr] = true
   136  
   137  		// check account specific validation
   138  		if err := acc.Validate(); err != nil {
   139  			return fmt.Errorf("invalid account found in genesis state; address: %s, error: %s", addrStr, err.Error())
   140  		}
   141  	}
   142  	return nil
   143  }
   144  
   145  // GenesisAccountIterator implements genesis account iteration.
   146  type GenesisAccountIterator struct{}
   147  
   148  // IterateGenesisAccounts iterates over all the genesis accounts found in
   149  // appGenesis and invokes a callback on each genesis account. If any call
   150  // returns true, iteration stops.
   151  func (GenesisAccountIterator) IterateGenesisAccounts(
   152  	cdc codec.Codec, appGenesis map[string]json.RawMessage, cb func(sdk.AccountI) (stop bool),
   153  ) {
   154  	for _, genAcc := range GetGenesisStateFromAppState(cdc, appGenesis).Accounts {
   155  		acc, ok := genAcc.GetCachedValue().(sdk.AccountI)
   156  		if !ok {
   157  			panic("expected account")
   158  		}
   159  		if cb(acc) {
   160  			break
   161  		}
   162  	}
   163  }
   164  
   165  // PackAccounts converts GenesisAccounts to Any slice
   166  func PackAccounts(accounts GenesisAccounts) ([]*types.Any, error) {
   167  	accountsAny := make([]*types.Any, len(accounts))
   168  	for i, acc := range accounts {
   169  		msg, ok := acc.(proto.Message)
   170  		if !ok {
   171  			return nil, fmt.Errorf("cannot proto marshal %T", acc)
   172  		}
   173  		any, err := types.NewAnyWithValue(msg)
   174  		if err != nil {
   175  			return nil, err
   176  		}
   177  		accountsAny[i] = any
   178  	}
   179  
   180  	return accountsAny, nil
   181  }
   182  
   183  // UnpackAccounts converts Any slice to GenesisAccounts
   184  func UnpackAccounts(accountsAny []*types.Any) (GenesisAccounts, error) {
   185  	accounts := make(GenesisAccounts, len(accountsAny))
   186  	for i, any := range accountsAny {
   187  		acc, ok := any.GetCachedValue().(GenesisAccount)
   188  		if !ok {
   189  			return nil, fmt.Errorf("expected genesis account")
   190  		}
   191  		accounts[i] = acc
   192  	}
   193  
   194  	return accounts, nil
   195  }