github.com/Finschia/finschia-sdk@v0.48.1/x/genutil/collect.go (about)

     1  package genutil
     2  
     3  // DONTCOVER
     4  
     5  import (
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"sort"
    13  	"strings"
    14  
    15  	cfg "github.com/Finschia/ostracon/config"
    16  	octypes "github.com/Finschia/ostracon/types"
    17  
    18  	"github.com/Finschia/finschia-sdk/client"
    19  	"github.com/Finschia/finschia-sdk/codec"
    20  	sdk "github.com/Finschia/finschia-sdk/types"
    21  	bankexported "github.com/Finschia/finschia-sdk/x/bank/exported"
    22  	"github.com/Finschia/finschia-sdk/x/genutil/types"
    23  	stakingtypes "github.com/Finschia/finschia-sdk/x/staking/types"
    24  )
    25  
    26  // GenAppStateFromConfig gets the genesis app state from the config
    27  func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodingConfig,
    28  	config *cfg.Config, initCfg types.InitConfig, genDoc octypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
    29  ) (appState json.RawMessage, err error) {
    30  	// process genesis transactions, else create default genesis.json
    31  	appGenTxs, persistentPeers, err := CollectTxs(
    32  		cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator,
    33  	)
    34  	if err != nil {
    35  		return appState, err
    36  	}
    37  
    38  	config.P2P.PersistentPeers = persistentPeers
    39  	cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
    40  
    41  	// if there are no gen txs to be processed, return the default empty state
    42  	if len(appGenTxs) == 0 {
    43  		return appState, errors.New("there must be at least one genesis tx")
    44  	}
    45  
    46  	// create the app state
    47  	appGenesisState, err := types.GenesisStateFromGenDoc(genDoc)
    48  	if err != nil {
    49  		return appState, err
    50  	}
    51  
    52  	appGenesisState, err = SetGenTxsInAppGenesisState(cdc, txEncodingConfig.TxJSONEncoder(), appGenesisState, appGenTxs)
    53  	if err != nil {
    54  		return appState, err
    55  	}
    56  
    57  	appState, err = json.MarshalIndent(appGenesisState, "", "  ")
    58  	if err != nil {
    59  		return appState, err
    60  	}
    61  
    62  	genDoc.AppState = appState
    63  	err = ExportGenesisFile(&genDoc, config.GenesisFile())
    64  
    65  	return appState, err
    66  }
    67  
    68  // CollectTxs processes and validates application's genesis Txs and returns
    69  // the list of appGenTxs, and persistent peers required to generate genesis.json.
    70  func CollectTxs(cdc codec.JSONCodec, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string,
    71  	genDoc octypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
    72  ) (appGenTxs []sdk.Tx, persistentPeers string, err error) {
    73  	// prepare a map of all balances in genesis state to then validate
    74  	// against the validators addresses
    75  	var appState map[string]json.RawMessage
    76  	if err := json.Unmarshal(genDoc.AppState, &appState); err != nil {
    77  		return appGenTxs, persistentPeers, err
    78  	}
    79  
    80  	fos, err := os.ReadDir(genTxsDir)
    81  	if err != nil {
    82  		return appGenTxs, persistentPeers, err
    83  	}
    84  
    85  	balancesMap := make(map[string]bankexported.GenesisBalance)
    86  
    87  	genBalIterator.IterateGenesisBalances(
    88  		cdc, appState,
    89  		func(balance bankexported.GenesisBalance) (stop bool) {
    90  			balancesMap[balance.GetAddress().String()] = balance
    91  			return false
    92  		},
    93  	)
    94  
    95  	// addresses and IPs (and port) validator server info
    96  	var addressesIPs []string
    97  
    98  	for _, fo := range fos {
    99  		if fo.IsDir() {
   100  			continue
   101  		}
   102  		if !strings.HasSuffix(fo.Name(), ".json") {
   103  			continue
   104  		}
   105  
   106  		// get the genTx
   107  		jsonRawTx, err := os.ReadFile(filepath.Join(genTxsDir, fo.Name()))
   108  		if err != nil {
   109  			return appGenTxs, persistentPeers, err
   110  		}
   111  
   112  		var genTx sdk.Tx
   113  		if genTx, err = txJSONDecoder(jsonRawTx); err != nil {
   114  			return appGenTxs, persistentPeers, err
   115  		}
   116  
   117  		appGenTxs = append(appGenTxs, genTx)
   118  
   119  		// the memo flag is used to store
   120  		// the ip and node-id, for example this may be:
   121  		// "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656"
   122  
   123  		memoTx, ok := genTx.(sdk.TxWithMemo)
   124  		if !ok {
   125  			return appGenTxs, persistentPeers, fmt.Errorf("expected TxWithMemo, got %T", genTx)
   126  		}
   127  		nodeAddrIP := memoTx.GetMemo()
   128  		if len(nodeAddrIP) == 0 {
   129  			return appGenTxs, persistentPeers, fmt.Errorf("failed to find node's address and IP in %s", fo.Name())
   130  		}
   131  
   132  		// genesis transactions must be single-message
   133  		msgs := genTx.GetMsgs()
   134  
   135  		// TODO abstract out staking message validation back to staking
   136  		msg := msgs[0].(*stakingtypes.MsgCreateValidator)
   137  
   138  		// validate delegator and validator addresses and funds against the accounts in the state
   139  		delAddr := msg.DelegatorAddress
   140  		valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
   141  		if err != nil {
   142  			return appGenTxs, persistentPeers, err
   143  		}
   144  
   145  		delBal, delOk := balancesMap[delAddr]
   146  		if !delOk {
   147  			_, file, no, ok := runtime.Caller(1)
   148  			if ok {
   149  				fmt.Printf("CollectTxs-1, called from %s#%d\n", file, no)
   150  			}
   151  
   152  			return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", delAddr, balancesMap)
   153  		}
   154  
   155  		_, valOk := balancesMap[sdk.AccAddress(valAddr).String()]
   156  		if !valOk {
   157  			_, file, no, ok := runtime.Caller(1)
   158  			if ok {
   159  				fmt.Printf("CollectTxs-2, called from %s#%d - %s\n", file, no, valAddr.String())
   160  			}
   161  			return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", valAddr, balancesMap)
   162  		}
   163  
   164  		if delBal.GetCoins().AmountOf(msg.Value.Denom).LT(msg.Value.Amount) {
   165  			return appGenTxs, persistentPeers, fmt.Errorf(
   166  				"insufficient fund for delegation %v: %v < %v",
   167  				delBal.GetAddress().String(), delBal.GetCoins().AmountOf(msg.Value.Denom), msg.Value.Amount,
   168  			)
   169  		}
   170  
   171  		// exclude itself from persistent peers
   172  		if msg.Description.Moniker != moniker {
   173  			addressesIPs = append(addressesIPs, nodeAddrIP)
   174  		}
   175  	}
   176  
   177  	sort.Strings(addressesIPs)
   178  	persistentPeers = strings.Join(addressesIPs, ",")
   179  
   180  	return appGenTxs, persistentPeers, nil
   181  }