Skip to content

Commit 1099bb8

Browse files
authored
Merge pull request #123 from initia-labs/feat/keyfile
feat: revamp mnemonic and key file logic
2 parents e4e7fd9 + 78c5bcb commit 1099bb8

File tree

17 files changed

+261
-252
lines changed

17 files changed

+261
-252
lines changed

cmd/opinit_bots.go

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"github.com/initia-labs/weave/common"
1616
weavecontext "github.com/initia-labs/weave/context"
1717
"github.com/initia-labs/weave/cosmosutils"
18-
"github.com/initia-labs/weave/io"
18+
weaveio "github.com/initia-labs/weave/io"
1919
"github.com/initia-labs/weave/models/opinit_bots"
2020
"github.com/initia-labs/weave/service"
2121
)
@@ -136,27 +136,6 @@ func OPInitBotsKeysSetupCommand() *cobra.Command {
136136
return setupCmd
137137
}
138138

139-
func generateKeyFile(keyPath string, botName string) (opinit_bots.KeyFile, error) {
140-
keyFile, err := opinit_bots.GenerateMnemonicKeyfile(botName)
141-
if err != nil {
142-
return keyFile, err
143-
}
144-
145-
// Marshal KeyFile to JSON
146-
data, err := json.MarshalIndent(keyFile, "", " ")
147-
if err != nil {
148-
return keyFile, fmt.Errorf("error marshaling KeyFile to JSON: %w", err)
149-
}
150-
151-
// Write JSON data to a file
152-
err = os.WriteFile(keyPath, data, 0644)
153-
if err != nil {
154-
return keyFile, fmt.Errorf("error writing to file: %w", err)
155-
}
156-
157-
return keyFile, nil
158-
}
159-
160139
func validateConfigFlags(args []string, configPath, keyFilePath string, isGenerateKeyFile bool) error {
161140
if configPath != "" {
162141
if len(args) == 0 {
@@ -168,7 +147,7 @@ func validateConfigFlags(args []string, configPath, keyFilePath string, isGenera
168147
if keyFilePath == "" && !isGenerateKeyFile {
169148
return fmt.Errorf("invalid configuration: if --with-config is set, either --generate-key-file or --key-file must be provided")
170149
}
171-
if !io.FileOrFolderExists(configPath) {
150+
if !weaveio.FileOrFolderExists(configPath) {
172151
return fmt.Errorf("the provided --with-config does not exist: %s", configPath)
173152
}
174153
} else {
@@ -192,16 +171,20 @@ func handleWithConfig(cmd *cobra.Command, userHome, opInitHome, configPath, keyF
192171
return err
193172
}
194173

195-
var keyFile opinit_bots.KeyFile
174+
var keyFile *weaveio.KeyFile
196175
if isGenerateKeyFile {
197176
keyPath := filepath.Join(userHome, common.WeaveDataDirectory, fmt.Sprintf("%s.%s.keyfile", common.OpinitGeneratedKeyFilename, botName))
198-
keyFile, err = generateKeyFile(keyPath, botName)
177+
keyFile, err = opinit_bots.GenerateMnemonicKeyfile(botName)
199178
if err != nil {
200-
return err
179+
return fmt.Errorf("error generating keyfile: %v", err)
180+
}
181+
err = keyFile.Write(keyPath)
182+
if err != nil {
183+
return fmt.Errorf("error writing to file: %w", err)
201184
}
202185
fmt.Printf("Key file successfully generated. You can find it at: %s\n", keyPath)
203186
} else {
204-
if !io.FileOrFolderExists(keyFilePath) {
187+
if !weaveio.FileOrFolderExists(keyFilePath) {
205188
return fmt.Errorf("key file is missing at path: %s", keyFilePath)
206189
}
207190

@@ -225,25 +208,25 @@ func handleWithConfig(cmd *cobra.Command, userHome, opInitHome, configPath, keyF
225208
}
226209

227210
// readAndUnmarshalKeyFile read and unmarshal the key file into the KeyFile struct
228-
func readAndUnmarshalKeyFile(keyFilePath string) (opinit_bots.KeyFile, error) {
211+
func readAndUnmarshalKeyFile(keyFilePath string) (*weaveio.KeyFile, error) {
229212
fileData, err := os.ReadFile(keyFilePath)
230213
if err != nil {
231-
return opinit_bots.KeyFile{}, err
214+
return nil, err
232215
}
233216

234-
var keyFile opinit_bots.KeyFile
235-
err = json.Unmarshal(fileData, &keyFile)
217+
keyFile := &weaveio.KeyFile{}
218+
err = json.Unmarshal(fileData, keyFile)
236219
return keyFile, err
237220
}
238221

239222
// handleExistingOpInitHome handle the case where the opInitHome directory exists
240223
func handleExistingOpInitHome(opInitHome string, botName string, force bool) error {
241-
if io.FileOrFolderExists(opInitHome) {
224+
if weaveio.FileOrFolderExists(opInitHome) {
242225
if force {
243226
// delete db
244227
dbPath := filepath.Join(opInitHome, fmt.Sprintf("%s.db", botName))
245-
if io.FileOrFolderExists(dbPath) {
246-
err := io.DeleteDirectory(dbPath)
228+
if weaveio.FileOrFolderExists(dbPath) {
229+
err := weaveio.DeleteDirectory(dbPath)
247230
if err != nil {
248231
return fmt.Errorf("failed to delete %s", dbPath)
249232
}
@@ -256,7 +239,7 @@ func handleExistingOpInitHome(opInitHome string, botName string, force bool) err
256239
}
257240

258241
// initializeBotWithConfig initialize a bot based on the provided config
259-
func initializeBotWithConfig(cmd *cobra.Command, fileData []byte, keyFile opinit_bots.KeyFile, opInitHome, userHome, botName string) error {
242+
func initializeBotWithConfig(cmd *cobra.Command, fileData []byte, keyFile *weaveio.KeyFile, opInitHome, userHome, botName string) error {
260243
var err error
261244

262245
switch botName {
@@ -266,14 +249,14 @@ func initializeBotWithConfig(cmd *cobra.Command, fileData []byte, keyFile opinit
266249
if err != nil {
267250
return err
268251
}
269-
err = opinit_bots.InitializeExecutorWithConfig(config, &keyFile, opInitHome, userHome)
252+
err = opinit_bots.InitializeExecutorWithConfig(config, keyFile, opInitHome, userHome)
270253
case "challenger":
271254
var config opinit_bots.ChallengerConfig
272255
err = json.Unmarshal(fileData, &config)
273256
if err != nil {
274257
return err
275258
}
276-
err = opinit_bots.InitializeChallengerWithConfig(config, &keyFile, opInitHome, userHome)
259+
err = opinit_bots.InitializeChallengerWithConfig(config, keyFile, opInitHome, userHome)
277260
}
278261
if err != nil {
279262
return err

common/constants.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package common
22

33
const (
44
WeaveDirectory = ".weave"
5+
WeaveConfigFile = WeaveDirectory + "/config.json"
56
WeaveDataDirectory = WeaveDirectory + "/data"
67
WeaveLogDirectory = WeaveDirectory + "/log"
78

@@ -18,11 +19,13 @@ const (
1819
MinitiaArtifactsConfigJson = "/artifacts/config.json"
1920
MinitiaArtifactsJson = "/artifacts/artifacts.json"
2021

21-
OPinitDirectory = ".opinit"
22-
OPinitAppName = "opinitd"
22+
OPinitDirectory = ".opinit"
23+
OPinitAppName = "opinitd"
24+
OPinitKeyFileJson = "/weave.keys.json"
25+
OpinitGeneratedKeyFilename = "weave.opinit.generated"
2326

2427
HermesHome = ".hermes"
2528
HermesKeysDirectory = HermesHome + "/keys"
29+
HermesKeyFileJson = HermesHome + "/weave.keys.json"
2630
HermesTempMnemonicFilename = "weave.mnemonic"
27-
OpinitGeneratedKeyFilename = "weave.opinit.generated"
2831
)

config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func InitializeConfig() error {
1919
return fmt.Errorf("failed to get user home directory: %v", err)
2020
}
2121

22-
configPath := filepath.Join(homeDir, common.WeaveDirectory, "config.json")
22+
configPath := filepath.Join(homeDir, common.WeaveConfigFile)
2323
if err := os.MkdirAll(filepath.Dir(configPath), os.ModePerm); err != nil {
2424
return fmt.Errorf("failed to create config directory: %v", err)
2525
}

config/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func (m *MockedFilesystem) Stat(name string) (os.FileInfo, error) {
4040
func TestInitializeConfig(t *testing.T) {
4141
fs := new(MockedFilesystem)
4242
home := "/mock/home"
43-
configPath := filepath.Join(home, common.WeaveDirectory, "config.json")
43+
configPath := filepath.Join(home, common.WeaveConfigFile)
4444

4545
// Resetting mocks for next test case
4646
fs.Mock = mock.Mock{}

context/path.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,11 @@ func GetOPInitHome(ctx context.Context) (string, error) {
7272
}
7373
return "", fmt.Errorf("cannot cast the OPInitHomeKey value into type string")
7474
}
75+
76+
func GetOPInitKeyFileJson(ctx context.Context) (string, error) {
77+
opInitHome, err := GetOPInitHome(ctx)
78+
if err != nil {
79+
return "", err
80+
}
81+
return filepath.Join(opInitHome, common.OPinitKeyFileJson), nil
82+
}

io/filesystem.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010
"path/filepath"
1111
"runtime"
1212

13-
"github.com/atotto/clipboard"
14-
1513
"github.com/initia-labs/weave/client"
1614
)
1715

@@ -154,12 +152,3 @@ func CopyDirectory(src, des string) error {
154152
}
155153
return nil
156154
}
157-
158-
// CopyToClipboard copies the given string to the clipboard.
159-
func CopyToClipboard(text string) error {
160-
// Copy the text to clipboard
161-
if err := clipboard.WriteAll(text); err != nil {
162-
return fmt.Errorf("failed to copy to clipboard: %v", err)
163-
}
164-
return nil
165-
}

io/keyfile.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package io
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
)
8+
9+
type KeyFile map[string]string
10+
11+
func NewKeyFile() *KeyFile {
12+
kf := make(KeyFile)
13+
return &kf
14+
}
15+
16+
func (k *KeyFile) AddMnemonic(name, mnemonic string) {
17+
(*k)[name] = mnemonic
18+
}
19+
20+
func (k *KeyFile) GetMnemonic(name string) string {
21+
return (*k)[name]
22+
}
23+
24+
func (k *KeyFile) Write(filePath string) error {
25+
data, err := json.MarshalIndent(k, "", " ")
26+
if err != nil {
27+
return fmt.Errorf("error marshaling KeyFile to JSON: %w", err)
28+
}
29+
30+
return os.WriteFile(filePath, data, 0644)
31+
}
32+
33+
// Load tries to load an existing key file into the struct if the file exists
34+
func (k *KeyFile) Load(filePath string) error {
35+
if _, err := os.Stat(filePath); os.IsNotExist(err) {
36+
return nil
37+
}
38+
39+
data, err := os.ReadFile(filePath)
40+
if err != nil {
41+
return fmt.Errorf("error reading file: %w", err)
42+
}
43+
44+
err = json.Unmarshal(data, k)
45+
if err != nil {
46+
return fmt.Errorf("error unmarshaling JSON: %w", err)
47+
}
48+
49+
return nil
50+
}

models/initialize.go

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package models
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"path/filepath"
68
"strings"
79
"time"
810

@@ -13,7 +15,6 @@ import (
1315
"github.com/initia-labs/weave/config"
1416
weavecontext "github.com/initia-labs/weave/context"
1517
"github.com/initia-labs/weave/crypto"
16-
"github.com/initia-labs/weave/io"
1718
"github.com/initia-labs/weave/styles"
1819
"github.com/initia-labs/weave/types"
1920
"github.com/initia-labs/weave/ui"
@@ -185,28 +186,15 @@ func (m *GenerateGasStationLoading) View() string {
185186

186187
type GasStationMnemonicDisplayInput struct {
187188
ui.TextInput
188-
ui.Clickable
189189
weavecontext.BaseModel
190-
question string
191-
generatedMnemonic string
190+
question string
192191
}
193192

194193
func NewSystemKeysMnemonicDisplayInput(ctx context.Context) *GasStationMnemonicDisplayInput {
195-
state := weavecontext.GetCurrentState[ExistingCheckerState](ctx)
196194
model := &GasStationMnemonicDisplayInput{
197195
TextInput: ui.NewTextInput(true),
198-
Clickable: *ui.NewClickable(
199-
ui.NewClickableItem(
200-
map[bool]string{
201-
true: "Copied! Click to copy again",
202-
false: "Click here to copy",
203-
}, func() error {
204-
return io.CopyToClipboard(state.generatedMnemonic)
205-
},
206-
)),
207-
BaseModel: weavecontext.BaseModel{Ctx: ctx, CannotBack: true},
208-
question: "Please type `continue` to proceed after you have securely stored the mnemonic.",
209-
generatedMnemonic: state.generatedMnemonic,
196+
BaseModel: weavecontext.BaseModel{Ctx: ctx, CannotBack: true},
197+
question: "Type `continue` to proceed.",
210198
}
211199
model.WithPlaceholder("Type `continue` to continue, Ctrl+C to quit.")
212200
model.WithValidatorFn(common.ValidateExactString("continue"))
@@ -218,24 +206,19 @@ func (m *GasStationMnemonicDisplayInput) GetQuestion() string {
218206
}
219207

220208
func (m *GasStationMnemonicDisplayInput) Init() tea.Cmd {
221-
return m.Clickable.Init()
209+
return nil
222210
}
223211

224212
func (m *GasStationMnemonicDisplayInput) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
225213
if model, cmd, handled := weavecontext.HandleCommonCommands[ExistingCheckerState](m, msg); handled {
226214
return model, cmd
227215
}
228216

229-
err := m.Clickable.ClickableUpdate(msg)
230-
if err != nil {
231-
return m, m.HandlePanic(err)
232-
}
233-
234217
input, cmd, done := m.TextInput.Update(msg)
235218
if done {
236219
state := weavecontext.PushPageAndGetState[ExistingCheckerState](m)
237220
model := NewWeaveAppInitialization(m.Ctx, state.generatedMnemonic)
238-
return model, tea.Batch(model.Init(), m.Clickable.PostUpdate())
221+
return model, model.Init()
239222
}
240223
m.TextInput = input
241224
return m, cmd
@@ -248,18 +231,18 @@ func (m *GasStationMnemonicDisplayInput) View() string {
248231
m.HandlePanic(fmt.Errorf("failed to convert mnemonic to bech32 address: %w", err))
249232
}
250233

234+
userHome, err := os.UserHomeDir()
235+
if err != nil {
236+
m.HandlePanic(fmt.Errorf("failed to get user home directory: %w", err))
237+
}
238+
251239
var mnemonicText string
252-
mnemonicText += styles.RenderMnemonic("Gas Station", gasStationAddress, m.generatedMnemonic, m.Clickable.ClickableView(0))
240+
mnemonicText += styles.RenderKey("Gas Station", gasStationAddress)
253241

254-
viewText := m.WrapView(InitHeader(state.isFirstTime) + "\n" + state.weave.Render() + "\n" +
242+
return m.WrapView(InitHeader(state.isFirstTime) + "\n" + state.weave.Render() + "\n" +
255243
styles.BoldUnderlineText("Important", styles.Yellow) + "\n" +
256-
styles.Text("Write down these mnemonic phrases and store them in a safe place. \nIt is the only way to recover your system keys.", styles.Yellow) + "\n\n" +
257-
mnemonicText + "\n" + styles.RenderPrompt(m.GetQuestion(), []string{"`continue`"}, styles.Question) + m.TextInput.View())
258-
err = m.Clickable.ClickableUpdatePositions(viewText)
259-
if err != nil {
260-
m.HandlePanic(err)
261-
}
262-
return viewText
244+
styles.Text(fmt.Sprintf("Note that the mnemonic phrase for Gas Station along with other configuration details will be stored in %s. You can revisit them anytime.", filepath.Join(userHome, common.WeaveConfigFile)), styles.Yellow) + "\n\n" +
245+
mnemonicText + styles.RenderPrompt(m.GetQuestion(), []string{"`continue`"}, styles.Question) + m.TextInput.View())
263246
}
264247

265248
type GasStationMnemonicInput struct {

0 commit comments

Comments
 (0)