From eb8ea5044a5808ede2b510f79999fc4353a59e2f Mon Sep 17 00:00:00 2001 From: Fangyu Gai Date: Wed, 17 Aug 2022 23:39:13 +0800 Subject: [PATCH 1/3] changed PoP --- cmd/babylond/cmd/testnet.go | 6 +---- crypto/bls12381/bls.go | 4 ++-- crypto/bls12381/types.go | 4 ++-- privval/types.go | 21 ++++++++++------ proto/babylon/checkpointing/bls_key.proto | 2 +- testutil/datagen/init_val.go | 9 ++++--- x/checkpointing/spec/registration.md | 29 +++++++++++++---------- x/checkpointing/types/bls_key.pb.go | 2 +- 8 files changed, 42 insertions(+), 35 deletions(-) diff --git a/cmd/babylond/cmd/testnet.go b/cmd/babylond/cmd/testnet.go index 68db7f6bd..6ffdefafb 100644 --- a/cmd/babylond/cmd/testnet.go +++ b/cmd/babylond/cmd/testnet.go @@ -178,13 +178,9 @@ func InitTestnet( _ = os.RemoveAll(outputDir) return err } - accKeyInfo, err := kb.Key(nodeDirName) - if err != nil { - return err - } // generate validator keys - nodeIDs[i], valKeys[i], err = datagen.InitializeNodeValidatorFiles(nodeConfig, accKeyInfo.GetPubKey()) + nodeIDs[i], valKeys[i], err = datagen.InitializeNodeValidatorFiles(nodeConfig) if err != nil { _ = os.RemoveAll(outputDir) return err diff --git a/crypto/bls12381/bls.go b/crypto/bls12381/bls.go index 927c36e56..02ce9b3c8 100644 --- a/crypto/bls12381/bls.go +++ b/crypto/bls12381/bls.go @@ -72,7 +72,7 @@ func AggrSigList(sigs []Signature) (Signature, error) { aggSig := new(BlsMultiSig) sigBytes := make([][]byte, len(sigs)) for i := 0; i < len(sigs); i++ { - sigBytes[i] = sigs[i].Byte() + sigBytes[i] = sigs[i].Bytes() } if !aggSig.AggregateCompressed(sigBytes, false) { return nil, errors.New("failed to aggregate bls signatures") @@ -94,7 +94,7 @@ func AggrPKList(pks []PublicKey) (PublicKey, error) { aggPk := new(BlsMultiPubKey) pkBytes := make([][]byte, len(pks)) for i := 0; i < len(pks); i++ { - pkBytes[i] = pks[i].Byte() + pkBytes[i] = pks[i].Bytes() } if !aggPk.AggregateCompressed(pkBytes, false) { return nil, errors.New("failed to aggregate bls public keys") diff --git a/crypto/bls12381/types.go b/crypto/bls12381/types.go index c5c196906..d62293ea6 100644 --- a/crypto/bls12381/types.go +++ b/crypto/bls12381/types.go @@ -80,7 +80,7 @@ func (sig *Signature) Unmarshal(data []byte) error { return nil } -func (sig Signature) Byte() []byte { +func (sig Signature) Bytes() []byte { return sig } @@ -145,7 +145,7 @@ func (pk PublicKey) Equal(k PublicKey) bool { return string(pk) == string(k) } -func (pk PublicKey) Byte() []byte { +func (pk PublicKey) Bytes() []byte { return pk } diff --git a/privval/types.go b/privval/types.go index 67335c65e..b818ee185 100644 --- a/privval/types.go +++ b/privval/types.go @@ -1,9 +1,10 @@ package privval import ( + "errors" + "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/x/checkpointing/types" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" tmcrypto "github.com/tendermint/tendermint/crypto" ) @@ -16,8 +17,8 @@ type ValidatorKeys struct { blsPrivkey bls12381.PrivateKey } -func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKey, accPubkey cryptotypes.PubKey) (*ValidatorKeys, error) { - pop, err := BuildPop(valPrivkey, blsPrivKey, accPubkey) +func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKey) (*ValidatorKeys, error) { + pop, err := BuildPoP(valPrivkey, blsPrivKey) if err != nil { return nil, err } @@ -30,10 +31,16 @@ func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKe }, nil } -// BuildPop builds a proof-of-possession by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = Secp256k1_pk)) -// where valPrivKey is Ed25519_sk, accPubkey is Secp256k1_pk, blsPrivkey is BLS_sk -func BuildPop(valPrivKey tmcrypto.PrivKey, blsPrivkey bls12381.PrivateKey, accPubkey cryptotypes.PubKey) (*types.ProofOfPossession, error) { - data, err := valPrivKey.Sign(accPubkey.Bytes()) +// BuildPoP builds a proof-of-possession by PoP=encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) +// where valPrivKey is Ed25519_sk and blsPrivkey is BLS_sk +func BuildPoP(valPrivKey tmcrypto.PrivKey, blsPrivkey bls12381.PrivateKey) (*types.ProofOfPossession, error) { + if valPrivKey == nil { + return nil, errors.New("validator private key is empty") + } + if blsPrivkey == nil { + return nil, errors.New("BLS private key is empty") + } + data, err := valPrivKey.Sign(blsPrivkey.PubKey().Bytes()) if err != nil { return nil, err } diff --git a/proto/babylon/checkpointing/bls_key.proto b/proto/babylon/checkpointing/bls_key.proto index 6d64058cd..2f83968d0 100644 --- a/proto/babylon/checkpointing/bls_key.proto +++ b/proto/babylon/checkpointing/bls_key.proto @@ -18,7 +18,7 @@ message BlsKey { // ProofOfPossession defines proof for the ownership of Ed25519 and BLS private keys message ProofOfPossession { - // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = Secp256k1_pk)) + // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) bytes bls_sig = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; diff --git a/testutil/datagen/init_val.go b/testutil/datagen/init_val.go index c43e43f37..4354eee62 100644 --- a/testutil/datagen/init_val.go +++ b/testutil/datagen/init_val.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/babylonchain/babylon/crypto/bls12381" "github.com/babylonchain/babylon/privval" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/go-bip39" cfg "github.com/tendermint/tendermint/config" tmed25519 "github.com/tendermint/tendermint/crypto/ed25519" @@ -14,11 +13,11 @@ import ( ) // InitializeNodeValidatorFiles creates private validator and p2p configuration files. -func InitializeNodeValidatorFiles(config *cfg.Config, accPubkey cryptotypes.PubKey) (string, *privval.ValidatorKeys, error) { - return InitializeNodeValidatorFilesFromMnemonic(config, "", accPubkey) +func InitializeNodeValidatorFiles(config *cfg.Config) (string, *privval.ValidatorKeys, error) { + return InitializeNodeValidatorFilesFromMnemonic(config, "") } -func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic string, accPubkey cryptotypes.PubKey) (nodeID string, valKeys *privval.ValidatorKeys, err error) { +func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic string) (nodeID string, valKeys *privval.ValidatorKeys, err error) { if len(mnemonic) > 0 && !bip39.IsMnemonicValid(mnemonic) { return "", nil, fmt.Errorf("invalid mnemonic") } @@ -51,7 +50,7 @@ func InitializeNodeValidatorFilesFromMnemonic(config *cfg.Config, mnemonic strin valPrivkey := filePV.GetValPrivKey() blsPrivkey := filePV.GetBlsPrivKey() - valKeys, err = privval.NewValidatorKeys(valPrivkey, blsPrivkey, accPubkey) + valKeys, err = privval.NewValidatorKeys(valPrivkey, blsPrivkey) if err != nil { return "", nil, err } diff --git a/x/checkpointing/spec/registration.md b/x/checkpointing/spec/registration.md index da47ed706..83942dce7 100644 --- a/x/checkpointing/spec/registration.md +++ b/x/checkpointing/spec/registration.md @@ -6,25 +6,30 @@ To participate in the checkpointing, a validator needs to also register its BLS The original registration is done via a transaction that carries a `MsgCreateValidator` message. To register a BLS public key, we need a wrapper message called `MsgWrappedCreateValidator` processed by the `Checkpointing` module. -This message wraps the original `MsgCreateValidator` message as well as a new message called `MsgCreateBlsKey` specifically for registering BLS public key. +This message wraps the original `MsgCreateValidator` message as well as a BLS public key and a `Proof-of-Possession` (PoP) for registering BLS public key. The execution of `MsgWrappedCreateValidator` is as follows. -1. The `Checkpointing` module first processes `MsgCreateBlsKey` to register the validator's BLS key. If success, then -2. delay the processing of `MsgCreateValidator` until the end of this epoch. If success, the registration is succeeded. -3. Otherwise, the corresponding BLS key registered before should be removed to ensure atomicity. -4. The validator should register again. +1. The `Checkpointing` module first processes `MsgWrappedCreateValidator` to register the validator's BLS key. If success, then +2. extract `MsgCreateValidator` and deliver `MsgCreateValidator` to the epoching module's message queue, which will be processed until the end of this epoch. If success, the registration is succeeded. +3. Otherwise, the registration fails and the validator should register again with the same keys. + +## Genesis + +Genesis validators are registered via the legacy `genutil` module from the Cosmos-SDK, which processes `MsgCreateValidator` messages contained in genesis transactions. +The BLS keys are registered as `GenesisState` in the checkpointing module. +The checkpointing module's `ValidateGenesis` should ensure that each genesis validator has both an Ed25519 key and BLS key which are bonded by PoP. ## Proof of Possession -A valid `MsgCreateBlsKey` needs to ensure that the sender of the BLS public key owns: +The purpose of PoP is to prove that one validator owns: 1. the corresponding BLS private key; 2. the corresponding Ed25519 private key associated with the public key in the `MsgCreateValidator` message. -To achieve that, the sender needs to include Proof-of-Possession (PoP) in the `MsgCreateBlsKey` message as follows. -``` -MsgCreateBlsKey = [BLS_pk, PoP], -``` -where `PoP = [m = Ed25519_pk, sig_BLS = sign(key = BLS_sk, data = m), sig_Ed25519 = sign(key = Ed25519_sk, data = sig_BLS)]` +To achieve that, PoP is calculated as follows. + +`PoP = sign(key = BLS_sk, data = sign(key = Ed25519_sk, data = BLS_pk)]` + +Since the delegator already relates its account with the validator's Ed25519 key through the signatures in `MsgCreateValidator`, the adversary cannot do registration with the same PoP. ## Verification @@ -32,7 +37,7 @@ To verify PoP, first we need to ensure that the BLS public key has never been re and that the current validator hasn't already registered a different BLS public key. Then, verify ``` -MsgCreateValidator.Ed25519_pk ?= decrypt(key = BLS_pk, data = decrypt(key = Ed25519_pk, data = PoP.sig_Ed25519)) +MsgWrappedCreateValidator.BLS_pk ?= decrypt(key = Ed25519_pk, data = decrypt(key = BLS_pk, data = PoP)) ``` If verification passes, the `Checkpointing` module stores the BLS public key and associates it with the validator. diff --git a/x/checkpointing/types/bls_key.pb.go b/x/checkpointing/types/bls_key.pb.go index a7db272db..a0b14c65f 100644 --- a/x/checkpointing/types/bls_key.pb.go +++ b/x/checkpointing/types/bls_key.pb.go @@ -74,7 +74,7 @@ func (m *BlsKey) GetPop() *ProofOfPossession { // ProofOfPossession defines proof for the ownership of Ed25519 and BLS private keys type ProofOfPossession struct { - // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = Secp256k1_pk)) + // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,1,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` } From 1e60391eb93de8a81a134ed3b49e056a67bf2cc8 Mon Sep 17 00:00:00 2001 From: Fangyu Gai Date: Fri, 19 Aug 2022 14:45:13 +0800 Subject: [PATCH 2/3] minor --- privval/types.go | 2 +- proto/babylon/checkpointing/bls_key.proto | 2 +- x/checkpointing/spec/registration.md | 2 +- x/checkpointing/types/bls_key.pb.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/privval/types.go b/privval/types.go index b818ee185..1a2e82814 100644 --- a/privval/types.go +++ b/privval/types.go @@ -31,7 +31,7 @@ func NewValidatorKeys(valPrivkey tmcrypto.PrivKey, blsPrivKey bls12381.PrivateKe }, nil } -// BuildPoP builds a proof-of-possession by PoP=encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) +// BuildPoP builds a proof-of-possession by PoP=sign(key = BLS_sk, data = sign(key = Ed25519_sk, data = BLS_pk)) // where valPrivKey is Ed25519_sk and blsPrivkey is BLS_sk func BuildPoP(valPrivKey tmcrypto.PrivKey, blsPrivkey bls12381.PrivateKey) (*types.ProofOfPossession, error) { if valPrivKey == nil { diff --git a/proto/babylon/checkpointing/bls_key.proto b/proto/babylon/checkpointing/bls_key.proto index 2f83968d0..e5d40ab80 100644 --- a/proto/babylon/checkpointing/bls_key.proto +++ b/proto/babylon/checkpointing/bls_key.proto @@ -18,7 +18,7 @@ message BlsKey { // ProofOfPossession defines proof for the ownership of Ed25519 and BLS private keys message ProofOfPossession { - // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) + // bls_sig is calculated by sign(key = BLS_sk, data = sign(key = Ed25519_sk, data = BLS_pk)) bytes bls_sig = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/crypto/bls12381.Signature" ]; diff --git a/x/checkpointing/spec/registration.md b/x/checkpointing/spec/registration.md index 83942dce7..ad49cee97 100644 --- a/x/checkpointing/spec/registration.md +++ b/x/checkpointing/spec/registration.md @@ -10,7 +10,7 @@ This message wraps the original `MsgCreateValidator` message as well as a BLS pu The execution of `MsgWrappedCreateValidator` is as follows. 1. The `Checkpointing` module first processes `MsgWrappedCreateValidator` to register the validator's BLS key. If success, then -2. extract `MsgCreateValidator` and deliver `MsgCreateValidator` to the epoching module's message queue, which will be processed until the end of this epoch. If success, the registration is succeeded. +2. extract `MsgCreateValidator` and deliver `MsgCreateValidator` to the epoching module's message queue, which will be processed at the end of this epoch. If success, the registration is succeeded. 3. Otherwise, the registration fails and the validator should register again with the same keys. ## Genesis diff --git a/x/checkpointing/types/bls_key.pb.go b/x/checkpointing/types/bls_key.pb.go index a0b14c65f..307615e6b 100644 --- a/x/checkpointing/types/bls_key.pb.go +++ b/x/checkpointing/types/bls_key.pb.go @@ -74,7 +74,7 @@ func (m *BlsKey) GetPop() *ProofOfPossession { // ProofOfPossession defines proof for the ownership of Ed25519 and BLS private keys type ProofOfPossession struct { - // bls_sig is calculated by encrypt(key = BLS_sk, data = encrypt(key = Ed25519_sk, data = BLS_pk)) + // bls_sig is calculated by sign(key = BLS_sk, data = sign(key = Ed25519_sk, data = BLS_pk)) BlsSig *github_com_babylonchain_babylon_crypto_bls12381.Signature `protobuf:"bytes,1,opt,name=bls_sig,json=blsSig,proto3,customtype=github.com/babylonchain/babylon/crypto/bls12381.Signature" json:"bls_sig,omitempty"` } From 35b137323914a355d4040311c8ae0d56341a5a84 Mon Sep 17 00:00:00 2001 From: Fangyu Gai Date: Fri, 19 Aug 2022 15:37:58 +0800 Subject: [PATCH 3/3] fixed epoch growth bug --- x/checkpointing/abci.go | 6 +++--- x/epoching/types/epoching.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x/checkpointing/abci.go b/x/checkpointing/abci.go index f37c8529c..206180beb 100644 --- a/x/checkpointing/abci.go +++ b/x/checkpointing/abci.go @@ -25,10 +25,10 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper, req abci.RequestBeginBlock) // if this block is the second block of an epoch epoch := k.GetEpoch(ctx) if epoch.IsSecondBlock(ctx) { - // note that this epochNum is obtained before the BeginBlocker of the epoching module is executed - // meaning that the epochNum has not been incremented upon a new epoch + // note that this epochNum is obtained after the BeginBlocker of the epoching module is executed + // meaning that the epochNum has been incremented upon a new epoch lch := ctx.BlockHeader().LastCommitHash - err := k.BuildRawCheckpoint(ctx, epoch.EpochNumber, lch) + err := k.BuildRawCheckpoint(ctx, epoch.EpochNumber-1, lch) if err != nil { panic("failed to generate a raw checkpoint") } diff --git a/x/epoching/types/epoching.go b/x/epoching/types/epoching.go index 43c17c085..51d4d164d 100644 --- a/x/epoching/types/epoching.go +++ b/x/epoching/types/epoching.go @@ -14,7 +14,7 @@ func (e Epoch) GetLastBlockHeight() uint64 { func (e Epoch) GetSecondBlockHeight() uint64 { if e.EpochNumber == 0 { - return 0 + panic("should not be called when epoch number is zero") } return e.FirstBlockHeight + 1 }