diff --git a/Makefile b/Makefile index 6fc8f2607..906e8c06a 100644 --- a/Makefile +++ b/Makefile @@ -180,6 +180,3 @@ debug_external: clean build_localnet_validator: bash test/build-localnet-validator.sh - -tt: - go test -v -test.run OnDisconnectCheck ./p2p/security \ No newline at end of file diff --git a/cmd/harmony/main.go b/cmd/harmony/main.go index 9fc89d45d..021061c75 100644 --- a/cmd/harmony/main.go +++ b/cmd/harmony/main.go @@ -788,6 +788,8 @@ func setupChain(hc harmonyconfig.HarmonyConfig, nodeConfig *nodeconfig.ConfigTyp } func setupConsensusAndNode(hc harmonyconfig.HarmonyConfig, nodeConfig *nodeconfig.ConfigType, registry *registry.Registry) *node.Node { + decider := quorum.NewDecider(quorum.SuperMajorityVote, uint32(hc.General.ShardID)) + // Parse minPeers from harmonyconfig.HarmonyConfig var minPeers int var aggregateSig bool @@ -821,7 +823,6 @@ func setupConsensusAndNode(hc harmonyconfig.HarmonyConfig, nodeConfig *nodeconfi registry.SetCxPool(cxPool) // Consensus object. - decider := quorum.NewDecider(quorum.SuperMajorityVote, nodeConfig.ShardID) registry.SetIsBackup(isBackup(hc)) currentConsensus, err := consensus.New( myHost, nodeConfig.ShardID, nodeConfig.ConsensusPriKey, registry, decider, minPeers, aggregateSig) diff --git a/consensus/consensus_v2.go b/consensus/consensus_v2.go index 6d3ef5b47..b3c94a77f 100644 --- a/consensus/consensus_v2.go +++ b/consensus/consensus_v2.go @@ -690,10 +690,15 @@ func (consensus *Consensus) commitBlock(blk *types.Block, committedMsg *FBFTMess // This function must be called with enabled leader rotation. func (consensus *Consensus) rotateLeader(epoch *big.Int) { var ( - bc = consensus.Blockchain() - prev = consensus.getLeaderPubKey() - leader = consensus.getLeaderPubKey() + bc = consensus.Blockchain() + prev = consensus.getLeaderPubKey() + leader = consensus.getLeaderPubKey() + curBlock = bc.CurrentBlock() + curNumber = curBlock.NumberU64() + curEpoch = curBlock.Epoch().Uint64() ) + const blocksCountAliveness = 10 + utils.Logger().Info().Msgf("[Rotating leader] epoch: %v rotation:%v external rotation %v", epoch.Uint64(), bc.Config().IsLeaderRotationInternalValidators(epoch), bc.Config().IsLeaderRotationExternalValidatorsAllowed(epoch)) ss, err := bc.ReadShardState(epoch) if err != nil { @@ -741,18 +746,59 @@ func (consensus *Consensus) rotateLeader(epoch *big.Int) { var ( wasFound bool next *bls.PublicKeyWrapper + offset = 1 ) - if bc.Config().IsLeaderRotationExternalValidatorsAllowed(epoch) { - wasFound, next = consensus.Decider.NthNextValidator(committee.Slots, leader, 1) - } else { - wasFound, next = consensus.Decider.NthNextHmy(shard.Schedule.InstanceForEpoch(epoch), leader, 1) - } - if !wasFound { - utils.Logger().Error().Msg("Failed to get next leader") - return - } else { + + for { + if bc.Config().IsLeaderRotationExternalValidatorsAllowed(epoch) { + wasFound, next = consensus.Decider.NthNextValidator(committee.Slots, leader, offset) + } else { + wasFound, next = consensus.Decider.NthNextHmy(shard.Schedule.InstanceForEpoch(epoch), leader, offset) + } + if !wasFound { + utils.Logger().Error().Msg("Failed to get next leader") + // Seems like nothing we can do here. + return + } + members := consensus.Decider.Participants() + mask := bls.NewMask(members) + skipped := 0 + for i := 0; i < blocksCountAliveness; i++ { + header := bc.GetHeaderByNumber(curNumber - uint64(i)) + if header == nil { + utils.Logger().Error().Msgf("Failed to get header by number %d", curNumber-uint64(i)) + return + } + // if epoch is different, we should not check this block. + if header.Epoch().Uint64() != curEpoch { + break + } + // Populate the mask with the bitmap. + err = mask.SetMask(header.LastCommitBitmap()) + if err != nil { + utils.Logger().Err(err).Msg("Failed to set mask") + return + } + ok, err := mask.KeyEnabled(next.Bytes) + if err != nil { + utils.Logger().Err(err).Msg("Failed to get key enabled") + return + } + if !ok { + skipped++ + } + } + + // no signature from the next leader at all, we should skip it. + if skipped >= blocksCountAliveness { + // Next leader is not signing blocks, we should skip it. + offset++ + continue + } consensus.setLeaderPubKey(next) + break } + if consensus.isLeader() && !consensus.getLeaderPubKey().Object.IsEqual(prev.Object) { // leader changed go func() { diff --git a/consensus/quorum/quorum.go b/consensus/quorum/quorum.go index aaeaab236..3930abef1 100644 --- a/consensus/quorum/quorum.go +++ b/consensus/quorum/quorum.go @@ -77,7 +77,7 @@ type ParticipantTracker interface { ParticipantsCount() int64 // NthNextValidator returns key for next validator. It assumes external validators and leader rotation. NthNextValidator(slotList shard.SlotList, pubKey *bls.PublicKeyWrapper, next int) (bool, *bls.PublicKeyWrapper) - NthNextHmy(shardingconfig.Instance, *bls.PublicKeyWrapper, int) (bool, *bls.PublicKeyWrapper) + NthNextHmy(instance shardingconfig.Instance, pubkey *bls.PublicKeyWrapper, next int) (bool, *bls.PublicKeyWrapper) NthNextHmyExt(shardingconfig.Instance, *bls.PublicKeyWrapper, int) (bool, *bls.PublicKeyWrapper) FirstParticipant(shardingconfig.Instance) *bls.PublicKeyWrapper UpdateParticipants(pubKeys, allowlist []bls.PublicKeyWrapper) diff --git a/core/blockchain_impl.go b/core/blockchain_impl.go index aeb76362b..294bf82f0 100644 --- a/core/blockchain_impl.go +++ b/core/blockchain_impl.go @@ -1683,8 +1683,6 @@ func (bc *BlockChainImpl) insertChain(chain types.Blocks, verifyHeaders bool) (i if len(chain) == 0 { return 0, nil, nil, ErrEmptyChain } - first := chain[0] - fmt.Println("insertChain", utils.GetPort(), first.ShardID(), first.Epoch().Uint64(), first.NumberU64()) // Do a sanity check that the provided chain is actually ordered and linked for i := 1; i < len(chain); i++ { if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() { diff --git a/core/state_processor.go b/core/state_processor.go index 1037941d1..47b7c7eaa 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -312,7 +312,15 @@ func ApplyTransaction(bc ChainContext, author *common.Address, gp *GasPool, stat // Apply the transaction to the current state (included in the env) result, err := ApplyMessage(vmenv, msg, gp) if err != nil { - return nil, nil, nil, 0, errors.Wrapf(err, "apply failed from='%s' to='%s' balance='%s'", msg.From().Hex(), msg.To().Hex(), statedb.GetBalance(msg.From()).String()) + to := "" + if m := msg.To(); m != nil { + to = m.Hex() + } + balance := "" + if a := statedb.GetBalance(msg.From()); a != nil { + balance = a.String() + } + return nil, nil, nil, 0, errors.Wrapf(err, "apply failed from='%s' to='%s' balance='%s'", msg.From().Hex(), to, balance) } // Update the state with pending changes var root []byte diff --git a/internal/utils/math.go b/internal/utils/math.go new file mode 100644 index 000000000..6dceec5eb --- /dev/null +++ b/internal/utils/math.go @@ -0,0 +1,17 @@ +package utils + +import "golang.org/x/exp/constraints" + +func Min[T constraints.Ordered](a, b T) T { + if a < b { + return a + } + return b +} + +func Max[T constraints.Ordered](a, b T) T { + if a > b { + return a + } + return b +}