From c4db1c0c4bde3c600862d004aa9cca496e140989 Mon Sep 17 00:00:00 2001 From: Janet Liang <56005637+janet-harmony@users.noreply.github.com> Date: Thu, 28 May 2020 00:26:34 -0700 Subject: [PATCH] [RPC] Add RPCs to get list of BLS public keys for committees (#3080) * [rpc] Fix sanity check to accept "latest" * [rpc] Return as much data possible when using GetAllValidatorInformationByBlockNumber * [rpc] Remove beacon check for getValidators, returns valid information on all shards * [rpc] Add GetValidatorKeys to get BLS Keys of committee for a particular epoch * [rpc] Add getBlockSignerKeys to return public bls keys of signers for a block * [rpc] Fix lint * [rpc] Address PR comments * [rpc] Add common function to API backend instead of separate implementations in V1 & V2 * [rpc] Remove extra parenthesis * [rpc] Fix lint [validator] Fix typo * [rpc] Revert change to sanity check in V2 * [rpc] Fix lint --- hmy/api_backend.go | 32 ++++++++++ internal/hmyapi/apiv1/backend.go | 2 + internal/hmyapi/apiv1/blockchain.go | 98 +++++++++++++---------------- internal/hmyapi/apiv2/backend.go | 2 + internal/hmyapi/apiv2/blockchain.go | 98 +++++++++++++---------------- internal/hmyapi/backend.go | 2 + staking/types/validator.go | 4 +- 7 files changed, 128 insertions(+), 110 deletions(-) diff --git a/hmy/api_backend.go b/hmy/api_backend.go index 363c4d310..0839b3470 100644 --- a/hmy/api_backend.go +++ b/hmy/api_backend.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" + "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/api/proto" "github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/consensus/quorum" @@ -20,6 +21,7 @@ import ( "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + internal_bls "github.com/harmony-one/harmony/crypto/bls" internal_common "github.com/harmony-one/harmony/internal/common" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" @@ -848,3 +850,33 @@ func (b *APIBackend) GetNodeMetadata() commonRPC.NodeMetadata { c, } } + +// GetBlockSigners .. +func (b *APIBackend) GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *internal_bls.Mask, error) { + block, err := b.BlockByNumber(ctx, blockNr) + if err != nil { + return nil, nil, err + } + blockWithSigners, err := b.BlockByNumber(ctx, blockNr+1) + if err != nil { + return nil, nil, err + } + committee, err := b.GetValidators(block.Epoch()) + if err != nil { + return nil, nil, err + } + pubkeys := make([]*bls.PublicKey, len(committee.Slots)) + for i, validator := range committee.Slots { + pubkeys[i] = new(bls.PublicKey) + validator.BLSPublicKey.ToLibBLSPublicKey(pubkeys[i]) + } + mask, err := internal_bls.NewMask(pubkeys, nil) + if err != nil { + return nil, nil, err + } + err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) + if err != nil { + return nil, nil, err + } + return committee.Slots, mask, nil +} diff --git a/internal/hmyapi/apiv1/backend.go b/internal/hmyapi/apiv1/backend.go index cc013a1c3..ebfa83deb 100644 --- a/internal/hmyapi/apiv1/backend.go +++ b/internal/hmyapi/apiv1/backend.go @@ -14,6 +14,7 @@ import ( "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/crypto/bls" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/shard" @@ -91,4 +92,5 @@ type Backend interface { GetLastCrossLinks() ([]*types.CrossLink, error) GetLatestChainHeaders() *block.HeaderPair GetNodeMetadata() commonRPC.NodeMetadata + GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) } diff --git a/internal/hmyapi/apiv1/blockchain.go b/internal/hmyapi/apiv1/blockchain.go index 15672d536..995c640da 100644 --- a/internal/hmyapi/apiv1/blockchain.go +++ b/internal/hmyapi/apiv1/blockchain.go @@ -18,7 +18,6 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" - internal_bls "github.com/harmony-one/harmony/crypto/bls" internal_common "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" @@ -183,9 +182,6 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart rpc.Bloc // GetValidators returns validators list for a particular epoch. func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) { - if err := s.isBeaconShard(); err != nil { - return nil, err - } committee, err := s.b.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err @@ -217,6 +213,20 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m return result, nil } +// GetValidatorKeys returns list of bls public keys in the committee for a particular epoch. +func (s *PublicBlockChainAPI) GetValidatorKeys(ctx context.Context, epoch int64) ([]string, error) { + committee, err := s.b.GetValidators(big.NewInt(epoch)) + if err != nil { + return nil, err + } + + validators := make([]string, len(committee.Slots)) + for i, v := range committee.Slots { + validators[i] = v.BLSPublicKey.Hex() + } + return validators, nil +} + // IsLastBlock checks if block is last epoch block. func (s *PublicBlockChainAPI) IsLastBlock(blockNum uint64) (bool, error) { if err := s.isBeaconShard(); err != nil { @@ -241,44 +251,46 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr rpc.B if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, blockNr) - if err != nil { - return nil, err - } - blockWithSigners, err := s.b.BlockByNumber(ctx, blockNr+1) + slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) if err != nil { return nil, err } - committee, err := s.b.GetValidators(block.Epoch()) - if err != nil { - return nil, err + signers := []string{} + for _, validator := range slots { + oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) + if err != nil { + return nil, err + } + blsPublicKey := new(bls.PublicKey) + validator.BLSPublicKey.ToLibBLSPublicKey(blsPublicKey) + if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { + signers = append(signers, oneAddress) + } } - pubkeys := make([]*bls.PublicKey, len(committee.Slots)) - for i, validator := range committee.Slots { - pubkeys[i] = new(bls.PublicKey) - validator.BLSPublicKey.ToLibBLSPublicKey(pubkeys[i]) + return signers, nil +} + +// GetBlockSignerKeys returns bls public keys that signed the block. +func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr rpc.BlockNumber) ([]string, error) { + if uint64(blockNr) == 0 { + return []string{}, nil } - mask, err := internal_bls.NewMask(pubkeys, nil) - if err != nil { + if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) + slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) if err != nil { return nil, err } - result := []string{} - for _, validator := range committee.Slots { - oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) - if err != nil { - return nil, err - } + signers := []string{} + for _, validator := range slots { blsPublicKey := new(bls.PublicKey) validator.BLSPublicKey.ToLibBLSPublicKey(blsPublicKey) if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { - result = append(result, oneAddress) + signers = append(signers, validator.BLSPublicKey.Hex()) } } - return result, nil + return signers, nil } // IsBlockSigner returns true if validator with address signed blockNr block. @@ -289,32 +301,11 @@ func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr rpc.Blo if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return false, err } - block, err := s.b.BlockByNumber(ctx, blockNr) - if err != nil { - return false, err - } - blockWithSigners, err := s.b.BlockByNumber(ctx, blockNr+1) + slots, mask, err := s.b.GetBlockSigners(ctx, blockNr) if err != nil { return false, err } - committee, err := s.b.GetValidators(block.Epoch()) - if err != nil { - return false, err - } - pubkeys := make([]*bls.PublicKey, len(committee.Slots)) - for i, validator := range committee.Slots { - pubkeys[i] = new(bls.PublicKey) - validator.BLSPublicKey.ToLibBLSPublicKey(pubkeys[i]) - } - mask, err := internal_bls.NewMask(pubkeys, nil) - if err != nil { - return false, err - } - err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) - if err != nil { - return false, err - } - for _, validator := range committee.Slots { + for _, validator := range slots { oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) if err != nil { return false, err @@ -683,17 +674,16 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( validatorsNum = len(addresses) - start } } - validators := make([]*staking.ValidatorRPCEnhanced, validatorsNum) + validators := []*staking.ValidatorRPCEnhanced{} block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) } for i := start; i < start+validatorsNum; i++ { information, err := s.b.GetValidatorInformation(addresses[i], block) - if err != nil { - return nil, err + if err == nil { + validators = append(validators, information) } - validators[i-start] = information } return validators, nil } diff --git a/internal/hmyapi/apiv2/backend.go b/internal/hmyapi/apiv2/backend.go index 55a53b379..654b56965 100644 --- a/internal/hmyapi/apiv2/backend.go +++ b/internal/hmyapi/apiv2/backend.go @@ -14,6 +14,7 @@ import ( "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/crypto/bls" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/shard" @@ -87,4 +88,5 @@ type Backend interface { GetLastCrossLinks() ([]*types.CrossLink, error) GetLatestChainHeaders() *block.HeaderPair GetNodeMetadata() commonRPC.NodeMetadata + GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) } diff --git a/internal/hmyapi/apiv2/blockchain.go b/internal/hmyapi/apiv2/blockchain.go index a3805ddcf..ab5eeade6 100644 --- a/internal/hmyapi/apiv2/blockchain.go +++ b/internal/hmyapi/apiv2/blockchain.go @@ -17,7 +17,6 @@ import ( "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" - internal_bls "github.com/harmony-one/harmony/crypto/bls" internal_common "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" @@ -144,9 +143,6 @@ func (s *PublicBlockChainAPI) GetBlocks(ctx context.Context, blockStart, blockEn // GetValidators returns validators list for a particular epoch. func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (map[string]interface{}, error) { - if err := s.isBeaconShard(); err != nil { - return nil, err - } committee, err := s.b.GetValidators(big.NewInt(epoch)) if err != nil { return nil, err @@ -179,6 +175,20 @@ func (s *PublicBlockChainAPI) GetValidators(ctx context.Context, epoch int64) (m return result, nil } +// GetValidatorKeys returns list of bls public keys in the committee for a particular epoch. +func (s *PublicBlockChainAPI) GetValidatorKeys(ctx context.Context, epoch int64) ([]string, error) { + committee, err := s.b.GetValidators(big.NewInt(epoch)) + if err != nil { + return nil, err + } + + validators := make([]string, len(committee.Slots)) + for i, v := range committee.Slots { + validators[i] = v.BLSPublicKey.Hex() + } + return validators, nil +} + // IsLastBlock checks if block is last epoch block. func (s *PublicBlockChainAPI) IsLastBlock(blockNum uint64) (bool, error) { if err := s.isBeaconShard(); err != nil { @@ -203,44 +213,46 @@ func (s *PublicBlockChainAPI) GetBlockSigners(ctx context.Context, blockNr uint6 if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) - if err != nil { - return nil, err - } - blockWithSigners, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr+1)) + slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) if err != nil { return nil, err } - committee, err := s.b.GetValidators(block.Epoch()) - if err != nil { - return nil, err + signers := []string{} + for _, validator := range slots { + oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) + if err != nil { + return nil, err + } + blsPublicKey := new(bls.PublicKey) + validator.BLSPublicKey.ToLibBLSPublicKey(blsPublicKey) + if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { + signers = append(signers, oneAddress) + } } - pubkeys := make([]*bls.PublicKey, len(committee.Slots)) - for i, validator := range committee.Slots { - pubkeys[i] = new(bls.PublicKey) - validator.BLSPublicKey.ToLibBLSPublicKey(pubkeys[i]) + return signers, nil +} + +// GetBlockSignerKeys returns bls public keys that signed the block. +func (s *PublicBlockChainAPI) GetBlockSignerKeys(ctx context.Context, blockNr uint64) ([]string, error) { + if blockNr == 0 || blockNr >= uint64(s.BlockNumber()) { + return []string{}, nil } - mask, err := internal_bls.NewMask(pubkeys, nil) - if err != nil { + if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return nil, err } - err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) + slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) if err != nil { return nil, err } - result := []string{} - for _, validator := range committee.Slots { - oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) - if err != nil { - return nil, err - } + signers := []string{} + for _, validator := range slots { blsPublicKey := new(bls.PublicKey) validator.BLSPublicKey.ToLibBLSPublicKey(blsPublicKey) if ok, err := mask.KeyEnabled(blsPublicKey); err == nil && ok { - result = append(result, oneAddress) + signers = append(signers, validator.BLSPublicKey.Hex()) } } - return result, nil + return signers, nil } // IsBlockSigner returns true if validator with address signed blockNr block. @@ -251,32 +263,11 @@ func (s *PublicBlockChainAPI) IsBlockSigner(ctx context.Context, blockNr uint64, if err := s.isBlockGreaterThanLatest(blockNr); err != nil { return false, err } - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) - if err != nil { - return false, err - } - blockWithSigners, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr+1)) + slots, mask, err := s.b.GetBlockSigners(ctx, rpc.BlockNumber(blockNr)) if err != nil { return false, err } - committee, err := s.b.GetValidators(block.Epoch()) - if err != nil { - return false, err - } - pubkeys := make([]*bls.PublicKey, len(committee.Slots)) - for i, validator := range committee.Slots { - pubkeys[i] = new(bls.PublicKey) - validator.BLSPublicKey.ToLibBLSPublicKey(pubkeys[i]) - } - mask, err := internal_bls.NewMask(pubkeys, nil) - if err != nil { - return false, err - } - err = mask.SetMask(blockWithSigners.Header().LastCommitBitmap()) - if err != nil { - return false, err - } - for _, validator := range committee.Slots { + for _, validator := range slots { oneAddress, err := internal_common.AddressToBech32(validator.EcdsaAddress) if err != nil { return false, err @@ -629,17 +620,16 @@ func (s *PublicBlockChainAPI) getAllValidatorInformation( validatorsNum = len(addresses) - start } } - validators := make([]*staking.ValidatorRPCEnhanced, validatorsNum) + validators := []*staking.ValidatorRPCEnhanced{} block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(blockNr)) if err != nil { return nil, errors.Wrapf(err, "could not retrieve the block information for block number: %d", blockNr) } for i := start; i < start+validatorsNum; i++ { information, err := s.b.GetValidatorInformation(addresses[i], block) - if err != nil { - return nil, err + if err == nil { + validators = append(validators, information) } - validators[i-start] = information } return validators, nil } diff --git a/internal/hmyapi/backend.go b/internal/hmyapi/backend.go index 7bf8a3fc9..c0f5f9dca 100644 --- a/internal/hmyapi/backend.go +++ b/internal/hmyapi/backend.go @@ -14,6 +14,7 @@ import ( "github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/vm" + "github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/internal/hmyapi/apiv1" "github.com/harmony-one/harmony/internal/hmyapi/apiv2" commonRPC "github.com/harmony-one/harmony/internal/hmyapi/common" @@ -81,6 +82,7 @@ type Backend interface { GetLastCrossLinks() ([]*types.CrossLink, error) GetLatestChainHeaders() *block.HeaderPair GetNodeMetadata() commonRPC.NodeMetadata + GetBlockSigners(ctx context.Context, blockNr rpc.BlockNumber) (shard.SlotList, *bls.Mask, error) } // GetAPIs returns all the APIs. diff --git a/staking/types/validator.go b/staking/types/validator.go index 6e6d9a6e1..5020c5460 100644 --- a/staking/types/validator.go +++ b/staking/types/validator.go @@ -62,7 +62,7 @@ var ( errDuplicateSlotKeys = errors.New("slot keys can not have duplicates") // ErrExcessiveBLSKeys .. ErrExcessiveBLSKeys = errors.New("more slot keys provided than allowed") - errCannotChangeBannedTaint = errors.New("cannot change validator banned status") + errCannotChangeBannedTrait = errors.New("cannot change validator banned status") ) // ValidatorSnapshotReader .. @@ -645,7 +645,7 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator, epoch switch validator.Status { case effective.Banned: - return errCannotChangeBannedTaint + return errCannotChangeBannedTrait default: switch edit.EPOSStatus { case effective.Active, effective.Inactive: