Skip to content

Commit

Permalink
Merge pull request #123 from initia-labs/feat/keyfile
Browse files Browse the repository at this point in the history
feat: revamp mnemonic and key file logic
  • Loading branch information
traviolus authored Feb 4, 2025
2 parents e4e7fd9 + 78c5bcb commit 1099bb8
Show file tree
Hide file tree
Showing 17 changed files with 261 additions and 252 deletions.
57 changes: 20 additions & 37 deletions cmd/opinit_bots.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/initia-labs/weave/common"
weavecontext "github.com/initia-labs/weave/context"
"github.com/initia-labs/weave/cosmosutils"
"github.com/initia-labs/weave/io"
weaveio "github.com/initia-labs/weave/io"
"github.com/initia-labs/weave/models/opinit_bots"
"github.com/initia-labs/weave/service"
)
Expand Down Expand Up @@ -136,27 +136,6 @@ func OPInitBotsKeysSetupCommand() *cobra.Command {
return setupCmd
}

func generateKeyFile(keyPath string, botName string) (opinit_bots.KeyFile, error) {
keyFile, err := opinit_bots.GenerateMnemonicKeyfile(botName)
if err != nil {
return keyFile, err
}

// Marshal KeyFile to JSON
data, err := json.MarshalIndent(keyFile, "", " ")
if err != nil {
return keyFile, fmt.Errorf("error marshaling KeyFile to JSON: %w", err)
}

// Write JSON data to a file
err = os.WriteFile(keyPath, data, 0644)
if err != nil {
return keyFile, fmt.Errorf("error writing to file: %w", err)
}

return keyFile, nil
}

func validateConfigFlags(args []string, configPath, keyFilePath string, isGenerateKeyFile bool) error {
if configPath != "" {
if len(args) == 0 {
Expand All @@ -168,7 +147,7 @@ func validateConfigFlags(args []string, configPath, keyFilePath string, isGenera
if keyFilePath == "" && !isGenerateKeyFile {
return fmt.Errorf("invalid configuration: if --with-config is set, either --generate-key-file or --key-file must be provided")
}
if !io.FileOrFolderExists(configPath) {
if !weaveio.FileOrFolderExists(configPath) {
return fmt.Errorf("the provided --with-config does not exist: %s", configPath)
}
} else {
Expand All @@ -192,16 +171,20 @@ func handleWithConfig(cmd *cobra.Command, userHome, opInitHome, configPath, keyF
return err
}

var keyFile opinit_bots.KeyFile
var keyFile *weaveio.KeyFile
if isGenerateKeyFile {
keyPath := filepath.Join(userHome, common.WeaveDataDirectory, fmt.Sprintf("%s.%s.keyfile", common.OpinitGeneratedKeyFilename, botName))
keyFile, err = generateKeyFile(keyPath, botName)
keyFile, err = opinit_bots.GenerateMnemonicKeyfile(botName)
if err != nil {
return err
return fmt.Errorf("error generating keyfile: %v", err)
}
err = keyFile.Write(keyPath)
if err != nil {
return fmt.Errorf("error writing to file: %w", err)
}
fmt.Printf("Key file successfully generated. You can find it at: %s\n", keyPath)
} else {
if !io.FileOrFolderExists(keyFilePath) {
if !weaveio.FileOrFolderExists(keyFilePath) {
return fmt.Errorf("key file is missing at path: %s", keyFilePath)
}

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

// readAndUnmarshalKeyFile read and unmarshal the key file into the KeyFile struct
func readAndUnmarshalKeyFile(keyFilePath string) (opinit_bots.KeyFile, error) {
func readAndUnmarshalKeyFile(keyFilePath string) (*weaveio.KeyFile, error) {
fileData, err := os.ReadFile(keyFilePath)
if err != nil {
return opinit_bots.KeyFile{}, err
return nil, err
}

var keyFile opinit_bots.KeyFile
err = json.Unmarshal(fileData, &keyFile)
keyFile := &weaveio.KeyFile{}
err = json.Unmarshal(fileData, keyFile)
return keyFile, err
}

// handleExistingOpInitHome handle the case where the opInitHome directory exists
func handleExistingOpInitHome(opInitHome string, botName string, force bool) error {
if io.FileOrFolderExists(opInitHome) {
if weaveio.FileOrFolderExists(opInitHome) {
if force {
// delete db
dbPath := filepath.Join(opInitHome, fmt.Sprintf("%s.db", botName))
if io.FileOrFolderExists(dbPath) {
err := io.DeleteDirectory(dbPath)
if weaveio.FileOrFolderExists(dbPath) {
err := weaveio.DeleteDirectory(dbPath)
if err != nil {
return fmt.Errorf("failed to delete %s", dbPath)
}
Expand All @@ -256,7 +239,7 @@ func handleExistingOpInitHome(opInitHome string, botName string, force bool) err
}

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

switch botName {
Expand All @@ -266,14 +249,14 @@ func initializeBotWithConfig(cmd *cobra.Command, fileData []byte, keyFile opinit
if err != nil {
return err
}
err = opinit_bots.InitializeExecutorWithConfig(config, &keyFile, opInitHome, userHome)
err = opinit_bots.InitializeExecutorWithConfig(config, keyFile, opInitHome, userHome)
case "challenger":
var config opinit_bots.ChallengerConfig
err = json.Unmarshal(fileData, &config)
if err != nil {
return err
}
err = opinit_bots.InitializeChallengerWithConfig(config, &keyFile, opInitHome, userHome)
err = opinit_bots.InitializeChallengerWithConfig(config, keyFile, opInitHome, userHome)
}
if err != nil {
return err
Expand Down
9 changes: 6 additions & 3 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

const (
WeaveDirectory = ".weave"
WeaveConfigFile = WeaveDirectory + "/config.json"
WeaveDataDirectory = WeaveDirectory + "/data"
WeaveLogDirectory = WeaveDirectory + "/log"

Expand All @@ -18,11 +19,13 @@ const (
MinitiaArtifactsConfigJson = "/artifacts/config.json"
MinitiaArtifactsJson = "/artifacts/artifacts.json"

OPinitDirectory = ".opinit"
OPinitAppName = "opinitd"
OPinitDirectory = ".opinit"
OPinitAppName = "opinitd"
OPinitKeyFileJson = "/weave.keys.json"
OpinitGeneratedKeyFilename = "weave.opinit.generated"

HermesHome = ".hermes"
HermesKeysDirectory = HermesHome + "/keys"
HermesKeyFileJson = HermesHome + "/weave.keys.json"
HermesTempMnemonicFilename = "weave.mnemonic"
OpinitGeneratedKeyFilename = "weave.opinit.generated"
)
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func InitializeConfig() error {
return fmt.Errorf("failed to get user home directory: %v", err)
}

configPath := filepath.Join(homeDir, common.WeaveDirectory, "config.json")
configPath := filepath.Join(homeDir, common.WeaveConfigFile)
if err := os.MkdirAll(filepath.Dir(configPath), os.ModePerm); err != nil {
return fmt.Errorf("failed to create config directory: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func (m *MockedFilesystem) Stat(name string) (os.FileInfo, error) {
func TestInitializeConfig(t *testing.T) {
fs := new(MockedFilesystem)
home := "/mock/home"
configPath := filepath.Join(home, common.WeaveDirectory, "config.json")
configPath := filepath.Join(home, common.WeaveConfigFile)

// Resetting mocks for next test case
fs.Mock = mock.Mock{}
Expand Down
8 changes: 8 additions & 0 deletions context/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,11 @@ func GetOPInitHome(ctx context.Context) (string, error) {
}
return "", fmt.Errorf("cannot cast the OPInitHomeKey value into type string")
}

func GetOPInitKeyFileJson(ctx context.Context) (string, error) {
opInitHome, err := GetOPInitHome(ctx)
if err != nil {
return "", err
}
return filepath.Join(opInitHome, common.OPinitKeyFileJson), nil
}
11 changes: 0 additions & 11 deletions io/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"path/filepath"
"runtime"

"github.com/atotto/clipboard"

"github.com/initia-labs/weave/client"
)

Expand Down Expand Up @@ -154,12 +152,3 @@ func CopyDirectory(src, des string) error {
}
return nil
}

// CopyToClipboard copies the given string to the clipboard.
func CopyToClipboard(text string) error {
// Copy the text to clipboard
if err := clipboard.WriteAll(text); err != nil {
return fmt.Errorf("failed to copy to clipboard: %v", err)
}
return nil
}
50 changes: 50 additions & 0 deletions io/keyfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io

import (
"encoding/json"
"fmt"
"os"
)

type KeyFile map[string]string

func NewKeyFile() *KeyFile {
kf := make(KeyFile)
return &kf
}

func (k *KeyFile) AddMnemonic(name, mnemonic string) {
(*k)[name] = mnemonic
}

func (k *KeyFile) GetMnemonic(name string) string {
return (*k)[name]
}

func (k *KeyFile) Write(filePath string) error {
data, err := json.MarshalIndent(k, "", " ")
if err != nil {
return fmt.Errorf("error marshaling KeyFile to JSON: %w", err)
}

return os.WriteFile(filePath, data, 0644)
}

// Load tries to load an existing key file into the struct if the file exists
func (k *KeyFile) Load(filePath string) error {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil
}

data, err := os.ReadFile(filePath)
if err != nil {
return fmt.Errorf("error reading file: %w", err)
}

err = json.Unmarshal(data, k)
if err != nil {
return fmt.Errorf("error unmarshaling JSON: %w", err)
}

return nil
}
49 changes: 16 additions & 33 deletions models/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package models
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"

Expand All @@ -13,7 +15,6 @@ import (
"github.com/initia-labs/weave/config"
weavecontext "github.com/initia-labs/weave/context"
"github.com/initia-labs/weave/crypto"
"github.com/initia-labs/weave/io"
"github.com/initia-labs/weave/styles"
"github.com/initia-labs/weave/types"
"github.com/initia-labs/weave/ui"
Expand Down Expand Up @@ -185,28 +186,15 @@ func (m *GenerateGasStationLoading) View() string {

type GasStationMnemonicDisplayInput struct {
ui.TextInput
ui.Clickable
weavecontext.BaseModel
question string
generatedMnemonic string
question string
}

func NewSystemKeysMnemonicDisplayInput(ctx context.Context) *GasStationMnemonicDisplayInput {
state := weavecontext.GetCurrentState[ExistingCheckerState](ctx)
model := &GasStationMnemonicDisplayInput{
TextInput: ui.NewTextInput(true),
Clickable: *ui.NewClickable(
ui.NewClickableItem(
map[bool]string{
true: "Copied! Click to copy again",
false: "Click here to copy",
}, func() error {
return io.CopyToClipboard(state.generatedMnemonic)
},
)),
BaseModel: weavecontext.BaseModel{Ctx: ctx, CannotBack: true},
question: "Please type `continue` to proceed after you have securely stored the mnemonic.",
generatedMnemonic: state.generatedMnemonic,
BaseModel: weavecontext.BaseModel{Ctx: ctx, CannotBack: true},
question: "Type `continue` to proceed.",
}
model.WithPlaceholder("Type `continue` to continue, Ctrl+C to quit.")
model.WithValidatorFn(common.ValidateExactString("continue"))
Expand All @@ -218,24 +206,19 @@ func (m *GasStationMnemonicDisplayInput) GetQuestion() string {
}

func (m *GasStationMnemonicDisplayInput) Init() tea.Cmd {
return m.Clickable.Init()
return nil
}

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

err := m.Clickable.ClickableUpdate(msg)
if err != nil {
return m, m.HandlePanic(err)
}

input, cmd, done := m.TextInput.Update(msg)
if done {
state := weavecontext.PushPageAndGetState[ExistingCheckerState](m)
model := NewWeaveAppInitialization(m.Ctx, state.generatedMnemonic)
return model, tea.Batch(model.Init(), m.Clickable.PostUpdate())
return model, model.Init()
}
m.TextInput = input
return m, cmd
Expand All @@ -248,18 +231,18 @@ func (m *GasStationMnemonicDisplayInput) View() string {
m.HandlePanic(fmt.Errorf("failed to convert mnemonic to bech32 address: %w", err))
}

userHome, err := os.UserHomeDir()
if err != nil {
m.HandlePanic(fmt.Errorf("failed to get user home directory: %w", err))
}

var mnemonicText string
mnemonicText += styles.RenderMnemonic("Gas Station", gasStationAddress, m.generatedMnemonic, m.Clickable.ClickableView(0))
mnemonicText += styles.RenderKey("Gas Station", gasStationAddress)

viewText := m.WrapView(InitHeader(state.isFirstTime) + "\n" + state.weave.Render() + "\n" +
return m.WrapView(InitHeader(state.isFirstTime) + "\n" + state.weave.Render() + "\n" +
styles.BoldUnderlineText("Important", styles.Yellow) + "\n" +
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" +
mnemonicText + "\n" + styles.RenderPrompt(m.GetQuestion(), []string{"`continue`"}, styles.Question) + m.TextInput.View())
err = m.Clickable.ClickableUpdatePositions(viewText)
if err != nil {
m.HandlePanic(err)
}
return viewText
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" +
mnemonicText + styles.RenderPrompt(m.GetQuestion(), []string{"`continue`"}, styles.Question) + m.TextInput.View())
}

type GasStationMnemonicInput struct {
Expand Down
Loading

0 comments on commit 1099bb8

Please sign in to comment.