generated from dymensionxyz/rollapp
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
200895e
commit 5ae9dc3
Showing
2 changed files
with
285 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"runtime" | ||
Check notice Code scanning / CodeQL Sensitive package import Note
Certain system packages contain functions which may be a possible source of non-determinism
|
||
"sort" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/codec" | ||
"github.com/cosmos/cosmos-sdk/server" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
bankexported "github.com/cosmos/cosmos-sdk/x/bank/exported" | ||
"github.com/cosmos/cosmos-sdk/x/genutil" | ||
"github.com/cosmos/cosmos-sdk/x/genutil/types" | ||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" | ||
cfg "github.com/tendermint/tendermint/config" | ||
) | ||
|
||
const flagGenTxDir = "gentx-dir" | ||
|
||
type printInfo struct { | ||
Moniker string `json:"moniker" yaml:"moniker"` | ||
ChainID string `json:"chain_id" yaml:"chain_id"` | ||
NodeID string `json:"node_id" yaml:"node_id"` | ||
GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"` | ||
AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` | ||
} | ||
|
||
func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { | ||
return printInfo{ | ||
Moniker: moniker, | ||
ChainID: chainID, | ||
NodeID: nodeID, | ||
GenTxsDir: genTxsDir, | ||
AppMessage: appMessage, | ||
} | ||
} | ||
|
||
func displayInfo(info printInfo) error { | ||
out, err := json.MarshalIndent(info, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = fmt.Fprintf(os.Stderr, "%s\n", sdk.MustSortJSON(out)) | ||
|
||
return err | ||
} | ||
|
||
// CollectGenTxsCmd - return the cobra command to collect genesis transactions | ||
func CollectGenTxsCmd(genBalIterator types.GenesisBalancesIterator, defaultNodeHome string) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "collect-gentxs", | ||
Short: "Collect genesis txs and output a genesis.json file", | ||
RunE: func(cmd *cobra.Command, _ []string) error { | ||
serverCtx := server.GetServerContextFromCmd(cmd) | ||
config := serverCtx.Config | ||
|
||
clientCtx := client.GetClientContextFromCmd(cmd) | ||
cdc := clientCtx.Codec | ||
|
||
config.SetRoot(clientCtx.HomeDir) | ||
|
||
nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to initialize node validator files") | ||
} | ||
|
||
genDoc, err := GenesisDocFromFile(config.GenesisFile()) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to read genesis doc from file") | ||
} | ||
|
||
genTxDir, _ := cmd.Flags().GetString(flagGenTxDir) | ||
genTxsDir := genTxDir | ||
if genTxsDir == "" { | ||
genTxsDir = filepath.Join(config.RootDir, "config", "gentx") | ||
} | ||
|
||
toPrint := newPrintInfo(config.Moniker, genDoc["chain_id"].(string), nodeID, genTxsDir, json.RawMessage("")) | ||
initCfg := types.NewInitConfig(genDoc["chain_id"].(string), genTxsDir, nodeID, valPubKey) | ||
|
||
appMessage, err := GenAppStateFromConfig(cdc, | ||
clientCtx.TxConfig, | ||
config, initCfg, genDoc, genBalIterator) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to get genesis app state from config") | ||
} | ||
|
||
toPrint.AppMessage = appMessage | ||
|
||
return displayInfo(toPrint) | ||
}, | ||
} | ||
|
||
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") | ||
cmd.Flags().String(flagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") | ||
|
||
return cmd | ||
} | ||
|
||
// GenAppStateFromConfig gets the genesis app state from the config | ||
func GenAppStateFromConfig(cdc codec.JSONCodec, txEncodingConfig client.TxEncodingConfig, | ||
config *cfg.Config, initCfg types.InitConfig, genDoc map[string]interface{}, 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") | ||
} | ||
|
||
var appGenesisState map[string]json.RawMessage | ||
|
||
bz, err := json.Marshal(genDoc["app_state"]) | ||
if err != nil { | ||
return appState, err | ||
} | ||
|
||
err = json.Unmarshal(bz, &appGenesisState) | ||
if err != nil { | ||
return appState, err | ||
} | ||
|
||
appGenesisState, err = genutil.SetGenTxsInAppGenesisState(cdc, txEncodingConfig.TxJSONEncoder(), appGenesisState, appGenTxs) | ||
if err != nil { | ||
return appState, err | ||
} | ||
|
||
appState, err = json.MarshalIndent(appGenesisState, "", " ") | ||
if err != nil { | ||
return appState, err | ||
} | ||
|
||
genDoc["app_state"] = appState | ||
genDocBytes, err := json.MarshalIndent(genDoc, "", " ") | ||
if err != nil { | ||
return appState, err | ||
} | ||
err = os.WriteFile(config.GenesisFile(), genDocBytes, 0o644) | ||
if err != nil { | ||
return appState, err | ||
} | ||
|
||
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.JSONCodec, txJSONDecoder sdk.TxDecoder, moniker, genTxsDir string, | ||
genDoc map[string]interface{}, genBalIterator types.GenesisBalancesIterator, | ||
) (appGenTxs []sdk.Tx, persistentPeers string, err error) { | ||
// prepare a map of all balances in genesis state to then validate | ||
// against the validators addresses | ||
var appState map[string]json.RawMessage | ||
bz, err := json.Marshal(genDoc["app_state"]) | ||
if err != nil { | ||
return appGenTxs, persistentPeers, err | ||
} | ||
|
||
if err := json.Unmarshal(bz, &appState); err != nil { | ||
return appGenTxs, persistentPeers, err | ||
} | ||
|
||
var fos []os.DirEntry | ||
fos, err = os.ReadDir(genTxsDir) | ||
if 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 { | ||
if fo.IsDir() { | ||
continue | ||
} | ||
if !strings.HasSuffix(fo.Name(), ".json") { | ||
continue | ||
} | ||
|
||
// get the genTx | ||
jsonRawTx, err := os.ReadFile(filepath.Join(genTxsDir, fo.Name())) | ||
if err != nil { | ||
return appGenTxs, persistentPeers, err | ||
} | ||
|
||
genTx, err := types.ValidateAndGetGenTx(jsonRawTx, txJSONDecoder) | ||
if 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: | ||
// "[email protected]: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() | ||
|
||
// 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 | ||
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) | ||
if err != nil { | ||
return appGenTxs, persistentPeers, err | ||
} | ||
|
||
delBal, delOk := balancesMap[delAddr] | ||
if !delOk { | ||
_, file, no, ok := runtime.Caller(1) | ||
if ok { | ||
fmt.Printf("CollectTxs-1, called from %s#%d\n", file, no) | ||
} | ||
|
||
return appGenTxs, persistentPeers, fmt.Errorf("account %s balance not in genesis state: %+v", delAddr, balancesMap) | ||
} | ||
|
||
_, valOk := balancesMap[sdk.AccAddress(valAddr).String()] | ||
if !valOk { | ||
_, file, no, ok := runtime.Caller(1) | ||
if ok { | ||
fmt.Printf("CollectTxs-2, called from %s#%d - %s\n", file, no, sdk.AccAddress(msg.ValidatorAddress).String()) | ||
} | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters