Skip to content

Commit 2b74e15

Browse files
committed
refactor: move masternode mode logic to governance/signing.cpp
Review with `git log -p -n1 --color-moved=dimmed_zebra`.
1 parent fefbe27 commit 2b74e15

File tree

3 files changed

+289
-270
lines changed

3 files changed

+289
-270
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ libbitcoin_node_a_SOURCES = \
510510
governance/exceptions.cpp \
511511
governance/governance.cpp \
512512
governance/object.cpp \
513+
governance/signing.cpp \
513514
governance/validators.cpp \
514515
governance/vote.cpp \
515516
governance/votedb.cpp \

src/governance/governance.cpp

Lines changed: 0 additions & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
865595
void CGovernanceManager::DoMaintenance(CConnman& connman)
866596
{
867597
if (!IsValid()) return;

0 commit comments

Comments
 (0)