diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7847365..bc7249c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Improvements +- [#396](https://github.com/babylonlabs-io/babylon/pull/396) BLS Key Separation and ERC2335 Implementation - [#391](https://github.com/babylonlabs-io/babylon/pull/391) Fix e2e `TestBTCRewardsDistribution` flunky check of rewards diff --git a/app/signer/private.go b/app/signer/private.go index 8fcd1f217..491658b36 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -1,12 +1,12 @@ package signer import ( - "path/filepath" + "fmt" cmtconfig "github.com/cometbft/cometbft/config" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/babylonlabs-io/babylon/privval" + cmtprivval "github.com/cometbft/cometbft/privval" ) type PrivSigner struct { @@ -15,17 +15,27 @@ type PrivSigner struct { func InitPrivSigner(nodeDir string) (*PrivSigner, error) { nodeCfg := cmtconfig.DefaultConfig() - pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) - err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) - if err != nil { - return nil, err + nodeCfg.SetRoot(nodeDir) + + pvKeyFile := nodeCfg.PrivValidatorKeyFile() + pvStateFile := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(nodeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) + + if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + return nil, fmt.Errorf("failed to ensure dirs: %w", err) } - pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) - if err != nil { - return nil, err + + cometPV := cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) + blsPV := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) + + wrappedPV := &privval.WrappedFilePV{ + Key: privval.WrappedFilePVKey{ + CometPVKey: cometPV.Key, + BlsPVKey: blsPV.Key, + }, + LastSignState: cometPV.LastSignState, } - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) return &PrivSigner{ WrappedPV: wrappedPV, diff --git a/app/test_helpers.go b/app/test_helpers.go index a53305374..6bdcb4eda 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -239,14 +239,14 @@ func SetupWithBitcoinConf(t *testing.T, isCheckTx bool, btcConf bbn.SupportedBtc ps, err := signer.SetupTestPrivSigner() require.NoError(t, err) - valPubKey := ps.WrappedPV.Key.PubKey + valPubKey := ps.WrappedPV.Key.CometPVKey.PubKey // generate genesis account acc := authtypes.NewBaseAccount(valPubKey.Address().Bytes(), &cosmosed.PubKey{Key: valPubKey.Bytes()}, 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), Coins: sdk.NewCoins(sdk.NewCoin(appparams.DefaultBondDenom, math.NewInt(100000000000000))), } - ps.WrappedPV.Key.DelegatorAddress = acc.GetAddress().String() + ps.WrappedPV.Key.BlsPVKey.DelegatorAddress = acc.GetAddress().String() // create validator set with single validator genesisKey, err := signer.GenesisKeyFromPrivSigner(ps) require.NoError(t, err) diff --git a/cmd/babylond/cmd/create_bls_key.go b/cmd/babylond/cmd/create_bls_key.go index 37e4f4be4..a3f8c755a 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -1,23 +1,22 @@ package cmd import ( - "errors" "fmt" - "path/filepath" "strings" - cmtconfig "github.com/cometbft/cometbft/config" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" "github.com/babylonlabs-io/babylon/app" appparams "github.com/babylonlabs-io/babylon/app/params" - "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/privval" ) +const ( + FlagPassword = "bls-password" +) + func CreateBlsKeyCmd() *cobra.Command { bech32PrefixAccAddr := appparams.Bech32PrefixAccAddr @@ -47,38 +46,26 @@ $ babylond create-bls-key %s1f5tnl46mk4dfp4nx3n2vnrvyw2h2ydz6ykhk3r --home ./ return err } - return CreateBlsKey(homeDir, addr) + var password string + password, _ = cmd.Flags().GetString(FlagPassword) + if password == "" { + password = privval.NewBlsPassword() + } + return CreateBlsKey(homeDir, password, addr) }, } cmd.Flags().String(flags.FlagHome, app.DefaultNodeHome, "The node home directory") - + cmd.Flags().String(FlagPassword, "", "The password for the BLS key. If a flag is set, the non-empty password should be provided. If a flag is not set, the password will be read from the prompt.") return cmd } -func CreateBlsKey(home string, addr sdk.AccAddress) error { - nodeCfg := cmtconfig.DefaultConfig() - keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) - - pv, err := LoadWrappedFilePV(keyPath, statePath) - if err != nil { - return err - } - - wrappedPV := privval.NewWrappedFilePV(pv.GetValPrivKey(), bls12381.GenPrivKey(), keyPath, statePath) - wrappedPV.SetAccAddress(addr) - +func CreateBlsKey(home, password string, addr sdk.AccAddress) error { + privval.GenBlsPV( + privval.DefaultBlsKeyFile(home), + privval.DefaultBlsPasswordFile(home), + password, + addr.String(), + ) return nil } - -// LoadWrappedFilePV loads the wrapped file private key from the file path. -func LoadWrappedFilePV(keyPath, statePath string) (*privval.WrappedFilePV, error) { - if !cmtos.FileExists(keyPath) { - return nil, errors.New("validator key file does not exist") - } - if !cmtos.FileExists(statePath) { - return nil, errors.New("validator state file does not exist") - } - return privval.LoadWrappedFilePV(keyPath, statePath), nil -} diff --git a/cmd/babylond/cmd/genhelpers/bls_add_test.go b/cmd/babylond/cmd/genhelpers/bls_add_test.go index 52e50dfcc..12bb9b85f 100644 --- a/cmd/babylond/cmd/genhelpers/bls_add_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_add_test.go @@ -3,6 +3,7 @@ package genhelpers_test import ( "context" "fmt" + "os" "path/filepath" "testing" @@ -20,6 +21,7 @@ import ( cmtconfig "github.com/cometbft/cometbft/config" tmjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/libs/tempfile" + cmtprivval "github.com/cometbft/cometbft/privval" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" @@ -147,13 +149,24 @@ func Test_CmdAddBlsWithGentx(t *testing.T) { v := testNetwork.Validators[i] // build and create genesis BLS key genBlsCmd := genhelpers.CmdCreateBls() - nodeCfg := cmtconfig.DefaultConfig() homeDir := filepath.Join(v.Dir, "simd") + + nodeCfg := cmtconfig.DefaultConfig() nodeCfg.SetRoot(homeDir) + keyPath := nodeCfg.PrivValidatorKeyFile() statePath := nodeCfg.PrivValidatorStateFile() - filePV := privval.GenWrappedFilePV(keyPath, statePath) - filePV.SetAccAddress(v.Address) + blsKeyFile := privval.DefaultBlsKeyFile(homeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(homeDir) + + err := privval.EnsureDirs(keyPath, statePath, blsKeyFile, blsPasswordFile) + require.NoError(t, err) + + filePV := cmtprivval.GenFilePV(keyPath, statePath) + filePV.Key.Save() + filePV.LastSignState.Save() + privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", v.Address.String()) + _, err = cli.ExecTestCLICmd(v.ClientCtx, genBlsCmd, []string{fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir)}) require.NoError(t, err) genKeyFileName := filepath.Join(filepath.Dir(keyPath), fmt.Sprintf("gen-bls-%s.json", v.ValAddress)) @@ -175,6 +188,13 @@ func Test_CmdAddBlsWithGentx(t *testing.T) { require.NotEmpty(t, checkpointingGenState.GenesisKeys) gks := checkpointingGenState.GetGenesisKeys() require.Equal(t, genKey, gks[i]) - filePV.Clean(keyPath, statePath) + Clean(keyPath, statePath, blsKeyFile, blsPasswordFile) + } +} + +// Clean removes PVKey file and PVState file +func Clean(paths ...string) { + for _, path := range paths { + _ = os.RemoveAll(filepath.Dir(path)) } } diff --git a/cmd/babylond/cmd/genhelpers/bls_create.go b/cmd/babylond/cmd/genhelpers/bls_create.go index ac1ab7882..2bdfcc29a 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create.go +++ b/cmd/babylond/cmd/genhelpers/bls_create.go @@ -1,7 +1,7 @@ package genhelpers import ( - "errors" + "fmt" "path/filepath" "strings" @@ -12,6 +12,7 @@ import ( "github.com/babylonlabs-io/babylon/app" "github.com/babylonlabs-io/babylon/privval" + cmtprivval "github.com/cometbft/cometbft/privval" ) // CmdCreateBls CLI command to create BLS file with proof of possession. @@ -35,15 +36,35 @@ $ babylond genbls --home ./ homeDir, _ := cmd.Flags().GetString(flags.FlagHome) nodeCfg := cmtconfig.DefaultConfig() - keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - if !cmtos.FileExists(keyPath) { - return errors.New("validator key file does not exist") + nodeCfg.SetRoot(homeDir) + cmtPvKeyFile := nodeCfg.PrivValidatorKeyFile() + cmtPvStateFile := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(homeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(homeDir) + + if err := func(paths ...string) error { + for _, path := range paths { + if !cmtos.FileExists(path) { + return fmt.Errorf("file does not exist in %s", path) + } + } + return nil + }(cmtPvKeyFile, cmtPvStateFile, blsKeyFile, blsPasswordFile); err != nil { + return err } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) + cmtPV := cmtprivval.LoadFilePV(cmtPvKeyFile, cmtPvStateFile) + blsPV := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) + + wrappedPV := &privval.WrappedFilePV{ + Key: privval.WrappedFilePVKey{ + CometPVKey: cmtPV.Key, + BlsPVKey: blsPV.Key, + }, + LastSignState: cmtPV.LastSignState, + } - outputFileName, err := wrappedPV.ExportGenBls(filepath.Dir(keyPath)) + outputFileName, err := wrappedPV.ExportGenBls(filepath.Dir(cmtPvKeyFile)) if err != nil { return err } diff --git a/cmd/babylond/cmd/genhelpers/bls_create_test.go b/cmd/babylond/cmd/genhelpers/bls_create_test.go index 5b171fa90..ea157304e 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_create_test.go @@ -27,6 +27,7 @@ import ( "github.com/babylonlabs-io/babylon/privval" "github.com/babylonlabs-io/babylon/testutil/signer" "github.com/babylonlabs-io/babylon/x/checkpointing/types" + cmtprivval "github.com/cometbft/cometbft/privval" ) func Test_CmdCreateBls(t *testing.T) { @@ -70,11 +71,21 @@ func Test_CmdCreateBls(t *testing.T) { // create BLS keys nodeCfg := cmtconfig.DefaultConfig() - keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) - filePV := privval.GenWrappedFilePV(keyPath, statePath) - defer filePV.Clean(keyPath, statePath) - filePV.SetAccAddress(addr) + nodeCfg.SetRoot(home) + + keyPath := nodeCfg.PrivValidatorKeyFile() + statePath := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(home) + blsPasswordFile := privval.DefaultBlsPasswordFile(home) + + err = privval.EnsureDirs(keyPath, statePath, blsKeyFile, blsPasswordFile) + require.NoError(t, err) + + filePV := cmtprivval.GenFilePV(keyPath, statePath) + filePV.Key.Save() + + blsPV := privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", addr.String()) + defer Clean(keyPath, statePath, blsKeyFile, blsPasswordFile) // execute the gen-bls cmd err = genBlsCmd.ExecuteContext(ctx) @@ -82,9 +93,11 @@ func Test_CmdCreateBls(t *testing.T) { outputFilePath := filepath.Join(filepath.Dir(keyPath), fmt.Sprintf("gen-bls-%s.json", sdk.ValAddress(addr).String())) require.NoError(t, err) genKey, err := types.LoadGenesisKeyFromFile(outputFilePath) + require.NoError(t, err) require.Equal(t, sdk.ValAddress(addr).String(), genKey.ValidatorAddress) - require.True(t, filePV.Key.BlsPubKey.Equal(*genKey.BlsKey.Pubkey)) require.Equal(t, filePV.Key.PubKey.Bytes(), genKey.ValPubkey.Bytes()) + require.True(t, blsPV.Key.PubKey.Equal(*genKey.BlsKey.Pubkey)) + require.True(t, genKey.BlsKey.Pop.IsValid(*genKey.BlsKey.Pubkey, genKey.ValPubkey)) } diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go new file mode 100644 index 000000000..e018f7250 --- /dev/null +++ b/crypto/erc2335/erc2335.go @@ -0,0 +1,62 @@ +package erc2335 + +import ( + "encoding/json" + "fmt" + "os" + + keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" +) + +// Erc2335KeyStore represents an ERC-2335 compatible keystore used in keystorev4. +type Erc2335KeyStore struct { + Crypto map[string]interface{} `json:"crypto"` // Map containing the encryption details for the keystore such as checksum, cipher, and kdf. + Version uint `json:"version"` // Version of the keystore format (e.g., 4 for keystorev4). + UUID string `json:"uuid"` // Unique identifier for the keystore. + Path string `json:"path"` // File path where the keystore is stored. + Pubkey string `json:"pubkey"` // Public key associated with the keystore, stored as a hexadecimal string. + Description string `json:"description"` // Optional description of the keystore, currently used to store the delegator address. +} + +// Encrypt encrypts the private key using the keystorev4 encryptor. +func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { + if privKey == nil { + return nil, fmt.Errorf("private key cannot be nil") + } + + encryptor := keystorev4.New() + cryptoFields, err := encryptor.Encrypt(privKey, password) + if err != nil { + return nil, fmt.Errorf("failed to encrypt private key: %w", err) + } + + keystoreJSON := Erc2335KeyStore{ + Crypto: cryptoFields, + Version: 4, + Pubkey: fmt.Sprintf("%x", pubKey), + } + + return json.Marshal(keystoreJSON) +} + +// Decrypt decrypts the private key from the keystore using the given password. +func Decrypt(keystore Erc2335KeyStore, password string) ([]byte, error) { + encryptor := keystorev4.New() + return encryptor.Decrypt(keystore.Crypto, password) +} + +// LoadKeyStore loads a keystore from a file. +func LoadKeyStore(filePath string) (Erc2335KeyStore, error) { + var keystore Erc2335KeyStore + + keyJSONBytes, err := os.ReadFile(filePath) + if err != nil { + return Erc2335KeyStore{}, fmt.Errorf("failed to read keystore file: %w", err) + } + + if err := json.Unmarshal(keyJSONBytes, &keystore); err != nil { + return Erc2335KeyStore{}, fmt.Errorf("failed to unmarshal keystore: %w", err) + } + + return keystore, nil +} diff --git a/crypto/erc2335/erc2335_test.go b/crypto/erc2335/erc2335_test.go new file mode 100644 index 000000000..9706b599a --- /dev/null +++ b/crypto/erc2335/erc2335_test.go @@ -0,0 +1,94 @@ +package erc2335 + +import ( + "encoding/json" + "os" + "testing" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/cometbft/cometbft/libs/tempfile" + "github.com/test-go/testify/require" +) + +const password string = "password" + +func TestEncryptBLS(t *testing.T) { + t.Run("create bls key", func(t *testing.T) { + blsPrivKey := bls12381.GenPrivKey() + blsPubKey := blsPrivKey.PubKey().Bytes() + + t.Run("encrypt bls key", func(t *testing.T) { + encryptedBlsKey, err := Encrypt(blsPrivKey, blsPubKey, password) + require.NoError(t, err) + t.Logf("encrypted bls key: %s", encryptedBlsKey) + + t.Run("decrypt bls key", func(t *testing.T) { + var keystore Erc2335KeyStore + err = json.Unmarshal(encryptedBlsKey, &keystore) + require.NoError(t, err) + + decryptedBlsKey, err := Decrypt(keystore, password) + require.NoError(t, err) + require.Equal(t, blsPrivKey, bls12381.PrivateKey(decryptedBlsKey)) + }) + + t.Run("decrypt bls key with wrong password", func(t *testing.T) { + var keystore Erc2335KeyStore + err = json.Unmarshal(encryptedBlsKey, &keystore) + require.NoError(t, err) + _, err := Decrypt(keystore, "wrong password") + require.Error(t, err) + }) + }) + + t.Run("save password and encrypt bls key", func(t *testing.T) { + encryptedBlsKey, err := Encrypt(blsPrivKey, blsPubKey, password) + require.NoError(t, err) + t.Logf("encrypted bls key: %s", encryptedBlsKey) + err = tempfile.WriteFileAtomic("password.txt", []byte(password), 0600) + require.NoError(t, err) + + t.Run("load password and decrypt bls key", func(t *testing.T) { + passwordBytes, err := os.ReadFile("password.txt") + require.NoError(t, err) + password := string(passwordBytes) + + var keystore Erc2335KeyStore + err = json.Unmarshal(encryptedBlsKey, &keystore) + require.NoError(t, err) + + decryptedBlsKey, err := Decrypt(keystore, password) + require.NoError(t, err) + require.Equal(t, blsPrivKey, bls12381.PrivateKey(decryptedBlsKey)) + }) + + t.Run("save new password into same file", func(t *testing.T) { + newPassword := "new password" + err = tempfile.WriteFileAtomic("password.txt", []byte(newPassword), 0600) + require.NoError(t, err) + }) + + t.Run("failed when load different password and decrypt bls key", func(t *testing.T) { + passwordBytes, err := os.ReadFile("password.txt") + require.NoError(t, err) + password := string(passwordBytes) + + var keystore Erc2335KeyStore + err = json.Unmarshal(encryptedBlsKey, &keystore) + require.NoError(t, err) + + _, err = Decrypt(keystore, password) + require.Error(t, err) + }) + + t.Run("failed when password file don't exist", func(t *testing.T) { + _, err := os.ReadFile("nopassword.txt") + require.Error(t, err) + }) + }) + + t.Run("clean test files", func(t *testing.T) { + _ = os.RemoveAll("password.txt") + }) + }) +} diff --git a/go.mod b/go.mod index 4dfae8240..b7a47577b 100644 --- a/go.mod +++ b/go.mod @@ -233,9 +233,11 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/strangelove-ventures/cometbft-client v0.1.1 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/test-go/testify v1.1.4 github.com/tidwall/btree v1.7.0 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect + github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect diff --git a/go.sum b/go.sum index 26ff7cc0c..f45efe15e 100644 --- a/go.sum +++ b/go.sum @@ -494,6 +494,9 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM= +github.com/ferranbt/fastssz v0.1.2 h1:Dky6dXlngF6Qjc+EfDipAkE83N5I5DE68bY6O0VLNPk= +github.com/ferranbt/fastssz v0.1.2/go.mod h1:X5UPrE2u1UJjxHA8X54u04SBwdAQjG2sFtWs39YxyWs= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -672,6 +675,7 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -766,6 +770,8 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU= github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo= +github.com/herumi/bls-eth-go-binary v0.0.0-20210130185500-57372fb27371 h1:LEw2KkKciJEr3eKDLzdZ/rjzSR6Y+BS6xKxdA78Bq6s= +github.com/herumi/bls-eth-go-binary v0.0.0-20210130185500-57372fb27371/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= @@ -828,6 +834,8 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -877,6 +885,9 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -888,6 +899,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= @@ -1115,6 +1128,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -1137,6 +1152,12 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vulpine-io/io-test v1.0.0 h1:Ot8vMh+ssm1VWDAwJ3U4C5qG9aRnr5YfQFZPNZBAUGI= github.com/vulpine-io/io-test v1.0.0/go.mod h1:X1I+p5GCxVX9m4nFd1HBtr2bVX9v1ZE6x8w+Obt36AU= +github.com/wealdtech/go-eth2-types/v2 v2.5.2 h1:tiA6T88M6XQIbrV5Zz53l1G5HtRERcxQfmET225V4Ls= +github.com/wealdtech/go-eth2-types/v2 v2.5.2/go.mod h1:8lkNUbgklSQ4LZ2oMSuxSdR7WwJW3L9ge1dcoCVyzws= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3 h1:SxrDVSr+oXuT1x8kZt4uWqNCvv5xXEGV9zd7cuSrZS8= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3/go.mod h1:qiIimacW5NhVRy8o+YxWo9YrecXqDAKKbL0+sOa0SJ4= +github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2 h1:264/meVYWt1wFw6Mtn+xwkZkXjID42gNra4rycoiDXI= +github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2/go.mod h1:k6kmiKWSWBTd4OxFifTEkPaBLhZspnO2KFD5XJY9nqg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -1211,6 +1232,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= @@ -1457,6 +1479,7 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/privval/bls.go b/privval/bls.go new file mode 100644 index 000000000..1621865ba --- /dev/null +++ b/privval/bls.go @@ -0,0 +1,151 @@ +package privval + +import ( + "bufio" + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/babylonlabs-io/babylon/crypto/erc2335" + cmtcfg "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" + "github.com/cometbft/cometbft/libs/tempfile" + "github.com/cosmos/cosmos-sdk/client/input" +) + +const ( + DefaultBlsKeyName = "bls_key.json" // Default file name for BLS key + DefaultBlsPasswordName = "bls_password.txt" // Default file name for BLS password +) + +var ( + defaultBlsKeyFilePath = filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName) // Default file path for BLS key + defaultBlsPasswordPath = filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsPasswordName) // Default file path for BLS password +) + +// BlsPV is a wrapper around BlsPVKey +type BlsPV struct { + // Key is a structure containing bls12381 keys, + // paths of both key and password files, + // and delegator address + Key BlsPVKey +} + +// BlsPVKey is a wrapper containing bls12381 keys, +// paths of both key and password files, and delegator address. +type BlsPVKey struct { + PubKey bls12381.PublicKey `json:"bls_pub_key"` // Public Key of BLS + PrivKey bls12381.PrivateKey `json:"bls_priv_key"` // Private Key of BLS + DelegatorAddress string `json:"delegator_address"` // Delegate Address + filePath string // File Path of BLS Key + passwordPath string // File Path of BLS Password +} + +// NewBlsPV returns a new BlsPV. +func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath, passwordFilePath, delegatorAddress string) *BlsPV { + return &BlsPV{ + Key: BlsPVKey{ + PubKey: privKey.PubKey(), + PrivKey: privKey, + DelegatorAddress: delegatorAddress, + filePath: keyFilePath, + passwordPath: passwordFilePath, + }, + } +} + +// GenBlsPV returns a new BlsPV after saving it to the file. +func GenBlsPV(keyFilePath, passwordFilePath, password, delegatorAddress string) *BlsPV { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, delegatorAddress) + pv.Key.Save(password, delegatorAddress) + return pv +} + +// LoadBlsPV returns a BlsPV after loading the erc2335 type of structure +// from the file and decrypt it using a password. +func LoadBlsPV(keyFilePath, passwordFilePath string) *BlsPV { + passwordBytes, err := os.ReadFile(passwordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) + } + password := string(passwordBytes) + + keystore, err := erc2335.LoadKeyStore(keyFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read erc2335 keystore: %v", err.Error())) + } + + // decrypt bls key from erc2335 type of structure + privKey, err := erc2335.Decrypt(keystore, password) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to decrypt BLS key: %v", err.Error())) + } + + blsPrivKey := bls12381.PrivateKey(privKey) + return &BlsPV{ + Key: BlsPVKey{ + PubKey: blsPrivKey.PubKey(), + PrivKey: blsPrivKey, + DelegatorAddress: keystore.Description, + filePath: keyFilePath, + passwordPath: passwordFilePath, + }, + } +} + +// NewBlsPassword returns a password from the user prompt. +func NewBlsPassword() string { + inBuf := bufio.NewReader(os.Stdin) + password, err := input.GetString("Enter your bls password", inBuf) + if err != nil { + cmtos.Exit("failed to get BLS password") + } + return password +} + +// Save saves the bls12381 key to the file. +// The file stores an erc2335 structure containing the encrypted bls private key. +func (k *BlsPVKey) Save(password, addr string) { + // encrypt the bls12381 key to erc2335 type + erc2335BlsPvKey, err := erc2335.Encrypt(k.PrivKey, k.PubKey.Bytes(), password) + if err != nil { + panic(err) + } + + // Parse the encrypted key back to Erc2335KeyStore structure + var keystore erc2335.Erc2335KeyStore + if err := json.Unmarshal(erc2335BlsPvKey, &keystore); err != nil { + panic(err) + } + + // save the delegator address to description field + keystore.Description = addr + + // convert keystore to json + jsonBytes, err := json.MarshalIndent(keystore, "", " ") + if err != nil { + panic(err) + } + + // write generated erc2335 keystore to file + if err := tempfile.WriteFileAtomic(k.filePath, jsonBytes, 0600); err != nil { + panic(err) + } + + // save used password to file + if err := tempfile.WriteFileAtomic(k.passwordPath, []byte(password), 0600); err != nil { + panic(err) + } +} + +// DefaultBlsKeyFile returns the default BLS key file path. +func DefaultBlsKeyFile(home string) string { + return filepath.Join(home, defaultBlsKeyFilePath) +} + +// DefaultBlsPasswordFile returns the default BLS password file path. +func DefaultBlsPasswordFile(home string) string { + return filepath.Join(home, defaultBlsPasswordPath) +} diff --git a/privval/bls_test.go b/privval/bls_test.go new file mode 100644 index 000000000..829747713 --- /dev/null +++ b/privval/bls_test.go @@ -0,0 +1,58 @@ +package privval + +import ( + "os" + "testing" + + "github.com/cometbft/cometbft/crypto/ed25519" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/cosmos/cosmos-sdk/types" + "github.com/test-go/testify/assert" +) + +func TestNewBlsPV(t *testing.T) { + tempDir := t.TempDir() + defer os.RemoveAll(tempDir) + + keyFilePath := DefaultBlsKeyFile(tempDir) + passwordFilePath := DefaultBlsPasswordFile(tempDir) + + err := EnsureDirs(keyFilePath, passwordFilePath) + assert.NoError(t, err) + + t.Run("save bls key to file without delegator address", func(t *testing.T) { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, "") + assert.NotNil(t, pv) + + password := "password" + pv.Key.Save(password, "") + + t.Run("load bls key from file", func(t *testing.T) { + loadedPv := LoadBlsPV(keyFilePath, passwordFilePath) + assert.NotNil(t, loadedPv) + + assert.Equal(t, pv.Key.PrivKey, loadedPv.Key.PrivKey) + assert.Equal(t, pv.Key.PubKey.Bytes(), loadedPv.Key.PubKey.Bytes()) + }) + }) + + t.Run("save bls key to file with delegator address", func(t *testing.T) { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, "") + assert.NotNil(t, pv) + + password := "password" + + delegatorAddress := types.AccAddress(ed25519.GenPrivKey().PubKey().Address()).String() + pv.Key.Save(password, delegatorAddress) + + t.Run("load bls key from file", func(t *testing.T) { + loadedPv := LoadBlsPV(keyFilePath, passwordFilePath) + assert.NotNil(t, loadedPv) + + assert.Equal(t, pv.Key.PrivKey, loadedPv.Key.PrivKey) + assert.Equal(t, pv.Key.PubKey.Bytes(), loadedPv.Key.PubKey.Bytes()) + assert.Equal(t, delegatorAddress, loadedPv.Key.DelegatorAddress) + }) + }) +} diff --git a/privval/file.go b/privval/file.go index ebfac1314..dbdffdd71 100644 --- a/privval/file.go +++ b/privval/file.go @@ -3,175 +3,32 @@ package privval import ( "errors" "fmt" - "os" "path/filepath" cmtcrypto "github.com/cometbft/cometbft/crypto" - "github.com/cometbft/cometbft/crypto/ed25519" cmtjson "github.com/cometbft/cometbft/libs/json" cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/libs/tempfile" "github.com/cometbft/cometbft/privval" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/babylonlabs-io/babylon/crypto/bls12381" checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -// copied from github.com/cometbft/cometbft/privval/file.go" -// -//nolint:unused -const ( - stepNone int8 = 0 // Used to distinguish the initial state - stepPropose int8 = 1 - stepPrevote int8 = 2 - stepPrecommit int8 = 3 -) - -// copied from github.com/cometbft/cometbft/privval/file.go" -// -//nolint:unused -func voteToStep(vote *cmtproto.Vote) int8 { - switch vote.Type { - case cmtproto.PrevoteType: - return stepPrevote - case cmtproto.PrecommitType: - return stepPrecommit - default: - panic(fmt.Sprintf("Unknown vote type: %v", vote.Type)) - } -} - // WrappedFilePVKey wraps FilePVKey with BLS keys. type WrappedFilePVKey struct { - DelegatorAddress string `json:"acc_address"` - Address types.Address `json:"address"` - PubKey cmtcrypto.PubKey `json:"pub_key"` - PrivKey cmtcrypto.PrivKey `json:"priv_key"` - BlsPubKey bls12381.PublicKey `json:"bls_pub_key"` - BlsPrivKey bls12381.PrivateKey `json:"bls_priv_key"` - - filePath string + CometPVKey privval.FilePVKey + BlsPVKey BlsPVKey } -// Save persists the FilePVKey to its filePath. -func (pvKey WrappedFilePVKey) Save() { - outFile := pvKey.filePath - if outFile == "" { - panic("cannot save PrivValidator key: filePath not set") - } - - jsonBytes, err := cmtjson.MarshalIndent(pvKey, "", " ") - if err != nil { - panic(err) - } - - if err := tempfile.WriteFileAtomic(outFile, jsonBytes, 0600); err != nil { - panic(err) - } -} - -// ------------------------------------------------------------------------------- - // WrappedFilePV wraps FilePV with WrappedFilePVKey. type WrappedFilePV struct { Key WrappedFilePVKey LastSignState privval.FilePVLastSignState } -// NewWrappedFilePV wraps FilePV -func NewWrappedFilePV(privKey cmtcrypto.PrivKey, blsPrivKey bls12381.PrivateKey, keyFilePath, stateFilePath string) *WrappedFilePV { - filePV := privval.NewFilePV(privKey, keyFilePath, stateFilePath) - return &WrappedFilePV{ - Key: WrappedFilePVKey{ - Address: privKey.PubKey().Address(), - PubKey: privKey.PubKey(), - PrivKey: privKey, - BlsPubKey: blsPrivKey.PubKey(), - BlsPrivKey: blsPrivKey, - filePath: keyFilePath, - }, - LastSignState: filePV.LastSignState, - } -} - -// GenWrappedFilePV generates a new validator with randomly generated private key -// and sets the filePaths, but does not call Save(). -func GenWrappedFilePV(keyFilePath, stateFilePath string) *WrappedFilePV { - return NewWrappedFilePV(ed25519.GenPrivKey(), bls12381.GenPrivKey(), keyFilePath, stateFilePath) -} - -// LoadWrappedFilePV loads a FilePV from the filePaths. The FilePV handles double -// signing prevention by persisting data to the stateFilePath. If either file path -// does not exist, the program will exit. -func LoadWrappedFilePV(keyFilePath, stateFilePath string) *WrappedFilePV { - return loadWrappedFilePV(keyFilePath, stateFilePath, true) -} - -// LoadWrappedFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState. -// If the keyFilePath does not exist, the program will exit. -func LoadWrappedFilePVEmptyState(keyFilePath, stateFilePath string) *WrappedFilePV { - return loadWrappedFilePV(keyFilePath, stateFilePath, false) -} - -// If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. -func loadWrappedFilePV(keyFilePath, stateFilePath string, loadState bool) *WrappedFilePV { - keyFilePath = filepath.Clean(keyFilePath) - keyJSONBytes, err := os.ReadFile(keyFilePath) - if err != nil { - cmtos.Exit(err.Error()) - } - pvKey := WrappedFilePVKey{} - err = cmtjson.Unmarshal(keyJSONBytes, &pvKey) - if err != nil { - cmtos.Exit(fmt.Sprintf("Error reading PrivValidator key from %v: %v\n", keyFilePath, err)) - } - - // overwrite pubkey and address for convenience - pvKey.PubKey = pvKey.PrivKey.PubKey() - pvKey.Address = pvKey.PubKey.Address() - pvKey.BlsPubKey = pvKey.BlsPrivKey.PubKey() - pvKey.filePath = keyFilePath - - pvState := privval.FilePVLastSignState{} - - if loadState { - stateFilePath := filepath.Clean(stateFilePath) - stateJSONBytes, err := os.ReadFile(stateFilePath) - if err != nil { - cmtos.Exit(err.Error()) - } - err = cmtjson.Unmarshal(stateJSONBytes, &pvState) - if err != nil { - cmtos.Exit(fmt.Sprintf("Error reading PrivValidator state from %v: %v\n", stateFilePath, err)) - } - } - - // adding path is not needed - // pvState.filePath = stateFilePath - - return &WrappedFilePV{ - Key: pvKey, - LastSignState: pvState, - } -} - -// LoadOrGenWrappedFilePV loads a FilePV from the given filePaths -// or else generates a new one and saves it to the filePaths. -func LoadOrGenWrappedFilePV(keyFilePath, stateFilePath string) *WrappedFilePV { - var pv *WrappedFilePV - if cmtos.FileExists(keyFilePath) { - pv = LoadWrappedFilePV(keyFilePath, stateFilePath) - } else { - pv = GenWrappedFilePV(keyFilePath, stateFilePath) - pv.Save() - } - return pv -} - // ExportGenBls writes a {address, bls_pub_key, pop, and pub_key} into a json file func (pv *WrappedFilePV) ExportGenBls(filePath string) (outputFileName string, err error) { if !cmtos.FileExists(filePath) { @@ -211,43 +68,32 @@ func (pv *WrappedFilePV) ExportGenBls(filePath string) (outputFileName string, e // GetAddress returns the delegator address of the validator. // Implements PrivValidator. func (pv *WrappedFilePV) GetAddress() sdk.ValAddress { - if pv.Key.DelegatorAddress == "" { + if pv.Key.BlsPVKey.DelegatorAddress == "" { return sdk.ValAddress{} } - addr, err := sdk.AccAddressFromBech32(pv.Key.DelegatorAddress) + addr, err := sdk.AccAddressFromBech32(pv.Key.BlsPVKey.DelegatorAddress) if err != nil { cmtos.Exit(err.Error()) } return sdk.ValAddress(addr) } -func (pv *WrappedFilePV) SetAccAddress(addr sdk.AccAddress) { - pv.Key.DelegatorAddress = addr.String() - pv.Key.Save() -} - // GetPubKey returns the public key of the validator. -// Implements PrivValidator. func (pv *WrappedFilePV) GetPubKey() (cmtcrypto.PubKey, error) { - return pv.Key.PubKey, nil + return pv.Key.CometPVKey.PubKey, nil } +// GetValPrivKey returns the private key of the validator. func (pv *WrappedFilePV) GetValPrivKey() cmtcrypto.PrivKey { - return pv.Key.PrivKey + return pv.Key.CometPVKey.PrivKey } +// GetBlsPrivKey returns the private key of the BLS. func (pv *WrappedFilePV) GetBlsPrivKey() bls12381.PrivateKey { - return pv.Key.BlsPrivKey -} - -func (pv *WrappedFilePV) SignMsgWithBls(msg []byte) (bls12381.Signature, error) { - blsPrivKey := pv.GetBlsPrivKey() - if blsPrivKey == nil { - return nil, checkpointingtypes.ErrBlsPrivKeyDoesNotExist - } - return bls12381.Sign(blsPrivKey, msg), nil + return pv.Key.BlsPVKey.PrivKey } +// GetBlsPubkey returns the public key of the BLS func (pv *WrappedFilePV) GetBlsPubkey() (bls12381.PublicKey, error) { blsPrivKey := pv.GetBlsPrivKey() if blsPrivKey == nil { @@ -260,37 +106,11 @@ func (pv *WrappedFilePV) GetValidatorPubkey() (cmtcrypto.PubKey, error) { return pv.GetPubKey() } -// Save persists the FilePV to disk. -func (pv *WrappedFilePV) Save() { - pv.Key.Save() - pv.LastSignState.Save() -} - -// Reset resets all fields in the FilePV. -// NOTE: Unsafe! -func (pv *WrappedFilePV) Reset() { - var sig []byte - pv.LastSignState.Height = 0 - pv.LastSignState.Round = 0 - pv.LastSignState.Step = 0 - pv.LastSignState.Signature = sig - pv.LastSignState.SignBytes = nil - pv.Save() -} - -// Clean removes PVKey file and PVState file -func (pv *WrappedFilePV) Clean(keyFilePath, stateFilePath string) { - _ = os.RemoveAll(filepath.Dir(keyFilePath)) - _ = os.RemoveAll(filepath.Dir(stateFilePath)) -} - -// String returns a string representation of the FilePV. -func (pv *WrappedFilePV) String() string { - return fmt.Sprintf( - "PrivValidator{%v LH:%v, LR:%v, LS:%v}", - pv.GetAddress(), - pv.LastSignState.Height, - pv.LastSignState.Round, - pv.LastSignState.Step, - ) +// SignMsgWithBls signs a message with BLS +func (pv *WrappedFilePV) SignMsgWithBls(msg []byte) (bls12381.Signature, error) { + blsPrivKey := pv.GetBlsPrivKey() + if blsPrivKey == nil { + return nil, checkpointingtypes.ErrBlsPrivKeyDoesNotExist + } + return bls12381.Sign(blsPrivKey, msg), nil } diff --git a/privval/util.go b/privval/util.go new file mode 100644 index 000000000..3535c4056 --- /dev/null +++ b/privval/util.go @@ -0,0 +1,21 @@ +package privval + +import ( + "fmt" + "path/filepath" + + cmtos "github.com/cometbft/cometbft/libs/os" +) + +func EnsureDirs(paths ...string) error { + // Check file path of bls key + for _, path := range paths { + if path == "" { + return fmt.Errorf("filePath for bls key not set") + } + if err := cmtos.EnsureDir(filepath.Dir(path), 0777); err != nil { + return fmt.Errorf("failed to ensure key path dir: %w", err) + } + } + return nil +} diff --git a/test/e2e/initialization/config.go b/test/e2e/initialization/config.go index c1f4da6f7..5a5e2d32c 100644 --- a/test/e2e/initialization/config.go +++ b/test/e2e/initialization/config.go @@ -404,19 +404,19 @@ func updateCheckpointingGenesis(c *internalChain) func(*checkpointingtypes.Genes continue } - proofOfPossession, err := privval.BuildPoP(node.consensusKey.PrivKey, node.consensusKey.BlsPrivKey) + proofOfPossession, err := privval.BuildPoP(node.consensusKey.CometPVKey.PrivKey, node.consensusKey.BlsPVKey.PrivKey) if err != nil { panic("It should be possible to build proof of possession from validator private keys") } - valPubKey, err := cryptocodec.FromCmtPubKeyInterface(node.consensusKey.PubKey) + valPubKey, err := cryptocodec.FromCmtPubKeyInterface(node.consensusKey.CometPVKey.PubKey) if err != nil { panic("It should be possible to retrieve validator public key") } - da, err := sdk.AccAddressFromBech32(node.consensusKey.DelegatorAddress) + da, err := sdk.AccAddressFromBech32(node.consensusKey.BlsPVKey.DelegatorAddress) if err != nil { panic("It should be possible to get validator address from delegator address") @@ -427,7 +427,7 @@ func updateCheckpointingGenesis(c *internalChain) func(*checkpointingtypes.Genes genKey := &checkpointingtypes.GenesisKey{ ValidatorAddress: va.String(), BlsKey: &checkpointingtypes.BlsKey{ - Pubkey: &node.consensusKey.BlsPubKey, + Pubkey: &node.consensusKey.BlsPVKey.PubKey, Pop: proofOfPossession, }, ValPubkey: valPubKey.(*ed25519.PubKey), diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index 7452bdd58..808ad3bb5 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -8,10 +8,10 @@ import ( "path/filepath" "strings" + "github.com/cometbft/cometbft/crypto/ed25519" + "cosmossdk.io/math" cmtconfig "github.com/cometbft/cometbft/config" - cmted25519 "github.com/cometbft/cometbft/crypto/ed25519" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/p2p" cmttypes "github.com/cometbft/cometbft/types" sdkcrypto "github.com/cosmos/cosmos-sdk/crypto" @@ -34,9 +34,9 @@ import ( babylonApp "github.com/babylonlabs-io/babylon/app" appparams "github.com/babylonlabs-io/babylon/app/params" "github.com/babylonlabs-io/babylon/cmd/babylond/cmd" - "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/privval" "github.com/babylonlabs-io/babylon/test/e2e/util" + cmtprivval "github.com/cometbft/cometbft/privval" ) type internalNode struct { @@ -90,7 +90,7 @@ func (n *internalNode) buildCreateValidatorMsg(amount sdk.Coin) (sdk.Msg, error) // get the initial validator min self delegation minSelfDelegation, _ := math.NewIntFromString("1") - valPubKey, err := cryptocodec.FromCmtPubKeyInterface(n.consensusKey.PubKey) + valPubKey, err := cryptocodec.FromCmtPubKeyInterface(n.consensusKey.CometPVKey.PubKey) if err != nil { return nil, err } @@ -159,30 +159,37 @@ func (n *internalNode) createNodeKey() error { func (n *internalNode) createConsensusKey() error { serverCtx := server.NewDefaultContext() config := serverCtx.Config - config.SetRoot(n.configDir()) config.Moniker = n.moniker pvKeyFile := config.PrivValidatorKeyFile() - if err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0o777); err != nil { - return err - } - pvStateFile := config.PrivValidatorStateFile() - if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { - return err - } - - privKey := cmted25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) - blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) - filePV := privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) + blsKeyFile := privval.DefaultBlsKeyFile(n.configDir()) + blsPasswordFile := privval.DefaultBlsPasswordFile(n.configDir()) + if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + return fmt.Errorf("failed to ensure dirs: %w", err) + } accAddress, _ := n.keyInfo.GetAddress() - filePV.Save() - filePV.SetAccAddress(accAddress) - n.consensusKey = filePV.Key + // create file pv + var privKey ed25519.PrivKey + if n.mnemonic == "" { + privKey = ed25519.GenPrivKey() + } else { + privKey = ed25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) + } + filePV := cmtprivval.NewFilePV(privKey, pvKeyFile, pvStateFile) + filePV.Key.Save() + filePV.LastSignState.Save() + + // create bls pv + blsPV := privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", accAddress.String()) + n.consensusKey = privval.WrappedFilePVKey{ + CometPVKey: filePV.Key, + BlsPVKey: blsPV.Key, + } return nil } diff --git a/testutil/datagen/btc_blockchain.go b/testutil/datagen/btc_blockchain.go index d963e50e1..da0a207da 100644 --- a/testutil/datagen/btc_blockchain.go +++ b/testutil/datagen/btc_blockchain.go @@ -104,7 +104,7 @@ func GenRandomBtcdBlockWithTransactions( var proofs []*btcctypes.BTCSpvProof - for i, _ := range msgTxs { + for i := range msgTxs { headerBytes := bbn.NewBTCHeaderBytesFromBlockHeader(header) proof, err := btcctypes.SpvProofFromHeaderAndTransactions(&headerBytes, txBytes, uint(i)) if err != nil { diff --git a/testutil/datagen/genesiskey.go b/testutil/datagen/genesiskey.go index dbc39dda2..aa77c8be8 100644 --- a/testutil/datagen/genesiskey.go +++ b/testutil/datagen/genesiskey.go @@ -99,8 +99,8 @@ func GenesisValidatorSetWithPrivSigner(numVals int) (*GenesisValidators, *appsig } signerVal := &GenesisKeyWithBLS{ GenesisKey: *signerGenesisKey, - PrivateKey: ps.WrappedPV.Key.BlsPrivKey, - PrivKey: ps.WrappedPV.Key.PrivKey, + PrivateKey: ps.WrappedPV.Key.BlsPVKey.PrivKey, + PrivKey: ps.WrappedPV.Key.CometPVKey.PrivKey, } genesisVals, err := GenesisValidatorSet(numVals) if err != nil { diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index 46e883b46..b7fb30c5b 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -2,17 +2,16 @@ package datagen import ( "fmt" - "path/filepath" cfg "github.com/cometbft/cometbft/config" - cmted25519 "github.com/cometbft/cometbft/crypto/ed25519" - cmtos "github.com/cometbft/cometbft/libs/os" + "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/p2p" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/go-bip39" - "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/privval" + cmtos "github.com/cometbft/cometbft/libs/os" + cmtprivval "github.com/cometbft/cometbft/privval" ) // InitializeNodeValidatorFiles creates private validator and p2p configuration files. @@ -32,29 +31,41 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin nodeID = string(nodeKey.ID()) - pvKeyFile := config.PrivValidatorKeyFile() - if err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { - return "", nil, err + cmtKeyFile := config.PrivValidatorKeyFile() + cmtStateFile := config.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(config.RootDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(config.RootDir) + if err := privval.EnsureDirs(cmtKeyFile, cmtStateFile, blsKeyFile, blsPasswordFile); err != nil { + return "", nil, fmt.Errorf("failed to ensure dirs: %w", err) } - pvStateFile := config.PrivValidatorStateFile() - if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { - return "", nil, err + var filePV *cmtprivval.FilePV + if cmtos.FileExists(cmtKeyFile) { + filePV = cmtprivval.LoadFilePV(cmtKeyFile, cmtStateFile) + } else { + var privKey ed25519.PrivKey + if len(mnemonic) == 0 { + privKey = ed25519.GenPrivKey() + } else { + privKey = ed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + } + filePV = cmtprivval.NewFilePV(privKey, cmtKeyFile, cmtStateFile) + filePV.Key.Save() + filePV.LastSignState.Save() } - var filePV *privval.WrappedFilePV - if len(mnemonic) == 0 { - filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + var blsPV *privval.BlsPV + if cmtos.FileExists(blsKeyFile) { + // if key file exists but password file does not exist -> error + if !cmtos.FileExists(blsPasswordFile) { + cmtos.Exit(fmt.Sprintf("BLS password file does not exist: %v", blsPasswordFile)) + } + blsPV = privval.LoadBlsPV(blsKeyFile, blsPasswordFile) } else { - privKey := cmted25519.GenPrivKeyFromSecret([]byte(mnemonic)) - blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) - filePV = privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) + blsPV = privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", addr.String()) } - filePV.SetAccAddress(addr) - valPrivkey := filePV.GetValPrivKey() - blsPrivkey := filePV.GetBlsPrivKey() - valKeys, err = privval.NewValidatorKeys(valPrivkey, blsPrivkey) + valKeys, err = privval.NewValidatorKeys(filePV.Key.PrivKey, blsPV.Key.PrivKey) if err != nil { return "", nil, err } diff --git a/testutil/helper/helper.go b/testutil/helper/helper.go index 46ba8d9dd..4423d0866 100644 --- a/testutil/helper/helper.go +++ b/testutil/helper/helper.go @@ -59,9 +59,9 @@ func NewHelper(t *testing.T) *Helper { // the privSigner is the 0th validator in valSet func NewHelperWithValSet(t *testing.T, valSet *datagen.GenesisValidators, privSigner *signer.PrivSigner) *Helper { // generate the genesis account - signerPubKey := privSigner.WrappedPV.Key.PubKey + signerPubKey := privSigner.WrappedPV.Key.CometPVKey.PubKey acc := authtypes.NewBaseAccount(signerPubKey.Address().Bytes(), &cosmosed.PubKey{Key: signerPubKey.Bytes()}, 0, 0) - privSigner.WrappedPV.Key.DelegatorAddress = acc.Address + privSigner.WrappedPV.Key.BlsPVKey.DelegatorAddress = acc.Address valSet.Keys[0].ValidatorAddress = privSigner.WrappedPV.GetAddress().String() // ensure the genesis account has a sufficient amount of tokens balance := banktypes.Balance{ @@ -97,9 +97,9 @@ func NewHelperWithValSet(t *testing.T, valSet *datagen.GenesisValidators, privSi // included in the validator set func NewHelperWithValSetNoSigner(t *testing.T, valSet *datagen.GenesisValidators, privSigner *signer.PrivSigner) *Helper { // generate the genesis account - signerPubKey := privSigner.WrappedPV.Key.PubKey + signerPubKey := privSigner.WrappedPV.Key.CometPVKey.PubKey acc := authtypes.NewBaseAccount(signerPubKey.Address().Bytes(), &cosmosed.PubKey{Key: signerPubKey.Bytes()}, 0, 0) - privSigner.WrappedPV.Key.DelegatorAddress = acc.Address + privSigner.WrappedPV.Key.BlsPVKey.DelegatorAddress = acc.Address // set a random validator address instead of the privSigner's valSet.Keys[0].ValidatorAddress = datagen.GenRandomValidatorAddress().String() // ensure the genesis account has a sufficient amount of tokens diff --git a/testutil/signer/private.go b/testutil/signer/private.go index a4f39cb3a..98f4852fe 100644 --- a/testutil/signer/private.go +++ b/testutil/signer/private.go @@ -1,6 +1,7 @@ package signer import ( + "fmt" "os" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" @@ -9,6 +10,8 @@ import ( "github.com/babylonlabs-io/babylon/app/signer" "github.com/babylonlabs-io/babylon/privval" checkpointingtypes "github.com/babylonlabs-io/babylon/x/checkpointing/types" + cmtconfig "github.com/cometbft/cometbft/config" + cmtprivval "github.com/cometbft/cometbft/privval" ) // SetupTestPrivSigner sets up a PrivSigner for testing @@ -21,6 +24,12 @@ func SetupTestPrivSigner() (*signer.PrivSigner, error) { defer func() { _ = os.RemoveAll(nodeDir) }() + + // generate a privSigner + if err := GeneratePrivSigner(nodeDir); err != nil { + return nil, err + } + privSigner, _ := signer.InitPrivSigner(nodeDir) return privSigner, nil } @@ -41,3 +50,24 @@ func GenesisKeyFromPrivSigner(ps *signer.PrivSigner) (*checkpointingtypes.Genesi &cosmosed.PubKey{Key: valPubkey.Bytes()}, ) } + +func GeneratePrivSigner(nodeDir string) error { + nodeCfg := cmtconfig.DefaultConfig() + nodeCfg.SetRoot(nodeDir) + + cmtKeyFile := nodeCfg.PrivValidatorKeyFile() + cmtStateFile := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(nodeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) + + if err := privval.EnsureDirs(cmtKeyFile, cmtStateFile, blsKeyFile, blsPasswordFile); err != nil { + return fmt.Errorf("failed to ensure dirs: %w", err) + } + + cometPV := cmtprivval.GenFilePV(cmtKeyFile, cmtStateFile) + cometPV.Key.Save() + cometPV.LastSignState.Save() + + privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", "") + return nil +} diff --git a/x/checkpointing/client/cli/tx_test.go b/x/checkpointing/client/cli/tx_test.go index bee75d842..3a12e687f 100644 --- a/x/checkpointing/client/cli/tx_test.go +++ b/x/checkpointing/client/cli/tx_test.go @@ -4,14 +4,12 @@ import ( "context" "fmt" "io" - "path/filepath" "testing" sdkmath "cosmossdk.io/math" abci "github.com/cometbft/cometbft/abci/types" cmtconfig "github.com/cometbft/cometbft/config" cmtbytes "github.com/cometbft/cometbft/libs/bytes" - cmtos "github.com/cometbft/cometbft/libs/os" rpcclient "github.com/cometbft/cometbft/rpc/client" rpcclientmock "github.com/cometbft/cometbft/rpc/client/mock" coretypes "github.com/cometbft/cometbft/rpc/core/types" @@ -31,6 +29,7 @@ import ( "github.com/babylonlabs-io/babylon/privval" testutilcli "github.com/babylonlabs-io/babylon/testutil/cli" checkpointcli "github.com/babylonlabs-io/babylon/x/checkpointing/client/cli" + cmtprivval "github.com/cometbft/cometbft/privval" ) type mockCometRPC struct { @@ -103,17 +102,27 @@ func (s *CLITestSuite) SetupSuite() { func (s *CLITestSuite) TestCmdWrappedCreateValidator() { require := s.Require() homeDir := s.T().TempDir() + + // create BLS keys nodeCfg := cmtconfig.DefaultConfig() - pvKeyFile := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) - require.NoError(err) - pvStateFile := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) + nodeCfg.SetRoot(homeDir) + + cmtKeyPath := nodeCfg.PrivValidatorKeyFile() + cmtStatePath := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(homeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(homeDir) + + err := privval.EnsureDirs(cmtKeyPath, cmtStatePath, blsKeyFile, blsPasswordFile) require.NoError(err) - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + + filePV := cmtprivval.GenFilePV(cmtKeyPath, cmtStatePath) + filePV.Key.Save() + filePV.LastSignState.Save() + + privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", "") cmd := checkpointcli.CmdWrappedCreateValidator(authcodec.NewBech32Codec("cosmosvaloper")) - consPrivKey := wrappedPV.GetValPrivKey() + consPrivKey := filePV.Key.PrivKey consPubKey, err := cryptocodec.FromCmtPubKeyInterface(consPrivKey.PubKey()) require.NoError(err) consPubKeyBz, err := s.clientCtx.Codec.MarshalInterfaceJSON(consPubKey) diff --git a/x/checkpointing/client/cli/utils.go b/x/checkpointing/client/cli/utils.go index c30ad96dd..695a57f99 100644 --- a/x/checkpointing/client/cli/utils.go +++ b/x/checkpointing/client/cli/utils.go @@ -11,7 +11,6 @@ import ( errorsmod "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" cmtconfig "github.com/cometbft/cometbft/config" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" @@ -25,6 +24,7 @@ import ( "github.com/babylonlabs-io/babylon/privval" "github.com/babylonlabs-io/babylon/x/checkpointing/types" + cmtprivval "github.com/cometbft/cometbft/privval" ) // validator struct to define the fields of the validator @@ -204,12 +204,19 @@ func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commiss func getValKeyFromFile(homeDir string) (*privval.ValidatorKeys, error) { nodeCfg := cmtconfig.DefaultConfig() - keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - if !cmtos.FileExists(keyPath) { - return nil, errors.New("validator key file does not exist") + nodeCfg.SetRoot(homeDir) + + cmtKeyPath := nodeCfg.PrivValidatorKeyFile() + cmtStatePath := nodeCfg.PrivValidatorStateFile() + blsKeyPath := privval.DefaultBlsKeyFile(homeDir) + blsPasswordPath := privval.DefaultBlsPasswordFile(homeDir) + + if err := privval.EnsureDirs(cmtKeyPath, cmtStatePath, blsKeyPath, blsPasswordPath); err != nil { + return nil, fmt.Errorf("failed to ensure dirs: %w", err) } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) - return privval.NewValidatorKeys(wrappedPV.GetValPrivKey(), wrappedPV.GetBlsPrivKey()) + filePV := cmtprivval.LoadFilePV(cmtKeyPath, cmtStatePath) + blsPV := privval.LoadBlsPV(blsKeyPath, blsPasswordPath) + + return privval.NewValidatorKeys(filePV.Key.PrivKey, blsPV.Key.PrivKey) } diff --git a/x/checkpointing/vote_ext_test.go b/x/checkpointing/vote_ext_test.go index 90c57342f..81ecdbe00 100644 --- a/x/checkpointing/vote_ext_test.go +++ b/x/checkpointing/vote_ext_test.go @@ -188,7 +188,7 @@ func FuzzExtendVote_EmptyBLSPrivKey(f *testing.F) { require.NoError(t, err) // set the BLS private key to be nil to trigger panic - ps.WrappedPV.Key.BlsPrivKey = nil + ps.WrappedPV.Key.BlsPVKey.PrivKey = nil helper := testhelper.NewHelperWithValSet(t, genesisValSet, ps) ek := helper.App.EpochingKeeper