diff --git a/CHANGELOG.md b/CHANGELOG.md index 46f39ee45..d355fd273 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..d95fdc910 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -4,9 +4,9 @@ import ( "path/filepath" 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() + blsCfg := privval.DefaultBlsConfig() + pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) - err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) - if err != nil { - return nil, err - } pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) - if err != nil { + blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return nil, err } - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + + 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, + } 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/client/flags/flags.go b/client/flags/flags.go new file mode 100644 index 000000000..5e58fe954 --- /dev/null +++ b/client/flags/flags.go @@ -0,0 +1,5 @@ +package flags + +const ( + FlagBlsPassword = "bls-password" +) diff --git a/cmd/babylond/cmd/create_bls_key.go b/cmd/babylond/cmd/create_bls_key.go index 37e4f4be4..6a6b62ea8 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -1,20 +1,16 @@ 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" ) @@ -57,28 +53,13 @@ $ babylond create-bls-key %s1f5tnl46mk4dfp4nx3n2vnrvyw2h2ydz6ykhk3r --home ./ } 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 - } + blsCfg := privval.DefaultBlsConfig() + keyPath := filepath.Join(home, blsCfg.BlsKeyFile()) + passwordPath := filepath.Join(home, blsCfg.BlsPasswordFile()) - wrappedPV := privval.NewWrappedFilePV(pv.GetValPrivKey(), bls12381.GenPrivKey(), keyPath, statePath) - wrappedPV.SetAccAddress(addr) + password := privval.GetBlsPassword() + privval.GenBlsPV(keyPath, passwordPath, 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..4c7b8f21e 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,26 @@ 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) + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(homeDir) + keyPath := nodeCfg.PrivValidatorKeyFile() statePath := nodeCfg.PrivValidatorStateFile() - filePV := privval.GenWrappedFilePV(keyPath, statePath) - filePV.SetAccAddress(v.Address) + blsKeyFile := blsCfg.BlsKeyFile() + blsPasswordFile := blsCfg.BlsPasswordFile() + + err := privval.IsValidFilePath(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 +190,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..357139863 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create.go +++ b/cmd/babylond/cmd/genhelpers/bls_create.go @@ -1,17 +1,16 @@ package genhelpers import ( - "errors" "path/filepath" "strings" cmtconfig "github.com/cometbft/cometbft/config" - cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cobra" "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,13 +34,27 @@ $ babylond genbls --home ./ homeDir, _ := cmd.Flags().GetString(flags.FlagHome) nodeCfg := cmtconfig.DefaultConfig() + blsCfg := privval.DefaultBlsConfig() + 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") + blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) + blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + + if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + return err } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) + filePV := cmtprivval.LoadFilePV(keyPath, statePath) + blsPV := privval.LoadBlsPV(blsKeyPath, blsPasswordPath) + + wrappedPV := &privval.WrappedFilePV{ + Key: privval.WrappedFilePVKey{ + CometPVKey: filePV.Key, + BlsPVKey: blsPV.Key, + }, + LastSignState: filePV.LastSignState, + } outputFileName, err := wrappedPV.ExportGenBls(filepath.Dir(keyPath)) if err != nil { diff --git a/cmd/babylond/cmd/genhelpers/bls_create_test.go b/cmd/babylond/cmd/genhelpers/bls_create_test.go index 5b171fa90..b80f1bbad 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() + blsCfg := privval.DefaultBlsConfig() + 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) + blsKeyFile := filepath.Join(home, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) + + err = privval.IsValidFilePath(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..6933500b5 --- /dev/null +++ b/crypto/erc2335/erc2335.go @@ -0,0 +1,83 @@ +package erc2335 + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "fmt" + "os" + + "github.com/pkg/errors" + keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" +) + +type Erc2335KeyStore struct { + Crypto map[string]interface{} `json:"crypto"` + Version uint `json:"version"` + UUID string `json:"uuid"` + Path string `json:"path"` + Pubkey string `json:"pubkey"` + Description string `json:"description"` +} + +// wonjoon: encrypt key pair to erc2335 keystore +// available to handle all keys in []byte format +func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { + if privKey == nil { + return nil, errors.New("private key cannot be nil") + } + + encryptor := keystorev4.New() + cryptoFields, err := encryptor.Encrypt(privKey, password) + if err != nil { + return nil, errors.Wrap(err, "failed to encrypt private key") + } + + // Create the keystore json structure + keystoreJSON := Erc2335KeyStore{ + Crypto: cryptoFields, + Version: 4, + Pubkey: fmt.Sprintf("%x", pubKey), + } + + return json.Marshal(keystoreJSON) +} + +func LoadKeyStore(filePath string) (Erc2335KeyStore, error) { + var keystore Erc2335KeyStore + + keyJSONBytes, err := os.ReadFile(filePath) + if err != nil { + return Erc2335KeyStore{}, err + } + + if err := json.Unmarshal(keyJSONBytes, &keystore); err != nil { + return Erc2335KeyStore{}, err + } + + return keystore, nil +} + +// decrypt private key from erc2335 keystore +func Decrypt(keystore Erc2335KeyStore, password string) ([]byte, error) { + encryptor := keystorev4.New() + return encryptor.Decrypt(keystore.Crypto, password) +} + +func SavePasswordToFile(password, filePath string) error { + return os.WriteFile(filePath, []byte(password), 0600) +} + +func LoadPaswordFromFile(filePath string) (string, error) { + password, err := os.ReadFile(filePath) + return string(password), err +} + +func CreateRandomPassword() string { + password := make([]byte, 32) + _, err := rand.Read(password) + if err != nil { + panic(err) + } + return hex.EncodeToString(password) +} diff --git a/crypto/erc2335/erc2335_test.go b/crypto/erc2335/erc2335_test.go new file mode 100644 index 000000000..aa56f650e --- /dev/null +++ b/crypto/erc2335/erc2335_test.go @@ -0,0 +1,95 @@ +package erc2335 + +import ( + "encoding/json" + "os" + "testing" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/test-go/testify/require" +) + +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) { + password := CreateRandomPassword() + t.Logf("password: %s", password) + 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) { + password := CreateRandomPassword() + t.Logf("password: %s", password) + + encryptedBlsKey, err := Encrypt(blsPrivKey, blsPubKey, password) + require.NoError(t, err) + t.Logf("encrypted bls key: %s", encryptedBlsKey) + err = SavePasswordToFile(password, "password.txt") + require.NoError(t, err) + + t.Run("load password and decrypt bls key", func(t *testing.T) { + password, err := LoadPaswordFromFile("password.txt") + require.NoError(t, err) + + 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 := CreateRandomPassword() + t.Logf("new password: %s", newPassword) + err = SavePasswordToFile(newPassword, "password.txt") + require.NoError(t, err) + }) + + t.Run("failed when load different password and decrypt bls key", func(t *testing.T) { + password, err := LoadPaswordFromFile("password.txt") + require.NoError(t, err) + + 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 := LoadPaswordFromFile("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 ad2796aa2..e3efba2b1 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.0 // 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 28eaac7bc..8788e3f95 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..e990db8e3 --- /dev/null +++ b/privval/bls.go @@ -0,0 +1,171 @@ +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" + DefaultBlsPasswordName = "bls_password.txt" +) + +type BlsPV struct { + Key BlsPVKey +} + +type BlsPVKey struct { + PubKey bls12381.PublicKey `json:"bls_pub_key"` + PrivKey bls12381.PrivateKey `json:"bls_priv_key"` + + DelegatorAddress string + + filePath string + passwordPath string +} + +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, + }, + } +} + +func GenBlsPV(keyFilePath, passwordFilePath, password, delegatorAddress string) *BlsPV { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, delegatorAddress) + pv.Key.Save(password, delegatorAddress) + return pv +} + +func LoadBlsPV(keyFilePath, passwordFilePath string) *BlsPV { + password, err := erc2335.LoadPaswordFromFile(passwordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) + } + + 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, + }, + } +} + +func GetBlsPassword() 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 bls key using password +// Check both paths of bls key and password inside function +func (k *BlsPVKey) Save(password, addr string) { + // check file path is valid + if err := IsValidFilePath(k.filePath, k.passwordPath); err != nil { + panic(err) + } + + // 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 + err = erc2335.SavePasswordToFile(password, k.passwordPath) + if err != nil { + panic(err) + } +} + +// ------------------------------------------------------------------------------- +// ---------------------------- BLS Config --------------------------------------- +// ------------------------------------------------------------------------------- + +type BlsConfig struct { + RootDir string `mapstructure:"home"` + BlsKeyPath string `mapstructure:"bls_key_file"` + BlsPasswordPath string `mapstructure:"bls_password_file"` +} + +func DefaultBlsConfig() BlsConfig { + return BlsConfig{ + BlsKeyPath: filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName), + BlsPasswordPath: filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsPasswordName), + } +} + +func (cfg *BlsConfig) SetRoot(root string) *BlsConfig { + cfg.RootDir = root + return cfg +} + +func (cfg BlsConfig) BlsKeyFile() string { + return rootify(cfg.BlsKeyPath, cfg.RootDir) +} + +func (cfg BlsConfig) BlsPasswordFile() string { + return rootify(cfg.BlsPasswordPath, cfg.RootDir) +} + +// helper function to make config creation independent of root dir +// copied from https://github.com/cometbft/cometbft/blob/v0.38.15/config/config.go +func rootify(path, root string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(root, path) +} diff --git a/privval/bls_test.go b/privval/bls_test.go new file mode 100644 index 000000000..6e6c134e5 --- /dev/null +++ b/privval/bls_test.go @@ -0,0 +1,59 @@ +package privval + +import ( + "os" + "path/filepath" + "testing" + + "github.com/cometbft/cometbft/crypto/ed25519" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/babylonlabs-io/babylon/crypto/erc2335" + "github.com/cosmos/cosmos-sdk/types" + "github.com/test-go/testify/assert" +) + +func TestNewBlsPV(t *testing.T) { + tempDir := t.TempDir() + defer os.RemoveAll(tempDir) + + cfg := DefaultBlsConfig() + + keyFilePath := filepath.Join(tempDir, cfg.BlsKeyFile()) + passwordFilePath := filepath.Join(tempDir, cfg.BlsPasswordFile()) + + 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 := erc2335.CreateRandomPassword() + 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 := erc2335.CreateRandomPassword() + + 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..802240580 --- /dev/null +++ b/privval/util.go @@ -0,0 +1,21 @@ +package privval + +import ( + "fmt" + "path/filepath" + + cmtos "github.com/cometbft/cometbft/libs/os" +) + +func IsValidFilePath(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/configurer/upgrade.go b/test/e2e/configurer/upgrade.go index 123a3924f..3e5d6bacb 100644 --- a/test/e2e/configurer/upgrade.go +++ b/test/e2e/configurer/upgrade.go @@ -257,6 +257,12 @@ func (uc *UpgradeConfigurer) upgradeContainers(chainConfig *chain.Config, propHe uc.containerManager.CurrentTag = "latest" for _, node := range chainConfig.NodeConfigs { + + // log.Print("consensus key: ", node.ConsensusKey) + + // if err := saveIfNotExists(node); err != nil { + // return err + // } if err := node.Run(); err != nil { return err } @@ -268,6 +274,15 @@ func (uc *UpgradeConfigurer) upgradeContainers(chainConfig *chain.Config, propHe return nil } +// func saveIfNotExists(cfg *chain.NodeConfig) error { +// nodeDir := cfg.Node.ConfigDir +// if !cmtos.FileExists(filepath.Join(nodeDir, cmtCfg.DefaultConfig().PrivValidatorKeyFile())) { +// return signer.GeneratePrivSigner(nodeDir) +// } +// log.Print("file is exists: ", filepath.Join(nodeDir, cmtCfg.DefaultConfig().PrivValidatorKeyFile())) +// return nil +// } + // ParseGovPropFromFile loads the proposal from the UpgradeSignetLaunchFilePath func (uc *UpgradeConfigurer) ParseGovPropFromFile() (*upgradetypes.MsgSoftwareUpgrade, error) { pwd, err := os.Getwd() diff --git a/test/e2e/containers/containers.go b/test/e2e/containers/containers.go index b6863d1ef..adc639d3a 100644 --- a/test/e2e/containers/containers.go +++ b/test/e2e/containers/containers.go @@ -5,12 +5,16 @@ import ( "context" "errors" "fmt" + "log" "os" + "path/filepath" "regexp" "strings" "testing" "time" + cmtCfg "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/stretchr/testify/require" @@ -263,6 +267,12 @@ func (m *Manager) RunNodeResource(chainId string, containerName, valCondifDir st return nil, err } + if cmtos.FileExists(filepath.Join(valCondifDir, cmtCfg.DefaultConfig().PrivValidatorKeyFile())) { + log.Print("file is exists: ", filepath.Join(valCondifDir, cmtCfg.DefaultConfig().PrivValidatorKeyFile())) + } else { + log.Print("file is not exists: ", filepath.Join(valCondifDir, cmtCfg.DefaultConfig().PrivValidatorKeyFile())) + } + runOpts := &dockertest.RunOptions{ Name: containerName, Repository: m.CurrentRepository, 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/export.go b/test/e2e/initialization/export.go index 6c741a4c0..e4ab0622a 100644 --- a/test/e2e/initialization/export.go +++ b/test/e2e/initialization/export.go @@ -2,6 +2,8 @@ package initialization import ( "fmt" + + "github.com/babylonlabs-io/babylon/privval" ) type ChainMeta struct { @@ -19,6 +21,7 @@ type Node struct { PrivateKey []byte `json:"privateKey"` PeerId string `json:"peerId"` IsValidator bool `json:"isValidator"` + ConsensusKey privval.WrappedFilePVKey } type Chain struct { diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index 7452bdd58..0dfae6766 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" @@ -37,6 +37,7 @@ import ( "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 +91,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 +160,49 @@ 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 - } + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(n.configDir()) + pvKeyFile := config.PrivValidatorKeyFile() pvStateFile := config.PrivValidatorStateFile() - if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0o777); err != nil { + blsKeyFile := blsCfg.BlsKeyFile() + blsPasswordFile := blsCfg.BlsPasswordFile() + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return err } - privKey := cmted25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) - blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) - filePV := privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) - + // delegator address accAddress, _ := n.keyInfo.GetAddress() - filePV.Save() - filePV.SetAccAddress(accAddress) - n.consensusKey = filePV.Key + // create new key for consensus + // file pv + var cmtPrivKey ed25519.PrivKey + var blsPrivKey bls12381.PrivateKey + if n.mnemonic == "" { + cmtPrivKey = ed25519.GenPrivKey() + blsPrivKey = bls12381.GenPrivKey() + } else { + cmtPrivKey = ed25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) + blsPrivKey = bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) + } + filePV := cmtprivval.NewFilePV(cmtPrivKey, pvKeyFile, pvStateFile) + filePV.Key.Save() + filePV.LastSignState.Save() + + // bls pv + blsPV := privval.NewBlsPV(blsPrivKey, blsKeyFile, blsPasswordFile, accAddress.String()) + blsPV.Key.Save("password", accAddress.String()) + + n.consensusKey = privval.WrappedFilePVKey{ + CometPVKey: filePV.Key, + BlsPVKey: blsPV.Key, + } return nil } @@ -252,6 +272,7 @@ func (n *internalNode) export() *Node { PrivateKey: n.privateKey.Bytes(), PeerId: n.peerId, IsValidator: n.isValidator, + ConsensusKey: n.consensusKey, } } 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..10ee141c9 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. @@ -33,28 +32,51 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin nodeID = string(nodeKey.ID()) pvKeyFile := config.PrivValidatorKeyFile() - if err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777); err != nil { + pvStateFile := config.PrivValidatorStateFile() + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile); err != nil { return "", nil, err } - pvStateFile := config.PrivValidatorStateFile() - if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { + // bls config + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(config.RootDir) + + blsKeyFile := blsCfg.BlsKeyFile() + blsPasswordFile := blsCfg.BlsPasswordFile() + if err := privval.IsValidFilePath(blsKeyFile, blsPasswordFile); err != nil { return "", nil, err } - var filePV *privval.WrappedFilePV - if len(mnemonic) == 0 { - filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + // load or generate private validator + var filePV *cmtprivval.FilePV + if cmtos.FileExists(pvKeyFile) { + filePV = cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) + } else { + var privKey ed25519.PrivKey + if len(mnemonic) == 0 { + privKey = ed25519.GenPrivKey() + } else { + privKey = ed25519.GenPrivKeyFromSecret([]byte(mnemonic)) + } + filePV = cmtprivval.NewFilePV(privKey, pvKeyFile, pvStateFile) + filePV.Key.Save() + filePV.LastSignState.Save() + } + + // load or generate BLS private validator + 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..8d1d4538e 100644 --- a/testutil/signer/private.go +++ b/testutil/signer/private.go @@ -2,6 +2,7 @@ package signer import ( "os" + "path/filepath" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -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() + blsCfg := privval.DefaultBlsConfig() + + pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) + pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) + blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + return err + } + + cometPV := cmtprivval.GenFilePV(pvKeyFile, pvStateFile) + 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..5cccefdaf 100644 --- a/x/checkpointing/client/cli/tx_test.go +++ b/x/checkpointing/client/cli/tx_test.go @@ -11,7 +11,6 @@ import ( 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 +30,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 +103,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) + blsCfg := privval.DefaultBlsConfig() + + keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) + statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) + blsKeyFile := filepath.Join(homeDir, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + + err := privval.IsValidFilePath(keyPath, statePath, blsKeyFile, blsPasswordFile) require.NoError(err) - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + + filePV := cmtprivval.GenFilePV(keyPath, statePath) + 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..e25bbc750 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() + blsCfg := privval.DefaultBlsConfig() + 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") + blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) + blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + + if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + return nil, err } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) - return privval.NewValidatorKeys(wrappedPV.GetValPrivKey(), wrappedPV.GetBlsPrivKey()) + filePV := cmtprivval.LoadFilePV(keyPath, statePath) + 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