diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index e87ea2101..5fe176c2f 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/common" bls2 "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/consensus/signature" - "github.com/harmony-one/harmony/internal/chain" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/utils" @@ -697,7 +696,7 @@ func (consensus *Consensus) rotateLeader(epoch *big.Int) { return } // Check if the same leader. - pub, err := chain.GetLeaderPubKeyFromCoinbase(consensus.Blockchain, header) + pub, err := consensus.Blockchain.GetLeaderPubKeyFromCoinbase(header) if err != nil { utils.Logger().Error().Err(err).Msg("Failed to get leader public key from coinbase") return @@ -708,16 +707,7 @@ func (consensus *Consensus) rotateLeader(epoch *big.Int) { } } // Passed all checks, we can change leader. - var ( - wasFound bool - next *bls.PublicKeyWrapper - ) - // The same leader for N blocks. - if consensus.Blockchain.Config().IsAllowlistEpoch(epoch) { - wasFound, next = consensus.Decider.NthNextHmyExt(shard.Schedule.InstanceForEpoch(epoch), leader, 1) - } else { - wasFound, next = consensus.Decider.NthNextHmy(shard.Schedule.InstanceForEpoch(epoch), leader, 1) - } + wasFound, next := consensus.Decider.NthNextHmy(shard.Schedule.InstanceForEpoch(epoch), leader, 1) if !wasFound { utils.Logger().Error().Msg("Failed to get next leader") return diff --git a/core/blockchain.go b/core/blockchain.go index 1c7ea43d3..fda483165 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -13,6 +13,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" harmonyconfig "github.com/harmony-one/harmony/internal/configs/harmony" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/tikv/redis_helper" @@ -334,6 +335,8 @@ type BlockChain interface { state *state.DB, ) (status WriteStatus, err error) + GetLeaderPubKeyFromCoinbase(h *block.Header) (*bls.PublicKeyWrapper, error) + // ========== Only For Tikv Start ========== // return true if is tikv writer master diff --git a/core/blockchain_impl.go b/core/blockchain_impl.go index 73f37f862..489fb3661 100644 --- a/core/blockchain_impl.go +++ b/core/blockchain_impl.go @@ -39,6 +39,7 @@ import ( "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rlp" + bls2 "github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/harmony/block" consensus_engine "github.com/harmony-one/harmony/consensus/engine" "github.com/harmony-one/harmony/consensus/reward" @@ -47,6 +48,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" harmonyconfig "github.com/harmony-one/harmony/internal/configs/harmony" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/tikv" @@ -181,6 +183,7 @@ type BlockChainImpl struct { validatorListByDelegatorCache *lru.Cache // Cache of validator list by delegator pendingCrossLinksCache *lru.Cache // Cache of last pending crosslinks blockAccumulatorCache *lru.Cache // Cache of block accumulators + leaderPubKeyFromCoinbase *lru.Cache // Cache of leader public key from coinbase quit chan struct{} // blockchain quit channel running int32 // running must be called atomically blockchainPruner *blockchainPruner // use to prune beacon chain @@ -242,6 +245,7 @@ func newBlockChainWithOptions( validatorListByDelegatorCache, _ := lru.New(validatorListByDelegatorCacheLimit) pendingCrossLinksCache, _ := lru.New(pendingCrossLinksCacheLimit) blockAccumulatorCache, _ := lru.New(blockAccumulatorCacheLimit) + leaderPubKeyFromCoinbase, _ := lru.New(chainConfig.LeaderRotationBlocksCount + 2) bc := &BlockChainImpl{ chainConfig: chainConfig, @@ -265,6 +269,7 @@ func newBlockChainWithOptions( validatorListByDelegatorCache: validatorListByDelegatorCache, pendingCrossLinksCache: pendingCrossLinksCache, blockAccumulatorCache: blockAccumulatorCache, + leaderPubKeyFromCoinbase: leaderPubKeyFromCoinbase, blockchainPruner: newBlockchainPruner(db), engine: engine, vmConfig: vmConfig, @@ -3256,6 +3261,60 @@ func (bc *BlockChainImpl) SuperCommitteeForNextEpoch( return nextCommittee, err } +// GetLeaderPubKeyFromCoinbase retrieve corresponding blsPublicKey from Coinbase Address +func (bc *BlockChainImpl) GetLeaderPubKeyFromCoinbase(h *block.Header) (*bls.PublicKeyWrapper, error) { + if cached, ok := bc.leaderPubKeyFromCoinbase.Get(h.Number().Uint64()); ok { + return cached.(*bls.PublicKeyWrapper), nil + } + rs, err := bc.getLeaderPubKeyFromCoinbase(h) + if err != nil { + return nil, err + } + bc.leaderPubKeyFromCoinbase.Add(h.Number().Uint64(), rs) + return rs, nil +} + +// getLeaderPubKeyFromCoinbase retrieve corresponding blsPublicKey from Coinbase Address +func (bc *BlockChainImpl) getLeaderPubKeyFromCoinbase(h *block.Header) (*bls.PublicKeyWrapper, error) { + shardState, err := bc.ReadShardState(h.Epoch()) + if err != nil { + return nil, errors.Wrapf(err, "cannot read shard state %v %s", + h.Epoch(), + h.Coinbase().Hash().Hex(), + ) + } + + committee, err := shardState.FindCommitteeByID(h.ShardID()) + if err != nil { + return nil, err + } + + committerKey := new(bls2.PublicKey) + isStaking := bc.Config().IsStaking(h.Epoch()) + for _, member := range committee.Slots { + if isStaking { + // After staking the coinbase address will be the address of bls public key + if utils.GetAddressFromBLSPubKeyBytes(member.BLSPublicKey[:]) == h.Coinbase() { + if committerKey, err = bls.BytesToBLSPublicKey(member.BLSPublicKey[:]); err != nil { + return nil, err + } + return &bls.PublicKeyWrapper{Object: committerKey, Bytes: member.BLSPublicKey}, nil + } + } else { + if member.EcdsaAddress == h.Coinbase() { + if committerKey, err = bls.BytesToBLSPublicKey(member.BLSPublicKey[:]); err != nil { + return nil, err + } + return &bls.PublicKeyWrapper{Object: committerKey, Bytes: member.BLSPublicKey}, nil + } + } + } + return nil, errors.Errorf( + "cannot find corresponding BLS Public Key coinbase %s", + h.Coinbase().Hex(), + ) +} + func (bc *BlockChainImpl) EnablePruneBeaconChainFeature() { bc.pruneBeaconChainEnable = true } diff --git a/core/blockchain_stub.go b/core/blockchain_stub.go index ccb1c9847..f9e9111ea 100644 --- a/core/blockchain_stub.go +++ b/core/blockchain_stub.go @@ -13,6 +13,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" harmonyconfig "github.com/harmony-one/harmony/internal/configs/harmony" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/tikv/redis_helper" @@ -403,6 +404,10 @@ func (a Stub) CommitOffChainData(batch rawdb.DatabaseWriter, block *types.Block, return 0, errors.Errorf("method CommitOffChainData not implemented for %s", a.Name) } +func (a Stub) GetLeaderPubKeyFromCoinbase(h *block.Header) (*bls.PublicKeyWrapper, error) { + return nil, errors.Errorf("method GetLeaderPubKeyFromCoinbase not implemented for %s", a.Name) +} + func (a Stub) IsTikvWriterMaster() bool { return false }