@@ -592,276 +592,6 @@ struct sortProposalsByVotes {
592592 }
593593};
594594
595- std::optional<const CSuperblock> CGovernanceManager::CreateSuperblockCandidate (int nHeight) const
596- {
597- if (!IsValid ()) return std::nullopt ;
598- if (!m_mn_sync.IsSynced ()) return std::nullopt ;
599- if (nHeight % Params ().GetConsensus ().nSuperblockCycle < Params ().GetConsensus ().nSuperblockCycle - Params ().GetConsensus ().nSuperblockMaturityWindow ) return std::nullopt ;
600- if (HasAlreadyVotedFundingTrigger ()) return std::nullopt ;
601-
602- // A proposal is considered passing if (YES votes) >= (Total Weight of Masternodes / 10),
603- // count total valid (ENABLED) masternodes to determine passing threshold.
604- const auto tip_mn_list = Assert (m_dmnman)->GetListAtChainTip ();
605- const int nWeightedMnCount = tip_mn_list.GetValidWeightedMNsCount ();
606- const int nAbsVoteReq = std::max (Params ().GetConsensus ().nGovernanceMinQuorum , nWeightedMnCount / 10 );
607-
608- // Use std::vector of std::shared_ptr<const CGovernanceObject> because CGovernanceObject doesn't support move operations (needed for sorting the vector later)
609- std::vector<std::shared_ptr<const CGovernanceObject>> approvedProposals;
610-
611- {
612- LOCK (cs);
613- for (const auto & [unused, object] : mapObjects) {
614- // Skip all non-proposals objects
615- if (object.GetObjectType () != GovernanceObject::PROPOSAL) continue ;
616-
617- const int absYesCount = object.GetAbsoluteYesCount (tip_mn_list, VOTE_SIGNAL_FUNDING);
618- // Skip non-passing proposals
619- if (absYesCount < nAbsVoteReq) continue ;
620-
621- approvedProposals.emplace_back (std::make_shared<const CGovernanceObject>(object));
622- }
623- } // cs
624-
625- // Sort approved proposals by absolute Yes votes descending
626- std::sort (approvedProposals.begin (), approvedProposals.end (), [tip_mn_list](std::shared_ptr<const CGovernanceObject> a, std::shared_ptr<const CGovernanceObject> b) {
627- const auto a_yes = a->GetAbsoluteYesCount (tip_mn_list, VOTE_SIGNAL_FUNDING);
628- const auto b_yes = b->GetAbsoluteYesCount (tip_mn_list, VOTE_SIGNAL_FUNDING);
629- return a_yes == b_yes ? UintToArith256 (a->GetHash ()) > UintToArith256 (b->GetHash ()) : a_yes > b_yes;
630- });
631-
632- if (approvedProposals.empty ()) {
633- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s nHeight:%d empty approvedProposals\n " , __func__, nHeight);
634- return std::nullopt ;
635- }
636-
637- std::vector<CGovernancePayment> payments;
638- int nLastSuperblock;
639- int nNextSuperblock;
640-
641- CSuperblock::GetNearestSuperblocksHeights (nHeight, nLastSuperblock, nNextSuperblock);
642- auto SBEpochTime = static_cast <int64_t >(GetTime<std::chrono::seconds>().count () + (nNextSuperblock - nHeight) * 2.62 * 60 );
643- auto governanceBudget = CSuperblock::GetPaymentsLimit (m_chainman.ActiveChain (), nNextSuperblock);
644-
645- CAmount budgetAllocated{};
646- for (const auto & proposal : approvedProposals) {
647- // Extract payment address and amount from proposal
648- UniValue jproposal = proposal->GetJSONObject ();
649-
650- CTxDestination dest = DecodeDestination (jproposal[" payment_address" ].getValStr ());
651- if (!IsValidDestination (dest)) continue ;
652-
653- CAmount nAmount{};
654- try {
655- nAmount = ParsePaymentAmount (jproposal[" payment_amount" ].getValStr ());
656- }
657- catch (const std::runtime_error& e) {
658- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s nHeight:%d Skipping payment exception:%s\n " , __func__,nHeight, e.what ());
659- continue ;
660- }
661-
662- // Construct CGovernancePayment object and make sure it is valid
663- CGovernancePayment payment (dest, nAmount, proposal->GetHash ());
664- if (!payment.IsValid ()) continue ;
665-
666- // Skip proposals that are too expensive
667- if (budgetAllocated + payment.nAmount > governanceBudget) continue ;
668-
669- int64_t windowStart = jproposal[" start_epoch" ].getInt <int64_t >() - GOVERNANCE_FUDGE_WINDOW;
670- int64_t windowEnd = jproposal[" end_epoch" ].getInt <int64_t >() + GOVERNANCE_FUDGE_WINDOW;
671-
672- // Skip proposals if the SB isn't within the proposal time window
673- if (SBEpochTime < windowStart) {
674- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s nHeight:%d SB:%d windowStart:%d\n " , __func__,nHeight, SBEpochTime, windowStart);
675- continue ;
676- }
677- if (SBEpochTime > windowEnd) {
678- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s nHeight:%d SB:%d windowEnd:%d\n " , __func__,nHeight, SBEpochTime, windowEnd);
679- continue ;
680- }
681-
682- // Keep track of total budget allocation
683- budgetAllocated += payment.nAmount ;
684-
685- // Add the payment
686- payments.push_back (payment);
687- }
688-
689- // No proposals made the cut
690- if (payments.empty ()) {
691- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s CreateSuperblockCandidate nHeight:%d empty payments\n " , __func__, nHeight);
692- return std::nullopt ;
693- }
694-
695- // Sort by proposal hash descending
696- std::sort (payments.begin (), payments.end (), [](const CGovernancePayment& a, const CGovernancePayment& b) {
697- return UintToArith256 (a.proposalHash ) > UintToArith256 (b.proposalHash );
698- });
699-
700- // Create Superblock
701- return CSuperblock (nNextSuperblock, std::move (payments));
702- }
703-
704- std::optional<const CGovernanceObject> CGovernanceManager::CreateGovernanceTrigger (const std::optional<const CSuperblock>& sb_opt, PeerManager& peerman,
705- const CActiveMasternodeManager& mn_activeman)
706- {
707- // no sb_opt, no trigger
708- if (!sb_opt.has_value ()) return std::nullopt ;
709-
710- // TODO: Check if nHashParentIn, nRevision and nCollateralHashIn are correct
711- LOCK2 (::cs_main, cs);
712-
713- // Check if identical trigger (equal DataHash()) is already created (signed by other masternode)
714- CGovernanceObject gov_sb (uint256 (), 1 , GetAdjustedTime (), uint256 (), sb_opt.value ().GetHexStrData ());
715- if (auto identical_sb = FindGovernanceObjectByDataHash (gov_sb.GetDataHash ())) {
716- // Somebody submitted a trigger with the same data, support it instead of submitting a duplicate
717- return std::make_optional<CGovernanceObject>(*identical_sb);
718- }
719-
720- // Nobody submitted a trigger we'd like to see, so let's do it but only if we are the payee
721- const CBlockIndex* tip = m_chainman.ActiveChain ().Tip ();
722- const auto mnList = Assert (m_dmnman)->GetListForBlock (tip);
723- const auto mn_payees = mnList.GetProjectedMNPayees (tip);
724-
725- if (mn_payees.empty ()) {
726- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s payee list is empty\n " , __func__);
727- return std::nullopt ;
728- }
729-
730- if (mn_payees.front ()->proTxHash != mn_activeman.GetProTxHash ()) {
731- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s we are not the payee, skipping\n " , __func__);
732- return std::nullopt ;
733- }
734- gov_sb.SetMasternodeOutpoint (mn_activeman.GetOutPoint ());
735- gov_sb.SetSignature (mn_activeman.SignBasic (gov_sb.GetSignatureHash ()));
736-
737- if (std::string strError; !gov_sb.IsValidLocally (m_dmnman->GetListAtChainTip (), m_chainman, strError, true )) {
738- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Created trigger is invalid:%s\n " , __func__, strError);
739- return std::nullopt ;
740- }
741-
742- if (!MasternodeRateCheck (gov_sb)) {
743- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Trigger rejected because of rate check failure hash(%s)\n " , __func__, gov_sb.GetHash ().ToString ());
744- return std::nullopt ;
745- }
746-
747- // The trigger we just created looks good, submit it
748- AddGovernanceObject (gov_sb, peerman);
749- return std::make_optional<CGovernanceObject>(gov_sb);
750- }
751-
752- void CGovernanceManager::VoteGovernanceTriggers (const std::optional<const CGovernanceObject>& trigger_opt, CConnman& connman, PeerManager& peerman,
753- const CActiveMasternodeManager& mn_activeman)
754- {
755- // only active masternodes can vote on triggers
756- if (mn_activeman.GetProTxHash ().IsNull ()) return ;
757-
758- LOCK2 (::cs_main, cs);
759-
760- if (trigger_opt.has_value ()) {
761- // We should never vote "yes" on another trigger or the same trigger twice
762- assert (!votedFundingYesTriggerHash.has_value ());
763- // Vote YES-FUNDING for the trigger we like, unless we already did
764- const uint256 gov_sb_hash = trigger_opt.value ().GetHash ();
765- bool voted_already{false };
766- if (vote_rec_t voteRecord; trigger_opt.value ().GetCurrentMNVotes (mn_activeman.GetOutPoint (), voteRecord)) {
767- const auto & strFunc = __func__;
768- // Let's see if there is a VOTE_SIGNAL_FUNDING vote from us already
769- voted_already = ranges::any_of (voteRecord.mapInstances , [&](const auto & voteInstancePair) {
770- if (voteInstancePair.first == VOTE_SIGNAL_FUNDING) {
771- if (voteInstancePair.second .eOutcome == VOTE_OUTCOME_YES) {
772- votedFundingYesTriggerHash = gov_sb_hash;
773- }
774- LogPrint (BCLog::GOBJECT, /* Continued */
775- " CGovernanceManager::%s "
776- " Not voting YES-FUNDING for trigger:%s, we voted %s for it already\n " ,
777- strFunc, gov_sb_hash.ToString (),
778- CGovernanceVoting::ConvertOutcomeToString (voteInstancePair.second .eOutcome ));
779- return true ;
780- }
781- return false ;
782- });
783- }
784- if (!voted_already) {
785- // No previous VOTE_SIGNAL_FUNDING was found, vote now
786- if (VoteFundingTrigger (gov_sb_hash, VOTE_OUTCOME_YES, connman, peerman, mn_activeman)) {
787- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Voting YES-FUNDING for new trigger:%s success\n " ,
788- __func__, gov_sb_hash.ToString ());
789- votedFundingYesTriggerHash = gov_sb_hash;
790- } else {
791- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Voting YES-FUNDING for new trigger:%s failed\n " ,
792- __func__, gov_sb_hash.ToString ());
793- // this should never happen, bail out
794- return ;
795- }
796- }
797- }
798-
799- // Vote NO-FUNDING for the rest of the active triggers
800- const auto activeTriggers = GetActiveTriggers ();
801- for (const auto & trigger : activeTriggers) {
802- const auto govobj = FindGovernanceObject (trigger->GetGovernanceObjHash ());
803- const uint256 trigger_hash = govobj->GetHash ();
804- if (trigger->GetBlockHeight () <= nCachedBlockHeight) {
805- // ignore triggers from the past
806- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Not voting NO-FUNDING for outdated trigger:%s\n " , __func__, trigger_hash.ToString ());
807- continue ;
808- }
809- if (trigger_hash == votedFundingYesTriggerHash) {
810- // Skip actual trigger
811- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Not voting NO-FUNDING for trigger:%s, we voted yes for it already\n " , __func__, trigger_hash.ToString ());
812- continue ;
813- }
814- if (vote_rec_t voteRecord; govobj->GetCurrentMNVotes (mn_activeman.GetOutPoint (), voteRecord)) {
815- const auto & strFunc = __func__;
816- if (ranges::any_of (voteRecord.mapInstances , [&](const auto & voteInstancePair) {
817- if (voteInstancePair.first == VOTE_SIGNAL_FUNDING) {
818- LogPrint (BCLog::GOBJECT, /* Continued */
819- " CGovernanceManager::%s "
820- " Not voting NO-FUNDING for trigger:%s, we voted %s for it already\n " ,
821- strFunc, trigger_hash.ToString (),
822- CGovernanceVoting::ConvertOutcomeToString (voteInstancePair.second .eOutcome ));
823- return true ;
824- }
825- return false ;
826- })) {
827- continue ;
828- }
829- }
830- if (!VoteFundingTrigger (trigger_hash, VOTE_OUTCOME_NO, connman, peerman, mn_activeman)) {
831- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Voting NO-FUNDING for trigger:%s failed\n " , __func__, trigger_hash.ToString ());
832- // failing here is ok-ish
833- continue ;
834- }
835- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Voting NO-FUNDING for trigger:%s success\n " , __func__, trigger_hash.ToString ());
836- }
837- }
838-
839- bool CGovernanceManager::VoteFundingTrigger (const uint256& nHash, const vote_outcome_enum_t outcome, CConnman& connman, PeerManager& peerman,
840- const CActiveMasternodeManager& mn_activeman)
841- {
842- CGovernanceVote vote (mn_activeman.GetOutPoint (), nHash, VOTE_SIGNAL_FUNDING, outcome);
843- vote.SetTime (GetAdjustedTime ());
844- vote.SetSignature (mn_activeman.SignBasic (vote.GetSignatureHash ()));
845-
846- CGovernanceException exception;
847- if (!ProcessVoteAndRelay (vote, exception, connman, peerman)) {
848- LogPrint (BCLog::GOBJECT, " CGovernanceManager::%s Vote FUNDING %d for trigger:%s failed:%s\n " , __func__, outcome, nHash.ToString (), exception.what ());
849- return false ;
850- }
851-
852- return true ;
853- }
854-
855- bool CGovernanceManager::HasAlreadyVotedFundingTrigger () const
856- {
857- return votedFundingYesTriggerHash.has_value ();
858- }
859-
860- void CGovernanceManager::ResetVotedFundingTrigger ()
861- {
862- votedFundingYesTriggerHash = std::nullopt ;
863- }
864-
865595void CGovernanceManager::DoMaintenance (CConnman& connman)
866596{
867597 if (!IsValid ()) return ;
0 commit comments