Skip to content

Commit c44ae90

Browse files
Merge #6132: refactor: trim and document assumptions for GetQuorum, Get*MN* and friends
a014cf3 refactor: trim and document assumptions for `Get`*`MN`* and friends (Kittywhiskers Van Gogh) 8c9f57d refactor: trim and document assumptions for `GetQuorum` and friends (Kittywhiskers Van Gogh) Pull request description: ## Additional Information This pull request aims to document assumptions when handling `CDeterministicMNCPtr` and `CQuorumCPtr` entities, which can be `nullptr`. In some instances, mishandling or missing validation logic can result in an assertion failure or a null pointer dereference (in both circumstances, the client will crash). While in other cases, assumptions are made based on prior code that affirms that the returned value will be valid. For the former, bail-out logic has been introduced and for the latter, assertions and code comments have been added. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: knst: utACK a014cf3 UdjinM6: utACK a014cf3 Tree-SHA512: e21824d61d81c4ca4b5b4a545a833932946eb0f279d15c586bb5eae96aefcc88d1e3b3fdfa7a01d161f1650351a7cac4bc917b2d1109d77ea2eedd8408d8f37d
2 parents 3aa51d6 + a014cf3 commit c44ae90

16 files changed

+114
-47
lines changed

src/evo/assetlocktx.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint
140140
const auto quorum = qman.GetQuorum(llmqType, quorumHash);
141141
// quorum must be valid at this point. Let's check and throw error just in case
142142
if (!quorum) {
143-
return state.Invalid(TxValidationResult::TX_CONSENSUS, "internal-error");
143+
LogPrintf("%s: ERROR! No quorum for credit pool found for hash=%s\n", __func__, quorumHash.ToString());
144+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-quorum-internal-error");
144145
}
145146

146147
const uint256 requestId = ::SerializeHash(std::make_pair(ASSETUNLOCK_REQUESTID_PREFIX, index));

src/evo/deterministicmns.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ CDeterministicMNList CDeterministicMNList::ApplyDiff(gsl::not_null<const CBlockI
428428
for (const auto& id : diff.removedMns) {
429429
auto dmn = result.GetMNByInternalId(id);
430430
if (!dmn) {
431-
throw(std::runtime_error(strprintf("%s: can't find a removed masternode, id=%d", __func__, id)));
431+
throw std::runtime_error(strprintf("%s: can't find a removed masternode, id=%d", __func__, id));
432432
}
433433
result.RemoveMN(dmn->proTxHash);
434434
}
@@ -437,6 +437,9 @@ CDeterministicMNList CDeterministicMNList::ApplyDiff(gsl::not_null<const CBlockI
437437
}
438438
for (const auto& p : diff.updatedMNs) {
439439
auto dmn = result.GetMNByInternalId(p.first);
440+
if (!dmn) {
441+
throw std::runtime_error(strprintf("%s: can't find an updated masternode, id=%d", __func__, p.first));
442+
}
440443
result.UpdateMN(*dmn, p.second);
441444
}
442445

@@ -818,7 +821,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
818821
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr");
819822
}
820823

821-
CDeterministicMNCPtr dmn = newList.GetMN(opt_proTx->proTxHash);
824+
auto dmn = newList.GetMN(opt_proTx->proTxHash);
822825
if (!dmn) {
823826
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
824827
}
@@ -859,7 +862,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
859862
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
860863
}
861864

862-
CDeterministicMNCPtr dmn = newList.GetMN(opt_proTx->proTxHash);
865+
auto dmn = newList.GetMN(opt_proTx->proTxHash);
863866
if (!dmn) {
864867
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
865868
}
@@ -887,7 +890,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
887890
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
888891
}
889892

890-
CDeterministicMNCPtr dmn = newList.GetMN(opt_proTx->proTxHash);
893+
auto dmn = newList.GetMN(opt_proTx->proTxHash);
891894
if (!dmn) {
892895
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
893896
}
@@ -947,6 +950,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
947950
// current block. We still pay that MN one last time, however.
948951
if (payee && newList.HasMN(payee->proTxHash)) {
949952
auto dmn = newList.GetMN(payee->proTxHash);
953+
// HasMN has reported that GetMN should succeed, enforce that.
954+
assert(dmn);
950955
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
951956
newState->nLastPaidHeight = nHeight;
952957
// Starting from v19 and until MNRewardReallocation, EvoNodes will be paid 4 blocks in a row
@@ -962,6 +967,9 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
962967
newList.UpdateMN(payee->proTxHash, newState);
963968
if (debugLogs) {
964969
dmn = newList.GetMN(payee->proTxHash);
970+
// Since the previous GetMN query returned a value, after an update, querying the same
971+
// hash *must* give us a result. If it doesn't, that would be a potential logic bug.
972+
assert(dmn);
965973
LogPrint(BCLog::MNPAYMENTS, "CDeterministicMNManager::%s -- MN %s, nConsecutivePayments=%d\n",
966974
__func__, dmn->proTxHash.ToString(), dmn->pdmnState->nConsecutivePayments);
967975
}

src/evo/mnhftx.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ bool MNHFTx::Verify(const llmq::CQuorumManager& qman, const uint256& quorumHash,
9696
const Consensus::LLMQType& llmqType = Params().GetConsensus().llmqTypeMnhf;
9797
const auto quorum = qman.GetQuorum(llmqType, quorumHash);
9898

99+
if (!quorum) {
100+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-missing-quorum");
101+
}
102+
99103
const uint256 signHash = llmq::BuildSignHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
100104
if (!sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash)) {
101105
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-invalid");

src/evo/simplifiedmns.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,23 @@ bool CSimplifiedMNListDiff::BuildQuorumsDiff(const CBlockIndex* baseBlockIndex,
187187
return true;
188188
}
189189

190-
void CSimplifiedMNListDiff::BuildQuorumChainlockInfo(const llmq::CQuorumManager& qman, const CBlockIndex* blockIndex)
190+
bool CSimplifiedMNListDiff::BuildQuorumChainlockInfo(const llmq::CQuorumManager& qman, const CBlockIndex* blockIndex)
191191
{
192192
// Group quorums (indexes corresponding to entries of newQuorums) per CBlockIndex containing the expected CL signature in CbTx.
193193
// We want to avoid to load CbTx now, as more than one quorum will target the same block: hence we want to load CbTxs once per block (heavy operation).
194194
std::multimap<const CBlockIndex*, uint16_t> workBaseBlockIndexMap;
195195

196196
for (const auto [idx, e] : enumerate(newQuorums)) {
197+
// We assume that we have on hand, quorums that correspond to the hashes queried.
198+
// If we cannot find them, something must have gone wrong and we should cease trying
199+
// to build any further.
197200
auto quorum = qman.GetQuorum(e.llmqType, e.quorumHash);
201+
if (!quorum) {
202+
LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, quorumHash=%s\n", __func__,
203+
ToUnderlying(e.llmqType), e.quorumHash.ToString());
204+
return false;
205+
}
206+
198207
// In case of rotation, all rotated quorums rely on the CL sig expected in the cycleBlock (the block of the first DKG) - 8
199208
// In case of non-rotation, quorums rely on the CL sig expected in the block of the DKG - 8
200209
const CBlockIndex* pWorkBaseBlockIndex =
@@ -203,7 +212,7 @@ void CSimplifiedMNListDiff::BuildQuorumChainlockInfo(const llmq::CQuorumManager&
203212
workBaseBlockIndexMap.insert(std::make_pair(pWorkBaseBlockIndex, idx));
204213
}
205214

206-
for(auto it = workBaseBlockIndexMap.begin(); it != workBaseBlockIndexMap.end(); ) {
215+
for (auto it = workBaseBlockIndexMap.begin(); it != workBaseBlockIndexMap.end();) {
207216
// Process each key (CBlockIndex containing the expected CL signature in CbTx) of the std::multimap once
208217
const CBlockIndex* pWorkBaseBlockIndex = it->first;
209218
const auto cbcl = GetNonNullCoinbaseChainlock(pWorkBaseBlockIndex);
@@ -224,6 +233,8 @@ void CSimplifiedMNListDiff::BuildQuorumChainlockInfo(const llmq::CQuorumManager&
224233
it_sig->second.insert(idx_set.begin(), idx_set.end());
225234
}
226235
}
236+
237+
return true;
227238
}
228239

229240
UniValue CSimplifiedMNListDiff::ToJson(bool extended) const
@@ -366,7 +377,10 @@ bool BuildSimplifiedMNListDiff(CDeterministicMNManager& dmnman, const Chainstate
366377
}
367378

368379
if (DeploymentActiveAfter(blockIndex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20)) {
369-
mnListDiffRet.BuildQuorumChainlockInfo(qman, blockIndex);
380+
if (!mnListDiffRet.BuildQuorumChainlockInfo(qman, blockIndex)) {
381+
errorRet = strprintf("failed to build quorum chainlock info");
382+
return false;
383+
}
370384
}
371385

372386
// TODO store coinbase TX in CBlockIndex

src/evo/simplifiedmns.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class CSimplifiedMNListDiff
170170

171171
bool BuildQuorumsDiff(const CBlockIndex* baseBlockIndex, const CBlockIndex* blockIndex,
172172
const llmq::CQuorumBlockProcessor& quorum_block_processor);
173-
void BuildQuorumChainlockInfo(const llmq::CQuorumManager& qman, const CBlockIndex* blockIndex);
173+
bool BuildQuorumChainlockInfo(const llmq::CQuorumManager& qman, const CBlockIndex* blockIndex);
174174

175175
[[nodiscard]] UniValue ToJson(bool extended = false) const;
176176
};

src/governance/governance.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,9 @@ void CGovernanceManager::RemoveInvalidVotes()
16011601
std::vector<COutPoint> changedKeyMNs;
16021602
for (const auto& p : diff.updatedMNs) {
16031603
auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(p.first);
1604+
// BuildDiff will construct itself with MNs that we already have knowledge
1605+
// of, meaning that fetch operations should never fail.
1606+
assert(oldDmn);
16041607
if ((p.second.fields & CDeterministicMNStateDiff::Field_keyIDVoting) && p.second.state.keyIDVoting != oldDmn->pdmnState->keyIDVoting) {
16051608
changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
16061609
} else if ((p.second.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) && p.second.state.pubKeyOperator != oldDmn->pdmnState->pubKeyOperator) {
@@ -1609,6 +1612,9 @@ void CGovernanceManager::RemoveInvalidVotes()
16091612
}
16101613
for (const auto& id : diff.removedMns) {
16111614
auto oldDmn = lastMNListForVotingKeys->GetMNByInternalId(id);
1615+
// BuildDiff will construct itself with MNs that we already have knowledge
1616+
// of, meaning that fetch operations should never fail.
1617+
assert(oldDmn);
16121618
changedKeyMNs.emplace_back(oldDmn->collateralOutpoint);
16131619
}
16141620

src/llmq/quorums.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,17 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
602602
assert(pQuorumBaseBlockIndex);
603603
// populate cache for keepOldConnections most recent quorums only
604604
bool populate_cache = vecResultQuorums.size() < static_cast<size_t>(llmq_params_opt->keepOldConnections);
605+
606+
// We assume that every quorum asked for is available to us on hand, if this
607+
// fails then we can assume that something has gone wrong and we should stop
608+
// trying to process any further and return a blank.
605609
auto quorum = GetQuorum(llmqType, pQuorumBaseBlockIndex, populate_cache);
606-
assert(quorum != nullptr);
610+
if (!quorum) {
611+
LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, blockHash=%s, populate_cache=%s\n",
612+
__func__, ToUnderlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(),
613+
populate_cache ? "true" : "false");
614+
return {};
615+
}
607616
vecResultQuorums.emplace_back(quorum);
608617
}
609618

@@ -742,7 +751,7 @@ PeerMsgRet CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_t
742751
return sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND, request_limit_exceeded);
743752
}
744753

745-
const CQuorumCPtr pQuorum = GetQuorum(request.GetLLMQType(), pQuorumBaseBlockIndex);
754+
const auto pQuorum = GetQuorum(request.GetLLMQType(), pQuorumBaseBlockIndex);
746755
if (pQuorum == nullptr) {
747756
return sendQDATA(CQuorumDataRequest::Errors::QUORUM_NOT_FOUND, request_limit_exceeded);
748757
}

src/llmq/signing.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ static bool PreVerifyRecoveredSig(const CQuorumManager& quorum_manager, const CR
588588
return false;
589589
}
590590

591-
CQuorumCPtr quorum = quorum_manager.GetQuorum(llmqType, recoveredSig.getQuorumHash());
591+
auto quorum = quorum_manager.GetQuorum(llmqType, recoveredSig.getQuorumHash());
592592

593593
if (!quorum) {
594594
LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found\n", __func__,
@@ -681,7 +681,7 @@ void CSigningManager::CollectPendingRecoveredSigsToVerify(
681681
auto llmqType = recSig->getLlmqType();
682682
auto quorumKey = std::make_pair(recSig->getLlmqType(), recSig->getQuorumHash());
683683
if (!retQuorums.count(quorumKey)) {
684-
CQuorumCPtr quorum = qman.GetQuorum(llmqType, recSig->getQuorumHash());
684+
auto quorum = qman.GetQuorum(llmqType, recSig->getQuorumHash());
685685
if (!quorum) {
686686
LogPrint(BCLog::LLMQ, "CSigningManager::%s -- quorum %s not found, node=%d\n", __func__,
687687
recSig->getQuorumHash().ToString(), nodeId);
@@ -881,7 +881,7 @@ bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, CSigShares
881881
if (m_mn_activeman == nullptr) return false;
882882
if (m_mn_activeman->GetProTxHash().IsNull()) return false;
883883

884-
const CQuorumCPtr quorum = [&]() {
884+
const auto quorum = [&]() {
885885
if (quorumHash.IsNull()) {
886886
// This might end up giving different results on different members
887887
// This might happen when we are on the brink of confirming a new quorum

src/llmq/signing_shares.cpp

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -549,15 +549,14 @@ bool CSigSharesManager::PreVerifyBatchedSigShares(const CActiveMasternodeManager
549549
return true;
550550
}
551551

552-
void CSigSharesManager::CollectPendingSigSharesToVerify(
553-
size_t maxUniqueSessions,
554-
std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
555-
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
552+
bool CSigSharesManager::CollectPendingSigSharesToVerify(
553+
size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
554+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
556555
{
557556
{
558557
LOCK(cs);
559558
if (nodeStates.empty()) {
560-
return;
559+
return false;
561560
}
562561

563562
// This will iterate node states in random order and pick one sig share at a time. This avoids processing
@@ -590,7 +589,7 @@ void CSigSharesManager::CollectPendingSigSharesToVerify(
590589
rnd);
591590

592591
if (retSigShares.empty()) {
593-
return;
592+
return false;
594593
}
595594
}
596595

@@ -605,11 +604,20 @@ void CSigSharesManager::CollectPendingSigSharesToVerify(
605604
continue;
606605
}
607606

608-
CQuorumCPtr quorum = qman.GetQuorum(llmqType, sigShare.getQuorumHash());
609-
assert(quorum != nullptr);
607+
auto quorum = qman.GetQuorum(llmqType, sigShare.getQuorumHash());
608+
// Despite constructing a convenience map, we assume that the quorum *must* be present.
609+
// The absence of it might indicate an inconsistent internal state, so we should report
610+
// nothing instead of reporting flawed data.
611+
if (!quorum) {
612+
LogPrintf("%s: ERROR! Unexpected missing quorum with llmqType=%d, quorumHash=%s\n", __func__,
613+
ToUnderlying(llmqType), sigShare.getQuorumHash().ToString());
614+
return false;
615+
}
610616
retQuorums.try_emplace(k, quorum);
611617
}
612618
}
619+
620+
return true;
613621
}
614622

615623
bool CSigSharesManager::ProcessPendingSigShares(const CConnman& connman)
@@ -618,8 +626,8 @@ bool CSigSharesManager::ProcessPendingSigShares(const CConnman& connman)
618626
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher> quorums;
619627

620628
const size_t nMaxBatchSize{32};
621-
CollectPendingSigSharesToVerify(nMaxBatchSize, sigSharesByNodes, quorums);
622-
if (sigSharesByNodes.empty()) {
629+
bool collect_status = CollectPendingSigSharesToVerify(nMaxBatchSize, sigSharesByNodes, quorums);
630+
if (!collect_status || sigSharesByNodes.empty()) {
623631
return false;
624632
}
625633

@@ -1269,11 +1277,14 @@ void CSigSharesManager::Cleanup()
12691277
// Find quorums which became inactive
12701278
for (auto it = quorums.begin(); it != quorums.end(); ) {
12711279
if (IsQuorumActive(it->first.first, qman, it->first.second)) {
1272-
it->second = qman.GetQuorum(it->first.first, it->first.second);
1273-
++it;
1274-
} else {
1275-
it = quorums.erase(it);
1280+
auto quorum = qman.GetQuorum(it->first.first, it->first.second);
1281+
if (quorum) {
1282+
it->second = quorum;
1283+
++it;
1284+
continue;
1285+
}
12761286
}
1287+
it = quorums.erase(it);
12771288
}
12781289

12791290
{

src/llmq/signing_shares.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,9 @@ class CSigSharesManager : public CRecoveredSigsListener
452452
static bool PreVerifyBatchedSigShares(const CActiveMasternodeManager& mn_activeman, const CQuorumManager& quorum_manager,
453453
const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares, bool& retBan);
454454

455-
void CollectPendingSigSharesToVerify(size_t maxUniqueSessions,
456-
std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
457-
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
455+
bool CollectPendingSigSharesToVerify(
456+
size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
457+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
458458
bool ProcessPendingSigShares(const CConnman& connman);
459459

460460
void ProcessPendingSigShares(const std::vector<CSigShare>& sigSharesToProcess,

0 commit comments

Comments
 (0)