-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor: drop dependency of spork manager on network code #7303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 3 commits
bd2bff1
7c899a2
7460211
6a4ee52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,18 +4,15 @@ | |||||||||||||
|
|
||||||||||||||
| #include <spork.h> | ||||||||||||||
|
|
||||||||||||||
| #include <flat-database.h> | ||||||||||||||
| #include <util/helpers.h> | ||||||||||||||
|
|
||||||||||||||
| #include <chainparams.h> | ||||||||||||||
| #include <flat-database.h> | ||||||||||||||
| #include <key_io.h> | ||||||||||||||
| #include <logging.h> | ||||||||||||||
| #include <messagesigner.h> | ||||||||||||||
| #include <net.h> | ||||||||||||||
| #include <netmessagemaker.h> | ||||||||||||||
| #include <protocol.h> | ||||||||||||||
| #include <script/standard.h> | ||||||||||||||
| #include <timedata.h> | ||||||||||||||
| #include <util/helpers.h> | ||||||||||||||
| #include <util/message.h> // for MESSAGE_MAGIC | ||||||||||||||
| #include <util/string.h> | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -126,51 +123,37 @@ void CSporkManager::CheckAndRemove() | |||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| MessageProcessingResult CSporkManager::ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, CDataStream& vRecv) | ||||||||||||||
| { | ||||||||||||||
| if (msg_type == NetMsgType::SPORK) { | ||||||||||||||
| return ProcessSpork(peer.GetId(), vRecv); | ||||||||||||||
| } else if (msg_type == NetMsgType::GETSPORKS) { | ||||||||||||||
| ProcessGetSporks(peer, connman); | ||||||||||||||
| } | ||||||||||||||
| return {}; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| MessageProcessingResult CSporkManager::ProcessSpork(NodeId from, CDataStream& vRecv) | ||||||||||||||
| bool CSporkManager::IsSporkValid(const CSporkMessage& spork) const | ||||||||||||||
| { | ||||||||||||||
| CSporkMessage spork; | ||||||||||||||
| vRecv >> spork; | ||||||||||||||
|
|
||||||||||||||
| uint256 hash = spork.GetHash(); | ||||||||||||||
|
|
||||||||||||||
| MessageProcessingResult ret{}; | ||||||||||||||
| ret.m_to_erase = CInv{MSG_SPORK, hash}; | ||||||||||||||
|
|
||||||||||||||
| if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) { | ||||||||||||||
| LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: too far into the future\n"); | ||||||||||||||
| ret.m_error = MisbehavingError{100}; | ||||||||||||||
| return ret; | ||||||||||||||
| LogPrint(BCLog::SPORK, "CSporkManager::%s -- ERROR: too far into the future\n", __func__); | ||||||||||||||
| return false; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| auto opt_keyIDSigner = spork.GetSignerKeyID(); | ||||||||||||||
|
|
||||||||||||||
| if (opt_keyIDSigner == std::nullopt || WITH_LOCK(cs, return !setSporkPubKeyIDs.count(*opt_keyIDSigner))) { | ||||||||||||||
| LogPrint(BCLog::SPORK, "CSporkManager::ProcessSpork -- ERROR: invalid signature\n"); | ||||||||||||||
| ret.m_error = MisbehavingError{100}; | ||||||||||||||
| return ret; | ||||||||||||||
| LogPrint(BCLog::SPORK, "CSporkManager::%s -- ERROR: invalid signature\n", __func__); | ||||||||||||||
| return false; | ||||||||||||||
| } | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| std::string strLogMsg{strprintf("SPORK -- hash: %s id: %d value: %10d peer=%d", hash.ToString(), spork.nSporkID, | ||||||||||||||
| spork.nValue, from)}; | ||||||||||||||
| auto keyIDSigner = *opt_keyIDSigner; | ||||||||||||||
| bool CSporkManager::ProcessSpork(const CSporkMessage& spork) | ||||||||||||||
| { | ||||||||||||||
| uint256 hash = spork.GetHash(); | ||||||||||||||
| std::string strLogMsg{strprintf("SPORK -- hash: %s id: %d value: %10d", hash.ToString(), spork.nSporkID, | ||||||||||||||
| spork.nValue)}; | ||||||||||||||
| assert(spork.GetSignerKeyID().has_value()); | ||||||||||||||
| auto keyIDSigner = spork.GetSignerKeyID().value(); | ||||||||||||||
|
Comment on lines
+142
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: Undocumented precondition that IsSporkValid was called first
source: ['claude']
Comment on lines
+148
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: ProcessSpork() invokes GetSignerKeyID() twice CSporkMessage::GetSignerKeyID() performs a CPubKey::RecoverCompact on the signature each call and is not cached on the message. ProcessSpork() now calls it twice in succession (line 148 for the has_value() check, line 150 for value()), and the prior IsSporkValid() in the caller has already done it once — three recoveries per SPORK message on the happy path. Sporks are infrequent so the practical impact is negligible, but caching the optional locally avoids the redundant ECDSA recovery. 💡 Suggested change
Suggested change
source: ['claude'] |
||||||||||||||
|
|
||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||
| { | ||||||||||||||
| LOCK(cs); // make sure to not lock this together with cs_main | ||||||||||||||
| if (mapSporksActive.count(spork.nSporkID)) { | ||||||||||||||
| if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) { | ||||||||||||||
| if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) { | ||||||||||||||
| LogPrint(BCLog::SPORK, "%s seen\n", strLogMsg); | ||||||||||||||
| return ret; | ||||||||||||||
| return false; | ||||||||||||||
| } else { | ||||||||||||||
| LogPrintf("%s updated\n", strLogMsg); | ||||||||||||||
| } | ||||||||||||||
|
|
@@ -191,21 +174,15 @@ MessageProcessingResult CSporkManager::ProcessSpork(NodeId from, CDataStream& vR | |||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| ret.m_inventory.emplace_back(MSG_SPORK, hash); | ||||||||||||||
| return ret; | ||||||||||||||
| return true; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| void CSporkManager::ProcessGetSporks(CNode& peer, CConnman& connman) | ||||||||||||||
| std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage>> CSporkManager::ActiveSporks() const | ||||||||||||||
| { | ||||||||||||||
| LOCK(cs); // make sure to not lock this together with cs_main | ||||||||||||||
| for (const auto& pair : mapSporksActive) { | ||||||||||||||
| for (const auto& signerSporkPair : pair.second) { | ||||||||||||||
| connman.PushMessage(&peer, CNetMsgMaker(peer.GetCommonVersion()).Make(NetMsgType::SPORK, signerSporkPair.second)); | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| return mapSporksActive; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| std::optional<CInv> CSporkManager::UpdateSpork(SporkId nSporkID, SporkValue nValue) | ||||||||||||||
| { | ||||||||||||||
| CSporkMessage spork(nSporkID, nValue, GetAdjustedTime()); | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,28 +5,23 @@ | |||||||||||||||||||||||||||||||||||||||
| #ifndef BITCOIN_SPORK_H | ||||||||||||||||||||||||||||||||||||||||
| #define BITCOIN_SPORK_H | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #include <msg_result.h> | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #include <hash.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <key.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <net.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <net_types.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <pubkey.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <saltedhasher.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <sync.h> | ||||||||||||||||||||||||||||||||||||||||
| #include <uint256.h> | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #include <array> | ||||||||||||||||||||||||||||||||||||||||
| #include <optional> | ||||||||||||||||||||||||||||||||||||||||
| #include <string_view> | ||||||||||||||||||||||||||||||||||||||||
| #include <unordered_map> | ||||||||||||||||||||||||||||||||||||||||
| #include <vector> | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| class CConnman; | ||||||||||||||||||||||||||||||||||||||||
| template<typename T> | ||||||||||||||||||||||||||||||||||||||||
| class CFlatDB; | ||||||||||||||||||||||||||||||||||||||||
| class CNode; | ||||||||||||||||||||||||||||||||||||||||
| class CDataStream; | ||||||||||||||||||||||||||||||||||||||||
| class uint256; | ||||||||||||||||||||||||||||||||||||||||
| class CInv; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| class CSporkMessage; | ||||||||||||||||||||||||||||||||||||||||
| class CSporkManager; | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -249,26 +244,24 @@ class CSporkManager : public SporkStore | |||||||||||||||||||||||||||||||||||||||
| void CheckAndRemove() EXCLUSIVE_LOCKS_REQUIRED(!cs); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||
| * ProcessMessage is used to call ProcessSpork and ProcessGetSporks. See below | ||||||||||||||||||||||||||||||||||||||||
| * IsSporkValid validate signed time and pubkey | ||||||||||||||||||||||||||||||||||||||||
| * If these values mismatch function returns false [spork is invalid] | ||||||||||||||||||||||||||||||||||||||||
| * If spork validation failed, peer should be punished | ||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||
| [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& peer, CConnman& connman, std::string_view msg_type, | ||||||||||||||||||||||||||||||||||||||||
| CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_cache); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| [[nodiscard]] bool IsSporkValid(const CSporkMessage& spork) const EXCLUSIVE_LOCKS_REQUIRED(!cs); | ||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||
| * ProcessSpork is used to handle the 'spork' p2p message. | ||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||
| * For 'spork', it validates the spork and adds it to the internal spork storage and | ||||||||||||||||||||||||||||||||||||||||
| * performs any necessary processing. | ||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||
| [[nodiscard]] MessageProcessingResult ProcessSpork(NodeId from, CDataStream& vRecv) | ||||||||||||||||||||||||||||||||||||||||
| [[nodiscard]] bool ProcessSpork(const CSporkMessage& spork) | ||||||||||||||||||||||||||||||||||||||||
| EXCLUSIVE_LOCKS_REQUIRED(!cs, !cs_cache); | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
252
to
259
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: ProcessSpork() has an undocumented validity precondition after the split The refactor split spork handling into IsSporkValid() and ProcessSpork(), but ProcessSpork() now asserts spork.GetSignerKeyID().has_value() at src/spork.cpp:147 and unconditionally dereferences the optional on the next line. That precondition is only satisfied if IsSporkValid() ran first and returned true. The single current caller in net_processing.cpp does this correctly, but two things make the new API a footgun for future use: (1) the Doxygen comment at src/spork.h:255 still claims "it validates the spork and adds it to the internal spork storage", which is no longer true; (2) since the stated goal of the refactor is to make spork management reusable outside the networking layer, a future caller is likely to copy this pattern incorrectly and either crash in debug builds or throw std::bad_optional_access in release. Either fold the validity check back into ProcessSpork(), update the comment to state the precondition explicitly, or change the signature to take a validated wrapper type so the unsafe call is unrepresentable. source: ['codex'] 🤖 Fix this with AI agents
Comment on lines
252
to
259
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Suggestion: ProcessSpork() doc comment is stale and hides an unsafe precondition The Doxygen comment claims ProcessSpork() "validates the spork and adds it to the internal spork storage," but after the refactor validation (timestamp window + signer-pubkey membership in setSporkPubKeyIDs) lives entirely in IsSporkValid(). ProcessSpork() now only checks 💡 Suggested change
Suggested change
source: ['claude', 'codex'] 🤖 Fix this with AI agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||
| * ProcessGetSporks is used to handle the 'getsporks' p2p message. | ||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||
| * For 'getsporks', it sends active sporks to the requesting peer. | ||||||||||||||||||||||||||||||||||||||||
| * ActiveSporks is used to handle the 'getsporks' p2p message. | ||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||
| void ProcessGetSporks(CNode& peer, CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs); | ||||||||||||||||||||||||||||||||||||||||
| std::unordered_map<SporkId, std::map<CKeyID, CSporkMessage>> ActiveSporks() const EXCLUSIVE_LOCKS_REQUIRED(!cs); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||
| * UpdateSpork is used by the spork RPC command to set a new spork value, sign | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.