From dda703032020b34e05c4bbec6070c859dac44be1 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 20:06:08 +0900 Subject: [PATCH 1/8] feat: add erc2335 structure --- crypto/erc2335/erc2335.go | 65 ++++++++++++++++++++++++++++++++++ crypto/erc2335/erc2335_test.go | 37 +++++++++++++++++++ go.mod | 2 ++ go.sum | 4 +++ 4 files changed, 108 insertions(+) create mode 100644 crypto/erc2335/erc2335.go create mode 100644 crypto/erc2335/erc2335_test.go diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go new file mode 100644 index 000000000..9096a4530 --- /dev/null +++ b/crypto/erc2335/erc2335.go @@ -0,0 +1,65 @@ +package erc2335 + +import ( + "encoding/json" + "fmt" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "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"` +} + +// Encrypt encrypts a BLS private key using the EIP-2335 format +func EncryptBLS(privKey *bls12381.PrivateKey, 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") + } + + pubKey := privKey.PubKey().Bytes() + + // Create the keystore json structure + keystoreJSON := Erc2335KeyStore{ + Crypto: cryptoFields, + Version: 4, + Pubkey: fmt.Sprintf("%x", pubKey), + } + + return json.Marshal(keystoreJSON) +} + +// Decrypt decrypts an EIP-2335 keystore JSON and returns the BLS private key +func DecryptBLS(keystoreJSON []byte, password string) (bls12381.PrivateKey, error) { + // Parse the keystore json + var keystore Erc2335KeyStore + + if err := json.Unmarshal(keystoreJSON, &keystore); err != nil { + return nil, errors.Wrap(err, "failed to parse keystore json") + } + + // Verify version + if keystore.Version != 4 { + return nil, fmt.Errorf("invalid keystore version: %d", keystore.Version) + } + + encryptor := keystorev4.New() + privateKeyBytes, err := encryptor.Decrypt(keystore.Crypto, password) + if err != nil { + return nil, errors.Wrap(err, "failed to decrypt keystore") + } + return bls12381.PrivateKey(privateKeyBytes), nil + +} diff --git a/crypto/erc2335/erc2335_test.go b/crypto/erc2335/erc2335_test.go new file mode 100644 index 000000000..a53459798 --- /dev/null +++ b/crypto/erc2335/erc2335_test.go @@ -0,0 +1,37 @@ +package erc2335 + +import ( + "testing" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/test-go/testify/require" +) + +func TestEncryptBLS(t *testing.T) { + // TODO + t.Run("create bls key", func(t *testing.T) { + // TODO + blsPrivKey := bls12381.GenPrivKey() + password := "password" + + t.Run("encrypt bls key", func(t *testing.T) { + // TODO + encryptedBlsKey, err := EncryptBLS(&blsPrivKey, password) + require.NoError(t, err) + t.Logf("encrypted bls key: %s", encryptedBlsKey) + + t.Run("decrypt bls key", func(t *testing.T) { + // TODO + decryptedBlsKey, err := DecryptBLS(encryptedBlsKey, password) + require.NoError(t, err) + require.Equal(t, blsPrivKey, decryptedBlsKey) + }) + + t.Run("decrypt bls key with wrong password", func(t *testing.T) { + // TODO + _, err := DecryptBLS(encryptedBlsKey, "wrong password") + require.Error(t, err) + }) + }) + }) +} diff --git a/go.mod b/go.mod index ad2796aa2..e63d5c966 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.4.1 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..f699c7a91 100644 --- a/go.sum +++ b/go.sum @@ -1115,6 +1115,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 +1139,8 @@ 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-wallet-encryptor-keystorev4 v1.4.1 h1:9j7bpwjT9wmwBb54ZkBhTm1uNIlFFcCJXefd/YskZPw= +github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1/go.mod h1:+tI1VD76E1WINI+Nstg7RVGpUolL5ql10nu2YztMO/4= 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= From 857ab38abf8988a7dfd118fe79ce9654c0a8cda1 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 20:09:18 +0900 Subject: [PATCH 2/8] feat: separate bls structure --- app/test_helpers.go | 2 +- .../cmd/genhelpers/bls_create_test.go | 4 +- crypto/erc2335/erc2335.go | 21 +- crypto/erc2335/erc2335_test.go | 5 +- go.mod | 2 +- go.sum | 23 ++- privval/bls.go | 166 ++++++++++++++++ privval/bls_test.go | 54 ++++++ privval/file.go | 180 ++++++++++-------- test/e2e/initialization/config.go | 6 +- test/e2e/initialization/node.go | 2 +- testutil/datagen/genesiskey.go | 4 +- testutil/helper/helper.go | 4 +- x/checkpointing/vote_ext_test.go | 2 +- 14 files changed, 360 insertions(+), 115 deletions(-) create mode 100644 privval/bls.go create mode 100644 privval/bls_test.go diff --git a/app/test_helpers.go b/app/test_helpers.go index a53305374..385a557d8 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -239,7 +239,7 @@ 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{ diff --git a/cmd/babylond/cmd/genhelpers/bls_create_test.go b/cmd/babylond/cmd/genhelpers/bls_create_test.go index 5b171fa90..c93d38de9 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_create_test.go @@ -84,7 +84,7 @@ func Test_CmdCreateBls(t *testing.T) { 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, filePV.Key.BlsPVKey.PubKey.Equal(*genKey.BlsKey.Pubkey)) + require.Equal(t, filePV.Key.CometPVKey.PubKey.Bytes(), genKey.ValPubkey.Bytes()) require.True(t, genKey.BlsKey.Pop.IsValid(*genKey.BlsKey.Pubkey, genKey.ValPubkey)) } diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go index 9096a4530..9559e4970 100644 --- a/crypto/erc2335/erc2335.go +++ b/crypto/erc2335/erc2335.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" - "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/pkg/errors" keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" ) @@ -17,20 +16,19 @@ type Erc2335KeyStore struct { Pubkey string `json:"pubkey"` } -// Encrypt encrypts a BLS private key using the EIP-2335 format -func EncryptBLS(privKey *bls12381.PrivateKey, password string) ([]byte, error) { +// wonjoon: encrypt key pair to erc2335 keystore +// available to handle all keys in []byte format +func EncryptBLS(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) + cryptoFields, err := encryptor.Encrypt(privKey, password) if err != nil { return nil, errors.Wrap(err, "failed to encrypt private key") } - pubKey := privKey.PubKey().Bytes() - // Create the keystore json structure keystoreJSON := Erc2335KeyStore{ Crypto: cryptoFields, @@ -41,8 +39,8 @@ func EncryptBLS(privKey *bls12381.PrivateKey, password string) ([]byte, error) { return json.Marshal(keystoreJSON) } -// Decrypt decrypts an EIP-2335 keystore JSON and returns the BLS private key -func DecryptBLS(keystoreJSON []byte, password string) (bls12381.PrivateKey, error) { +// decrypt private key from erc2335 keystore +func DecryptBLS(keystoreJSON []byte, password string) ([]byte, error) { // Parse the keystore json var keystore Erc2335KeyStore @@ -56,10 +54,5 @@ func DecryptBLS(keystoreJSON []byte, password string) (bls12381.PrivateKey, erro } encryptor := keystorev4.New() - privateKeyBytes, err := encryptor.Decrypt(keystore.Crypto, password) - if err != nil { - return nil, errors.Wrap(err, "failed to decrypt keystore") - } - return bls12381.PrivateKey(privateKeyBytes), nil - + return encryptor.Decrypt(keystore.Crypto, password) } diff --git a/crypto/erc2335/erc2335_test.go b/crypto/erc2335/erc2335_test.go index a53459798..e6ccc1702 100644 --- a/crypto/erc2335/erc2335_test.go +++ b/crypto/erc2335/erc2335_test.go @@ -12,11 +12,12 @@ func TestEncryptBLS(t *testing.T) { t.Run("create bls key", func(t *testing.T) { // TODO blsPrivKey := bls12381.GenPrivKey() + blsPubKey := blsPrivKey.PubKey().Bytes() password := "password" t.Run("encrypt bls key", func(t *testing.T) { // TODO - encryptedBlsKey, err := EncryptBLS(&blsPrivKey, password) + encryptedBlsKey, err := EncryptBLS(blsPrivKey, blsPubKey, password) require.NoError(t, err) t.Logf("encrypted bls key: %s", encryptedBlsKey) @@ -24,7 +25,7 @@ func TestEncryptBLS(t *testing.T) { // TODO decryptedBlsKey, err := DecryptBLS(encryptedBlsKey, password) require.NoError(t, err) - require.Equal(t, blsPrivKey, decryptedBlsKey) + require.Equal(t, blsPrivKey, bls12381.PrivateKey(decryptedBlsKey)) }) t.Run("decrypt bls key with wrong password", func(t *testing.T) { diff --git a/go.mod b/go.mod index e63d5c966..e3efba2b1 100644 --- a/go.mod +++ b/go.mod @@ -237,7 +237,7 @@ require ( 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.4.1 + 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 f699c7a91..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= @@ -1139,8 +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-wallet-encryptor-keystorev4 v1.4.1 h1:9j7bpwjT9wmwBb54ZkBhTm1uNIlFFcCJXefd/YskZPw= -github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.4.1/go.mod h1:+tI1VD76E1WINI+Nstg7RVGpUolL5ql10nu2YztMO/4= +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= @@ -1215,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= @@ -1461,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..71118fe25 --- /dev/null +++ b/privval/bls.go @@ -0,0 +1,166 @@ +package privval + +import ( + "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/go-bip39" +) + +const DefaultBlsKeyName = "bls_key.json" + +type BlsPV struct { + Key BlsPVKey +} + +type BlsPVKey struct { + PubKey bls12381.PublicKey + PrivKey bls12381.PrivateKey + filePath string +} + +// todo: where +func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath string) *BlsPV { + return &BlsPV{ + Key: BlsPVKey{ + PubKey: privKey.PubKey(), + PrivKey: privKey, + filePath: keyFilePath, + }, + } +} + +func GenBlsPV(keyFilePath string) *BlsPV { + return NewBlsPV(bls12381.GenPrivKey(), keyFilePath) +} + +func LoadBlsPV(keyFilePath, password string) *BlsPV { + + keyJSONBytes, err := os.ReadFile(keyFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS file: %v", err.Error())) + } + + // decrypt bls key from erc2335 type of structure + privKey, err := erc2335.DecryptBLS(keyJSONBytes, 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, + filePath: keyFilePath, + }, + } +} + +func LoadOrGenBlsPV(keyFilePath, password string) *BlsPV { + + var pv *BlsPV + if cmtos.FileExists(keyFilePath) { + pv = LoadBlsPV(keyFilePath, password) + } else { + pv = GenBlsPV(keyFilePath) + pv.Save(password) + } + return pv +} + +func (pv *BlsPV) Save(password string) { + pv.Key.Save(password) +} + +func (k *BlsPVKey) Save(password string) { + outFile := k.filePath + if outFile == "" { + panic("cannot save PrivValidator BLS key: filePath not set") + } + + // encrypt the bls12381 key to erc2335 type + erc2335BlsPvKey, err := erc2335.EncryptBLS(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) + } + + jsonBytes, err := json.MarshalIndent(keystore, "", " ") + if err != nil { + panic(err) + } + + if err := tempfile.WriteFileAtomic(outFile, jsonBytes, 0600); err != nil { + panic(err) + } +} + +func InitializeNodeValidatorBlsFiles(config *BlsConfig, password string) (blsPubKey []byte, err error) { + return InitializeNodeValidatorBlsFilesFromMnemonic(config, "", password) +} + +func InitializeNodeValidatorBlsFilesFromMnemonic(config *BlsConfig, mnemonic, password string) (blsPubKey []byte, err error) { + if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { + return nil, fmt.Errorf("invalid mnemonic") + } + + blsKeyFile := config.BlsKeyFile() + if err := cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777); err != nil { + return nil, fmt.Errorf("could not create directory %q: %w", filepath.Dir(blsKeyFile), err) + } + + var blsPv *BlsPV + if len(mnemonic) == 0 { + blsPv = LoadOrGenBlsPV(blsKeyFile, password) + } else { + privKey := bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) + blsPv = NewBlsPV(privKey, blsKeyFile) + blsPv.Save(password) + } + + return blsPv.Key.PubKey.Bytes(), nil +} + +// ------------------------------------------------------------------------------- +// ---------------------------- BLS Config --------------------------------------- +// ------------------------------------------------------------------------------- + +type BlsConfig struct { + RootDir string `mapstructure:"home"` + BlsKeyPath string `mapstructure:"bls_key_file"` +} + +func DefaultBlsConfig() BlsConfig { + return BlsConfig{ + RootDir: cmtcfg.DefaultDataDir, + BlsKeyPath: filepath.Join(cmtcfg.DefaultDataDir, DefaultBlsKeyName), + } +} + +func SetBlsConfig(rootDir, blsKeyPath string) BlsConfig { + return BlsConfig{ + RootDir: rootDir, + BlsKeyPath: blsKeyPath, + } +} + +func (cfg BlsConfig) BlsKeyFile() string { + if filepath.IsAbs(cfg.BlsKeyPath) { + return cfg.BlsKeyPath + } + return filepath.Join(cfg.RootDir, cfg.BlsKeyPath) +} diff --git a/privval/bls_test.go b/privval/bls_test.go new file mode 100644 index 000000000..b86bedade --- /dev/null +++ b/privval/bls_test.go @@ -0,0 +1,54 @@ +package privval + +import ( + "os" + "path/filepath" + "testing" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" + cmtcfg "github.com/cometbft/cometbft/config" + "github.com/test-go/testify/assert" +) + +func TestNewBlsPV(t *testing.T) { + pv := NewBlsPV(bls12381.GenPrivKey(), "test") + assert.NotNil(t, pv) +} + +func TestCleanUp(t *testing.T) { + blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() + t.Log("bls key file path", blsKeyFilePath) + cleanup(blsKeyFilePath) +} + +func TestLoadOrGenBlsPV(t *testing.T) { + + t.Run("clean file path", func(t *testing.T) { + blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() + t.Log("bls key file path", blsKeyFilePath) + cleanup(blsKeyFilePath) + }) + + t.Run("set default config", func(t *testing.T) { + blsCfg := DefaultBlsConfig() + assert.NotNil(t, blsCfg) + assert.Equal(t, blsCfg.RootDir, cmtcfg.DefaultDataDir) + assert.Equal(t, blsCfg.BlsKeyPath, filepath.Join(cmtcfg.DefaultDataDir, DefaultBlsKeyName)) + + t.Run("generate key without mnemonic", func(t *testing.T) { + blsPubKey, err := InitializeNodeValidatorBlsFiles(&blsCfg, "password") + assert.NoError(t, err) + assert.NotNil(t, blsPubKey) + }) + + t.Run("load key with password", func(t *testing.T) { + blsPubKey, err := InitializeNodeValidatorBlsFiles(&blsCfg, "password") + assert.NoError(t, err) + assert.NotNil(t, blsPubKey) + }) + }) +} + +func cleanup(blsKeyPath string) { + _ = os.RemoveAll(filepath.Dir(blsKeyPath)) +} diff --git a/privval/file.go b/privval/file.go index ebfac1314..6b11e9028 100644 --- a/privval/file.go +++ b/privval/file.go @@ -13,7 +13,6 @@ import ( "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" @@ -47,52 +46,40 @@ func voteToStep(vote *cmtproto.Vote) int8 { // 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 + // wonjoon/feat: separate cometPvKey and blsPvKey + CometPVKey privval.FilePVKey + BlsPVKey BlsPVKey + + // wonjoon/todo: remove + DelegatorAddress string `json:"acc_address"` } -// Save persists the FilePVKey to its filePath. +// wonjoon/todo: remove or refactoring 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) - } + pvKey.CometPVKey.Save() + pvKey.BlsPVKey.Save("") } -// ------------------------------------------------------------------------------- - -// 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) +// wonjoon/todo: refactoring +func NewWrappedFilePV( + cometPrivKey cmtcrypto.PrivKey, + blsPrivKey bls12381.PrivateKey, + cometKeyFilePath, cometStateFilePath string, + // blsKeyFilePath string, +) *WrappedFilePV { + filePV := privval.NewFilePV(cometPrivKey, cometKeyFilePath, cometStateFilePath) return &WrappedFilePV{ Key: WrappedFilePVKey{ - Address: privKey.PubKey().Address(), - PubKey: privKey.PubKey(), - PrivKey: privKey, - BlsPubKey: blsPrivKey.PubKey(), - BlsPrivKey: blsPrivKey, - filePath: keyFilePath, + CometPVKey: filePV.Key, + BlsPVKey: NewBlsPV( + blsPrivKey, + DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, + ).Key, }, LastSignState: filePV.LastSignState, } @@ -100,88 +87,110 @@ func NewWrappedFilePV(privKey cmtcrypto.PrivKey, blsPrivKey bls12381.PrivateKey, // 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) +// wonjoon/todo: refactoring +func GenWrappedFilePV( + cometKeyFilePath, cometStateFilePath string, + //blsKeyFilePath string, +) *WrappedFilePV { + return NewWrappedFilePV( + ed25519.GenPrivKey(), + bls12381.GenPrivKey(), + cometKeyFilePath, + cometStateFilePath, + //blsKeyFilePath, + ) } // 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) +// wonjoon/todo: refactoring +func LoadWrappedFilePV( + cometKeyFilePath, cometStateFilePath string, + // blsKeyFilePath, blsPassword string +) *WrappedFilePV { + return loadWrappedFilePV( + cometKeyFilePath, + cometStateFilePath, + DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, + "", // blsPassword, + 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) +// wonjoon/todo: refactoring +func LoadWrappedFilePVEmptyState( + cometKeyFilePath, cometStateFilePath string, + // blsKeyFilePath, blsPassword string +) *WrappedFilePV { + return loadWrappedFilePV( + cometKeyFilePath, + cometStateFilePath, + DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, + "", // blsPassword, + 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{} +func loadWrappedFilePV(cometKeyFilePath, cometStateFilePath, blsKeyFilePath, blsPassword string, loadState bool) *WrappedFilePV { + // comet + var cometPv *privval.FilePV 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)) - } + cometPv = privval.LoadFilePV(cometKeyFilePath, cometStateFilePath) + } else { + cometPv = privval.LoadFilePVEmptyState(cometKeyFilePath, cometStateFilePath) } - // adding path is not needed - // pvState.filePath = stateFilePath + // bls + blsPv := LoadBlsPV(blsKeyFilePath, blsPassword) return &WrappedFilePV{ - Key: pvKey, - LastSignState: pvState, + Key: WrappedFilePVKey{ + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, + }, + LastSignState: cometPv.LastSignState, } } // 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() +// wonjoon/todo: refactoring +func LoadOrGenWrappedFilePV( + cometKeyFilePath, cometStateFilePath string, + // blsKeyFilePath, blsPassword string, +) *WrappedFilePV { + cometPv := privval.LoadOrGenFilePV(cometKeyFilePath, cometStateFilePath) + blsPv := LoadOrGenBlsPV( + DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, + "", // blsPassword, + ) + return &WrappedFilePV{ + Key: WrappedFilePVKey{ + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, + }, + LastSignState: cometPv.LastSignState, } - 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) { + // file check if !cmtos.FileExists(filePath) { return outputFileName, errors.New("export file path does not exist") } + // ---- Should be removed ---// valAddress := pv.GetAddress() if valAddress.Empty() { return outputFileName, errors.New("validator address should not be empty") } + //-------------------------// validatorKey, err := NewValidatorKeys(pv.GetValPrivKey(), pv.GetBlsPrivKey()) if err != nil { @@ -229,15 +238,15 @@ func (pv *WrappedFilePV) SetAccAddress(addr sdk.AccAddress) { // 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 } func (pv *WrappedFilePV) GetValPrivKey() cmtcrypto.PrivKey { - return pv.Key.PrivKey + return pv.Key.CometPVKey.PrivKey } func (pv *WrappedFilePV) GetBlsPrivKey() bls12381.PrivateKey { - return pv.Key.BlsPrivKey + return pv.Key.BlsPVKey.PrivKey } func (pv *WrappedFilePV) SignMsgWithBls(msg []byte) (bls12381.Signature, error) { @@ -260,12 +269,15 @@ func (pv *WrappedFilePV) GetValidatorPubkey() (cmtcrypto.PubKey, error) { return pv.GetPubKey() } +// ---- Should be removed ---//₩ // 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() { diff --git a/test/e2e/initialization/config.go b/test/e2e/initialization/config.go index c1f4da6f7..e61985e7c 100644 --- a/test/e2e/initialization/config.go +++ b/test/e2e/initialization/config.go @@ -404,13 +404,13 @@ 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") @@ -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..0c710c46e 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -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 } 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/helper/helper.go b/testutil/helper/helper.go index 46ba8d9dd..dd6eab288 100644 --- a/testutil/helper/helper.go +++ b/testutil/helper/helper.go @@ -59,7 +59,7 @@ 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 valSet.Keys[0].ValidatorAddress = privSigner.WrappedPV.GetAddress().String() @@ -97,7 +97,7 @@ 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 // set a random validator address instead of the privSigner's 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 From 255d25c8f663b28d9ca13d278e5d0948eacd8e6c Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 20:11:27 +0900 Subject: [PATCH 3/8] fix: get bls password from prompt and create bls structure in create-bls-key --- app/signer/private.go | 30 ++++- client/flags/flags.go | 5 + cmd/babylond/cmd/create_bls_key.go | 38 +++--- cmd/babylond/cmd/init.go | 195 +++++++++++++++++++++++++++++ cmd/babylond/cmd/init_test.go | 21 ++++ cmd/babylond/cmd/root.go | 3 +- crypto/erc2335/erc2335.go | 25 +++- crypto/erc2335/erc2335_test.go | 65 ++++++++-- privval/bls.go | 104 ++++++++++++--- privval/bls_test.go | 57 +++++++-- 10 files changed, 479 insertions(+), 64 deletions(-) create mode 100644 client/flags/flags.go create mode 100644 cmd/babylond/cmd/init.go create mode 100644 cmd/babylond/cmd/init_test.go diff --git a/app/signer/private.go b/app/signer/private.go index 8fcd1f217..46061677e 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -7,6 +7,7 @@ import ( cmtos "github.com/cometbft/cometbft/libs/os" "github.com/babylonlabs-io/babylon/privval" + cmtprivval "github.com/cometbft/cometbft/privval" ) type PrivSigner struct { @@ -14,6 +15,8 @@ type PrivSigner struct { } func InitPrivSigner(nodeDir string) (*PrivSigner, error) { + + // cometPv nodeCfg := cmtconfig.DefaultConfig() pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) @@ -25,9 +28,32 @@ func InitPrivSigner(nodeDir string) (*PrivSigner, error) { if err != nil { return nil, err } - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + cometPv := cmtprivval.LoadOrGenFilePV(pvKeyFile, pvStateFile) + + // blsPv + blsCfg := privval.DefaultBlsConfig() + blsCfg.RootDir = nodeCfg.RootDir + blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) + err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) + if err != nil { + return nil, err + } + blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) + if err != nil { + return nil, err + } + + password := privval.LoadOrGenBlsPassword(blsPasswordFile) + blsPv := privval.LoadOrGenBlsPV(blsKeyFile, password) return &PrivSigner{ - WrappedPV: wrappedPV, + WrappedPV: &privval.WrappedFilePV{ + Key: privval.WrappedFilePVKey{ + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, + }, + LastSignState: cometPv.LastSignState, + }, }, nil } 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..46e747919 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -1,21 +1,19 @@ 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" + cmtprivval "github.com/cometbft/cometbft/privval" ) func CreateBlsKeyCmd() *cobra.Command { @@ -57,28 +55,28 @@ $ babylond create-bls-key %s1f5tnl46mk4dfp4nx3n2vnrvyw2h2ydz6ykhk3r --home ./ } func CreateBlsKey(home string, addr sdk.AccAddress) error { + // comet 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) + // bls + blsCfg := privval.DefaultBlsConfig() + blsKeyFilePath := filepath.Join(home, blsCfg.BlsKeyFile()) + blsPasswordPath := filepath.Join(home, blsCfg.BlsPasswordFile()) - return nil -} + cometPv := cmtprivval.LoadOrGenFilePV(keyPath, statePath) + blsPv := privval.LoadOrGenBlsPV(blsKeyFilePath, blsPasswordPath) -// 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") + // wrappedFilePv + wrappedFilePv := privval.WrappedFilePV{ + Key: privval.WrappedFilePVKey{ + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, + }, + LastSignState: cometPv.LastSignState, } - return privval.LoadWrappedFilePV(keyPath, statePath), nil + wrappedFilePv.SetAccAddress(addr) + + return nil } diff --git a/cmd/babylond/cmd/init.go b/cmd/babylond/cmd/init.go new file mode 100644 index 000000000..4d9ef56c5 --- /dev/null +++ b/cmd/babylond/cmd/init.go @@ -0,0 +1,195 @@ +package cmd + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "cosmossdk.io/math/unsafe" + "github.com/babylonlabs-io/babylon/privval" + "github.com/spf13/cobra" + + errorsmod "cosmossdk.io/errors" + cfg "github.com/cometbft/cometbft/config" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/input" + "github.com/cosmos/cosmos-sdk/server" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/x/genutil" + cmtcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/go-bip39" +) + +// wonjoon +// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" +func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "init [moniker]", + Short: "Initialize private validator, p2p, genesis, and application configuration files", + Long: `Initialize validators's and node's configuration files.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx := client.GetClientContextFromCmd(cmd) + cdc := clientCtx.Codec + + serverCtx := server.GetServerContextFromCmd(cmd) + config := serverCtx.Config + config.SetRoot(clientCtx.HomeDir) + + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) + switch { + case chainID != "": + case clientCtx.ChainID != "": + chainID = clientCtx.ChainID + default: + chainID = fmt.Sprintf("test-chain-%v", unsafe.Str(6)) + } + + // Get bip39 mnemonic + var mnemonic string + recover, _ := cmd.Flags().GetBool(cmtcli.FlagRecover) + if recover { + inBuf := bufio.NewReader(cmd.InOrStdin()) + value, err := input.GetString("Enter your bip39 mnemonic", inBuf) + if err != nil { + return err + } + + mnemonic = value + if !bip39.IsMnemonicValid(mnemonic) { + return errors.New("invalid mnemonic") + } + } + + // wonjoon: make config for bls + blsCfg := privval.BlsConfig{ + RootDir: config.RootDir, + BlsKeyPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), + BlsPasswordPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), + } + + // Get BLS password + blsPassword := privval.LoadOrGenBlsPassword(blsCfg.BlsPasswordFile()) + + // Get initial height + initHeight, _ := cmd.Flags().GetInt64(flags.FlagInitHeight) + if initHeight < 1 { + initHeight = 1 + } + + // InitializeNodeValidatorFilesFromMnemonic from cosmos-sdk/x/genutil/utils.go + nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) + if err != nil { + return err + } + + // InitializeNodeValidatorBlsFilesFromMnemonic from babylon/x/privval/bls.go + _, err = privval.InitializeNodeValidatorBlsFilesFromMnemonic(&blsCfg, mnemonic, blsPassword) + if err != nil { + return err + } + + config.Moniker = args[0] + + genFile := config.GenesisFile() + overwrite, _ := cmd.Flags().GetBool(cmtcli.FlagOverwrite) + defaultDenom, _ := cmd.Flags().GetString(cmtcli.FlagDefaultBondDenom) + + // use os.Stat to check if the file exists + _, err = os.Stat(genFile) + if !overwrite && !os.IsNotExist(err) { + return fmt.Errorf("genesis.json file already exists: %v", genFile) + } + + // Overwrites the SDK default denom for side-effects + if defaultDenom != "" { + sdk.DefaultBondDenom = defaultDenom + } + appGenState := mbm.DefaultGenesis(cdc) + + appState, err := json.MarshalIndent(appGenState, "", " ") + if err != nil { + return errorsmod.Wrap(err, "Failed to marshal default genesis state") + } + + appGenesis := &types.AppGenesis{} + if _, err := os.Stat(genFile); err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + appGenesis, err = types.AppGenesisFromFile(genFile) + if err != nil { + return errorsmod.Wrap(err, "Failed to read genesis doc from file") + } + } + + appGenesis.AppName = version.AppName + appGenesis.AppVersion = version.Version + appGenesis.ChainID = chainID + appGenesis.AppState = appState + appGenesis.InitialHeight = initHeight + appGenesis.Consensus = &types.ConsensusGenesis{ + Validators: nil, + } + + if err = genutil.ExportGenesisFile(appGenesis, genFile); err != nil { + return errorsmod.Wrap(err, "Failed to export genesis file") + } + + toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) + + cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) + return displayInfo(toPrint) + }, + } + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "node's home directory") + cmd.Flags().BoolP(cmtcli.FlagOverwrite, "o", false, "overwrite the genesis.json file") + cmd.Flags().Bool(cmtcli.FlagRecover, false, "provide seed phrase to recover existing key instead of creating") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(cmtcli.FlagDefaultBondDenom, "", "genesis file default denomination, if left blank default value is 'stake'") + cmd.Flags().Int64(flags.FlagInitHeight, 1, "specify the initial block height at genesis") + cmd.Flags().String("bls-password", "", "BLS key password (if not provided, will prompt for input)") + + return cmd +} + +// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" +type printInfo struct { + Moniker string `json:"moniker" yaml:"moniker"` + ChainID string `json:"chain_id" yaml:"chain_id"` + NodeID string `json:"node_id" yaml:"node_id"` + GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"` + AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` +} + +// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" +func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { + return printInfo{ + Moniker: moniker, + ChainID: chainID, + NodeID: nodeID, + GenTxsDir: genTxsDir, + AppMessage: appMessage, + } +} + +// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" +func displayInfo(info printInfo) error { + out, err := json.MarshalIndent(info, "", " ") + if err != nil { + return err + } + + _, err = fmt.Fprintf(os.Stderr, "%s\n", out) + + return err +} diff --git a/cmd/babylond/cmd/init_test.go b/cmd/babylond/cmd/init_test.go new file mode 100644 index 000000000..1f7441912 --- /dev/null +++ b/cmd/babylond/cmd/init_test.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "path/filepath" + "testing" + + "github.com/babylonlabs-io/babylon/privval" + cfg "github.com/cometbft/cometbft/config" +) + +func TestSaveFiles(t *testing.T) { + + // clientCtx := client.GetClientContextFromCmd(&cobra.Command{}) + homeDir := "test" + blsCfg := privval.BlsConfig{ + RootDir: homeDir, + BlsKeyPath: filepath.Join(homeDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), + BlsPasswordPath: filepath.Join(homeDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), + } + t.Log(blsCfg) +} diff --git a/cmd/babylond/cmd/root.go b/cmd/babylond/cmd/root.go index 7687ab268..cf78b1beb 100644 --- a/cmd/babylond/cmd/root.go +++ b/cmd/babylond/cmd/root.go @@ -167,7 +167,8 @@ func initRootCmd(rootCmd *cobra.Command, txConfig client.TxEncodingConfig, basic gentxModule := basicManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) rootCmd.AddCommand( - genutilcli.InitCmd(basicManager, app.DefaultNodeHome), + // genutilcli.InitCmd(basicManager, app.DefaultNodeHome), + InitCmd(basicManager, app.DefaultNodeHome), genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, gentxModule.GenTxValidator, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), genutilcli.MigrateGenesisCmd(genutilcli.MigrationMap), genutilcli.GenTxCmd(basicManager, txConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go index 9559e4970..4c78aa3ce 100644 --- a/crypto/erc2335/erc2335.go +++ b/crypto/erc2335/erc2335.go @@ -1,8 +1,11 @@ package erc2335 import ( + "crypto/rand" + "encoding/hex" "encoding/json" "fmt" + "os" "github.com/pkg/errors" keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" @@ -18,7 +21,7 @@ type Erc2335KeyStore struct { // wonjoon: encrypt key pair to erc2335 keystore // available to handle all keys in []byte format -func EncryptBLS(privKey, pubKey []byte, password string) ([]byte, error) { +func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { if privKey == nil { return nil, errors.New("private key cannot be nil") } @@ -40,7 +43,7 @@ func EncryptBLS(privKey, pubKey []byte, password string) ([]byte, error) { } // decrypt private key from erc2335 keystore -func DecryptBLS(keystoreJSON []byte, password string) ([]byte, error) { +func Decrypt(keystoreJSON []byte, password string) ([]byte, error) { // Parse the keystore json var keystore Erc2335KeyStore @@ -56,3 +59,21 @@ func DecryptBLS(keystoreJSON []byte, 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 index e6ccc1702..c6c51d4b3 100644 --- a/crypto/erc2335/erc2335_test.go +++ b/crypto/erc2335/erc2335_test.go @@ -1,6 +1,7 @@ package erc2335 import ( + "os" "testing" "github.com/babylonlabs-io/babylon/crypto/bls12381" @@ -10,29 +11,77 @@ import ( func TestEncryptBLS(t *testing.T) { // TODO t.Run("create bls key", func(t *testing.T) { - // TODO + blsPrivKey := bls12381.GenPrivKey() blsPubKey := blsPrivKey.PubKey().Bytes() - password := "password" t.Run("encrypt bls key", func(t *testing.T) { - // TODO - encryptedBlsKey, err := EncryptBLS(blsPrivKey, blsPubKey, password) + + 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) { - // TODO - decryptedBlsKey, err := DecryptBLS(encryptedBlsKey, password) + + decryptedBlsKey, err := Decrypt(encryptedBlsKey, password) require.NoError(t, err) require.Equal(t, blsPrivKey, bls12381.PrivateKey(decryptedBlsKey)) }) t.Run("decrypt bls key with wrong password", func(t *testing.T) { - // TODO - _, err := DecryptBLS(encryptedBlsKey, "wrong password") + + _, err := Decrypt(encryptedBlsKey, "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) + decryptedBlsKey, err := Decrypt(encryptedBlsKey, 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) + _, err = Decrypt(encryptedBlsKey, 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/privval/bls.go b/privval/bls.go index 71118fe25..a435ab200 100644 --- a/privval/bls.go +++ b/privval/bls.go @@ -1,8 +1,10 @@ package privval import ( + "bufio" "encoding/json" "fmt" + "log" "os" "path/filepath" @@ -11,10 +13,14 @@ import ( 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" "github.com/cosmos/go-bip39" ) -const DefaultBlsKeyName = "bls_key.json" +const ( + DefaultBlsKeyName = "bls_key.json" + DefaultBlsPasswordName = "bls_password.txt" +) type BlsPV struct { Key BlsPVKey @@ -49,7 +55,7 @@ func LoadBlsPV(keyFilePath, password string) *BlsPV { } // decrypt bls key from erc2335 type of structure - privKey, err := erc2335.DecryptBLS(keyJSONBytes, password) + privKey, err := erc2335.Decrypt(keyJSONBytes, password) if err != nil { cmtos.Exit(fmt.Sprintf("failed to decrypt BLS key: %v", err.Error())) } @@ -77,18 +83,59 @@ func LoadOrGenBlsPV(keyFilePath, password string) *BlsPV { return pv } +func LoadOrGenBlsPassword(passwordFilePath string) string { + password, isExist, err := LoadBlsPassword(passwordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) + } + + if !isExist { + newPassword, err := GenBlsPassword() + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to generate BLS password: %v", err.Error())) + } + err = erc2335.SavePasswordToFile(newPassword, passwordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to save BLS password file: %v", err.Error())) + } + log.Print("BLS password saved to ", passwordFilePath) + return newPassword + } + return password +} + +func LoadBlsPassword(passwordFilePath string) (string, bool, error) { + if cmtos.FileExists(passwordFilePath) { + password, err := os.ReadFile(passwordFilePath) + if err != nil { + return "", true, fmt.Errorf("failed to read BLS password file: %v", err.Error()) + } + return string(password), true, nil + } + return "", false, nil +} + +func GenRandomBlsPassword() string { + return erc2335.CreateRandomPassword() +} + +func GenBlsPassword() (string, error) { + inBuf := bufio.NewReader(os.Stdin) + return input.GetString("Enter your bls password", inBuf) +} + func (pv *BlsPV) Save(password string) { pv.Key.Save(password) } -func (k *BlsPVKey) Save(password string) { - outFile := k.filePath +func (pvKey *BlsPVKey) Save(password string) { + outFile := pvKey.filePath if outFile == "" { panic("cannot save PrivValidator BLS key: filePath not set") } // encrypt the bls12381 key to erc2335 type - erc2335BlsPvKey, err := erc2335.EncryptBLS(k.PrivKey, k.PubKey.Bytes(), password) + erc2335BlsPvKey, err := erc2335.Encrypt(pvKey.PrivKey, pvKey.PubKey.Bytes(), password) if err != nil { panic(err) } @@ -109,17 +156,29 @@ func (k *BlsPVKey) Save(password string) { } } -func InitializeNodeValidatorBlsFiles(config *BlsConfig, password string) (blsPubKey []byte, err error) { +// initialize node validator bls key with random password +func InitializeNodeValidatorBlsFiles(config *BlsConfig) (blsPubKey []byte, err error) { + password := erc2335.CreateRandomPassword() return InitializeNodeValidatorBlsFilesFromMnemonic(config, "", password) } +// initialize node validator bls key with specific password +// if you want to generate a random password, +// use 'CreateRandomBlsPassword' to generate a random password and pass it to this function +func InitializeNodeValidatorBlsFilesWithPassword(config *BlsConfig, password string) (blsPubKey []byte, err error) { + return InitializeNodeValidatorBlsFilesFromMnemonic(config, "", password) +} + +// initialize node validator bls key with mnemonic and specific password +// if you want to generate a random password, +// use 'CreateRandomBlsPassword' to generate a random password and pass it to this function func InitializeNodeValidatorBlsFilesFromMnemonic(config *BlsConfig, mnemonic, password string) (blsPubKey []byte, err error) { if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { return nil, fmt.Errorf("invalid mnemonic") } blsKeyFile := config.BlsKeyFile() - if err := cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777); err != nil { + if err := os.MkdirAll(filepath.Dir(blsKeyFile), 0o777); err != nil { return nil, fmt.Errorf("could not create directory %q: %w", filepath.Dir(blsKeyFile), err) } @@ -140,27 +199,32 @@ func InitializeNodeValidatorBlsFilesFromMnemonic(config *BlsConfig, mnemonic, pa // ------------------------------------------------------------------------------- type BlsConfig struct { - RootDir string `mapstructure:"home"` - BlsKeyPath string `mapstructure:"bls_key_file"` + RootDir string `mapstructure:"home"` + BlsKeyPath string `mapstructure:"bls_key_file"` + BlsPasswordPath string `mapstructure:"bls_password_file"` } func DefaultBlsConfig() BlsConfig { return BlsConfig{ - RootDir: cmtcfg.DefaultDataDir, - BlsKeyPath: filepath.Join(cmtcfg.DefaultDataDir, DefaultBlsKeyName), + RootDir: cmtcfg.DefaultConfigDir, + BlsKeyPath: filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName), + BlsPasswordPath: filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsPasswordName), } } -func SetBlsConfig(rootDir, blsKeyPath string) BlsConfig { - return BlsConfig{ - RootDir: rootDir, - BlsKeyPath: blsKeyPath, - } +func (cfg BlsConfig) BlsKeyFile() string { + return rootify(cfg.BlsKeyPath, cfg.RootDir) } -func (cfg BlsConfig) BlsKeyFile() string { - if filepath.IsAbs(cfg.BlsKeyPath) { - return cfg.BlsKeyPath +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(cfg.RootDir, cfg.BlsKeyPath) + return filepath.Join(root, path) } diff --git a/privval/bls_test.go b/privval/bls_test.go index b86bedade..302d1562f 100644 --- a/privval/bls_test.go +++ b/privval/bls_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/babylonlabs-io/babylon/crypto/erc2335" cmtcfg "github.com/cometbft/cometbft/config" "github.com/test-go/testify/assert" ) @@ -21,31 +22,65 @@ func TestCleanUp(t *testing.T) { cleanup(blsKeyFilePath) } -func TestLoadOrGenBlsPV(t *testing.T) { - - t.Run("clean file path", func(t *testing.T) { - blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() - t.Log("bls key file path", blsKeyFilePath) - cleanup(blsKeyFilePath) - }) +func TestInitializeNodeValidatorBlsFiles(t *testing.T) { t.Run("set default config", func(t *testing.T) { blsCfg := DefaultBlsConfig() assert.NotNil(t, blsCfg) - assert.Equal(t, blsCfg.RootDir, cmtcfg.DefaultDataDir) - assert.Equal(t, blsCfg.BlsKeyPath, filepath.Join(cmtcfg.DefaultDataDir, DefaultBlsKeyName)) + assert.Equal(t, blsCfg.RootDir, cmtcfg.DefaultConfigDir) + assert.Equal(t, blsCfg.BlsKeyPath, filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName)) + + password := erc2335.CreateRandomPassword() + t.Log("password", password) t.Run("generate key without mnemonic", func(t *testing.T) { - blsPubKey, err := InitializeNodeValidatorBlsFiles(&blsCfg, "password") + blsPubKey, err := InitializeNodeValidatorBlsFilesWithPassword(&blsCfg, password) assert.NoError(t, err) assert.NotNil(t, blsPubKey) }) t.Run("load key with password", func(t *testing.T) { - blsPubKey, err := InitializeNodeValidatorBlsFiles(&blsCfg, "password") + blsPubKey, err := InitializeNodeValidatorBlsFilesWithPassword(&blsCfg, password) assert.NoError(t, err) assert.NotNil(t, blsPubKey) }) + + t.Run("clean file path", func(t *testing.T) { + blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() + t.Log("bls key file path", blsKeyFilePath) + cleanup(blsKeyFilePath) + }) + }) +} + +func TestSavePasswordToFile(t *testing.T) { + + blsCfg := DefaultBlsConfig() + + t.Run("failed to load unsaved file", func(t *testing.T) { + _, err := erc2335.LoadPaswordFromFile(blsCfg.BlsPasswordFile()) + assert.Error(t, err) + }) + + t.Run("create password file", func(t *testing.T) { + password := erc2335.CreateRandomPassword() + t.Log("password", password) + + err := erc2335.SavePasswordToFile(password, blsCfg.BlsPasswordFile()) + assert.NoError(t, err) + + t.Run("load password file", func(t *testing.T) { + + loadPassword, err := erc2335.LoadPaswordFromFile(blsCfg.BlsPasswordFile()) + assert.NoError(t, err) + assert.Equal(t, password, loadPassword) + }) + }) + + t.Run("clean file path", func(t *testing.T) { + blsPasswordFilePath := DefaultBlsConfig().BlsPasswordFile() + t.Log("bls passwordd file path", blsPasswordFilePath) + cleanup(blsPasswordFilePath) }) } From 6138f622a88b6fe2a55f85479186abade26c4d06 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 20:13:00 +0900 Subject: [PATCH 4/8] fix: move delegator address to bls structure and save to bls_key.json in erc2335 description --- app/signer/private.go | 66 +++- cmd/babylond/cmd/create_bls_key.go | 45 ++- cmd/babylond/cmd/genhelpers/bls_add_test.go | 9 +- cmd/babylond/cmd/genhelpers/bls_create.go | 13 +- .../cmd/genhelpers/bls_create_test.go | 8 +- cmd/babylond/cmd/init.go | 32 +- crypto/erc2335/erc2335.go | 11 +- privval/bls.go | 187 +++++------- privval/bls_test.go | 21 +- privval/file.go | 285 +++++++++--------- privval/file_test.go | 74 +++++ test/e2e/initialization/node.go | 25 +- testutil/datagen/init_val.go | 12 +- testutil/signer/private.go | 2 +- x/checkpointing/client/cli/tx_test.go | 11 +- x/checkpointing/client/cli/utils.go | 9 +- 16 files changed, 471 insertions(+), 339 deletions(-) create mode 100644 privval/file_test.go diff --git a/app/signer/private.go b/app/signer/private.go index 46061677e..22012963c 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -1,6 +1,7 @@ package signer import ( + "fmt" "path/filepath" cmtconfig "github.com/cometbft/cometbft/config" @@ -15,8 +16,56 @@ 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 + } + pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) + err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) + if err != nil { + return nil, err + } + + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(nodeCfg.RootDir) + blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) + err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) + if err != nil { + return nil, err + } + blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) + if err != nil { + return nil, err + } + + if !cmtos.FileExists(blsKeyFile) { + return nil, fmt.Errorf("BLS key file does not exist: %v", blsKeyFile) + } + + if !cmtos.FileExists(blsPasswordFile) { + return nil, fmt.Errorf("BLS password file does not exist: %v", blsPasswordFile) + } + + blsPv := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) + cmtPv := cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) + wrappedPvKey := privval.WrappedFilePVKey{ + CometPVKey: cmtPv.Key, + BlsPVKey: blsPv.Key, + } + wrappedPV := &privval.WrappedFilePV{ + Key: wrappedPvKey, + LastSignState: cmtPv.LastSignState, + } + + return &PrivSigner{ + WrappedPV: wrappedPV, + }, nil +} - // cometPv +func InitTestPrivSigner(nodeDir string) (*PrivSigner, error) { nodeCfg := cmtconfig.DefaultConfig() pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) @@ -28,11 +77,9 @@ func InitPrivSigner(nodeDir string) (*PrivSigner, error) { if err != nil { return nil, err } - cometPv := cmtprivval.LoadOrGenFilePV(pvKeyFile, pvStateFile) - // blsPv blsCfg := privval.DefaultBlsConfig() - blsCfg.RootDir = nodeCfg.RootDir + blsCfg.SetRoot(nodeCfg.RootDir) blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) if err != nil { @@ -44,16 +91,9 @@ func InitPrivSigner(nodeDir string) (*PrivSigner, error) { return nil, err } - password := privval.LoadOrGenBlsPassword(blsPasswordFile) - blsPv := privval.LoadOrGenBlsPV(blsKeyFile, password) + wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) return &PrivSigner{ - WrappedPV: &privval.WrappedFilePV{ - Key: privval.WrappedFilePVKey{ - CometPVKey: cometPv.Key, - BlsPVKey: blsPv.Key, - }, - LastSignState: cometPv.LastSignState, - }, + WrappedPV: wrappedPV, }, nil } diff --git a/cmd/babylond/cmd/create_bls_key.go b/cmd/babylond/cmd/create_bls_key.go index 46e747919..ac500cf8a 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -1,17 +1,24 @@ package cmd import ( + "bufio" "fmt" + "log" + "os" "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/cosmos/cosmos-sdk/client/input" 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/crypto/erc2335" "github.com/babylonlabs-io/babylon/privval" cmtprivval "github.com/cometbft/cometbft/privval" ) @@ -55,28 +62,42 @@ $ babylond create-bls-key %s1f5tnl46mk4dfp4nx3n2vnrvyw2h2ydz6ykhk3r --home ./ } func CreateBlsKey(home string, addr sdk.AccAddress) error { - // comet nodeCfg := cmtconfig.DefaultConfig() keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) + cmtPv := cmtprivval.LoadFilePV(keyPath, statePath) - // bls blsCfg := privval.DefaultBlsConfig() - blsKeyFilePath := filepath.Join(home, blsCfg.BlsKeyFile()) - blsPasswordPath := filepath.Join(home, blsCfg.BlsPasswordFile()) - - cometPv := cmtprivval.LoadOrGenFilePV(keyPath, statePath) - blsPv := privval.LoadOrGenBlsPV(blsKeyFilePath, blsPasswordPath) + blsKeyPath := filepath.Join(home, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) + + var blsPassword string + var err error + if !cmtos.FileExists(blsPasswordFile) { + log.Printf("BLS password file don't exists in file: %v", blsPasswordFile) + inBuf := bufio.NewReader(os.Stdin) + blsPassword, err = input.GetString("Enter your bls password", inBuf) + if err != nil { + return err + } + err = erc2335.SavePasswordToFile(blsPassword, blsPasswordFile) + if err != nil { + return err + } + } + blsPv := privval.NewBlsPV(bls12381.GenPrivKey(), blsKeyPath, blsPasswordFile) + blsPv.Key.Save(blsPassword) - // wrappedFilePv - wrappedFilePv := privval.WrappedFilePV{ + wrappedPv := privval.WrappedFilePV{ Key: privval.WrappedFilePVKey{ - CometPVKey: cometPv.Key, + CometPVKey: cmtPv.Key, BlsPVKey: blsPv.Key, }, - LastSignState: cometPv.LastSignState, + LastSignState: cmtPv.LastSignState, } - wrappedFilePv.SetAccAddress(addr) + wrappedPv.SetAccAddress(addr) + log.Printf("Saved delegator address: %s", addr.String()) + log.Printf("Saved delegator address in wrapperPv: %s", wrappedPv.Key.DelegatorAddress) return nil } diff --git a/cmd/babylond/cmd/genhelpers/bls_add_test.go b/cmd/babylond/cmd/genhelpers/bls_add_test.go index 52e50dfcc..817e77937 100644 --- a/cmd/babylond/cmd/genhelpers/bls_add_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_add_test.go @@ -152,7 +152,12 @@ func Test_CmdAddBlsWithGentx(t *testing.T) { nodeCfg.SetRoot(homeDir) keyPath := nodeCfg.PrivValidatorKeyFile() statePath := nodeCfg.PrivValidatorStateFile() - filePV := privval.GenWrappedFilePV(keyPath, statePath) + + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(homeDir) + blsKeyFile := blsCfg.BlsKeyFile() + blsPasswordFile := blsCfg.BlsPasswordFile() + filePV := privval.GenWrappedFilePV(keyPath, statePath, blsKeyFile, blsPasswordFile) filePV.SetAccAddress(v.Address) _, err = cli.ExecTestCLICmd(v.ClientCtx, genBlsCmd, []string{fmt.Sprintf("--%s=%s", flags.FlagHome, homeDir)}) require.NoError(t, err) @@ -175,6 +180,6 @@ func Test_CmdAddBlsWithGentx(t *testing.T) { require.NotEmpty(t, checkpointingGenState.GenesisKeys) gks := checkpointingGenState.GetGenesisKeys() require.Equal(t, genKey, gks[i]) - filePV.Clean(keyPath, statePath) + filePV.Clean(keyPath, statePath, blsKeyFile, blsPasswordFile) } } diff --git a/cmd/babylond/cmd/genhelpers/bls_create.go b/cmd/babylond/cmd/genhelpers/bls_create.go index ac1ab7882..5c20b8f65 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create.go +++ b/cmd/babylond/cmd/genhelpers/bls_create.go @@ -1,12 +1,11 @@ package genhelpers import ( - "errors" + "log" "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" @@ -35,13 +34,15 @@ $ 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()) + + wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath, blsKeyPath, blsPasswordPath) - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) + log.Printf("Loaded delegator address in wrapperPv: %s", wrappedPV.Key.DelegatorAddress) 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 c93d38de9..58c29176d 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_create_test.go @@ -72,8 +72,12 @@ func Test_CmdCreateBls(t *testing.T) { 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) + + blsCfg := privval.DefaultBlsConfig() + blsKeyFile := filepath.Join(home, blsCfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) + filePV := privval.GenWrappedFilePV(keyPath, statePath, blsKeyFile, blsPasswordFile) + defer filePV.Clean(keyPath, statePath, blsKeyFile, blsPasswordFile) filePV.SetAccAddress(addr) // execute the gen-bls cmd diff --git a/cmd/babylond/cmd/init.go b/cmd/babylond/cmd/init.go index 4d9ef56c5..7322d04e9 100644 --- a/cmd/babylond/cmd/init.go +++ b/cmd/babylond/cmd/init.go @@ -68,30 +68,34 @@ func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { } } - // wonjoon: make config for bls - blsCfg := privval.BlsConfig{ - RootDir: config.RootDir, - BlsKeyPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), - BlsPasswordPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), - } - - // Get BLS password - blsPassword := privval.LoadOrGenBlsPassword(blsCfg.BlsPasswordFile()) - // Get initial height initHeight, _ := cmd.Flags().GetInt64(flags.FlagInitHeight) if initHeight < 1 { initHeight = 1 } - // InitializeNodeValidatorFilesFromMnemonic from cosmos-sdk/x/genutil/utils.go - nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) + // Get bls password + inBuf := bufio.NewReader(cmd.InOrStdin()) + blsPassword, err := input.GetString("Enter your bls password", inBuf) if err != nil { return err } - // InitializeNodeValidatorBlsFilesFromMnemonic from babylon/x/privval/bls.go - _, err = privval.InitializeNodeValidatorBlsFilesFromMnemonic(&blsCfg, mnemonic, blsPassword) + // Initialize bls key + blsCfg := privval.BlsConfig{ + RootDir: config.RootDir, + BlsKeyPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), + BlsPasswordPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), + } + + // Initialize BLS key and save to file + _, err = privval.InitializeBlsFileFromMnemonic(&blsCfg, blsPassword, mnemonic) + if err != nil { + return err + } + + // InitializeNodeValidatorFilesFromMnemonic from cosmos-sdk/x/genutil/utils.go + nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) if err != nil { return err } diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go index 4c78aa3ce..673588e46 100644 --- a/crypto/erc2335/erc2335.go +++ b/crypto/erc2335/erc2335.go @@ -12,11 +12,12 @@ import ( ) 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"` + 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 diff --git a/privval/bls.go b/privval/bls.go index a435ab200..05da75790 100644 --- a/privval/bls.go +++ b/privval/bls.go @@ -1,10 +1,8 @@ package privval import ( - "bufio" "encoding/json" "fmt" - "log" "os" "path/filepath" @@ -13,7 +11,6 @@ import ( 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" "github.com/cosmos/go-bip39" ) @@ -27,101 +24,56 @@ type BlsPV struct { } type BlsPVKey struct { - PubKey bls12381.PublicKey - PrivKey bls12381.PrivateKey - filePath string -} + PubKey bls12381.PublicKey `json:"bls_pub_key"` + PrivKey bls12381.PrivateKey `json:"bls_priv_key"` -// todo: where -func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath string) *BlsPV { - return &BlsPV{ - Key: BlsPVKey{ - PubKey: privKey.PubKey(), - PrivKey: privKey, - filePath: keyFilePath, - }, - } + filePath string + passwordPath string } -func GenBlsPV(keyFilePath string) *BlsPV { - return NewBlsPV(bls12381.GenPrivKey(), keyFilePath) +// initialize node validator bls key with password +func InitializeBlsFile(config *BlsConfig, password string) (blsPubKey []byte, err error) { + return InitializeBlsFileFromMnemonic(config, password, "") } -func LoadBlsPV(keyFilePath, password string) *BlsPV { - - keyJSONBytes, err := os.ReadFile(keyFilePath) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to read BLS file: %v", err.Error())) +// initialize node validator bls key with mnemonic and password +func InitializeBlsFileFromMnemonic(config *BlsConfig, password, mnemonic string) (blsPubKey []byte, err error) { + if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { + return nil, fmt.Errorf("invalid mnemonic") } - // decrypt bls key from erc2335 type of structure - privKey, err := erc2335.Decrypt(keyJSONBytes, password) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to decrypt BLS key: %v", err.Error())) + blsKeyFile := config.BlsKeyFile() + if err := os.MkdirAll(filepath.Dir(blsKeyFile), 0o777); err != nil { + return nil, fmt.Errorf("could not create directory for bls key %q: %w", filepath.Dir(blsKeyFile), err) } - blsPrivKey := bls12381.PrivateKey(privKey) - - return &BlsPV{ - Key: BlsPVKey{ - PubKey: blsPrivKey.PubKey(), - PrivKey: blsPrivKey, - filePath: keyFilePath, - }, + blsPasswordFile := config.BlsPasswordFile() + if err := os.MkdirAll(filepath.Dir(blsPasswordFile), 0o777); err != nil { + return nil, fmt.Errorf("could not create directory for bls password %q: %w", filepath.Dir(blsPasswordFile), err) } -} - -func LoadOrGenBlsPV(keyFilePath, password string) *BlsPV { - var pv *BlsPV - if cmtos.FileExists(keyFilePath) { - pv = LoadBlsPV(keyFilePath, password) + // var blsPv *BlsPV + var privKey bls12381.PrivateKey + if len(mnemonic) == 0 { + privKey = bls12381.GenPrivKey() } else { - pv = GenBlsPV(keyFilePath) - pv.Save(password) + privKey = bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) } - return pv -} -func LoadOrGenBlsPassword(passwordFilePath string) string { - password, isExist, err := LoadBlsPassword(passwordFilePath) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) - } - - if !isExist { - newPassword, err := GenBlsPassword() - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to generate BLS password: %v", err.Error())) - } - err = erc2335.SavePasswordToFile(newPassword, passwordFilePath) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to save BLS password file: %v", err.Error())) - } - log.Print("BLS password saved to ", passwordFilePath) - return newPassword - } - return password + blsPv := NewBlsPV(privKey, blsKeyFile, blsPasswordFile) + blsPv.Save(password) + return privKey.PubKey().Bytes(), nil } -func LoadBlsPassword(passwordFilePath string) (string, bool, error) { - if cmtos.FileExists(passwordFilePath) { - password, err := os.ReadFile(passwordFilePath) - if err != nil { - return "", true, fmt.Errorf("failed to read BLS password file: %v", err.Error()) - } - return string(password), true, nil +func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath, passwordFilePath string) *BlsPV { + return &BlsPV{ + Key: BlsPVKey{ + PubKey: privKey.PubKey(), + PrivKey: privKey, + filePath: keyFilePath, + passwordPath: passwordFilePath, + }, } - return "", false, nil -} - -func GenRandomBlsPassword() string { - return erc2335.CreateRandomPassword() -} - -func GenBlsPassword() (string, error) { - inBuf := bufio.NewReader(os.Stdin) - return input.GetString("Enter your bls password", inBuf) } func (pv *BlsPV) Save(password string) { @@ -129,8 +81,20 @@ func (pv *BlsPV) Save(password string) { } func (pvKey *BlsPVKey) Save(password string) { - outFile := pvKey.filePath - if outFile == "" { + + passwordOutFile := pvKey.passwordPath + if passwordOutFile == "" { + panic("cannot save PrivValidator BLS key: password filePath not set") + } + + // save password to file + err := erc2335.SavePasswordToFile(password, passwordOutFile) + if err != nil { + panic(err) + } + + keyOutFile := pvKey.filePath + if keyOutFile == "" { panic("cannot save PrivValidator BLS key: filePath not set") } @@ -151,47 +115,38 @@ func (pvKey *BlsPVKey) Save(password string) { panic(err) } - if err := tempfile.WriteFileAtomic(outFile, jsonBytes, 0600); err != nil { + if err := tempfile.WriteFileAtomic(keyOutFile, jsonBytes, 0600); err != nil { panic(err) } } -// initialize node validator bls key with random password -func InitializeNodeValidatorBlsFiles(config *BlsConfig) (blsPubKey []byte, err error) { - password := erc2335.CreateRandomPassword() - return InitializeNodeValidatorBlsFilesFromMnemonic(config, "", password) -} - -// initialize node validator bls key with specific password -// if you want to generate a random password, -// use 'CreateRandomBlsPassword' to generate a random password and pass it to this function -func InitializeNodeValidatorBlsFilesWithPassword(config *BlsConfig, password string) (blsPubKey []byte, err error) { - return InitializeNodeValidatorBlsFilesFromMnemonic(config, "", password) -} +func LoadBlsPV(keyFilePath, passwordFilePath string) *BlsPV { -// initialize node validator bls key with mnemonic and specific password -// if you want to generate a random password, -// use 'CreateRandomBlsPassword' to generate a random password and pass it to this function -func InitializeNodeValidatorBlsFilesFromMnemonic(config *BlsConfig, mnemonic, password string) (blsPubKey []byte, err error) { - if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { - return nil, fmt.Errorf("invalid mnemonic") + password, err := erc2335.LoadPaswordFromFile(passwordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) } - blsKeyFile := config.BlsKeyFile() - if err := os.MkdirAll(filepath.Dir(blsKeyFile), 0o777); err != nil { - return nil, fmt.Errorf("could not create directory %q: %w", filepath.Dir(blsKeyFile), err) + keyJSONBytes, err := os.ReadFile(keyFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS file: %v", err.Error())) } - var blsPv *BlsPV - if len(mnemonic) == 0 { - blsPv = LoadOrGenBlsPV(blsKeyFile, password) - } else { - privKey := bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) - blsPv = NewBlsPV(privKey, blsKeyFile) - blsPv.Save(password) + // decrypt bls key from erc2335 type of structure + privKey, err := erc2335.Decrypt(keyJSONBytes, password) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to decrypt BLS key: %v", err.Error())) } - return blsPv.Key.PubKey.Bytes(), nil + blsPrivKey := bls12381.PrivateKey(privKey) + + return &BlsPV{ + Key: BlsPVKey{ + PubKey: blsPrivKey.PubKey(), + PrivKey: blsPrivKey, + filePath: keyFilePath, + }, + } } // ------------------------------------------------------------------------------- @@ -206,12 +161,16 @@ type BlsConfig struct { func DefaultBlsConfig() BlsConfig { return BlsConfig{ - RootDir: cmtcfg.DefaultConfigDir, 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) } diff --git a/privval/bls_test.go b/privval/bls_test.go index 302d1562f..03248ca6c 100644 --- a/privval/bls_test.go +++ b/privval/bls_test.go @@ -5,16 +5,15 @@ import ( "path/filepath" "testing" - "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/crypto/erc2335" cmtcfg "github.com/cometbft/cometbft/config" "github.com/test-go/testify/assert" ) -func TestNewBlsPV(t *testing.T) { - pv := NewBlsPV(bls12381.GenPrivKey(), "test") - assert.NotNil(t, pv) -} +// func TestNewBlsPV(t *testing.T) { +// pv := NewBlsPV(bls12381.GenPrivKey(), "test") +// assert.NotNil(t, pv) +// } func TestCleanUp(t *testing.T) { blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() @@ -22,25 +21,24 @@ func TestCleanUp(t *testing.T) { cleanup(blsKeyFilePath) } -func TestInitializeNodeValidatorBlsFiles(t *testing.T) { +func TestInitializeBlsFile(t *testing.T) { t.Run("set default config", func(t *testing.T) { blsCfg := DefaultBlsConfig() assert.NotNil(t, blsCfg) - assert.Equal(t, blsCfg.RootDir, cmtcfg.DefaultConfigDir) assert.Equal(t, blsCfg.BlsKeyPath, filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName)) password := erc2335.CreateRandomPassword() t.Log("password", password) t.Run("generate key without mnemonic", func(t *testing.T) { - blsPubKey, err := InitializeNodeValidatorBlsFilesWithPassword(&blsCfg, password) + blsPubKey, err := InitializeBlsFile(&blsCfg, password) assert.NoError(t, err) assert.NotNil(t, blsPubKey) }) t.Run("load key with password", func(t *testing.T) { - blsPubKey, err := InitializeNodeValidatorBlsFilesWithPassword(&blsCfg, password) + blsPubKey, err := InitializeBlsFile(&blsCfg, password) assert.NoError(t, err) assert.NotNil(t, blsPubKey) }) @@ -66,7 +64,10 @@ func TestSavePasswordToFile(t *testing.T) { password := erc2335.CreateRandomPassword() t.Log("password", password) - err := erc2335.SavePasswordToFile(password, blsCfg.BlsPasswordFile()) + err := os.MkdirAll(filepath.Dir(blsCfg.BlsPasswordFile()), 0o777) + assert.NoError(t, err) + + err = erc2335.SavePasswordToFile(password, blsCfg.BlsPasswordFile()) assert.NoError(t, err) t.Run("load password file", func(t *testing.T) { diff --git a/privval/file.go b/privval/file.go index 6b11e9028..879f94e64 100644 --- a/privval/file.go +++ b/privval/file.go @@ -1,6 +1,7 @@ package privval import ( + "encoding/json" "errors" "fmt" "os" @@ -14,10 +15,11 @@ import ( "github.com/cometbft/cometbft/privval" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cosmos/cosmos-sdk/crypto/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/babylonlabs-io/babylon/crypto/bls12381" + "github.com/babylonlabs-io/babylon/crypto/erc2335" 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" @@ -46,108 +48,34 @@ func voteToStep(vote *cmtproto.Vote) int8 { // WrappedFilePVKey wraps FilePVKey with BLS keys. type WrappedFilePVKey struct { - // wonjoon/feat: separate cometPvKey and blsPvKey - CometPVKey privval.FilePVKey - BlsPVKey BlsPVKey - - // wonjoon/todo: remove - DelegatorAddress string `json:"acc_address"` -} - -// wonjoon/todo: remove or refactoring -func (pvKey WrappedFilePVKey) Save() { - pvKey.CometPVKey.Save() - pvKey.BlsPVKey.Save("") + CometPVKey privval.FilePVKey + BlsPVKey BlsPVKey + DelegatorAddress string } +// WrappedFilePV wraps FilePV with WrappedFilePVKey. type WrappedFilePV struct { Key WrappedFilePVKey LastSignState privval.FilePVLastSignState } -// wonjoon/todo: refactoring -func NewWrappedFilePV( - cometPrivKey cmtcrypto.PrivKey, - blsPrivKey bls12381.PrivateKey, - cometKeyFilePath, cometStateFilePath string, - // blsKeyFilePath string, -) *WrappedFilePV { - filePV := privval.NewFilePV(cometPrivKey, cometKeyFilePath, cometStateFilePath) +// GenWrappedFilePV generates a new validator with randomly generated private key +// and sets the filePaths, but does not call Save(). +func GenWrappedFilePV(cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { + cometPv := privval.NewFilePV(ed25519.GenPrivKey(), cmtKeyFilePath, cmtStateFilePath) + blsPv := NewBlsPV(bls12381.GenPrivKey(), blsKeyFilePath, blsPasswordFilePath) return &WrappedFilePV{ Key: WrappedFilePVKey{ - CometPVKey: filePV.Key, - BlsPVKey: NewBlsPV( - blsPrivKey, - DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, - ).Key, + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, }, - LastSignState: filePV.LastSignState, + LastSignState: cometPv.LastSignState, } } -// GenWrappedFilePV generates a new validator with randomly generated private key -// and sets the filePaths, but does not call Save(). -// wonjoon/todo: refactoring -func GenWrappedFilePV( - cometKeyFilePath, cometStateFilePath string, - //blsKeyFilePath string, -) *WrappedFilePV { - return NewWrappedFilePV( - ed25519.GenPrivKey(), - bls12381.GenPrivKey(), - cometKeyFilePath, - cometStateFilePath, - //blsKeyFilePath, - ) -} - -// 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. -// wonjoon/todo: refactoring -func LoadWrappedFilePV( - cometKeyFilePath, cometStateFilePath string, - // blsKeyFilePath, blsPassword string -) *WrappedFilePV { - return loadWrappedFilePV( - cometKeyFilePath, - cometStateFilePath, - DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, - "", // blsPassword, - true, - ) -} - -// LoadWrappedFilePVEmptyState loads a FilePV from the given keyFilePath, with an empty LastSignState. -// If the keyFilePath does not exist, the program will exit. -// wonjoon/todo: refactoring -func LoadWrappedFilePVEmptyState( - cometKeyFilePath, cometStateFilePath string, - // blsKeyFilePath, blsPassword string -) *WrappedFilePV { - return loadWrappedFilePV( - cometKeyFilePath, - cometStateFilePath, - DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, - "", // blsPassword, - false, - ) -} - -// If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. -func loadWrappedFilePV(cometKeyFilePath, cometStateFilePath, blsKeyFilePath, blsPassword string, loadState bool) *WrappedFilePV { - - // comet - var cometPv *privval.FilePV - if loadState { - cometPv = privval.LoadFilePV(cometKeyFilePath, cometStateFilePath) - } else { - cometPv = privval.LoadFilePVEmptyState(cometKeyFilePath, cometStateFilePath) - } - - // bls - blsPv := LoadBlsPV(blsKeyFilePath, blsPassword) - +func GenWrappedFilePVWithMnemonic(mnemonic, cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { + cometPv := privval.NewFilePV(ed25519.GenPrivKeyFromSecret([]byte(mnemonic)), cmtKeyFilePath, cmtStateFilePath) + blsPv := NewBlsPV(bls12381.GenPrivKeyFromSecret([]byte(mnemonic)), blsKeyFilePath, blsPasswordFilePath) return &WrappedFilePV{ Key: WrappedFilePVKey{ CometPVKey: cometPv.Key, @@ -159,20 +87,66 @@ func loadWrappedFilePV(cometKeyFilePath, cometStateFilePath, blsKeyFilePath, bls // LoadOrGenWrappedFilePV loads a FilePV from the given filePaths // or else generates a new one and saves it to the filePaths. -// wonjoon/todo: refactoring -func LoadOrGenWrappedFilePV( - cometKeyFilePath, cometStateFilePath string, - // blsKeyFilePath, blsPassword string, -) *WrappedFilePV { - cometPv := privval.LoadOrGenFilePV(cometKeyFilePath, cometStateFilePath) - blsPv := LoadOrGenBlsPV( - DefaultBlsConfig().BlsKeyFile(), // blsKeyFilePath, - "", // blsPassword, - ) +func LoadOrGenWrappedFilePV(cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { + + var blsPV *BlsPV + + if !cmtos.FileExists(blsKeyFilePath) { + var blsPassword string + var err error + if cmtos.FileExists(blsPasswordFilePath) { + blsPassword, err = erc2335.LoadPaswordFromFile(blsPasswordFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) + } + } else { + blsPassword = erc2335.CreateRandomPassword() + } + + blsPV = NewBlsPV(bls12381.GenPrivKey(), blsKeyFilePath, blsPassword) + blsPV.Save(blsPassword) + } else { + blsPV = LoadBlsPV(blsKeyFilePath, blsPasswordFilePath) + } + + var cometPV *privval.FilePV + if cmtos.FileExists(cmtKeyFilePath) { + cometPV = privval.LoadFilePV(cmtKeyFilePath, cmtStateFilePath) + } else { + cometPV = privval.GenFilePV(cmtKeyFilePath, cmtStateFilePath) + cometPV.Key.Save() + } + + wrappedFilePV := &WrappedFilePV{ + Key: WrappedFilePVKey{ + CometPVKey: cometPV.Key, + BlsPVKey: blsPV.Key, + }, + LastSignState: cometPV.LastSignState, + } + + return wrappedFilePV +} + +func LoadWrappedFilePV(keyFilePath, stateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { + + if !cmtos.FileExists(blsKeyFilePath) { + cmtos.Exit(fmt.Sprintf("BLS key file does not exist: %v", blsKeyFilePath)) + } + + blsPv := LoadBlsPV(blsKeyFilePath, blsPasswordFilePath) + + if !cmtos.FileExists(keyFilePath) { + cmtos.Exit(fmt.Sprintf("validator key file does not exist: %v", keyFilePath)) + } + + cometPv := privval.LoadFilePV(keyFilePath, stateFilePath) + return &WrappedFilePV{ Key: WrappedFilePVKey{ - CometPVKey: cometPv.Key, - BlsPVKey: blsPv.Key, + CometPVKey: cometPv.Key, + BlsPVKey: blsPv.Key, + DelegatorAddress: ReadDelegatorAddressFromFile(blsKeyFilePath), }, LastSignState: cometPv.LastSignState, } @@ -180,17 +154,14 @@ func LoadOrGenWrappedFilePV( // 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) { - // file check if !cmtos.FileExists(filePath) { return outputFileName, errors.New("export file path does not exist") } - // ---- Should be removed ---// valAddress := pv.GetAddress() if valAddress.Empty() { return outputFileName, errors.New("validator address should not be empty") } - //-------------------------// validatorKey, err := NewValidatorKeys(pv.GetValPrivKey(), pv.GetBlsPrivKey()) if err != nil { @@ -232,31 +203,69 @@ func (pv *WrappedFilePV) GetAddress() sdk.ValAddress { func (pv *WrappedFilePV) SetAccAddress(addr sdk.AccAddress) { pv.Key.DelegatorAddress = addr.String() - pv.Key.Save() + // pv.Key.Save() + SaveDelegatorAddressToFile(pv.Key.DelegatorAddress, pv.Key.BlsPVKey.filePath) +} + +func SaveDelegatorAddressToFile(delegatorAddress, filePath string) { + + var data map[string]interface{} + if err := ReadJSON(filePath, &data); err != nil { + cmtos.Exit(fmt.Sprintf("Failed to read JSON file: %v\n", err)) + } + + data["description"] = delegatorAddress + if err := WriteJSON(filePath, data); err != nil { + cmtos.Exit(fmt.Sprintf("Failed to write to JSON file: %v\n", err)) + } +} + +func ReadDelegatorAddressFromFile(filePath string) string { + + var data map[string]interface{} + if err := ReadJSON(filePath, &data); err != nil { + cmtos.Exit(fmt.Sprintf("Failed to read JSON file: %v\n", err)) + } + return data["description"].(string) +} + +func WriteJSON(filePath string, v interface{}) error { + jsonBytes, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + return os.WriteFile(filePath, jsonBytes, 0644) +} + +func ReadJSON(filePath string, v interface{}) error { + file, err := os.Open(filePath) + if err != nil { + if os.IsNotExist(err) { + *v.(*map[string]interface{}) = make(map[string]interface{}) + return nil + } + return err + } + defer file.Close() + return json.NewDecoder(file).Decode(v) } // GetPubKey returns the public key of the validator. -// Implements PrivValidator. func (pv *WrappedFilePV) GetPubKey() (cmtcrypto.PubKey, error) { return pv.Key.CometPVKey.PubKey, nil } +// GetValPrivKey returns the private key of the validator. func (pv *WrappedFilePV) GetValPrivKey() cmtcrypto.PrivKey { return pv.Key.CometPVKey.PrivKey } +// GetBlsPrivKey returns the private key of the BLS. func (pv *WrappedFilePV) GetBlsPrivKey() bls12381.PrivateKey { return pv.Key.BlsPVKey.PrivKey } -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 -} - +// GetBlsPubkey returns the public key of the BLS func (pv *WrappedFilePV) GetBlsPubkey() (bls12381.PublicKey, error) { blsPrivKey := pv.GetBlsPrivKey() if blsPrivKey == nil { @@ -269,40 +278,24 @@ func (pv *WrappedFilePV) GetValidatorPubkey() (cmtcrypto.PubKey, error) { return pv.GetPubKey() } -// ---- Should be removed ---//₩ -// 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() +// 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 } // Clean removes PVKey file and PVState file -func (pv *WrappedFilePV) Clean(keyFilePath, stateFilePath string) { - _ = os.RemoveAll(filepath.Dir(keyFilePath)) - _ = os.RemoveAll(filepath.Dir(stateFilePath)) +func (pv *WrappedFilePV) Clean(paths ...string) { + for _, path := range paths { + _ = os.RemoveAll(filepath.Dir(path)) + } } -// 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, - ) +func (pv *WrappedFilePV) Save(password string) { + pv.Key.CometPVKey.Save() + pv.Key.BlsPVKey.Save(password) + pv.LastSignState.Save() } diff --git a/privval/file_test.go b/privval/file_test.go new file mode 100644 index 000000000..4afcb9d35 --- /dev/null +++ b/privval/file_test.go @@ -0,0 +1,74 @@ +package privval + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/babylonlabs-io/babylon/crypto/erc2335" + cmtcfg "github.com/cometbft/cometbft/config" + cmtos "github.com/cometbft/cometbft/libs/os" + + "github.com/cosmos/cosmos-sdk/types" + "github.com/test-go/testify/assert" +) + +func TestSaveDelegatorAddressToFile(t *testing.T) { + + tempDir := t.TempDir() + defer os.RemoveAll(tempDir) + + ccfg := cmtcfg.DefaultConfig() + ccfg.SetRoot(tempDir) + bcfg := DefaultBlsConfig() + bcfg.SetRoot(tempDir) + + pvKeyFile := filepath.Join(tempDir, ccfg.PrivValidatorKeyFile()) + pvStateFile := filepath.Join(tempDir, ccfg.PrivValidatorStateFile()) + blsKeyFile := filepath.Join(tempDir, bcfg.BlsKeyFile()) + blsPasswordFile := filepath.Join(tempDir, bcfg.BlsPasswordFile()) + + err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) + assert.Nil(t, err) + err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) + assert.Nil(t, err) + err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) + assert.Nil(t, err) + err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) + assert.Nil(t, err) + + pv := GenWrappedFilePV( + pvKeyFile, + pvStateFile, + blsKeyFile, + blsPasswordFile, + ) + + pv.Save("test") + + t.Run("save delegator address", func(t *testing.T) { + delegatorAddress := pv.Key.CometPVKey.PubKey.Address() + t.Log("delegatorAddress: ", delegatorAddress) + + // addr, err := sdk.AccAddressFromBech32(delegatorAddress.String()) + // assert.NoError(t, err) + // t.Log("sdk.AccAddressFromBech32(delegatorAddress.String()): ", addr) + + pv.SetAccAddress(types.AccAddress(delegatorAddress)) + + var keystore erc2335.Erc2335KeyStore + keyJSONBytes, err := os.ReadFile(blsKeyFile) + assert.NoError(t, err) + err = json.Unmarshal(keyJSONBytes, &keystore) + assert.NoError(t, err) + + // t.Log(keystore) + t.Log(keystore.Description) + }) + + t.Run("load delegator address", func(t *testing.T) { + delegatorAddress := ReadDelegatorAddressFromFile(blsKeyFile) + assert.Equal(t, delegatorAddress, pv.Key.DelegatorAddress) + }) +} diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index 0c710c46e..3e674f5fb 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -10,7 +10,6 @@ import ( "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" @@ -34,7 +33,7 @@ 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/crypto/erc2335" "github.com/babylonlabs-io/babylon/privval" "github.com/babylonlabs-io/babylon/test/e2e/util" ) @@ -173,12 +172,26 @@ func (n *internalNode) createConsensusKey() error { return err } - privKey := cmted25519.GenPrivKeyFromSecret([]byte(n.mnemonic)) - blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(n.mnemonic)) - filePV := privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(config.RootDir) + blsKeyFile := blsCfg.BlsKeyFile() + if err := cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0o777); err != nil { + return err + } + + blsPasswordFile := blsCfg.BlsPasswordFile() + if err := cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0o777); err != nil { + return err + } + + // for test e2e + // bls-password is random generated + blsPassword := erc2335.CreateRandomPassword() + + filePV := privval.GenWrappedFilePVWithMnemonic(n.mnemonic, pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) + filePV.Save(blsPassword) accAddress, _ := n.keyInfo.GetAddress() - filePV.Save() filePV.SetAccAddress(accAddress) n.consensusKey = filePV.Key diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index 46e883b46..827a9ca4f 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -5,13 +5,11 @@ import ( "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/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" ) @@ -42,13 +40,15 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin return "", nil, err } + // add bls config and set root same as config + blsCfg := privval.DefaultBlsConfig() + blsCfg.SetRoot(config.RootDir) + var filePV *privval.WrappedFilePV if len(mnemonic) == 0 { - filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsCfg.BlsKeyFile(), blsCfg.BlsPasswordFile()) } else { - privKey := cmted25519.GenPrivKeyFromSecret([]byte(mnemonic)) - blsPrivKey := bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) - filePV = privval.NewWrappedFilePV(privKey, blsPrivKey, pvKeyFile, pvStateFile) + filePV = privval.GenWrappedFilePVWithMnemonic(mnemonic, pvKeyFile, pvStateFile, blsCfg.BlsKeyFile(), blsCfg.BlsPasswordFile()) } filePV.SetAccAddress(addr) diff --git a/testutil/signer/private.go b/testutil/signer/private.go index a4f39cb3a..a46f3e862 100644 --- a/testutil/signer/private.go +++ b/testutil/signer/private.go @@ -21,7 +21,7 @@ func SetupTestPrivSigner() (*signer.PrivSigner, error) { defer func() { _ = os.RemoveAll(nodeDir) }() - privSigner, _ := signer.InitPrivSigner(nodeDir) + privSigner, _ := signer.InitTestPrivSigner(nodeDir) return privSigner, nil } diff --git a/x/checkpointing/client/cli/tx_test.go b/x/checkpointing/client/cli/tx_test.go index bee75d842..24c37771d 100644 --- a/x/checkpointing/client/cli/tx_test.go +++ b/x/checkpointing/client/cli/tx_test.go @@ -110,7 +110,16 @@ func (s *CLITestSuite) TestCmdWrappedCreateValidator() { pvStateFile := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) require.NoError(err) - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile) + + blsCfg := privval.DefaultBlsConfig() + blsKeyFile := filepath.Join(homeDir, blsCfg.BlsKeyFile()) + err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) + require.NoError(err) + blsPasswordFile := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) + require.NoError(err) + + wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) cmd := checkpointcli.CmdWrappedCreateValidator(authcodec.NewBech32Codec("cosmosvaloper")) consPrivKey := wrappedPV.GetValPrivKey() diff --git a/x/checkpointing/client/cli/utils.go b/x/checkpointing/client/cli/utils.go index c30ad96dd..7d4161390 100644 --- a/x/checkpointing/client/cli/utils.go +++ b/x/checkpointing/client/cli/utils.go @@ -209,7 +209,14 @@ func getValKeyFromFile(homeDir string) (*privval.ValidatorKeys, error) { if !cmtos.FileExists(keyPath) { return nil, errors.New("validator key file does not exist") } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath) + + blsCfg := privval.DefaultBlsConfig() + blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) + blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + if !cmtos.FileExists(blsKeyPath) { + return nil, errors.New("validator bls key file does not exist") + } + wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath, blsKeyPath, blsPasswordPath) return privval.NewValidatorKeys(wrappedPV.GetValPrivKey(), wrappedPV.GetBlsPrivKey()) } From f6dd2c1ac2b1387b39bd86aecaac053d811253b0 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 20:15:05 +0900 Subject: [PATCH 5/8] refactor: refactoring codes related to bls and test code --- app/signer/private.go | 78 +------ app/test_helpers.go | 2 +- cmd/babylond/cmd/create_bls_key.go | 46 +--- cmd/babylond/cmd/genhelpers/bls_add_test.go | 31 ++- cmd/babylond/cmd/genhelpers/bls_create.go | 18 +- .../cmd/genhelpers/bls_create_test.go | 23 +- cmd/babylond/cmd/init.go | 199 ------------------ cmd/babylond/cmd/init_test.go | 21 -- cmd/babylond/cmd/root.go | 3 +- crypto/erc2335/erc2335.go | 19 +- crypto/erc2335/erc2335_test.go | 34 +-- privval/bls.go | 134 +++++------- privval/bls_test.go | 91 +++----- privval/file.go | 193 +---------------- privval/file_test.go | 74 ------- privval/util.go | 21 ++ test/e2e/initialization/config.go | 2 +- test/e2e/initialization/node.go | 56 ++--- testutil/datagen/init_val.go | 54 +++-- testutil/helper/helper.go | 4 +- testutil/signer/private.go | 32 ++- x/checkpointing/client/cli/tx_test.go | 27 +-- x/checkpointing/client/cli/utils.go | 20 +- 23 files changed, 342 insertions(+), 840 deletions(-) delete mode 100644 cmd/babylond/cmd/init.go delete mode 100644 cmd/babylond/cmd/init_test.go delete mode 100644 privval/file_test.go create mode 100644 privval/util.go diff --git a/app/signer/private.go b/app/signer/private.go index 22012963c..d95fdc910 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -1,11 +1,9 @@ package signer import ( - "fmt" "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" @@ -17,83 +15,29 @@ 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 { - return nil, err - } - - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(nodeCfg.RootDir) blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) - err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) - if err != nil { - return nil, err - } blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) - err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) - if err != nil { + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return nil, err } - if !cmtos.FileExists(blsKeyFile) { - return nil, fmt.Errorf("BLS key file does not exist: %v", blsKeyFile) - } + cometPV := cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) + blsPV := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) - if !cmtos.FileExists(blsPasswordFile) { - return nil, fmt.Errorf("BLS password file does not exist: %v", blsPasswordFile) - } - - blsPv := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) - cmtPv := cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) - wrappedPvKey := privval.WrappedFilePVKey{ - CometPVKey: cmtPv.Key, - BlsPVKey: blsPv.Key, - } wrappedPV := &privval.WrappedFilePV{ - Key: wrappedPvKey, - LastSignState: cmtPv.LastSignState, + Key: privval.WrappedFilePVKey{ + CometPVKey: cometPV.Key, + BlsPVKey: blsPV.Key, + }, + LastSignState: cometPV.LastSignState, } return &PrivSigner{ WrappedPV: wrappedPV, }, nil } - -func InitTestPrivSigner(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 - } - pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) - if err != nil { - return nil, err - } - - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(nodeCfg.RootDir) - blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) - err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) - if err != nil { - return nil, err - } - blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) - err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) - if err != nil { - return nil, err - } - - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) - - return &PrivSigner{ - WrappedPV: wrappedPV, - }, nil -} diff --git a/app/test_helpers.go b/app/test_helpers.go index 385a557d8..6bdcb4eda 100644 --- a/app/test_helpers.go +++ b/app/test_helpers.go @@ -246,7 +246,7 @@ func SetupWithBitcoinConf(t *testing.T, isCheckTx bool, btcConf bbn.SupportedBtc 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 ac500cf8a..6a6b62ea8 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -1,26 +1,17 @@ package cmd import ( - "bufio" "fmt" - "log" - "os" "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/cosmos/cosmos-sdk/client/input" 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/crypto/erc2335" "github.com/babylonlabs-io/babylon/privval" - cmtprivval "github.com/cometbft/cometbft/privval" ) func CreateBlsKeyCmd() *cobra.Command { @@ -62,42 +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()) - cmtPv := cmtprivval.LoadFilePV(keyPath, statePath) blsCfg := privval.DefaultBlsConfig() - blsKeyPath := filepath.Join(home, blsCfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) - - var blsPassword string - var err error - if !cmtos.FileExists(blsPasswordFile) { - log.Printf("BLS password file don't exists in file: %v", blsPasswordFile) - inBuf := bufio.NewReader(os.Stdin) - blsPassword, err = input.GetString("Enter your bls password", inBuf) - if err != nil { - return err - } - err = erc2335.SavePasswordToFile(blsPassword, blsPasswordFile) - if err != nil { - return err - } - } - blsPv := privval.NewBlsPV(bls12381.GenPrivKey(), blsKeyPath, blsPasswordFile) - blsPv.Key.Save(blsPassword) + keyPath := filepath.Join(home, blsCfg.BlsKeyFile()) + passwordPath := filepath.Join(home, blsCfg.BlsPasswordFile()) - wrappedPv := privval.WrappedFilePV{ - Key: privval.WrappedFilePVKey{ - CometPVKey: cmtPv.Key, - BlsPVKey: blsPv.Key, - }, - LastSignState: cmtPv.LastSignState, - } + password := privval.GetBlsPassword() - wrappedPv.SetAccAddress(addr) - log.Printf("Saved delegator address: %s", addr.String()) - log.Printf("Saved delegator address in wrapperPv: %s", wrappedPv.Key.DelegatorAddress) + privval.GenBlsPV(keyPath, passwordPath, password, addr.String()) return nil } diff --git a/cmd/babylond/cmd/genhelpers/bls_add_test.go b/cmd/babylond/cmd/genhelpers/bls_add_test.go index 817e77937..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,18 +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.SetRoot(homeDir) - keyPath := nodeCfg.PrivValidatorKeyFile() - statePath := nodeCfg.PrivValidatorStateFile() + nodeCfg := cmtconfig.DefaultConfig() + nodeCfg.SetRoot(homeDir) blsCfg := privval.DefaultBlsConfig() blsCfg.SetRoot(homeDir) + + keyPath := nodeCfg.PrivValidatorKeyFile() + statePath := nodeCfg.PrivValidatorStateFile() blsKeyFile := blsCfg.BlsKeyFile() blsPasswordFile := blsCfg.BlsPasswordFile() - filePV := privval.GenWrappedFilePV(keyPath, statePath, blsKeyFile, blsPasswordFile) - filePV.SetAccAddress(v.Address) + + 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)) @@ -180,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, blsKeyFile, blsPasswordFile) + 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 5c20b8f65..357139863 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create.go +++ b/cmd/babylond/cmd/genhelpers/bls_create.go @@ -1,7 +1,6 @@ package genhelpers import ( - "log" "path/filepath" "strings" @@ -11,6 +10,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,14 +35,26 @@ $ babylond genbls --home ./ nodeCfg := cmtconfig.DefaultConfig() blsCfg := privval.DefaultBlsConfig() + keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath, blsKeyPath, blsPasswordPath) + if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + return err + } - log.Printf("Loaded delegator address in wrapperPv: %s", wrappedPV.Key.DelegatorAddress) + 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 58c29176d..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,15 +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()) - - blsCfg := privval.DefaultBlsConfig() blsKeyFile := filepath.Join(home, blsCfg.BlsKeyFile()) blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) - filePV := privval.GenWrappedFilePV(keyPath, statePath, blsKeyFile, blsPasswordFile) - defer filePV.Clean(keyPath, statePath, blsKeyFile, blsPasswordFile) - filePV.SetAccAddress(addr) + + 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) @@ -86,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.BlsPVKey.PubKey.Equal(*genKey.BlsKey.Pubkey)) - require.Equal(t, filePV.Key.CometPVKey.PubKey.Bytes(), genKey.ValPubkey.Bytes()) + 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/cmd/babylond/cmd/init.go b/cmd/babylond/cmd/init.go deleted file mode 100644 index 7322d04e9..000000000 --- a/cmd/babylond/cmd/init.go +++ /dev/null @@ -1,199 +0,0 @@ -package cmd - -import ( - "bufio" - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - - "cosmossdk.io/math/unsafe" - "github.com/babylonlabs-io/babylon/privval" - "github.com/spf13/cobra" - - errorsmod "cosmossdk.io/errors" - cfg "github.com/cometbft/cometbft/config" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/input" - "github.com/cosmos/cosmos-sdk/server" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/genutil" - cmtcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" - "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/cosmos/go-bip39" -) - -// wonjoon -// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" -func InitCmd(mbm module.BasicManager, defaultNodeHome string) *cobra.Command { - cmd := &cobra.Command{ - Use: "init [moniker]", - Short: "Initialize private validator, p2p, genesis, and application configuration files", - Long: `Initialize validators's and node's configuration files.`, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - clientCtx := client.GetClientContextFromCmd(cmd) - cdc := clientCtx.Codec - - serverCtx := server.GetServerContextFromCmd(cmd) - config := serverCtx.Config - config.SetRoot(clientCtx.HomeDir) - - chainID, _ := cmd.Flags().GetString(flags.FlagChainID) - switch { - case chainID != "": - case clientCtx.ChainID != "": - chainID = clientCtx.ChainID - default: - chainID = fmt.Sprintf("test-chain-%v", unsafe.Str(6)) - } - - // Get bip39 mnemonic - var mnemonic string - recover, _ := cmd.Flags().GetBool(cmtcli.FlagRecover) - if recover { - inBuf := bufio.NewReader(cmd.InOrStdin()) - value, err := input.GetString("Enter your bip39 mnemonic", inBuf) - if err != nil { - return err - } - - mnemonic = value - if !bip39.IsMnemonicValid(mnemonic) { - return errors.New("invalid mnemonic") - } - } - - // Get initial height - initHeight, _ := cmd.Flags().GetInt64(flags.FlagInitHeight) - if initHeight < 1 { - initHeight = 1 - } - - // Get bls password - inBuf := bufio.NewReader(cmd.InOrStdin()) - blsPassword, err := input.GetString("Enter your bls password", inBuf) - if err != nil { - return err - } - - // Initialize bls key - blsCfg := privval.BlsConfig{ - RootDir: config.RootDir, - BlsKeyPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), - BlsPasswordPath: filepath.Join(config.RootDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), - } - - // Initialize BLS key and save to file - _, err = privval.InitializeBlsFileFromMnemonic(&blsCfg, blsPassword, mnemonic) - if err != nil { - return err - } - - // InitializeNodeValidatorFilesFromMnemonic from cosmos-sdk/x/genutil/utils.go - nodeID, _, err := genutil.InitializeNodeValidatorFilesFromMnemonic(config, mnemonic) - if err != nil { - return err - } - - config.Moniker = args[0] - - genFile := config.GenesisFile() - overwrite, _ := cmd.Flags().GetBool(cmtcli.FlagOverwrite) - defaultDenom, _ := cmd.Flags().GetString(cmtcli.FlagDefaultBondDenom) - - // use os.Stat to check if the file exists - _, err = os.Stat(genFile) - if !overwrite && !os.IsNotExist(err) { - return fmt.Errorf("genesis.json file already exists: %v", genFile) - } - - // Overwrites the SDK default denom for side-effects - if defaultDenom != "" { - sdk.DefaultBondDenom = defaultDenom - } - appGenState := mbm.DefaultGenesis(cdc) - - appState, err := json.MarshalIndent(appGenState, "", " ") - if err != nil { - return errorsmod.Wrap(err, "Failed to marshal default genesis state") - } - - appGenesis := &types.AppGenesis{} - if _, err := os.Stat(genFile); err != nil { - if !os.IsNotExist(err) { - return err - } - } else { - appGenesis, err = types.AppGenesisFromFile(genFile) - if err != nil { - return errorsmod.Wrap(err, "Failed to read genesis doc from file") - } - } - - appGenesis.AppName = version.AppName - appGenesis.AppVersion = version.Version - appGenesis.ChainID = chainID - appGenesis.AppState = appState - appGenesis.InitialHeight = initHeight - appGenesis.Consensus = &types.ConsensusGenesis{ - Validators: nil, - } - - if err = genutil.ExportGenesisFile(appGenesis, genFile); err != nil { - return errorsmod.Wrap(err, "Failed to export genesis file") - } - - toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState) - - cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) - return displayInfo(toPrint) - }, - } - - cmd.Flags().String(flags.FlagHome, defaultNodeHome, "node's home directory") - cmd.Flags().BoolP(cmtcli.FlagOverwrite, "o", false, "overwrite the genesis.json file") - cmd.Flags().Bool(cmtcli.FlagRecover, false, "provide seed phrase to recover existing key instead of creating") - cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().String(cmtcli.FlagDefaultBondDenom, "", "genesis file default denomination, if left blank default value is 'stake'") - cmd.Flags().Int64(flags.FlagInitHeight, 1, "specify the initial block height at genesis") - cmd.Flags().String("bls-password", "", "BLS key password (if not provided, will prompt for input)") - - return cmd -} - -// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" -type printInfo struct { - Moniker string `json:"moniker" yaml:"moniker"` - ChainID string `json:"chain_id" yaml:"chain_id"` - NodeID string `json:"node_id" yaml:"node_id"` - GenTxsDir string `json:"gentxs_dir" yaml:"gentxs_dir"` - AppMessage json.RawMessage `json:"app_message" yaml:"app_message"` -} - -// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" -func newPrintInfo(moniker, chainID, nodeID, genTxsDir string, appMessage json.RawMessage) printInfo { - return printInfo{ - Moniker: moniker, - ChainID: chainID, - NodeID: nodeID, - GenTxsDir: genTxsDir, - AppMessage: appMessage, - } -} - -// copied from "https://github.com/cosmos/cosmos-sdk/x/genutil/client/cli/init.go" -func displayInfo(info printInfo) error { - out, err := json.MarshalIndent(info, "", " ") - if err != nil { - return err - } - - _, err = fmt.Fprintf(os.Stderr, "%s\n", out) - - return err -} diff --git a/cmd/babylond/cmd/init_test.go b/cmd/babylond/cmd/init_test.go deleted file mode 100644 index 1f7441912..000000000 --- a/cmd/babylond/cmd/init_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package cmd - -import ( - "path/filepath" - "testing" - - "github.com/babylonlabs-io/babylon/privval" - cfg "github.com/cometbft/cometbft/config" -) - -func TestSaveFiles(t *testing.T) { - - // clientCtx := client.GetClientContextFromCmd(&cobra.Command{}) - homeDir := "test" - blsCfg := privval.BlsConfig{ - RootDir: homeDir, - BlsKeyPath: filepath.Join(homeDir, cfg.DefaultConfigDir, privval.DefaultBlsKeyName), - BlsPasswordPath: filepath.Join(homeDir, cfg.DefaultConfigDir, privval.DefaultBlsPasswordName), - } - t.Log(blsCfg) -} diff --git a/cmd/babylond/cmd/root.go b/cmd/babylond/cmd/root.go index cf78b1beb..7687ab268 100644 --- a/cmd/babylond/cmd/root.go +++ b/cmd/babylond/cmd/root.go @@ -167,8 +167,7 @@ func initRootCmd(rootCmd *cobra.Command, txConfig client.TxEncodingConfig, basic gentxModule := basicManager[genutiltypes.ModuleName].(genutil.AppModuleBasic) rootCmd.AddCommand( - // genutilcli.InitCmd(basicManager, app.DefaultNodeHome), - InitCmd(basicManager, app.DefaultNodeHome), + genutilcli.InitCmd(basicManager, app.DefaultNodeHome), genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, gentxModule.GenTxValidator, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), genutilcli.MigrateGenesisCmd(genutilcli.MigrationMap), genutilcli.GenTxCmd(basicManager, txConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome, authcodec.NewBech32Codec(params.Bech32PrefixValAddr)), diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go index 673588e46..6933500b5 100644 --- a/crypto/erc2335/erc2335.go +++ b/crypto/erc2335/erc2335.go @@ -43,20 +43,23 @@ func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { return json.Marshal(keystoreJSON) } -// decrypt private key from erc2335 keystore -func Decrypt(keystoreJSON []byte, password string) ([]byte, error) { - // Parse the keystore json +func LoadKeyStore(filePath string) (Erc2335KeyStore, error) { var keystore Erc2335KeyStore - if err := json.Unmarshal(keystoreJSON, &keystore); err != nil { - return nil, errors.Wrap(err, "failed to parse keystore json") + keyJSONBytes, err := os.ReadFile(filePath) + if err != nil { + return Erc2335KeyStore{}, err } - // Verify version - if keystore.Version != 4 { - return nil, fmt.Errorf("invalid keystore version: %d", keystore.Version) + 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) } diff --git a/crypto/erc2335/erc2335_test.go b/crypto/erc2335/erc2335_test.go index c6c51d4b3..aa56f650e 100644 --- a/crypto/erc2335/erc2335_test.go +++ b/crypto/erc2335/erc2335_test.go @@ -1,6 +1,7 @@ package erc2335 import ( + "encoding/json" "os" "testing" @@ -9,37 +10,37 @@ import ( ) func TestEncryptBLS(t *testing.T) { - // TODO 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(encryptedBlsKey, password) + 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) { - - _, err := Decrypt(encryptedBlsKey, "wrong password") + 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) @@ -50,16 +51,19 @@ func TestEncryptBLS(t *testing.T) { 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) - decryptedBlsKey, err := Decrypt(encryptedBlsKey, password) + + 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") @@ -67,10 +71,14 @@ func TestEncryptBLS(t *testing.T) { }) t.Run("failed when load different password and decrypt bls key", func(t *testing.T) { - password, err := LoadPaswordFromFile("password.txt") require.NoError(t, err) - _, err = Decrypt(encryptedBlsKey, password) + + var keystore Erc2335KeyStore + err = json.Unmarshal(encryptedBlsKey, &keystore) + require.NoError(t, err) + + _, err = Decrypt(keystore, password) require.Error(t, err) }) diff --git a/privval/bls.go b/privval/bls.go index 05da75790..e990db8e3 100644 --- a/privval/bls.go +++ b/privval/bls.go @@ -1,6 +1,7 @@ package privval import ( + "bufio" "encoding/json" "fmt" "os" @@ -11,7 +12,7 @@ import ( cmtcfg "github.com/cometbft/cometbft/config" cmtos "github.com/cometbft/cometbft/libs/os" "github.com/cometbft/cometbft/libs/tempfile" - "github.com/cosmos/go-bip39" + "github.com/cosmos/cosmos-sdk/client/input" ) const ( @@ -27,79 +28,78 @@ type BlsPVKey struct { PubKey bls12381.PublicKey `json:"bls_pub_key"` PrivKey bls12381.PrivateKey `json:"bls_priv_key"` + DelegatorAddress string + filePath string passwordPath string } -// initialize node validator bls key with password -func InitializeBlsFile(config *BlsConfig, password string) (blsPubKey []byte, err error) { - return InitializeBlsFileFromMnemonic(config, password, "") +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, + }, + } } -// initialize node validator bls key with mnemonic and password -func InitializeBlsFileFromMnemonic(config *BlsConfig, password, mnemonic string) (blsPubKey []byte, err error) { - if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { - return nil, fmt.Errorf("invalid mnemonic") - } +func GenBlsPV(keyFilePath, passwordFilePath, password, delegatorAddress string) *BlsPV { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, delegatorAddress) + pv.Key.Save(password, delegatorAddress) + return pv +} - blsKeyFile := config.BlsKeyFile() - if err := os.MkdirAll(filepath.Dir(blsKeyFile), 0o777); err != nil { - return nil, fmt.Errorf("could not create directory for bls key %q: %w", filepath.Dir(blsKeyFile), err) +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())) } - blsPasswordFile := config.BlsPasswordFile() - if err := os.MkdirAll(filepath.Dir(blsPasswordFile), 0o777); err != nil { - return nil, fmt.Errorf("could not create directory for bls password %q: %w", filepath.Dir(blsPasswordFile), err) + keystore, err := erc2335.LoadKeyStore(keyFilePath) + if err != nil { + cmtos.Exit(fmt.Sprintf("failed to read erc2335 keystore: %v", err.Error())) } - // var blsPv *BlsPV - var privKey bls12381.PrivateKey - if len(mnemonic) == 0 { - privKey = bls12381.GenPrivKey() - } else { - privKey = bls12381.GenPrivKeyFromSecret([]byte(mnemonic)) + // 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())) } - blsPv := NewBlsPV(privKey, blsKeyFile, blsPasswordFile) - blsPv.Save(password) - return privKey.PubKey().Bytes(), nil -} - -func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath, passwordFilePath string) *BlsPV { + blsPrivKey := bls12381.PrivateKey(privKey) return &BlsPV{ Key: BlsPVKey{ - PubKey: privKey.PubKey(), - PrivKey: privKey, - filePath: keyFilePath, - passwordPath: passwordFilePath, + PubKey: blsPrivKey.PubKey(), + PrivKey: blsPrivKey, + DelegatorAddress: keystore.Description, + filePath: keyFilePath, + passwordPath: passwordFilePath, }, } } -func (pv *BlsPV) Save(password string) { - pv.Key.Save(password) -} - -func (pvKey *BlsPVKey) Save(password string) { - - passwordOutFile := pvKey.passwordPath - if passwordOutFile == "" { - panic("cannot save PrivValidator BLS key: password filePath not set") - } - - // save password to file - err := erc2335.SavePasswordToFile(password, passwordOutFile) +func GetBlsPassword() string { + inBuf := bufio.NewReader(os.Stdin) + password, err := input.GetString("Enter your bls password", inBuf) if err != nil { - panic(err) + cmtos.Exit("failed to get BLS password") } + return password +} - keyOutFile := pvKey.filePath - if keyOutFile == "" { - panic("cannot save PrivValidator BLS key: filePath not set") +// 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(pvKey.PrivKey, pvKey.PubKey.Bytes(), password) + erc2335BlsPvKey, err := erc2335.Encrypt(k.PrivKey, k.PubKey.Bytes(), password) if err != nil { panic(err) } @@ -110,42 +110,24 @@ func (pvKey *BlsPVKey) Save(password string) { 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) } - if err := tempfile.WriteFileAtomic(keyOutFile, jsonBytes, 0600); err != nil { + // write generated erc2335 keystore to file + if err := tempfile.WriteFileAtomic(k.filePath, jsonBytes, 0600); err != nil { panic(err) } -} - -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())) - } - - keyJSONBytes, err := os.ReadFile(keyFilePath) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to read BLS file: %v", err.Error())) - } - // decrypt bls key from erc2335 type of structure - privKey, err := erc2335.Decrypt(keyJSONBytes, password) + // save used password to file + err = erc2335.SavePasswordToFile(password, k.passwordPath) 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, - filePath: keyFilePath, - }, + panic(err) } } diff --git a/privval/bls_test.go b/privval/bls_test.go index 03248ca6c..6e6c134e5 100644 --- a/privval/bls_test.go +++ b/privval/bls_test.go @@ -5,86 +5,55 @@ import ( "path/filepath" "testing" + "github.com/cometbft/cometbft/crypto/ed25519" + + "github.com/babylonlabs-io/babylon/crypto/bls12381" "github.com/babylonlabs-io/babylon/crypto/erc2335" - cmtcfg "github.com/cometbft/cometbft/config" + "github.com/cosmos/cosmos-sdk/types" "github.com/test-go/testify/assert" ) -// func TestNewBlsPV(t *testing.T) { -// pv := NewBlsPV(bls12381.GenPrivKey(), "test") -// assert.NotNil(t, pv) -// } +func TestNewBlsPV(t *testing.T) { + tempDir := t.TempDir() + defer os.RemoveAll(tempDir) -func TestCleanUp(t *testing.T) { - blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() - t.Log("bls key file path", blsKeyFilePath) - cleanup(blsKeyFilePath) -} + cfg := DefaultBlsConfig() -func TestInitializeBlsFile(t *testing.T) { + keyFilePath := filepath.Join(tempDir, cfg.BlsKeyFile()) + passwordFilePath := filepath.Join(tempDir, cfg.BlsPasswordFile()) - t.Run("set default config", func(t *testing.T) { - blsCfg := DefaultBlsConfig() - assert.NotNil(t, blsCfg) - assert.Equal(t, blsCfg.BlsKeyPath, filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName)) + 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() - t.Log("password", password) + pv.Key.Save(password, "") - t.Run("generate key without mnemonic", func(t *testing.T) { - blsPubKey, err := InitializeBlsFile(&blsCfg, password) - assert.NoError(t, err) - assert.NotNil(t, blsPubKey) - }) + t.Run("load bls key from file", func(t *testing.T) { + loadedPv := LoadBlsPV(keyFilePath, passwordFilePath) + assert.NotNil(t, loadedPv) - t.Run("load key with password", func(t *testing.T) { - blsPubKey, err := InitializeBlsFile(&blsCfg, password) - assert.NoError(t, err) - assert.NotNil(t, blsPubKey) - }) - - t.Run("clean file path", func(t *testing.T) { - blsKeyFilePath := DefaultBlsConfig().BlsKeyFile() - t.Log("bls key file path", blsKeyFilePath) - cleanup(blsKeyFilePath) + assert.Equal(t, pv.Key.PrivKey, loadedPv.Key.PrivKey) + assert.Equal(t, pv.Key.PubKey.Bytes(), loadedPv.Key.PubKey.Bytes()) }) }) -} - -func TestSavePasswordToFile(t *testing.T) { - blsCfg := DefaultBlsConfig() + t.Run("save bls key to file with delegator address", func(t *testing.T) { + pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, "") + assert.NotNil(t, pv) - t.Run("failed to load unsaved file", func(t *testing.T) { - _, err := erc2335.LoadPaswordFromFile(blsCfg.BlsPasswordFile()) - assert.Error(t, err) - }) - - t.Run("create password file", func(t *testing.T) { password := erc2335.CreateRandomPassword() - t.Log("password", password) - err := os.MkdirAll(filepath.Dir(blsCfg.BlsPasswordFile()), 0o777) - assert.NoError(t, err) + delegatorAddress := types.AccAddress(ed25519.GenPrivKey().PubKey().Address()).String() + pv.Key.Save(password, delegatorAddress) - err = erc2335.SavePasswordToFile(password, blsCfg.BlsPasswordFile()) - assert.NoError(t, err) + t.Run("load bls key from file", func(t *testing.T) { + loadedPv := LoadBlsPV(keyFilePath, passwordFilePath) + assert.NotNil(t, loadedPv) - t.Run("load password file", func(t *testing.T) { - - loadPassword, err := erc2335.LoadPaswordFromFile(blsCfg.BlsPasswordFile()) - assert.NoError(t, err) - assert.Equal(t, password, loadPassword) + 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) }) }) - - t.Run("clean file path", func(t *testing.T) { - blsPasswordFilePath := DefaultBlsConfig().BlsPasswordFile() - t.Log("bls passwordd file path", blsPasswordFilePath) - cleanup(blsPasswordFilePath) - }) -} - -func cleanup(blsKeyPath string) { - _ = os.RemoveAll(filepath.Dir(blsKeyPath)) } diff --git a/privval/file.go b/privval/file.go index 879f94e64..dbdffdd71 100644 --- a/privval/file.go +++ b/privval/file.go @@ -1,56 +1,26 @@ package privval import ( - "encoding/json" "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/cosmos/cosmos-sdk/crypto/codec" "github.com/babylonlabs-io/babylon/crypto/bls12381" - "github.com/babylonlabs-io/babylon/crypto/erc2335" 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 { - CometPVKey privval.FilePVKey - BlsPVKey BlsPVKey - DelegatorAddress string + CometPVKey privval.FilePVKey + BlsPVKey BlsPVKey } // WrappedFilePV wraps FilePV with WrappedFilePVKey. @@ -59,99 +29,6 @@ type WrappedFilePV struct { LastSignState privval.FilePVLastSignState } -// GenWrappedFilePV generates a new validator with randomly generated private key -// and sets the filePaths, but does not call Save(). -func GenWrappedFilePV(cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { - cometPv := privval.NewFilePV(ed25519.GenPrivKey(), cmtKeyFilePath, cmtStateFilePath) - blsPv := NewBlsPV(bls12381.GenPrivKey(), blsKeyFilePath, blsPasswordFilePath) - return &WrappedFilePV{ - Key: WrappedFilePVKey{ - CometPVKey: cometPv.Key, - BlsPVKey: blsPv.Key, - }, - LastSignState: cometPv.LastSignState, - } -} - -func GenWrappedFilePVWithMnemonic(mnemonic, cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { - cometPv := privval.NewFilePV(ed25519.GenPrivKeyFromSecret([]byte(mnemonic)), cmtKeyFilePath, cmtStateFilePath) - blsPv := NewBlsPV(bls12381.GenPrivKeyFromSecret([]byte(mnemonic)), blsKeyFilePath, blsPasswordFilePath) - return &WrappedFilePV{ - Key: WrappedFilePVKey{ - CometPVKey: cometPv.Key, - BlsPVKey: blsPv.Key, - }, - LastSignState: cometPv.LastSignState, - } -} - -// LoadOrGenWrappedFilePV loads a FilePV from the given filePaths -// or else generates a new one and saves it to the filePaths. -func LoadOrGenWrappedFilePV(cmtKeyFilePath, cmtStateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { - - var blsPV *BlsPV - - if !cmtos.FileExists(blsKeyFilePath) { - var blsPassword string - var err error - if cmtos.FileExists(blsPasswordFilePath) { - blsPassword, err = erc2335.LoadPaswordFromFile(blsPasswordFilePath) - if err != nil { - cmtos.Exit(fmt.Sprintf("failed to read BLS password file: %v", err.Error())) - } - } else { - blsPassword = erc2335.CreateRandomPassword() - } - - blsPV = NewBlsPV(bls12381.GenPrivKey(), blsKeyFilePath, blsPassword) - blsPV.Save(blsPassword) - } else { - blsPV = LoadBlsPV(blsKeyFilePath, blsPasswordFilePath) - } - - var cometPV *privval.FilePV - if cmtos.FileExists(cmtKeyFilePath) { - cometPV = privval.LoadFilePV(cmtKeyFilePath, cmtStateFilePath) - } else { - cometPV = privval.GenFilePV(cmtKeyFilePath, cmtStateFilePath) - cometPV.Key.Save() - } - - wrappedFilePV := &WrappedFilePV{ - Key: WrappedFilePVKey{ - CometPVKey: cometPV.Key, - BlsPVKey: blsPV.Key, - }, - LastSignState: cometPV.LastSignState, - } - - return wrappedFilePV -} - -func LoadWrappedFilePV(keyFilePath, stateFilePath, blsKeyFilePath, blsPasswordFilePath string) *WrappedFilePV { - - if !cmtos.FileExists(blsKeyFilePath) { - cmtos.Exit(fmt.Sprintf("BLS key file does not exist: %v", blsKeyFilePath)) - } - - blsPv := LoadBlsPV(blsKeyFilePath, blsPasswordFilePath) - - if !cmtos.FileExists(keyFilePath) { - cmtos.Exit(fmt.Sprintf("validator key file does not exist: %v", keyFilePath)) - } - - cometPv := privval.LoadFilePV(keyFilePath, stateFilePath) - - return &WrappedFilePV{ - Key: WrappedFilePVKey{ - CometPVKey: cometPv.Key, - BlsPVKey: blsPv.Key, - DelegatorAddress: ReadDelegatorAddressFromFile(blsKeyFilePath), - }, - LastSignState: cometPv.LastSignState, - } -} - // 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) { @@ -191,65 +68,16 @@ 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() - SaveDelegatorAddressToFile(pv.Key.DelegatorAddress, pv.Key.BlsPVKey.filePath) -} - -func SaveDelegatorAddressToFile(delegatorAddress, filePath string) { - - var data map[string]interface{} - if err := ReadJSON(filePath, &data); err != nil { - cmtos.Exit(fmt.Sprintf("Failed to read JSON file: %v\n", err)) - } - - data["description"] = delegatorAddress - if err := WriteJSON(filePath, data); err != nil { - cmtos.Exit(fmt.Sprintf("Failed to write to JSON file: %v\n", err)) - } -} - -func ReadDelegatorAddressFromFile(filePath string) string { - - var data map[string]interface{} - if err := ReadJSON(filePath, &data); err != nil { - cmtos.Exit(fmt.Sprintf("Failed to read JSON file: %v\n", err)) - } - return data["description"].(string) -} - -func WriteJSON(filePath string, v interface{}) error { - jsonBytes, err := json.MarshalIndent(v, "", " ") - if err != nil { - return err - } - return os.WriteFile(filePath, jsonBytes, 0644) -} - -func ReadJSON(filePath string, v interface{}) error { - file, err := os.Open(filePath) - if err != nil { - if os.IsNotExist(err) { - *v.(*map[string]interface{}) = make(map[string]interface{}) - return nil - } - return err - } - defer file.Close() - return json.NewDecoder(file).Decode(v) -} - // GetPubKey returns the public key of the validator. func (pv *WrappedFilePV) GetPubKey() (cmtcrypto.PubKey, error) { return pv.Key.CometPVKey.PubKey, nil @@ -286,16 +114,3 @@ func (pv *WrappedFilePV) SignMsgWithBls(msg []byte) (bls12381.Signature, error) } return bls12381.Sign(blsPrivKey, msg), nil } - -// Clean removes PVKey file and PVState file -func (pv *WrappedFilePV) Clean(paths ...string) { - for _, path := range paths { - _ = os.RemoveAll(filepath.Dir(path)) - } -} - -func (pv *WrappedFilePV) Save(password string) { - pv.Key.CometPVKey.Save() - pv.Key.BlsPVKey.Save(password) - pv.LastSignState.Save() -} diff --git a/privval/file_test.go b/privval/file_test.go deleted file mode 100644 index 4afcb9d35..000000000 --- a/privval/file_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package privval - -import ( - "encoding/json" - "os" - "path/filepath" - "testing" - - "github.com/babylonlabs-io/babylon/crypto/erc2335" - cmtcfg "github.com/cometbft/cometbft/config" - cmtos "github.com/cometbft/cometbft/libs/os" - - "github.com/cosmos/cosmos-sdk/types" - "github.com/test-go/testify/assert" -) - -func TestSaveDelegatorAddressToFile(t *testing.T) { - - tempDir := t.TempDir() - defer os.RemoveAll(tempDir) - - ccfg := cmtcfg.DefaultConfig() - ccfg.SetRoot(tempDir) - bcfg := DefaultBlsConfig() - bcfg.SetRoot(tempDir) - - pvKeyFile := filepath.Join(tempDir, ccfg.PrivValidatorKeyFile()) - pvStateFile := filepath.Join(tempDir, ccfg.PrivValidatorStateFile()) - blsKeyFile := filepath.Join(tempDir, bcfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(tempDir, bcfg.BlsPasswordFile()) - - err := cmtos.EnsureDir(filepath.Dir(pvKeyFile), 0777) - assert.Nil(t, err) - err = cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777) - assert.Nil(t, err) - err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) - assert.Nil(t, err) - err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) - assert.Nil(t, err) - - pv := GenWrappedFilePV( - pvKeyFile, - pvStateFile, - blsKeyFile, - blsPasswordFile, - ) - - pv.Save("test") - - t.Run("save delegator address", func(t *testing.T) { - delegatorAddress := pv.Key.CometPVKey.PubKey.Address() - t.Log("delegatorAddress: ", delegatorAddress) - - // addr, err := sdk.AccAddressFromBech32(delegatorAddress.String()) - // assert.NoError(t, err) - // t.Log("sdk.AccAddressFromBech32(delegatorAddress.String()): ", addr) - - pv.SetAccAddress(types.AccAddress(delegatorAddress)) - - var keystore erc2335.Erc2335KeyStore - keyJSONBytes, err := os.ReadFile(blsKeyFile) - assert.NoError(t, err) - err = json.Unmarshal(keyJSONBytes, &keystore) - assert.NoError(t, err) - - // t.Log(keystore) - t.Log(keystore.Description) - }) - - t.Run("load delegator address", func(t *testing.T) { - delegatorAddress := ReadDelegatorAddressFromFile(blsKeyFile) - assert.Equal(t, delegatorAddress, pv.Key.DelegatorAddress) - }) -} 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/initialization/config.go b/test/e2e/initialization/config.go index e61985e7c..5a5e2d32c 100644 --- a/test/e2e/initialization/config.go +++ b/test/e2e/initialization/config.go @@ -416,7 +416,7 @@ func updateCheckpointingGenesis(c *internalChain) func(*checkpointingtypes.Genes 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") diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index 3e674f5fb..d148c0d00 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -8,9 +8,10 @@ import ( "path/filepath" "strings" + "github.com/cometbft/cometbft/crypto/ed25519" + "cosmossdk.io/math" cmtconfig "github.com/cometbft/cometbft/config" - 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" @@ -33,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/erc2335" "github.com/babylonlabs-io/babylon/privval" "github.com/babylonlabs-io/babylon/test/e2e/util" + cmtprivval "github.com/cometbft/cometbft/privval" ) type internalNode struct { @@ -158,44 +159,45 @@ 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 { - return err - } - - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(config.RootDir) blsKeyFile := blsCfg.BlsKeyFile() - if err := cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0o777); err != nil { - return err - } - blsPasswordFile := blsCfg.BlsPasswordFile() - if err := cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0o777); err != nil { + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return err } - // for test e2e - // bls-password is random generated - blsPassword := erc2335.CreateRandomPassword() - - filePV := privval.GenWrappedFilePVWithMnemonic(n.mnemonic, pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) - filePV.Save(blsPassword) - + // delegator address accAddress, _ := n.keyInfo.GetAddress() - filePV.SetAccAddress(accAddress) - n.consensusKey = filePV.Key + // create new key for consensus + // 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() + // bls pv + blsPV := privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", accAddress.String()) + + // n.consensusKey = filePV.Key + n.consensusKey = privval.WrappedFilePVKey{ + CometPVKey: filePV.Key, + BlsPVKey: blsPV.Key, + } return nil } diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index 827a9ca4f..10ee141c9 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -2,15 +2,16 @@ package datagen import ( "fmt" - "path/filepath" cfg "github.com/cometbft/cometbft/config" - 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/privval" + cmtos "github.com/cometbft/cometbft/libs/os" + cmtprivval "github.com/cometbft/cometbft/privval" ) // InitializeNodeValidatorFiles creates private validator and p2p configuration files. @@ -31,30 +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 { - return "", nil, err - } - pvStateFile := config.PrivValidatorStateFile() - if err := cmtos.EnsureDir(filepath.Dir(pvStateFile), 0777); err != nil { + + if err := privval.IsValidFilePath(pvKeyFile, pvStateFile); err != nil { return "", nil, err } - // add bls config and set root same as config + // bls config blsCfg := privval.DefaultBlsConfig() blsCfg.SetRoot(config.RootDir) - var filePV *privval.WrappedFilePV - if len(mnemonic) == 0 { - filePV = privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsCfg.BlsKeyFile(), blsCfg.BlsPasswordFile()) + blsKeyFile := blsCfg.BlsKeyFile() + blsPasswordFile := blsCfg.BlsPasswordFile() + if err := privval.IsValidFilePath(blsKeyFile, blsPasswordFile); err != nil { + return "", nil, err + } + + // 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 { - filePV = privval.GenWrappedFilePVWithMnemonic(mnemonic, pvKeyFile, pvStateFile, blsCfg.BlsKeyFile(), blsCfg.BlsPasswordFile()) + 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 dd6eab288..4423d0866 100644 --- a/testutil/helper/helper.go +++ b/testutil/helper/helper.go @@ -61,7 +61,7 @@ func NewHelperWithValSet(t *testing.T, valSet *datagen.GenesisValidators, privSi // generate the genesis account 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{ @@ -99,7 +99,7 @@ func NewHelperWithValSetNoSigner(t *testing.T, valSet *datagen.GenesisValidators // generate the genesis account 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 a46f3e862..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,7 +24,13 @@ func SetupTestPrivSigner() (*signer.PrivSigner, error) { defer func() { _ = os.RemoveAll(nodeDir) }() - privSigner, _ := signer.InitTestPrivSigner(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 24c37771d..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,26 +103,27 @@ func (s *CLITestSuite) SetupSuite() { func (s *CLITestSuite) TestCmdWrappedCreateValidator() { require := s.Require() homeDir := s.T().TempDir() - 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) - require.NoError(err) + // create BLS keys + nodeCfg := cmtconfig.DefaultConfig() blsCfg := privval.DefaultBlsConfig() + + keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) + statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) blsKeyFile := filepath.Join(homeDir, blsCfg.BlsKeyFile()) - err = cmtos.EnsureDir(filepath.Dir(blsKeyFile), 0777) - require.NoError(err) blsPasswordFile := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) - err = cmtos.EnsureDir(filepath.Dir(blsPasswordFile), 0777) + + err := privval.IsValidFilePath(keyPath, statePath, blsKeyFile, blsPasswordFile) require.NoError(err) - wrappedPV := privval.LoadOrGenWrappedFilePV(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile) + 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 7d4161390..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,19 +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") - } - - blsCfg := privval.DefaultBlsConfig() blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) - if !cmtos.FileExists(blsKeyPath) { - return nil, errors.New("validator bls key file does not exist") + + if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + return nil, err } - wrappedPV := privval.LoadWrappedFilePV(keyPath, statePath, blsKeyPath, blsPasswordPath) - 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) } From 80375689ff5727c2c030a631ec24f01e8fb18294 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Fri, 10 Jan 2025 21:16:16 +0900 Subject: [PATCH 6/8] feat: update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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 From 1f14b31db3dc3aef8655e416aae90ca12e0e8557 Mon Sep 17 00:00:00 2001 From: wonjoon Date: Tue, 14 Jan 2025 21:48:13 +0900 Subject: [PATCH 7/8] feat: add bls password flag option, refactor comments and fix lint --- app/signer/private.go | 14 +++-- client/flags/flags.go | 5 -- cmd/babylond/cmd/create_bls_key.go | 30 ++++++----- cmd/babylond/cmd/genhelpers/bls_add_test.go | 8 ++- cmd/babylond/cmd/genhelpers/bls_create.go | 34 ++++++++----- .../cmd/genhelpers/bls_create_test.go | 12 ++--- privval/bls.go | 51 ++++--------------- privval/bls_test.go | 8 +-- privval/util.go | 2 +- test/e2e/initialization/node.go | 18 ++----- testutil/datagen/btc_blockchain.go | 2 +- testutil/datagen/init_val.go | 27 +++------- testutil/signer/private.go | 15 +++--- x/checkpointing/client/cli/tx_test.go | 15 +++--- x/checkpointing/client/cli/utils.go | 14 ++--- 15 files changed, 104 insertions(+), 151 deletions(-) delete mode 100644 client/flags/flags.go diff --git a/app/signer/private.go b/app/signer/private.go index d95fdc910..c2839a673 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -1,8 +1,6 @@ package signer import ( - "path/filepath" - cmtconfig "github.com/cometbft/cometbft/config" "github.com/babylonlabs-io/babylon/privval" @@ -15,14 +13,14 @@ type PrivSigner struct { func InitPrivSigner(nodeDir string) (*PrivSigner, error) { nodeCfg := cmtconfig.DefaultConfig() - blsCfg := privval.DefaultBlsConfig() + nodeCfg.SetRoot(nodeDir) - pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) - pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + pvKeyFile := nodeCfg.PrivValidatorKeyFile() + pvStateFile := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(nodeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) - if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return nil, err } diff --git a/client/flags/flags.go b/client/flags/flags.go deleted file mode 100644 index 5e58fe954..000000000 --- a/client/flags/flags.go +++ /dev/null @@ -1,5 +0,0 @@ -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 6a6b62ea8..a3f8c755a 100644 --- a/cmd/babylond/cmd/create_bls_key.go +++ b/cmd/babylond/cmd/create_bls_key.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "path/filepath" "strings" "github.com/cosmos/cosmos-sdk/client/flags" @@ -14,6 +13,10 @@ import ( "github.com/babylonlabs-io/babylon/privval" ) +const ( + FlagPassword = "bls-password" +) + func CreateBlsKeyCmd() *cobra.Command { bech32PrefixAccAddr := appparams.Bech32PrefixAccAddr @@ -43,23 +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 { - - blsCfg := privval.DefaultBlsConfig() - keyPath := filepath.Join(home, blsCfg.BlsKeyFile()) - passwordPath := filepath.Join(home, blsCfg.BlsPasswordFile()) - - password := privval.GetBlsPassword() - - privval.GenBlsPV(keyPath, passwordPath, password, addr.String()) +func CreateBlsKey(home, password string, addr sdk.AccAddress) error { + privval.GenBlsPV( + privval.DefaultBlsKeyFile(home), + privval.DefaultBlsPasswordFile(home), + password, + addr.String(), + ) return nil } diff --git a/cmd/babylond/cmd/genhelpers/bls_add_test.go b/cmd/babylond/cmd/genhelpers/bls_add_test.go index 4c7b8f21e..12bb9b85f 100644 --- a/cmd/babylond/cmd/genhelpers/bls_add_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_add_test.go @@ -153,15 +153,13 @@ func Test_CmdAddBlsWithGentx(t *testing.T) { nodeCfg := cmtconfig.DefaultConfig() nodeCfg.SetRoot(homeDir) - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(homeDir) keyPath := nodeCfg.PrivValidatorKeyFile() statePath := nodeCfg.PrivValidatorStateFile() - blsKeyFile := blsCfg.BlsKeyFile() - blsPasswordFile := blsCfg.BlsPasswordFile() + blsKeyFile := privval.DefaultBlsKeyFile(homeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(homeDir) - err := privval.IsValidFilePath(keyPath, statePath, blsKeyFile, blsPasswordFile) + err := privval.EnsureDirs(keyPath, statePath, blsKeyFile, blsPasswordFile) require.NoError(t, err) filePV := cmtprivval.GenFilePV(keyPath, statePath) diff --git a/cmd/babylond/cmd/genhelpers/bls_create.go b/cmd/babylond/cmd/genhelpers/bls_create.go index 357139863..2bdfcc29a 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create.go +++ b/cmd/babylond/cmd/genhelpers/bls_create.go @@ -1,10 +1,12 @@ package genhelpers import ( + "fmt" "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" @@ -34,29 +36,35 @@ $ 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()) - blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) - blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) - - if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + 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 } - filePV := cmtprivval.LoadFilePV(keyPath, statePath) - blsPV := privval.LoadBlsPV(blsKeyPath, blsPasswordPath) + cmtPV := cmtprivval.LoadFilePV(cmtPvKeyFile, cmtPvStateFile) + blsPV := privval.LoadBlsPV(blsKeyFile, blsPasswordFile) wrappedPV := &privval.WrappedFilePV{ Key: privval.WrappedFilePVKey{ - CometPVKey: filePV.Key, + CometPVKey: cmtPV.Key, BlsPVKey: blsPV.Key, }, - LastSignState: filePV.LastSignState, + 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 b80f1bbad..ea157304e 100644 --- a/cmd/babylond/cmd/genhelpers/bls_create_test.go +++ b/cmd/babylond/cmd/genhelpers/bls_create_test.go @@ -71,14 +71,14 @@ func Test_CmdCreateBls(t *testing.T) { // create BLS keys nodeCfg := cmtconfig.DefaultConfig() - blsCfg := privval.DefaultBlsConfig() + nodeCfg.SetRoot(home) - keyPath := filepath.Join(home, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(home, nodeCfg.PrivValidatorStateFile()) - blsKeyFile := filepath.Join(home, blsCfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(home, blsCfg.BlsPasswordFile()) + keyPath := nodeCfg.PrivValidatorKeyFile() + statePath := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(home) + blsPasswordFile := privval.DefaultBlsPasswordFile(home) - err = privval.IsValidFilePath(keyPath, statePath, blsKeyFile, blsPasswordFile) + err = privval.EnsureDirs(keyPath, statePath, blsKeyFile, blsPasswordFile) require.NoError(t, err) filePV := cmtprivval.GenFilePV(keyPath, statePath) diff --git a/privval/bls.go b/privval/bls.go index e990db8e3..b68884ab9 100644 --- a/privval/bls.go +++ b/privval/bls.go @@ -20,6 +20,11 @@ const ( DefaultBlsPasswordName = "bls_password.txt" ) +var ( + defaultBlsKeyFilePath = filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsKeyName) + defaultBlsPasswordPath = filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsPasswordName) +) + type BlsPV struct { Key BlsPVKey } @@ -81,7 +86,7 @@ func LoadBlsPV(keyFilePath, passwordFilePath string) *BlsPV { } } -func GetBlsPassword() string { +func NewBlsPassword() string { inBuf := bufio.NewReader(os.Stdin) password, err := input.GetString("Enter your bls password", inBuf) if err != nil { @@ -93,11 +98,6 @@ func GetBlsPassword() string { // 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 { @@ -131,41 +131,10 @@ func (k *BlsPVKey) Save(password, addr string) { } } -// ------------------------------------------------------------------------------- -// ---------------------------- 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) +func DefaultBlsKeyFile(home string) string { + return filepath.Join(home, defaultBlsKeyFilePath) } -// 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) +func DefaultBlsPasswordFile(home string) string { + return filepath.Join(home, defaultBlsPasswordPath) } diff --git a/privval/bls_test.go b/privval/bls_test.go index 6e6c134e5..f88417140 100644 --- a/privval/bls_test.go +++ b/privval/bls_test.go @@ -2,7 +2,6 @@ package privval import ( "os" - "path/filepath" "testing" "github.com/cometbft/cometbft/crypto/ed25519" @@ -17,10 +16,11 @@ func TestNewBlsPV(t *testing.T) { tempDir := t.TempDir() defer os.RemoveAll(tempDir) - cfg := DefaultBlsConfig() + keyFilePath := DefaultBlsKeyFile(tempDir) + passwordFilePath := DefaultBlsPasswordFile(tempDir) - keyFilePath := filepath.Join(tempDir, cfg.BlsKeyFile()) - passwordFilePath := filepath.Join(tempDir, cfg.BlsPasswordFile()) + 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, "") diff --git a/privval/util.go b/privval/util.go index 802240580..3535c4056 100644 --- a/privval/util.go +++ b/privval/util.go @@ -7,7 +7,7 @@ import ( cmtos "github.com/cometbft/cometbft/libs/os" ) -func IsValidFilePath(paths ...string) error { +func EnsureDirs(paths ...string) error { // Check file path of bls key for _, path := range paths { if path == "" { diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index d148c0d00..eb894ae28 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -160,26 +160,19 @@ func (n *internalNode) createConsensusKey() error { serverCtx := server.NewDefaultContext() config := serverCtx.Config config.SetRoot(n.configDir()) - config.Moniker = n.moniker - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(n.configDir()) - pvKeyFile := config.PrivValidatorKeyFile() pvStateFile := config.PrivValidatorStateFile() - blsKeyFile := blsCfg.BlsKeyFile() - blsPasswordFile := blsCfg.BlsPasswordFile() + blsKeyFile := privval.DefaultBlsKeyFile(n.configDir()) + blsPasswordFile := privval.DefaultBlsPasswordFile(n.configDir()) - if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { return err } - - // delegator address accAddress, _ := n.keyInfo.GetAddress() - // create new key for consensus - // file pv + // create file pv var privKey ed25519.PrivKey if n.mnemonic == "" { privKey = ed25519.GenPrivKey() @@ -190,10 +183,9 @@ func (n *internalNode) createConsensusKey() error { filePV.Key.Save() filePV.LastSignState.Save() - // bls pv + // create bls pv blsPV := privval.GenBlsPV(blsKeyFile, blsPasswordFile, "password", accAddress.String()) - // n.consensusKey = filePV.Key n.consensusKey = privval.WrappedFilePVKey{ CometPVKey: filePV.Key, BlsPVKey: blsPV.Key, 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/init_val.go b/testutil/datagen/init_val.go index 10ee141c9..ac33ea596 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -31,27 +31,17 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin nodeID = string(nodeKey.ID()) - pvKeyFile := config.PrivValidatorKeyFile() - pvStateFile := config.PrivValidatorStateFile() - - if err := privval.IsValidFilePath(pvKeyFile, pvStateFile); err != nil { - return "", nil, err - } - - // bls config - blsCfg := privval.DefaultBlsConfig() - blsCfg.SetRoot(config.RootDir) - - blsKeyFile := blsCfg.BlsKeyFile() - blsPasswordFile := blsCfg.BlsPasswordFile() - if err := privval.IsValidFilePath(blsKeyFile, blsPasswordFile); err != nil { + 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, err } - // load or generate private validator var filePV *cmtprivval.FilePV - if cmtos.FileExists(pvKeyFile) { - filePV = cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) + if cmtos.FileExists(cmtKeyFile) { + filePV = cmtprivval.LoadFilePV(cmtKeyFile, cmtStateFile) } else { var privKey ed25519.PrivKey if len(mnemonic) == 0 { @@ -59,12 +49,11 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin } else { privKey = ed25519.GenPrivKeyFromSecret([]byte(mnemonic)) } - filePV = cmtprivval.NewFilePV(privKey, pvKeyFile, pvStateFile) + filePV = cmtprivval.NewFilePV(privKey, cmtKeyFile, cmtStateFile) 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 diff --git a/testutil/signer/private.go b/testutil/signer/private.go index 8d1d4538e..c1589ddae 100644 --- a/testutil/signer/private.go +++ b/testutil/signer/private.go @@ -2,7 +2,6 @@ package signer import ( "os" - "path/filepath" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cosmosed "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -53,18 +52,18 @@ func GenesisKeyFromPrivSigner(ps *signer.PrivSigner) (*checkpointingtypes.Genesi func GeneratePrivSigner(nodeDir string) error { nodeCfg := cmtconfig.DefaultConfig() - blsCfg := privval.DefaultBlsConfig() + nodeCfg.SetRoot(nodeDir) - pvKeyFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorKeyFile()) - pvStateFile := filepath.Join(nodeDir, nodeCfg.PrivValidatorStateFile()) - blsKeyFile := filepath.Join(nodeDir, blsCfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(nodeDir, blsCfg.BlsPasswordFile()) + cmtKeyFile := nodeCfg.PrivValidatorKeyFile() + cmtStateFile := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(nodeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) - if err := privval.IsValidFilePath(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { + if err := privval.EnsureDirs(cmtKeyFile, cmtStateFile, blsKeyFile, blsPasswordFile); err != nil { return err } - cometPV := cmtprivval.GenFilePV(pvKeyFile, pvStateFile) + cometPV := cmtprivval.GenFilePV(cmtKeyFile, cmtStateFile) cometPV.Key.Save() cometPV.LastSignState.Save() diff --git a/x/checkpointing/client/cli/tx_test.go b/x/checkpointing/client/cli/tx_test.go index 5cccefdaf..3a12e687f 100644 --- a/x/checkpointing/client/cli/tx_test.go +++ b/x/checkpointing/client/cli/tx_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "path/filepath" "testing" sdkmath "cosmossdk.io/math" @@ -106,17 +105,17 @@ func (s *CLITestSuite) TestCmdWrappedCreateValidator() { // create BLS keys nodeCfg := cmtconfig.DefaultConfig() - blsCfg := privval.DefaultBlsConfig() + nodeCfg.SetRoot(homeDir) - keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - blsKeyFile := filepath.Join(homeDir, blsCfg.BlsKeyFile()) - blsPasswordFile := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + cmtKeyPath := nodeCfg.PrivValidatorKeyFile() + cmtStatePath := nodeCfg.PrivValidatorStateFile() + blsKeyFile := privval.DefaultBlsKeyFile(homeDir) + blsPasswordFile := privval.DefaultBlsPasswordFile(homeDir) - err := privval.IsValidFilePath(keyPath, statePath, blsKeyFile, blsPasswordFile) + err := privval.EnsureDirs(cmtKeyPath, cmtStatePath, blsKeyFile, blsPasswordFile) require.NoError(err) - filePV := cmtprivval.GenFilePV(keyPath, statePath) + filePV := cmtprivval.GenFilePV(cmtKeyPath, cmtStatePath) filePV.Key.Save() filePV.LastSignState.Save() diff --git a/x/checkpointing/client/cli/utils.go b/x/checkpointing/client/cli/utils.go index e25bbc750..f29998631 100644 --- a/x/checkpointing/client/cli/utils.go +++ b/x/checkpointing/client/cli/utils.go @@ -204,18 +204,18 @@ func buildCommissionRates(rateStr, maxRateStr, maxChangeRateStr string) (commiss func getValKeyFromFile(homeDir string) (*privval.ValidatorKeys, error) { nodeCfg := cmtconfig.DefaultConfig() - blsCfg := privval.DefaultBlsConfig() + nodeCfg.SetRoot(homeDir) - keyPath := filepath.Join(homeDir, nodeCfg.PrivValidatorKeyFile()) - statePath := filepath.Join(homeDir, nodeCfg.PrivValidatorStateFile()) - blsKeyPath := filepath.Join(homeDir, blsCfg.BlsKeyFile()) - blsPasswordPath := filepath.Join(homeDir, blsCfg.BlsPasswordFile()) + cmtKeyPath := nodeCfg.PrivValidatorKeyFile() + cmtStatePath := nodeCfg.PrivValidatorStateFile() + blsKeyPath := privval.DefaultBlsKeyFile(homeDir) + blsPasswordPath := privval.DefaultBlsPasswordFile(homeDir) - if err := privval.IsValidFilePath(keyPath, statePath, blsKeyPath, blsPasswordPath); err != nil { + if err := privval.EnsureDirs(cmtKeyPath, cmtStatePath, blsKeyPath, blsPasswordPath); err != nil { return nil, err } - filePV := cmtprivval.LoadFilePV(keyPath, statePath) + filePV := cmtprivval.LoadFilePV(cmtKeyPath, cmtStatePath) blsPV := privval.LoadBlsPV(blsKeyPath, blsPasswordPath) return privval.NewValidatorKeys(filePV.Key.PrivKey, blsPV.Key.PrivKey) From 9dd45f35b08314b5c93bc2fdcd541344ffefc3db Mon Sep 17 00:00:00 2001 From: wonjoon Date: Wed, 15 Jan 2025 15:31:05 +0900 Subject: [PATCH 8/8] fix: add comments, wrapping errors and remove unused functions --- app/signer/private.go | 4 +- crypto/erc2335/erc2335.go | 59 ++++++++++------------------- crypto/erc2335/erc2335_test.go | 23 ++++++----- privval/bls.go | 43 +++++++++++++-------- privval/bls_test.go | 5 +-- test/e2e/initialization/node.go | 2 +- testutil/datagen/init_val.go | 2 +- testutil/signer/private.go | 3 +- x/checkpointing/client/cli/utils.go | 2 +- 9 files changed, 67 insertions(+), 76 deletions(-) diff --git a/app/signer/private.go b/app/signer/private.go index c2839a673..491658b36 100644 --- a/app/signer/private.go +++ b/app/signer/private.go @@ -1,6 +1,8 @@ package signer import ( + "fmt" + cmtconfig "github.com/cometbft/cometbft/config" "github.com/babylonlabs-io/babylon/privval" @@ -21,7 +23,7 @@ func InitPrivSigner(nodeDir string) (*PrivSigner, error) { blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { - return nil, err + return nil, fmt.Errorf("failed to ensure dirs: %w", err) } cometPV := cmtprivval.LoadFilePV(pvKeyFile, pvStateFile) diff --git a/crypto/erc2335/erc2335.go b/crypto/erc2335/erc2335.go index 6933500b5..e018f7250 100644 --- a/crypto/erc2335/erc2335.go +++ b/crypto/erc2335/erc2335.go @@ -1,39 +1,35 @@ package erc2335 import ( - "crypto/rand" - "encoding/hex" "encoding/json" "fmt" "os" - "github.com/pkg/errors" 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"` - Version uint `json:"version"` - UUID string `json:"uuid"` - Path string `json:"path"` - Pubkey string `json:"pubkey"` - Description string `json:"description"` + 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. } -// wonjoon: encrypt key pair to erc2335 keystore -// available to handle all keys in []byte format +// Encrypt encrypts the private key using the keystorev4 encryptor. func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { if privKey == nil { - return nil, errors.New("private key cannot be nil") + return nil, fmt.Errorf("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") + return nil, fmt.Errorf("failed to encrypt private key: %w", err) } - // Create the keystore json structure keystoreJSON := Erc2335KeyStore{ Crypto: cryptoFields, Version: 4, @@ -43,41 +39,24 @@ func Encrypt(privKey, pubKey []byte, password string) ([]byte, error) { 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{}, err + return Erc2335KeyStore{}, fmt.Errorf("failed to read keystore file: %w", err) } if err := json.Unmarshal(keyJSONBytes, &keystore); err != nil { - return Erc2335KeyStore{}, err + return Erc2335KeyStore{}, fmt.Errorf("failed to unmarshal keystore: %w", 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 index aa56f650e..9706b599a 100644 --- a/crypto/erc2335/erc2335_test.go +++ b/crypto/erc2335/erc2335_test.go @@ -6,17 +6,18 @@ import ( "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) { - password := CreateRandomPassword() - t.Logf("password: %s", password) encryptedBlsKey, err := Encrypt(blsPrivKey, blsPubKey, password) require.NoError(t, err) t.Logf("encrypted bls key: %s", encryptedBlsKey) @@ -41,18 +42,16 @@ func TestEncryptBLS(t *testing.T) { }) 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") + err = tempfile.WriteFileAtomic("password.txt", []byte(password), 0600) require.NoError(t, err) t.Run("load password and decrypt bls key", func(t *testing.T) { - password, err := LoadPaswordFromFile("password.txt") + passwordBytes, err := os.ReadFile("password.txt") require.NoError(t, err) + password := string(passwordBytes) var keystore Erc2335KeyStore err = json.Unmarshal(encryptedBlsKey, &keystore) @@ -64,15 +63,15 @@ func TestEncryptBLS(t *testing.T) { }) 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") + 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) { - password, err := LoadPaswordFromFile("password.txt") + passwordBytes, err := os.ReadFile("password.txt") require.NoError(t, err) + password := string(passwordBytes) var keystore Erc2335KeyStore err = json.Unmarshal(encryptedBlsKey, &keystore) @@ -83,7 +82,7 @@ func TestEncryptBLS(t *testing.T) { }) t.Run("failed when password file don't exist", func(t *testing.T) { - _, err := LoadPaswordFromFile("nopassword.txt") + _, err := os.ReadFile("nopassword.txt") require.Error(t, err) }) }) diff --git a/privval/bls.go b/privval/bls.go index b68884ab9..1621865ba 100644 --- a/privval/bls.go +++ b/privval/bls.go @@ -16,29 +16,34 @@ import ( ) const ( - DefaultBlsKeyName = "bls_key.json" - DefaultBlsPasswordName = "bls_password.txt" + 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) - defaultBlsPasswordPath = filepath.Join(cmtcfg.DefaultConfigDir, DefaultBlsPasswordName) + 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"` - PrivKey bls12381.PrivateKey `json:"bls_priv_key"` - - DelegatorAddress string - - filePath string - passwordPath string + 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{ @@ -51,17 +56,21 @@ func NewBlsPV(privKey bls12381.PrivateKey, keyFilePath, passwordFilePath, delega } } +// 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 { - password, err := erc2335.LoadPaswordFromFile(passwordFilePath) + 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 { @@ -86,6 +95,7 @@ func LoadBlsPV(keyFilePath, passwordFilePath string) *BlsPV { } } +// 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) @@ -95,8 +105,8 @@ func NewBlsPassword() string { return password } -// Save bls key using password -// Check both paths of bls key and password inside function +// 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) @@ -125,16 +135,17 @@ func (k *BlsPVKey) Save(password, addr string) { } // save used password to file - err = erc2335.SavePasswordToFile(password, k.passwordPath) - if err != nil { + 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 index f88417140..829747713 100644 --- a/privval/bls_test.go +++ b/privval/bls_test.go @@ -7,7 +7,6 @@ import ( "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" ) @@ -26,7 +25,7 @@ func TestNewBlsPV(t *testing.T) { pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, "") assert.NotNil(t, pv) - password := erc2335.CreateRandomPassword() + password := "password" pv.Key.Save(password, "") t.Run("load bls key from file", func(t *testing.T) { @@ -42,7 +41,7 @@ func TestNewBlsPV(t *testing.T) { pv := NewBlsPV(bls12381.GenPrivKey(), keyFilePath, passwordFilePath, "") assert.NotNil(t, pv) - password := erc2335.CreateRandomPassword() + password := "password" delegatorAddress := types.AccAddress(ed25519.GenPrivKey().PubKey().Address()).String() pv.Key.Save(password, delegatorAddress) diff --git a/test/e2e/initialization/node.go b/test/e2e/initialization/node.go index eb894ae28..808ad3bb5 100644 --- a/test/e2e/initialization/node.go +++ b/test/e2e/initialization/node.go @@ -168,7 +168,7 @@ func (n *internalNode) createConsensusKey() error { blsPasswordFile := privval.DefaultBlsPasswordFile(n.configDir()) if err := privval.EnsureDirs(pvKeyFile, pvStateFile, blsKeyFile, blsPasswordFile); err != nil { - return err + return fmt.Errorf("failed to ensure dirs: %w", err) } accAddress, _ := n.keyInfo.GetAddress() diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index ac33ea596..b7fb30c5b 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -36,7 +36,7 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin blsKeyFile := privval.DefaultBlsKeyFile(config.RootDir) blsPasswordFile := privval.DefaultBlsPasswordFile(config.RootDir) if err := privval.EnsureDirs(cmtKeyFile, cmtStateFile, blsKeyFile, blsPasswordFile); err != nil { - return "", nil, err + return "", nil, fmt.Errorf("failed to ensure dirs: %w", err) } var filePV *cmtprivval.FilePV diff --git a/testutil/signer/private.go b/testutil/signer/private.go index c1589ddae..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" @@ -60,7 +61,7 @@ func GeneratePrivSigner(nodeDir string) error { blsPasswordFile := privval.DefaultBlsPasswordFile(nodeDir) if err := privval.EnsureDirs(cmtKeyFile, cmtStateFile, blsKeyFile, blsPasswordFile); err != nil { - return err + return fmt.Errorf("failed to ensure dirs: %w", err) } cometPV := cmtprivval.GenFilePV(cmtKeyFile, cmtStateFile) diff --git a/x/checkpointing/client/cli/utils.go b/x/checkpointing/client/cli/utils.go index f29998631..695a57f99 100644 --- a/x/checkpointing/client/cli/utils.go +++ b/x/checkpointing/client/cli/utils.go @@ -212,7 +212,7 @@ func getValKeyFromFile(homeDir string) (*privval.ValidatorKeys, error) { blsPasswordPath := privval.DefaultBlsPasswordFile(homeDir) if err := privval.EnsureDirs(cmtKeyPath, cmtStatePath, blsKeyPath, blsPasswordPath); err != nil { - return nil, err + return nil, fmt.Errorf("failed to ensure dirs: %w", err) } filePV := cmtprivval.LoadFilePV(cmtKeyPath, cmtStatePath)