forked from cosmos/cosmos-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinit.go
237 lines (203 loc) · 6.21 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
package server
import (
"encoding/json"
"fmt"
"io/ioutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/spf13/cobra"
"github.com/tendermint/go-crypto/keys"
"github.com/tendermint/go-crypto/keys/words"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
tmtypes "github.com/tendermint/tendermint/types"
pvm "github.com/tendermint/tendermint/types/priv_validator"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
)
// testnetInformation contains the info necessary
// to setup a testnet including this account and validator.
type testnetInformation struct {
Secret string `json:"secret"`
ChainID string `json:"chain_id"`
Account string `json:"account"`
Validator tmtypes.GenesisValidator `json:"validator"`
NodeID p2p.ID `json:"node_id"`
}
type initCmd struct {
genAppState GenAppState
context *Context
}
// InitCmd will initialize all files for tendermint,
// along with proper app_state.
// The application can pass in a function to generate
// proper state. And may want to use GenerateCoinKey
// to create default account(s).
func InitCmd(gen GenAppState, ctx *Context) *cobra.Command {
cmd := initCmd{
genAppState: gen,
context: ctx,
}
cobraCmd := cobra.Command{
Use: "init",
Short: "Initialize genesis files",
RunE: cmd.run,
}
return &cobraCmd
}
func (c initCmd) run(cmd *cobra.Command, args []string) error {
// Store testnet information as we go
var testnetInfo testnetInformation
// Run the basic tendermint initialization,
// set up a default genesis with no app_options
config := c.context.Config
err := c.initTendermintFiles(config, &testnetInfo)
if err != nil {
return err
}
// no app_options, leave like tendermint
if c.genAppState == nil {
return nil
}
// generate secret and address
addr, secret, err := GenerateCoinKey()
if err != nil {
return err
}
var defaultDenom = "mycoin"
// Now, we want to add the custom app_state
appState, err := c.genAppState(args, addr, defaultDenom)
if err != nil {
return err
}
testnetInfo.Secret = secret
testnetInfo.Account = addr.String()
// And add them to the genesis file
genFile := config.GenesisFile()
if err := addGenesisState(genFile, appState); err != nil {
return err
}
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
if err != nil {
return err
}
testnetInfo.NodeID = nodeKey.ID()
out, err := wire.MarshalJSONIndent(cdc, testnetInfo)
if err != nil {
return err
}
fmt.Println(string(out))
return nil
}
// This was copied from tendermint/cmd/tendermint/commands/init.go
// so we could pass in the config and the logger.
func (c initCmd) initTendermintFiles(config *cfg.Config, info *testnetInformation) error {
// private validator
privValFile := config.PrivValidatorFile()
var privValidator *pvm.FilePV
if cmn.FileExists(privValFile) {
privValidator = pvm.LoadFilePV(privValFile)
c.context.Logger.Info("Found private validator", "path", privValFile)
} else {
privValidator = pvm.GenFilePV(privValFile)
privValidator.Save()
c.context.Logger.Info("Generated private validator", "path", privValFile)
}
// genesis file
genFile := config.GenesisFile()
if cmn.FileExists(genFile) {
c.context.Logger.Info("Found genesis file", "path", genFile)
} else {
genDoc := tmtypes.GenesisDoc{
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
}
genDoc.Validators = []tmtypes.GenesisValidator{{
PubKey: privValidator.GetPubKey(),
Power: 10,
}}
if err := genDoc.SaveAs(genFile); err != nil {
return err
}
c.context.Logger.Info("Generated genesis file", "path", genFile)
}
// reload the config file and find our validator info
loadedDoc, err := tmtypes.GenesisDocFromFile(genFile)
if err != nil {
return err
}
for _, validator := range loadedDoc.Validators {
if validator.PubKey == privValidator.GetPubKey() {
info.Validator = validator
}
}
info.ChainID = loadedDoc.ChainID
return nil
}
//-------------------------------------------------------------------
// GenAppState takes the command line args, as well
// as an address and coin denomination.
// It returns a default app_state to be included in
// in the genesis file.
// This is application-specific
type GenAppState func(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error)
// DefaultGenAppState expects two args: an account address
// and a coin denomination, and gives lots of coins to that address.
func DefaultGenAppState(args []string, addr sdk.Address, coinDenom string) (json.RawMessage, error) {
opts := fmt.Sprintf(`{
"accounts": [{
"address": "%s",
"coins": [
{
"denom": "%s",
"amount": 9007199254740992
}
]
}]
}`, addr.String(), coinDenom)
return json.RawMessage(opts), nil
}
//-------------------------------------------------------------------
// GenesisDoc involves some tendermint-specific structures we don't
// want to parse, so we just grab it into a raw object format,
// so we can add one line.
type GenesisDoc map[string]json.RawMessage
func addGenesisState(filename string, appState json.RawMessage) error {
bz, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
var doc GenesisDoc
err = cdc.UnmarshalJSON(bz, &doc)
if err != nil {
return err
}
doc["app_state"] = appState
out, err := wire.MarshalJSONIndent(cdc, doc)
if err != nil {
return err
}
return ioutil.WriteFile(filename, out, 0600)
}
//-------------------------------------------------------------------
// GenerateCoinKey returns the address of a public key,
// along with the secret phrase to recover the private key.
// You can give coins to this address and return the recovery
// phrase to the user to access them.
func GenerateCoinKey() (sdk.Address, string, error) {
// construct an in-memory key store
codec, err := words.LoadCodec("english")
if err != nil {
return nil, "", err
}
keybase := keys.New(
dbm.NewMemDB(),
codec,
)
// generate a private key, with recovery phrase
info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519)
if err != nil {
return nil, "", err
}
addr := info.PubKey.Address()
return addr, secret, nil
}