/
init.go
283 lines (256 loc) · 8.88 KB
/
init.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
package init
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
authtx "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
)
const (
flagWithTxs = "with-txs"
flagOverwrite = "overwrite"
flagClientHome = "home-client"
flagOverwriteKey = "overwrite-key"
flagSkipGenesis = "skip-genesis"
flagMoniker = "moniker"
)
type initConfig struct {
ChainID string
GenTxsDir string
Name string
NodeID string
ClientHome string
WithTxs bool
Overwrite bool
OverwriteKey bool
ValPubKey crypto.PubKey
}
type printInfo struct {
Moniker string `json:"moniker"`
ChainID string `json:"chain_id"`
NodeID string `json:"node_id"`
AppMessage json.RawMessage `json:"app_message"`
}
// nolint: errcheck
func displayInfo(cdc *codec.Codec, info printInfo) error {
out, err := codec.MarshalJSONIndent(cdc, info)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "%s\n", string(out))
return nil
}
// get cmd to initialize all files for tendermint and application
// nolint
func InitCmd(ctx *server.Context, cdc *codec.Codec, appInit server.AppInit) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "Initialize private validator, p2p, genesis, and application configuration files",
Long: `Initialize validators's and node's configuration files.
Note that only node's configuration files will be written if the flag --skip-genesis is
enabled, and the genesis file will not be generated.
`,
Args: cobra.NoArgs,
RunE: func(_ *cobra.Command, _ []string) error {
config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName)
chainID := viper.GetString(client.FlagChainID)
if chainID == "" {
chainID = fmt.Sprintf("test-chain-%v", common.RandStr(6))
}
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
if err != nil {
return err
}
if viper.GetString(flagMoniker) != "" {
config.Moniker = viper.GetString(flagMoniker)
}
if config.Moniker == "" && name != "" {
config.Moniker = name
}
toPrint := printInfo{
ChainID: chainID,
Moniker: config.Moniker,
NodeID: nodeID,
}
if viper.GetBool(flagSkipGenesis) {
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
return displayInfo(cdc, toPrint)
}
initCfg := initConfig{
ChainID: chainID,
GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"),
Name: name,
NodeID: nodeID,
ClientHome: viper.GetString(flagClientHome),
WithTxs: viper.GetBool(flagWithTxs),
Overwrite: viper.GetBool(flagOverwrite),
OverwriteKey: viper.GetBool(flagOverwriteKey),
ValPubKey: valPubKey,
}
appMessage, err := initWithConfig(cdc, config, initCfg)
// print out some key information
if err != nil {
return err
}
toPrint.AppMessage = appMessage
return displayInfo(cdc, toPrint)
},
}
// TODO(#118): need to figure out why cosmos add this flag: https://github.com/cosmos/cosmos-sdk/blob/v0.23.1/server/init.go
//cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file")
cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().Bool(flagWithTxs, false, "apply existing genesis transactions from [--home]/config/gentx/")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().String(flagMoniker, "", "overrides --name flag and set the validator's moniker to a different value; ignored if it runs without the --with-txs flag")
// TODO(#118): need to figure out why cosmos add this flag: https://github.com/cosmos/cosmos-sdk/blob/v0.23.1/server/init.go
//cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().Bool(flagOverwriteKey, false, "overwrite client's key")
cmd.Flags().Bool(flagSkipGenesis, false, "do not create genesis.json")
return cmd
}
// InitializeNodeValidatorFiles creates private validator and p2p configuration files.
func InitializeNodeValidatorFiles(config *cfg.Config) (nodeID string, valPubKey crypto.PubKey, err error) {
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return
}
nodeID = string(nodeKey.ID())
valPubKey = ReadOrCreatePrivValidator(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
return
}
func initWithConfig(cdc *codec.Codec, config *cfg.Config, initCfg initConfig) (
appMessage json.RawMessage, err error) {
genFile := config.GenesisFile()
if !initCfg.Overwrite && common.FileExists(genFile) {
err = fmt.Errorf("genesis.json file already exists: %v", genFile)
return
}
// process genesis transactions, else create default genesis.json
var appGenTxs []auth.StdTx
var persistentPeers string
var genTxs []json.RawMessage
var appState json.RawMessage
var jsonRawTx json.RawMessage
chainID := initCfg.ChainID
if initCfg.WithTxs {
_, appGenTxs, persistentPeers, err = app.CollectStdTxs(config.Moniker, initCfg.GenTxsDir, cdc)
if err != nil {
return
}
genTxs = make([]json.RawMessage, len(appGenTxs))
config.P2P.PersistentPeers = persistentPeers
for i, stdTx := range appGenTxs {
jsonRawTx, err = cdc.MarshalJSON(stdTx)
if err != nil {
return
}
genTxs[i] = jsonRawTx
}
} else {
var ip, keyPass, secret string
var addr sdk.AccAddress
var signedTx auth.StdTx
if initCfg.Name == "" {
err = errors.New("must specify validator's moniker (--name)")
return
}
config.Moniker = initCfg.Name
ip, err = server.ExternalIP()
if err != nil {
return
}
memo := fmt.Sprintf("%s@%s:26656", initCfg.NodeID, ip)
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password for account %q (default: %q):", initCfg.Name, app.DefaultKeyPass)
keyPass, err = client.GetPassword(prompt, buf)
if err != nil && keyPass != "" {
// An error was returned that either failed to read the password from
// STDIN or the given password is not empty but failed to meet minimum
// length requirements.
return
}
if keyPass == "" {
keyPass = app.DefaultKeyPass
}
addr, secret, err = server.GenerateSaveCoinKey(initCfg.ClientHome, initCfg.Name, keyPass, initCfg.OverwriteKey)
if err != nil {
return
}
appMessage, err = json.Marshal(map[string]string{"secret": secret})
if err != nil {
return
}
msg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr),
initCfg.ValPubKey,
sdk.NewCoin("steak", 100),
stake.NewDescription(config.Moniker, "", "", ""),
stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
)
txBldr := authtx.NewTxBuilderFromCLI().WithCodec(cdc).WithMemo(memo).WithChainID(chainID)
signedTx, err = txBldr.SignStdTx(
initCfg.Name, keyPass, auth.NewStdTx([]sdk.Msg{msg}, []auth.StdSignature{}, memo, auth.DefaultSource, nil), false,
)
if err != nil {
return
}
jsonRawTx, err = cdc.MarshalJSON(signedTx)
if err != nil {
return
}
genTxs = []json.RawMessage{jsonRawTx}
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)
appState, err = app.GaiaAppGenStateJSON(cdc, genTxs)
if err != nil {
return
}
err = WriteGenesisFile(genFile, chainID, nil, appState)
return
}
// WriteGenesisFile creates and writes the genesis configuration to disk. An
// error is returned if building or writing the configuration to file fails.
// nolint: unparam
func WriteGenesisFile(genesisFile, chainID string, validators []types.GenesisValidator, appState json.RawMessage) error {
genDoc := types.GenesisDoc{
ChainID: chainID,
Validators: validators,
AppState: appState,
}
if err := genDoc.ValidateAndComplete(); err != nil {
return err
}
return genDoc.SaveAs(genesisFile)
}
// read of create the private key file for this config
func ReadOrCreatePrivValidator(privValKeyFile, privValStateFile string) crypto.PubKey {
// private validator
var privValidator *privval.FilePV
if common.FileExists(privValKeyFile) && common.FileExists(privValStateFile) {
privValidator = privval.LoadFilePV(privValKeyFile, privValStateFile)
} else {
privValidator = privval.GenFilePV(privValKeyFile, privValStateFile)
privValidator.Save()
}
return privValidator.GetPubKey()
}