|
25 | 25 | #include <array> |
26 | 26 | #include <atomic> |
27 | 27 | #include <memory> |
| 28 | +#include <type_traits> |
28 | 29 |
|
29 | 30 | namespace llmq { |
30 | 31 | CDKGLogger::CDKGLogger(const CDKGSession& _quorumDkg, std::string_view _func, int source_line) : |
@@ -197,56 +198,27 @@ bool CDKGSession::PreVerifyMessage(const CDKGContribution& qc, bool& retBan) con |
197 | 198 | return true; |
198 | 199 | } |
199 | 200 |
|
200 | | -// TODO: remove duplicated code between all ReceiveMessage: CDKGContribution, CDKGComplaint, CDKGJustification, CDKGPrematureCommitment |
201 | 201 | std::optional<CInv> CDKGSession::ReceiveMessage(const CDKGContribution& qc) |
202 | 202 | { |
203 | 203 | CDKGLogger logger(*this, __func__, __LINE__); |
204 | | - |
205 | | - auto* member = GetMember(qc.proTxHash); |
206 | | - |
207 | 204 | cxxtimer::Timer t1(true); |
208 | | - logger.Batch("received contribution from %s", qc.proTxHash.ToString()); |
209 | 205 |
|
210 | | - // relay, no matter if further verification fails |
211 | | - // This ensures the whole quorum sees the bad behavior |
212 | | - |
213 | | - if (member->contributions.size() >= 2) { |
214 | | - // only relay up to 2 contributions, that's enough to let the other members know about his bad behavior |
215 | | - return std::nullopt; |
216 | | - } |
217 | | - |
218 | | - const uint256 hash = ::SerializeHash(qc); |
219 | | - WITH_LOCK(invCs, contributions.emplace(hash, qc)); |
220 | | - member->contributions.emplace(hash); |
221 | | - |
222 | | - CInv inv(MSG_QUORUM_CONTRIB, hash); |
223 | | - |
224 | | - dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, member->idx, [&](CDKGDebugMemberStatus& status) { |
225 | | - status.statusBits.receivedContribution = true; |
226 | | - return true; |
227 | | - }); |
228 | | - |
229 | | - if (member->contributions.size() > 1) { |
230 | | - // don't do any further processing if we got more than 1 contribution. we already relayed it, |
231 | | - // so others know about his bad behavior |
232 | | - MarkBadMember(member->idx); |
233 | | - logger.Batch("%s did send multiple contributions", member->dmn->proTxHash.ToString()); |
234 | | - return inv; |
235 | | - } |
| 206 | + auto state = WITH_LOCK(invCs, return ReceiveMessagePreamble(qc, MsgPhase::Contribution, logger)); |
| 207 | + if (!state) return std::nullopt; |
| 208 | + auto& [member, hash, inv, should_process] = *state; |
| 209 | + if (!should_process) return inv; |
236 | 210 |
|
237 | 211 | receivedVvecs[member->idx] = qc.vvec; |
238 | 212 |
|
239 | 213 | int receivedCount = ranges::count_if(members, [](const auto& m){return !m->contributions.empty();}); |
240 | 214 |
|
241 | 215 | logger.Batch("received and relayed contribution. received=%d/%d, time=%d", receivedCount, members.size(), t1.count()); |
242 | 216 |
|
243 | | - cxxtimer::Timer t2(true); |
244 | | - |
245 | 217 | if (!AreWeMember()) { |
246 | | - // can't further validate |
247 | 218 | return inv; |
248 | 219 | } |
249 | 220 |
|
| 221 | + cxxtimer::Timer t2(true); |
250 | 222 | dkgManager.WriteVerifiedVvecContribution(params.type, m_quorum_base_block_index, qc.proTxHash, qc.vvec); |
251 | 223 |
|
252 | 224 | bool complain = false; |
@@ -327,33 +299,10 @@ std::optional<CInv> CDKGSession::ReceiveMessage(const CDKGComplaint& qc) |
327 | 299 | { |
328 | 300 | CDKGLogger logger(*this, __func__, __LINE__); |
329 | 301 |
|
330 | | - logger.Batch("received complaint from %s", qc.proTxHash.ToString()); |
331 | | - |
332 | | - auto* member = GetMember(qc.proTxHash); |
333 | | - |
334 | | - if (member->complaints.size() >= 2) { |
335 | | - // only relay up to 2 complaints, that's enough to let the other members know about his bad behavior |
336 | | - return std::nullopt; |
337 | | - } |
338 | | - |
339 | | - const uint256 hash = ::SerializeHash(qc); |
340 | | - WITH_LOCK(invCs, complaints.emplace(hash, qc)); |
341 | | - member->complaints.emplace(hash); |
342 | | - |
343 | | - CInv inv(MSG_QUORUM_COMPLAINT, hash); |
344 | | - |
345 | | - dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, member->idx, [&](CDKGDebugMemberStatus& status) { |
346 | | - status.statusBits.receivedComplaint = true; |
347 | | - return true; |
348 | | - }); |
349 | | - |
350 | | - if (member->complaints.size() > 1) { |
351 | | - // don't do any further processing if we got more than 1 complaint. we already relayed it, |
352 | | - // so others know about his bad behavior |
353 | | - MarkBadMember(member->idx); |
354 | | - logger.Batch("%s did send multiple complaints", member->dmn->proTxHash.ToString()); |
355 | | - return inv; |
356 | | - } |
| 302 | + auto state = WITH_LOCK(invCs, return ReceiveMessagePreamble(qc, MsgPhase::Complaint, logger)); |
| 303 | + if (!state) return std::nullopt; |
| 304 | + auto& [member, hash, inv, should_process] = *state; |
| 305 | + if (!should_process) return inv; |
357 | 306 |
|
358 | 307 | int receivedCount = 0; |
359 | 308 | for (const auto i : irange::range(members.size())) { |
@@ -447,34 +396,10 @@ std::optional<CInv> CDKGSession::ReceiveMessage(const CDKGJustification& qj) |
447 | 396 | { |
448 | 397 | CDKGLogger logger(*this, __func__, __LINE__); |
449 | 398 |
|
450 | | - logger.Batch("received justification from %s", qj.proTxHash.ToString()); |
451 | | - |
452 | | - auto* member = GetMember(qj.proTxHash); |
453 | | - |
454 | | - if (member->justifications.size() >= 2) { |
455 | | - // only relay up to 2 justifications, that's enough to let the other members know about his bad behavior |
456 | | - return std::nullopt; |
457 | | - } |
458 | | - |
459 | | - const uint256 hash = ::SerializeHash(qj); |
460 | | - WITH_LOCK(invCs, justifications.emplace(hash, qj)); |
461 | | - member->justifications.emplace(hash); |
462 | | - |
463 | | - // we always relay, even if further verification fails |
464 | | - CInv inv(MSG_QUORUM_JUSTIFICATION, hash); |
465 | | - |
466 | | - dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, member->idx, [&](CDKGDebugMemberStatus& status) { |
467 | | - status.statusBits.receivedJustification = true; |
468 | | - return true; |
469 | | - }); |
470 | | - |
471 | | - if (member->justifications.size() > 1) { |
472 | | - // don't do any further processing if we got more than 1 justification. we already relayed it, |
473 | | - // so others know about his bad behavior |
474 | | - logger.Batch("%s did send multiple justifications", member->dmn->proTxHash.ToString()); |
475 | | - MarkBadMember(member->idx); |
476 | | - return inv; |
477 | | - } |
| 399 | + auto state = WITH_LOCK(invCs, return ReceiveMessagePreamble(qj, MsgPhase::Justification, logger)); |
| 400 | + if (!state) return std::nullopt; |
| 401 | + auto& [member, hash, inv, should_process] = *state; |
| 402 | + if (!should_process) return inv; |
478 | 403 |
|
479 | 404 | if (member->bad) { |
480 | 405 | // we locally determined him to be bad (sent none or more then one contributions) |
@@ -683,6 +608,76 @@ CDKGMember* CDKGSession::GetMember(const uint256& proTxHash) const |
683 | 608 | return members[it->second].get(); |
684 | 609 | } |
685 | 610 |
|
| 611 | +template <typename MsgType> |
| 612 | +std::optional<CDKGSession::ReceiveMessageState> CDKGSession::ReceiveMessagePreamble(const MsgType& msg, MsgPhase phase, CDKGLogger& logger) |
| 613 | +{ |
| 614 | + auto* member = GetMember(msg.proTxHash); |
| 615 | + |
| 616 | + GetDataMsg inv_type{0}; |
| 617 | + std::string msg_name; |
| 618 | + |
| 619 | + // Select member set, inv type, and name based on phase |
| 620 | + auto& member_set = [&]() -> Uint256HashSet& { |
| 621 | + switch (phase) { |
| 622 | + case MsgPhase::Contribution: |
| 623 | + inv_type = MSG_QUORUM_CONTRIB; |
| 624 | + msg_name = "contribution"; |
| 625 | + return member->contributions; |
| 626 | + case MsgPhase::Complaint: |
| 627 | + inv_type = MSG_QUORUM_COMPLAINT; |
| 628 | + msg_name = "complaint"; |
| 629 | + return member->complaints; |
| 630 | + case MsgPhase::Justification: |
| 631 | + inv_type = MSG_QUORUM_JUSTIFICATION; |
| 632 | + msg_name = "justification"; |
| 633 | + return member->justifications; |
| 634 | + } |
| 635 | + assert(false); |
| 636 | + }(); |
| 637 | + |
| 638 | + logger.Batch("received %s from %s", msg_name, msg.proTxHash.ToString()); |
| 639 | + |
| 640 | + if (member_set.size() >= 2) { |
| 641 | + // only relay up to 2 justifications, that's enough to let the other members know about his bad behavior |
| 642 | + return std::nullopt; |
| 643 | + } |
| 644 | + |
| 645 | + const uint256 hash = ::SerializeHash(msg); |
| 646 | + member_set.emplace(hash); |
| 647 | + if constexpr (std::is_same_v<MsgType, CDKGContribution>) { |
| 648 | + contributions.emplace(hash, msg); |
| 649 | + } else if constexpr (std::is_same_v<MsgType, CDKGComplaint>) { |
| 650 | + complaints.emplace(hash, msg); |
| 651 | + } else if constexpr (std::is_same_v<MsgType, CDKGJustification>) { |
| 652 | + justifications.emplace(hash, msg); |
| 653 | + } |
| 654 | + |
| 655 | + dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, member->idx, [phase](CDKGDebugMemberStatus& status) { |
| 656 | + switch (phase) { |
| 657 | + case MsgPhase::Contribution: status.statusBits.receivedContribution = true; break; |
| 658 | + case MsgPhase::Complaint: status.statusBits.receivedComplaint = true; break; |
| 659 | + case MsgPhase::Justification: status.statusBits.receivedJustification = true; break; |
| 660 | + } |
| 661 | + return true; |
| 662 | + }); |
| 663 | + |
| 664 | + bool should_process{true}; |
| 665 | + if (member_set.size() > 1) { |
| 666 | + // don't do any further processing if we got more than 1 justification. we already relayed it, |
| 667 | + // so others know about his bad behavior |
| 668 | + MarkBadMember(member->idx); |
| 669 | + logger.Batch("%s did send multiple %ss", member->dmn->proTxHash.ToString(), msg_name); |
| 670 | + should_process = false; |
| 671 | + } |
| 672 | + |
| 673 | + // we always relay, even if further verification fails |
| 674 | + return ReceiveMessageState{member, hash, CInv{inv_type, hash}, should_process}; |
| 675 | +} |
| 676 | + |
| 677 | +template std::optional<CDKGSession::ReceiveMessageState> CDKGSession::ReceiveMessagePreamble<CDKGContribution>(const CDKGContribution&, MsgPhase, CDKGLogger&); |
| 678 | +template std::optional<CDKGSession::ReceiveMessageState> CDKGSession::ReceiveMessagePreamble<CDKGComplaint>(const CDKGComplaint&, MsgPhase, CDKGLogger&); |
| 679 | +template std::optional<CDKGSession::ReceiveMessageState> CDKGSession::ReceiveMessagePreamble<CDKGJustification>(const CDKGJustification&, MsgPhase, CDKGLogger&); |
| 680 | + |
686 | 681 | void CDKGSession::MarkBadMember(size_t idx) |
687 | 682 | { |
688 | 683 | auto* member = members.at(idx).get(); |
|
0 commit comments