cosmos-sdk/x/genutil/collect.go

175 lines
5.3 KiB
Go

package genutil
// DONTCOVER
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
cfg "github.com/tendermint/tendermint/config"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported"
"github.com/cosmos/cosmos-sdk/x/genutil/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// GenAppStateFromConfig gets the genesis app state from the config
func GenAppStateFromConfig(cdc codec.JSONMarshaler, txEncodingConfig client.TxEncodingConfig,
config *cfg.Config, initCfg types.InitConfig, genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
) (appState json.RawMessage, err error) {
// process genesis transactions, else create default genesis.json
appGenTxs, persistentPeers, err := CollectTxs(
cdc, txEncodingConfig.TxJSONDecoder(), config.Moniker, initCfg.GenTxsDir, genDoc, genBalIterator,
)
if err != nil {
return appState, err
}
config.P2P.PersistentPeers = persistentPeers
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
// if there are no gen txs to be processed, return the default empty state
if len(appGenTxs) == 0 {
return appState, errors.New("there must be at least one genesis tx")
}
// create the app state
appGenesisState, err := types.GenesisStateFromGenDoc(genDoc)
if err != nil {
return appState, err
}
appGenesisState, err = SetGenTxsInAppGenesisState(cdc, txEncodingConfig.TxJSONEncoder(), appGenesisState, appGenTxs)
if err != nil {
return appState, err
}
appState, err = json.MarshalIndent(appGenesisState, "", " ")
if err != nil {
return appState, err
}
genDoc.AppState = appState
err = ExportGenesisFile(&genDoc, config.GenesisFile())
return appState, err
}
// CollectTxs processes and validates application's genesis Txs and returns
// the list of appGenTxs, and persistent peers required to generate genesis.json.
func CollectTxs(cdc codec.JSONMarshaler, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string,
genDoc tmtypes.GenesisDoc, genBalIterator types.GenesisBalancesIterator,
) (appGenTxs []sdk.Tx, persistentPeers string, err error) {
var fos []os.FileInfo
fos, err = ioutil.ReadDir(genTxsDir)
if err != nil {
return appGenTxs, persistentPeers, err
}
// prepare a map of all balances in genesis state to then validate
// against the validators addresses
var appState map[string]json.RawMessage
if err := json.Unmarshal(genDoc.AppState, &appState); err != nil {
return appGenTxs, persistentPeers, err
}
balancesMap := make(map[string]bankexported.GenesisBalance)
genBalIterator.IterateGenesisBalances(
cdc, appState,
func(balance bankexported.GenesisBalance) (stop bool) {
balancesMap[balance.GetAddress().String()] = balance
return false
},
)
// addresses and IPs (and port) validator server info
var addressesIPs []string
for _, fo := range fos {
filename := filepath.Join(genTxsDir, fo.Name())
if !fo.IsDir() && (filepath.Ext(filename) != ".json") {
continue
}
// get the genTx
var jsonRawTx []byte
if jsonRawTx, err = ioutil.ReadFile(filename); err != nil {
return appGenTxs, persistentPeers, err
}
var genTx sdk.Tx
if genTx, err = txJSONDecoder(jsonRawTx); err != nil {
return appGenTxs, persistentPeers, err
}
appGenTxs = append(appGenTxs, genTx)
// the memo flag is used to store
// the ip and node-id, for example this may be:
// "528fd3df22b31f4969b05652bfe8f0fe921321d5@192.168.2.37:26656"
memoTx, ok := genTx.(sdk.TxWithMemo)
if !ok {
return appGenTxs, persistentPeers, fmt.Errorf("expected TxWithMemo, got %T", genTx)
}
nodeAddrIP := memoTx.GetMemo()
if len(nodeAddrIP) == 0 {
return appGenTxs, persistentPeers, fmt.Errorf("failed to find node's address and IP in %s", fo.Name())
}
// genesis transactions must be single-message
msgs := genTx.GetMsgs()
if len(msgs) != 1 {
return appGenTxs, persistentPeers, errors.New("each genesis transaction must provide a single genesis message")
}
// TODO abstract out staking message validation back to staking
msg := msgs[0].(*stakingtypes.MsgCreateValidator)
// validate delegator and validator addresses and funds against the accounts in the state
delAddr := msg.DelegatorAddress.String()
valAddr := sdk.AccAddress(msg.ValidatorAddress).String()
delBal, delOk := balancesMap[delAddr]
if !delOk {
return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", delAddr, balancesMap)
}
_, valOk := balancesMap[valAddr]
if !valOk {
return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", valAddr, balancesMap)
}
if delBal.GetCoins().AmountOf(msg.Value.Denom).LT(msg.Value.Amount) {
return appGenTxs, persistentPeers, fmt.Errorf(
"insufficient fund for delegation %v: %v < %v",
delBal.GetAddress().String(), delBal.GetCoins().AmountOf(msg.Value.Denom), msg.Value.Amount,
)
}
// exclude itself from persistent peers
if msg.Description.Moniker != moniker {
addressesIPs = append(addressesIPs, nodeAddrIP)
}
}
sort.Strings(addressesIPs)
persistentPeers = strings.Join(addressesIPs, ",")
return appGenTxs, persistentPeers, nil
}