Repository: babylonlabs-io/babylon
Pull request #924: Fix: Improve BLS file check order in related commands
Author: login: wnjoon
Created: 2025-05-17T04:47:28Z | Merged: 2025-05-22T03:49:41Z

This PR includes the following changes:

Related issue is #917 

- Reverse the order of operations
   - First, check for the existence of `bls_key.json` at the command level (e.g., inside cmd/)
   - Then proceed next steps such as showing prompt for password only if the key file is confirmed to exist
- Remove the redundant key existence check from the helper functions in `bls.go` for a cleaner separation of concerns.


Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f66299cc7..796c647e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -79,6 +79,7 @@ check of rewards
 - [#897](https://github.com/babylonlabs-io/babylon/pull/897) Add Interchain Accounts (ICA) and Interchain Queries (ICQ) modules.
 - [#902](https://github.com/babylonlabs-io/babylon/pull/902) Add IBC rate limiter to IBC transfer module
 - [#913](https://github.com/babylonlabs-io/babylon/pull/913) Remove IBC-fee module.
+- [#924](https://github.com/babylonlabs-io/babylon/pull/924) Improve BLS file check order in related commands
 - [#926](https://github.com/babylonlabs-io/babylon/pull/926) Update genesis initialization validations.
 - [#933](https://github.com/babylonlabs-io/babylon/pull/933) Update repository version to `v4`.
 - [#935](https://github.com/babylonlabs-io/babylon/pull/935) Add `release/v4` branch to docker publish workflow.
diff --git a/app/signer/bls.go b/app/signer/bls.go
index 98f140401..0f81fc919 100644
--- a/app/signer/bls.go
+++ b/app/signer/bls.go
@@ -87,9 +87,9 @@ func GenBls(keyFilePath, passwordFilePath, password string) *Bls {
 	return pv
 }
 
-// loadBlsPrivKeyFromFile loads a BLS private key from a file.
+// LoadBlsPrivKeyFromFile loads a BLS private key from a file.
 // Password should be determined before calling this function.
-func loadBlsPrivKeyFromFile(keyFilePath, password string) (bls12381.PrivateKey, error) {
+func LoadBlsPrivKeyFromFile(keyFilePath, password string) (bls12381.PrivateKey, error) {
 	keystore, err := erc2335.LoadKeyStore(keyFilePath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load BLS key file: %w", err)
@@ -118,7 +118,7 @@ func TryLoadBlsFromFile(keyFilePath, passwordFilePath string) (*Bls, bool, error
 		return nil, false, fmt.Errorf("failed to get password: %w", err)
 	}
 
-	blsPrivKey, err := loadBlsPrivKeyFromFile(keyFilePath, password)
+	blsPrivKey, err := LoadBlsPrivKeyFromFile(keyFilePath, password)
 	if err != nil {
 		return nil, false, fmt.Errorf("failed to load BLS key: %w", err)
 	}
@@ -266,6 +266,11 @@ func (k *BlsKey) BlsPubKey() (bls12381.PublicKey, error) {
 	return k.PubKey, nil
 }
 
+// BlsKeyFilePath returns the file path of the BLS key
+func (k *BlsKey) BlsKeyFilePath() string {
+	return k.filePath
+}
+
 // GetBlsKeyPassword is a unified function to handle BLS key password retrieval.
 // Returns password if a password was found from one of the sources.
 // 1. If noPassword is true, returns empty string without checking other sources.
@@ -324,8 +329,8 @@ func ValidatePasswordMethods(noPassword bool, passwordFilePath string) error {
 // 4. Interactive prompt for password
 // Error will be returned if the key exists but cannot decrypt it
 func LoadBlsSignerIfExists(homeDir string, noPassword bool, customPasswordPath, customKeyPath string) (checkpointingtypes.BlsSigner, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, customKeyPath)
-	if !cmtos.FileExists(blsKeyFile) {
+	blsKeyFile, exist := GetBlsKeyFileIfExist(homeDir, customKeyPath)
+	if !exist {
 		return nil, nil
 	}
 
@@ -335,7 +340,7 @@ func LoadBlsSignerIfExists(homeDir string, noPassword bool, customPasswordPath,
 		return nil, fmt.Errorf("failed to get password: %w", err)
 	}
 
-	blsPrivKey, err := loadBlsPrivKeyFromFile(blsKeyFile, password)
+	blsPrivKey, err := LoadBlsPrivKeyFromFile(blsKeyFile, password)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load BLS key: %w", err)
 	}
@@ -355,24 +360,21 @@ func LoadBlsSignerIfExists(homeDir string, noPassword bool, customPasswordPath,
 // 2. Password file (ONLY if explicitly provided and file exists)
 // 3. Interactive prompt (with confirmation for new keys)
 func LoadOrGenBlsKey(homeDir string, noPassword bool, customPasswordPath, customKeyPath string) (checkpointingtypes.BlsSigner, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, customKeyPath)
-	keyExists := cmtos.FileExists(blsKeyFile)
-
-	if keyExists {
-		blsSigner, err := LoadBlsSignerIfExists(homeDir, noPassword, customPasswordPath, customKeyPath)
-		if err != nil {
-			return nil, fmt.Errorf("BLS key file exists at %s but could not be loaded: %w. If you need to generate a new key, please manually delete the existing file first", blsKeyFile, err)
-		}
-		if blsSigner != nil {
-			return blsSigner, nil
-		}
+	blsSigner, err := LoadBlsSignerIfExists(homeDir, noPassword, customPasswordPath, customKeyPath)
+	if err != nil {
+		return nil, fmt.Errorf(
+			"BLS key file exists at %s but could not be loaded: %w. If you need to generate a new key, please manually delete the existing file first",
+			blsSigner.BlsKeyFilePath(), err)
+	}
+	if blsSigner != nil {
+		return blsSigner, nil
 	}
 
 	genPassword, err := GetBlsKeyPassword(noPassword, customPasswordPath, true)
 	if err != nil {
 		return nil, fmt.Errorf("failed to get password: %w", err)
 	}
-	bls := GenBls(blsKeyFile, customPasswordPath, genPassword)
+	bls := GenBls(blsSigner.BlsKeyFilePath(), customPasswordPath, genPassword)
 	return &bls.Key, nil
 }
 
@@ -422,13 +424,11 @@ func LoadBlsPop(filePath string) (BlsPop, error) {
 
 // ShowBlsKey displays information about a BLS key
 // Takes a password that was determined by the password determination logic.
-func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
+func ShowBlsKey(blsKeyFile string, password string) (map[string]interface{}, error) {
 	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
+		return nil, fmt.Errorf("failed to show BLS key: BLS key file does not exist at %s", blsKeyFile)
 	}
-
-	blsPrivKey, err := loadBlsPrivKeyFromFile(blsKeyFile, password)
+	blsPrivKey, err := LoadBlsPrivKeyFromFile(blsKeyFile, password)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load BLS key: %w", err)
 	}
@@ -444,25 +444,11 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
+func CreateBlsKey(blsKeyFile string, password string, passwordFilePath string, cmd *cobra.Command) error {
 	if cmtos.FileExists(blsKeyFile) {
-		return fmt.Errorf("BLS key already exists at %s. If you need to generate a new key, please manually delete the existing file first", blsKeyFile)
+		return fmt.Errorf("failed to create BLS key: BLS key already exists at %s", blsKeyFile)
 	}
 
 	// Ensure the key file directory exists
@@ -508,12 +494,9 @@ func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd
 
 // UpdateBlsPassword updates the password for a BLS key
 // Takes a password that was determined by the password determination logic.
-func UpdateBlsPassword(homeDir string, blsPrivKey bls12381.PrivateKey, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
+func UpdateBlsPassword(blsKeyFile string, blsPrivKey bls12381.PrivateKey, password, passwordFilePath string, cmd *cobra.Command) error {
 	if !cmtos.FileExists(blsKeyFile) {
-		return fmt.Errorf("BLS key does not exist at %s", blsKeyFile)
+		return fmt.Errorf("failed to update BLS password: BLS key does not exist at %s", blsKeyFile)
 	}
 
 	// Create backup of BLS key file before removing it
@@ -567,3 +550,10 @@ func UpdateBlsPassword(homeDir string, blsPrivKey bls12381.PrivateKey, password
 
 	return nil
 }
+
+// GetBlsKeyFileIfExist returns the determined BLS key file path
+// and a boolean indicating whether the file exists
+func GetBlsKeyFileIfExist(homeDir, customKeyPath string) (string, bool) {
+	blsKeyFile := determineKeyFilePath(homeDir, customKeyPath)
+	return blsKeyFile, cmtos.FileExists(blsKeyFile)
+}
diff --git a/app/signer/bls_test.go b/app/signer/bls_test.go
index cbd1eaf2f..0e86cea36 100644
--- a/app/signer/bls_test.go
+++ b/app/signer/bls_test.go
@@ -139,6 +139,19 @@ func TestGetBlsPassword(t *testing.T) {
 	})
 }
 
+func TestGetBlsKeyFileIfExist(t *testing.T) {
+	origEnvValue := os.Getenv(BlsPasswordEnvVar)
+	defer t.Setenv(BlsPasswordEnvVar, origEnvValue)
+
+	t.Run("error with key does not exists", func(t *testing.T) {
+		os.Unsetenv(BlsPasswordEnvVar)
+
+		tempDir := t.TempDir()
+		_, exist := GetBlsKeyFileIfExist(tempDir, "")
+		assert.False(t, exist)
+	})
+}
+
 func TestLoadBlsWithEnvVar(t *testing.T) {
 	t.Run("load bls with environment variable", func(t *testing.T) {
 		originalValue := os.Getenv(BlsPasswordEnvVar)
@@ -459,7 +472,7 @@ func TestShowBlsKey(t *testing.T) {
 		err = os.WriteFile(passwordFile, []byte(keyPassword), 0600)
 		assert.NoError(t, err)
 
-		keyInfo, err := ShowBlsKey(tempDir, keyPassword)
+		keyInfo, err := ShowBlsKey(keyFile, keyPassword)
 		assert.NoError(t, err)
 		assert.NotNil(t, keyInfo)
 		assert.Equal(t, privKey.PubKey().Bytes(), keyInfo["pubkey"])
@@ -482,7 +495,7 @@ func TestShowBlsKey(t *testing.T) {
 		blsKey := NewBls(privKey, keyFile, "")
 		blsKey.Key.Save("")
 
-		keyInfo, err := ShowBlsKey(tempDir, "")
+		keyInfo, err := ShowBlsKey(keyFile, "")
 		assert.NoError(t, err)
 		assert.NotNil(t, keyInfo)
 		assert.Equal(t, privKey.PubKey().Bytes(), keyInfo["pubkey"])
@@ -527,8 +540,9 @@ func TestCreateBlsKey(t *testing.T) {
 		assert.False(t, exists, "Environment variable should be unset for no-password test")
 
 		tempDir := t.TempDir()
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
 
-		err := CreateBlsKey(tempDir, "", "", testCmd)
+		err := CreateBlsKey(blsKeyFile, "", "", testCmd)
 		assert.NoError(t, err)
 
 		configDir := filepath.Join(tempDir, "config")
@@ -545,11 +559,12 @@ func TestCreateBlsKey(t *testing.T) {
 		os.Unsetenv(BlsPasswordEnvVar)
 
 		tempDir := t.TempDir()
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
 
 		testPassword := "env-var-create-password"
 		t.Setenv(BlsPasswordEnvVar, testPassword)
 
-		err := CreateBlsKey(tempDir, testPassword, "", testCmd)
+		err := CreateBlsKey(blsKeyFile, testPassword, "", testCmd)
 		assert.NoError(t, err)
 
 		configDir := filepath.Join(tempDir, "config")
@@ -567,6 +582,7 @@ func TestCreateBlsKey(t *testing.T) {
 		assert.False(t, exists, "Environment variable should be unset for password file test")
 
 		tempDir := t.TempDir()
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
 
 		passwordFile := filepath.Join(tempDir, "password.txt")
 		testPassword := "file-create-password"
@@ -577,7 +593,7 @@ func TestCreateBlsKey(t *testing.T) {
 		err = os.MkdirAll(configDir, 0700)
 		assert.NoError(t, err)
 
-		err = CreateBlsKey(tempDir, testPassword, passwordFile, testCmd)
+		err = CreateBlsKey(blsKeyFile, testPassword, passwordFile, testCmd)
 		assert.NoError(t, err)
 
 		keyFile := filepath.Join(configDir, DefaultBlsKeyName)
@@ -589,13 +605,14 @@ func TestCreateBlsKey(t *testing.T) {
 		os.Unsetenv(BlsPasswordEnvVar)
 
 		tempDir := t.TempDir()
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
 
 		configDir := filepath.Join(tempDir, "config")
 		err := os.MkdirAll(configDir, 0700)
 		assert.NoError(t, err)
 
 		firstPassword := ""
-		err = CreateBlsKey(tempDir, firstPassword, "", testCmd)
+		err = CreateBlsKey(blsKeyFile, firstPassword, "", testCmd)
 		assert.NoError(t, err)
 
 		keyFile := filepath.Join(configDir, DefaultBlsKeyName)
@@ -605,10 +622,6 @@ func TestCreateBlsKey(t *testing.T) {
 
 		time.Sleep(10 * time.Millisecond)
 
-		err = CreateBlsKey(tempDir, "new-password", "", testCmd)
-		assert.Error(t, err)
-		assert.Contains(t, err.Error(), "already exists")
-
 		fileInfoAfter, err := os.Stat(keyFile)
 		assert.NoError(t, err)
 		assert.Equal(t, modTimeBefore, fileInfoAfter.ModTime(), "Key file should not be modified")
@@ -648,8 +661,9 @@ func TestUpdateBlsPassword(t *testing.T) {
 		assert.False(t, exists, "Environment variable should be unset for no-password test")
 
 		tempDir := t.TempDir()
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
 
-		err := CreateBlsKey(tempDir, "", "", testCmd)
+		err := CreateBlsKey(blsKeyFile, "", "", testCmd)
 		assert.NoError(t, err)
 
 		configDir := filepath.Join(tempDir, "config")
@@ -661,13 +675,13 @@ func TestUpdateBlsPassword(t *testing.T) {
 		assert.NoError(t, err)
 		assert.NotNil(t, blsSigner)
 
-		blsPrivKey, err := LoadBlsPrivKey(tempDir, "")
+		blsPrivKey, err := LoadBlsPrivKeyFromFile(blsKeyFile, "")
 		assert.NoError(t, err)
 
 		newPassword := "env-var-new-password"
 		t.Setenv(BlsPasswordEnvVar, newPassword)
 
-		err = UpdateBlsPassword(tempDir, blsPrivKey, newPassword, "", testCmd)
+		err = UpdateBlsPassword(blsKeyFile, blsPrivKey, newPassword, "", testCmd)
 		assert.NoError(t, err)
 
 		blsSigner, err = LoadBlsSignerIfExists(tempDir, false, "", "")
@@ -685,7 +699,9 @@ func TestUpdateBlsPassword(t *testing.T) {
 
 		tempDir := t.TempDir()
 
-		err := CreateBlsKey(tempDir, "", "", testCmd)
+		blsKeyFile, _ := GetBlsKeyFileIfExist(tempDir, "")
+
+		err := CreateBlsKey(blsKeyFile, "", "", testCmd)
 		assert.NoError(t, err)
 
 		configDir := filepath.Join(tempDir, "config")
@@ -697,7 +713,7 @@ func TestUpdateBlsPassword(t *testing.T) {
 		assert.NoError(t, err)
 		assert.NotNil(t, blsSigner)
 
-		blsPrivKey, err := LoadBlsPrivKey(tempDir, "")
+		blsPrivKey, err := LoadBlsPrivKeyFromFile(blsKeyFile, "")
 		assert.NoError(t, err)
 
 		newPassword := "env-var-new-password"
@@ -705,21 +721,11 @@ func TestUpdateBlsPassword(t *testing.T) {
 		err = os.WriteFile(newPasswordFile, []byte(newPassword), 0600)
 		assert.NoError(t, err)
 
-		err = UpdateBlsPassword(tempDir, blsPrivKey, newPassword, newPasswordFile, testCmd)
+		err = UpdateBlsPassword(blsKeyFile, blsPrivKey, newPassword, newPasswordFile, testCmd)
 		assert.NoError(t, err)
 
 		blsSigner, err = LoadBlsSignerIfExists(tempDir, false, newPasswordFile, "")
 		assert.NoError(t, err)
 		assert.NotNil(t, blsSigner)
 	})
-
-	t.Run("error with key does not exists", func(t *testing.T) {
-		os.Unsetenv(BlsPasswordEnvVar)
-
-		tempDir := t.TempDir()
-
-		err := UpdateBlsPassword(tempDir, nil, "new-password", "", testCmd)
-		assert.Error(t, err)
-		assert.Contains(t, err.Error(), "BLS key does not exist")
-	})
 }
diff --git a/cmd/babylond/cmd/create_bls_key.go b/cmd/babylond/cmd/create_bls_key.go
index a38427e39..885cdabb6 100644
--- a/cmd/babylond/cmd/create_bls_key.go
+++ b/cmd/babylond/cmd/create_bls_key.go
@@ -35,6 +35,14 @@ $ babylond create-bls-key --no-bls-password
 			if err != nil {
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
+
+			// If bls_key.json already exists,
+			// return error with message that Users should delete the existing file first
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if exist {
+				return fmt.Errorf("BLS key already exists at %s. If you need to generate a new key, please manually delete the existing file first", blsKeyFile)
+			}
+
 			noBlsPassword, err := cmd.Flags().GetBool(flagNoBlsPassword)
 			if err != nil {
 				return fmt.Errorf("failed to get noBlsPassword flag: %w", err)
@@ -51,7 +59,7 @@ $ babylond create-bls-key --no-bls-password
 			}
 
 			// Generate BLS key using the refactored function with explicit password
-			return appsigner.CreateBlsKey(homeDir, password, passwordFile, cmd)
+			return appsigner.CreateBlsKey(blsKeyFile, password, passwordFile, cmd)
 		},
 	}
 
diff --git a/cmd/babylond/cmd/init.go b/cmd/babylond/cmd/init.go
index e843e713c..92f535b23 100644
--- a/cmd/babylond/cmd/init.go
+++ b/cmd/babylond/cmd/init.go
@@ -32,6 +32,13 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
 			if err != nil {
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
+
+			// Check bls_key.json is already exist
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if exist {
+				return fmt.Errorf("BLS key already exists at %s. If you need to generate a new key, please manually delete the existing file first", blsKeyFile)
+			}
+
 			noBlsPassword, err := cmd.Flags().GetBool(flagNoBlsPassword)
 			if err != nil {
 				return fmt.Errorf("failed to get noBlsPassword flag: %w", err)
@@ -48,7 +55,7 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command {
 			}
 
 			// Generate BLS key using the refactored function with explicit password
-			if err := appsigner.CreateBlsKey(homeDir, password, passwordFile, cmd); err != nil {
+			if err := appsigner.CreateBlsKey(blsKeyFile, password, passwordFile, cmd); err != nil {
 				return fmt.Errorf("failed to create BLS key: %w", err)
 			}
 
diff --git a/cmd/babylond/cmd/show_bls_key.go b/cmd/babylond/cmd/show_bls_key.go
index 6dcbd67e1..1ec8824fb 100644
--- a/cmd/babylond/cmd/show_bls_key.go
+++ b/cmd/babylond/cmd/show_bls_key.go
@@ -36,6 +36,12 @@ $ babylond show-bls-key --no-bls-password
 			if err != nil {
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
+
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if !exist {
+				return fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
+			}
+
 			noBlsPassword, err := cmd.Flags().GetBool(flagNoBlsPassword)
 			if err != nil {
 				return fmt.Errorf("failed to get noBlsPassword flag: %w", err)
@@ -60,7 +66,7 @@ $ babylond show-bls-key --no-bls-password
 				return fmt.Errorf("failed to determine BLS password: %w", err)
 			}
 
-			info, err := appsigner.ShowBlsKey(homeDir, password)
+			info, err := appsigner.ShowBlsKey(blsKeyFile, password)
 			if err != nil {
 				return fmt.Errorf("failed to show BLS key: %w", err)
 			}
diff --git a/cmd/babylond/cmd/update_bls_password.go b/cmd/babylond/cmd/update_bls_password.go
index 2fe1e4a4d..8dd95ce62 100644
--- a/cmd/babylond/cmd/update_bls_password.go
+++ b/cmd/babylond/cmd/update_bls_password.go
@@ -36,6 +36,11 @@ $ babylond update-bls-password --no-bls-password
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
 
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if !exist {
+				return fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
+			}
+
 			cmd.Println("\n⚠️ IMPORTANT: Your BLS key file will be overwritten! ⚠️")
 			cmd.Println("1. (Recommended) Please make a backup of your BLS key before proceeding.")
 			cmd.Println("2. To update the password of BLS key, you need to provide the old password.")
@@ -69,7 +74,10 @@ $ babylond update-bls-password --no-bls-password
 				return fmt.Errorf("failed to unset BLS password environment variable: %w", err)
 			}
 
-			blsPrivKey, err := appsigner.LoadBlsPrivKey(homeDir, oldPassword)
+			// Command should be cancelled
+			// if the BLS key is failed to decrypt with old password,
+			// because it means the old password is incorrect.
+			blsPrivKey, err := appsigner.LoadBlsPrivKeyFromFile(blsKeyFile, oldPassword)
 			if err != nil {
 				return fmt.Errorf("failed to load BLS key: %w", err)
 			}
@@ -92,7 +100,7 @@ $ babylond update-bls-password --no-bls-password
 			}
 
 			// Generate BLS key using the refactored function with explicit password
-			return appsigner.UpdateBlsPassword(homeDir, blsPrivKey, newPassword, passwordFile, cmd)
+			return appsigner.UpdateBlsPassword(blsKeyFile, blsPrivKey, newPassword, passwordFile, cmd)
 		},
 	}
 
diff --git a/cmd/babylond/cmd/verify_val_bls_key.go b/cmd/babylond/cmd/verify_val_bls_key.go
index 958efe4a9..2d78f513b 100644
--- a/cmd/babylond/cmd/verify_val_bls_key.go
+++ b/cmd/babylond/cmd/verify_val_bls_key.go
@@ -49,6 +49,12 @@ $ babylond verify-validator-bls-key babylonvaloper1... --no-bls-password
 			if err != nil {
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
+
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if !exist {
+				return fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
+			}
+
 			noBlsPassword, err := cmd.Flags().GetBool(flagNoBlsPassword)
 			if err != nil {
 				return fmt.Errorf("failed to get noBlsPassword flag: %w", err)
@@ -75,7 +81,7 @@ $ babylond verify-validator-bls-key babylonvaloper1... --no-bls-password
 
 			// Get BLS public key
 			// Get BLS key information
-			info, err := appsigner.ShowBlsKey(homeDir, password)
+			info, err := appsigner.ShowBlsKey(blsKeyFile, password)
 			if err != nil {
 				return fmt.Errorf("failed to show BLS key: %w", err)
 			}
diff --git a/x/checkpointing/types/types.go b/x/checkpointing/types/types.go
index dd8e5cb8e..c2df4fef6 100644
--- a/x/checkpointing/types/types.go
+++ b/x/checkpointing/types/types.go
@@ -26,6 +26,7 @@ const (
 type BlsSigner interface {
 	SignMsgWithBls(msg []byte) (bls12381.Signature, error)
 	BlsPubKey() (bls12381.PublicKey, error)
+	BlsKeyFilePath() string
 }
 
 type BlockHash []byte


Reviews:
user: login: RafilxTenfen
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:00:11Z
user: login: RafilxTenfen
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T15:01:34Z
user: login: gitferry
body: In general I think it's good to check the key file existence first before executing the key related functions but I don't think we can enforce such a contract. Recommend we still check file existence in related functions
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T03:04:54Z
user: login: Lazar955
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-20T09:21:59Z
user: login: wnjoon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T10:47:18Z
user: login: gitferry
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T12:06:44Z
user: login: wnjoon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T12:19:28Z
user: login: gitferry
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-20T13:42:35Z
user: login: wnjoon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T02:26:59Z
user: login: gitferry
body: Lgmt! Thanks a lot for your contribution!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-22T03:49:11Z

Review comments:
diff_hunk: @@ -35,6 +35,14 @@ $ babylond create-bls-key --no-bls-password
 			if err != nil {
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
+
+			// If bls_key.json already exists,
+			// return error with message that Users should delete the existing file first
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
+			if exist {
+				return fmt.Errorf("BLS key already exists at %s. If you need to generate a new key, please manually delete the existing file first", blsKeyFile)
path: cmd/babylond/cmd/create_bls_key.go
user: login: RafilxTenfen
body: nice :+1: 
created_at: 2025-05-19T15:00:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -444,27 +441,9 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if cmtos.FileExists(blsKeyFile) {
path: app/signer/bls.go
user: login: gitferry
body: I don't think we can enforce a contract that the `blsKeyFile` does not exist
created_at: 2025-05-20T02:53:40Z
author_association: MEMBER
diff_hunk: @@ -508,14 +487,7 @@ func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd
 
 // UpdateBlsPassword updates the password for a BLS key
 // Takes a password that was determined by the password determination logic.
-func UpdateBlsPassword(homeDir string, blsPrivKey bls12381.PrivateKey, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if !cmtos.FileExists(blsKeyFile) {
-		return fmt.Errorf("BLS key does not exist at %s", blsKeyFile)
-	}
path: app/signer/bls.go
user: login: gitferry
body: I don't think we can enforce a contract that the blsKeyFile indeed exists
created_at: 2025-05-20T02:55:02Z
author_association: MEMBER
diff_hunk: @@ -36,6 +36,11 @@ $ babylond update-bls-password --no-bls-password
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
 
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
path: cmd/babylond/cmd/update_bls_password.go
user: login: gitferry
body: I think we probably should pass `blsKeyFile` to `UpdateBlsPassword` in Line 103 other than still passing `homeDir`
created_at: 2025-05-20T02:58:15Z
author_association: MEMBER
diff_hunk: @@ -444,27 +441,9 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if cmtos.FileExists(blsKeyFile) {
path: app/signer/bls.go
user: login: wnjoon
body: @gitferry 

Thanks for the feedback — that makes sense.

How about we introduce a wrapper pattern like CreateBlsKeyWithFileCheck, ShowBlsKeyWithFileCheck, etc.?
These versions would explicitly perform the file existence check internally, while leaving the original versions clean and testable when used in controlled environments (e.g. from CLI where the check is already done).

This would also make the contract clearer for future use and help avoid accidental misuse.
created_at: 2025-05-20T10:47:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -444,27 +441,9 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if cmtos.FileExists(blsKeyFile) {
path: app/signer/bls.go
user: login: gitferry
body: Hmmm, seems `xxxxWithFileCheck` is a bit redundent. How about we always do the file existence check?
created_at: 2025-05-20T12:06:43Z
author_association: MEMBER
diff_hunk: @@ -444,27 +441,9 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if cmtos.FileExists(blsKeyFile) {
path: app/signer/bls.go
user: login: wnjoon
body: Understood. In that case, the file existence will be checked twice — once in the CLI and once again in the `bls.go` function.
If you’re okay with that redundancy, I’ll go ahead and update the logic accordingly.
created_at: 2025-05-20T12:19:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -444,27 +441,9 @@ func ShowBlsKey(homeDir string, password string) (map[string]interface{}, error)
 	return result, nil
 }
 
-// LoadBlsPrivKey loads a BLS private key from a file.
-func LoadBlsPrivKey(homeDir, password string) (bls12381.PrivateKey, error) {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	if !cmtos.FileExists(blsKeyFile) {
-		return nil, fmt.Errorf("BLS key file does not exist at %s", blsKeyFile)
-	}
-
-	return loadBlsPrivKeyFromFile(blsKeyFile, password)
-}
-
 // CreateBlsKey creates a new BLS key
 // Takes a password that was determined by the password determination logic.
-func CreateBlsKey(homeDir string, password string, passwordFilePath string, cmd *cobra.Command) error {
-	blsKeyFile := determineKeyFilePath(homeDir, "")
-
-	// Check if BLS key already exists
-	if cmtos.FileExists(blsKeyFile) {
path: app/signer/bls.go
user: login: gitferry
body: Sounds good to me. Don't think this causes any issue. 
created_at: 2025-05-20T13:42:35Z
author_association: MEMBER
diff_hunk: @@ -36,6 +36,11 @@ $ babylond update-bls-password --no-bls-password
 				return fmt.Errorf("failed to get home directory: %w", err)
 			}
 
+			blsKeyFile, exist := appsigner.GetBlsKeyFileIfExist(homeDir, "")
path: cmd/babylond/cmd/update_bls_password.go
user: login: wnjoon
body: fixed [05a4d4d](https://github.com/babylonlabs-io/babylon/pull/924/commits/05a4d4d2fdd1641ab95abe6d736ddacaed5a6259)
created_at: 2025-05-22T02:26:59Z
author_association: CONTRIBUTOR

PR comments:
user: login: wnjoon
created_at: 2025-05-17T05:06:56Z
body: Commit Summary

[63ca49d](https://github.com/babylonlabs-io/babylon/pull/924/commits/63ca49d366ab52c4ecf1af2bd9b5e2adfca82007)

- Change `loadBlsPrivKeyFromFile` to public so it can be used directly in `/cmd`
- Update the `update-bls-password` command to check for the existence of `bls_key.json` first
- Modify `UpdateBlsPassword` to accept the file path of `bls_key.json`

[f7baf2b](https://github.com/babylonlabs-io/babylon/pull/924/commits/f7baf2b06b267842b86ef734c49e091e7cb31fb0)

- Update `create-bls-key` and init commands to check for the existence of `bls_key.json` first
- Modify `CreateBlsKey` to accept the file path of `bls_key.json`

[dd3cf57](https://github.com/babylonlabs-io/babylon/pull/924/commits/dd3cf57270470eb6aaa7bb90710cab804b7a2556)

- Update `show-bls-key` and `verify-validator-bls-key` commands to check for the existence of `bls_key.json` first
- Modify `ShowBlsKey` to accept the file path of `bls_key.json`

[a520cce](https://github.com/babylonlabs-io/babylon/pull/924/commits/a520ccec2117634b1036e2426041692624248b76)

- Fix `GetBlsKeyFileIfExist` to properly handle `customKeyFilePath`
- Remove duplicated logic from `LoadOrGenBlsKey`
   - Since `LoadBlsSignerIfExists` already checks for the existence of `bls_key.json`, `LoadOrGenBlsKey` no longer needs to duplicate this logic
   - To obtain `blsKeyFile` from the `blsSigner` returned by `LoadBlsSignerIfExists`, create a new `BlsKeyFilePath` method in the `BlsSigner` interface

@gitferry @RafilxTenfen 
author_association: CONTRIBUTOR
user: login: wnjoon
created_at: 2025-05-21T10:45:40Z
body: Commit Summary

[05a4d4d](https://github.com/babylonlabs-io/babylon/pull/924/commits/05a4d4d2fdd1641ab95abe6d736ddacaed5a6259)
- Update `CreateBlsKey`, `ShowBlsKey`, and `UpdateBlsPassword` to check `blsKeyFile` when the functions are called.
   - Given that we’re performing the file existence check both in the CLI and inside `bls.go`, I think it makes sense to pass the verified `blsKeyFile` as a parameter rather than just the root `homeDir`. This keeps the logic cleaner and ensures each function receives a valid, pre-computed file path.
- Update the `update-bls-password` command to accept `blsKeyFile` as a parameter instead of `homeDir`.

@gitferry 
author_association: CONTRIBUTOR