-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: memoize governance vote signature checks to bound govsync cost #7518
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 all commits
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 |
|---|---|---|
|
|
@@ -19,6 +19,42 @@ | |
| static_assert(CGovernanceVote::COMPACT_SIG_SIZE == CPubKey::COMPACT_SIGNATURE_SIZE); | ||
| static_assert(CGovernanceVote::BLS_SIG_SIZE == CBLSSignature::SerSize); | ||
|
|
||
| namespace { | ||
| //! Hash the inputs that determine the verification result. Dropping any of them | ||
| //! breaks the memo; see CGovernanceVote::SignatureMemo. | ||
| uint256 SignatureCacheKey(const CKeyID& key, const uint256& sigHash, const std::vector<unsigned char>& vchSig) | ||
| { | ||
| HashWriter ss{}; | ||
| ss << key << sigHash << vchSig; | ||
| return ss.GetHash(); | ||
| } | ||
|
|
||
| //! The BLS verdict is always computed with VerifyInsecure(..., /*specificLegacyScheme=*/false), | ||
| //! so the key must be fingerprinted under that same scheme. Serializing via operator<< would | ||
| //! instead use the mutable bls_legacy_scheme global, and a legacy encoding of P can equal the | ||
| //! basic encoding of -P. Across an activation or a reorg that flips the global, a verdict cached | ||
| //! for one key could then be served for the other. | ||
| uint256 SignatureCacheKey(const CBLSPublicKey& key, const uint256& sigHash, | ||
| const std::vector<unsigned char>& vchSig) | ||
| { | ||
| HashWriter ss{}; | ||
| const auto key_bytes = key.ToBytes(/*specificLegacyScheme=*/false); | ||
| ss.write(MakeByteSpan(key_bytes)); | ||
| ss << sigHash << vchSig; | ||
|
Comment on lines
+41
to
+43
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: Add a regression for cross-scheme BLS fingerprint collisions The explicit non-legacy key encoding prevents a valid cached verdict from being reused for a different point after source: ['codex'] |
||
| return ss.GetHash(); | ||
| } | ||
| } // namespace | ||
|
|
||
| std::optional<bool> CGovernanceVote::GetMemoisedVerdict(const CKeyID& keyID) const | ||
| { | ||
| return m_sig_memo.Lookup(SignatureCacheKey(keyID, GetSignatureHash(), vchSig)); | ||
| } | ||
|
|
||
| std::optional<bool> CGovernanceVote::GetMemoisedVerdict(const CBLSPublicKey& pubKey) const | ||
| { | ||
| return m_sig_memo.Lookup(SignatureCacheKey(pubKey, GetSignatureHash(), vchSig)); | ||
| } | ||
|
|
||
| std::string CGovernanceVoting::ConvertOutcomeToString(vote_outcome_enum_t nOutcome) | ||
| { | ||
| static const std::map<vote_outcome_enum_t, std::string> mapOutcomeString = { | ||
|
|
@@ -127,33 +163,49 @@ uint256 CGovernanceVote::GetHash() const | |
|
|
||
| bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const | ||
| { | ||
| const uint256 sigHash{GetSignatureHash()}; | ||
| const uint256 fingerprint{SignatureCacheKey(keyID, sigHash, vchSig)}; | ||
| if (const auto memoised{m_sig_memo.Lookup(fingerprint)}) { | ||
| return *memoised; | ||
| } | ||
|
|
||
| std::string strError; | ||
| bool valid{false}; | ||
|
|
||
| // Harden Spork6 so that it is active on testnet and no other networks | ||
| if (Params().NetworkIDString() == CBaseChainParams::TESTNET) { | ||
| if (!CHashSigner::VerifyHash(GetSignatureHash(), keyID, vchSig, strError)) { | ||
| valid = CHashSigner::VerifyHash(sigHash, keyID, vchSig, strError); | ||
| if (!valid) { | ||
| LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- VerifyHash() failed, error: %s\n", strError); | ||
| return false; | ||
| } | ||
| } else { | ||
| if (!CMessageSigner::VerifyMessage(keyID, vchSig, GetSignatureString(), strError)) { | ||
| valid = CMessageSigner::VerifyMessage(keyID, vchSig, GetSignatureString(), strError); | ||
| if (!valid) { | ||
| LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
| m_sig_memo.Store(fingerprint, valid); | ||
| return valid; | ||
| } | ||
|
|
||
| bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const | ||
| { | ||
| const uint256 sigHash{GetSignatureHash()}; | ||
| const uint256 fingerprint{SignatureCacheKey(pubKey, sigHash, vchSig)}; | ||
| if (const auto memoised{m_sig_memo.Lookup(fingerprint)}) { | ||
| return *memoised; | ||
| } | ||
|
|
||
| CBLSSignature sig; | ||
| sig.SetBytes(vchSig, false); | ||
| if (!sig.VerifyInsecure(pubKey, GetSignatureHash(), false)) { | ||
| const bool valid{sig.VerifyInsecure(pubKey, sigHash, false)}; | ||
| if (!valid) { | ||
| LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n"); | ||
| return false; | ||
| } | ||
| return true; | ||
|
|
||
| m_sig_memo.Store(fingerprint, valid); | ||
| return valid; | ||
| } | ||
|
|
||
| bool CGovernanceVote::IsValid(const CDeterministicMNList& tip_mn_list, bool useVotingKey) const | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During a BLS activation or reorg where
bls_legacy_schemechanges,ss << keyserializesCBLSPublicKeyusing that mutable global, while the cached operation is alwaysVerifyInsecure(..., false). Legacy serialization of a key can equal basic serialization of its negation, so if an operator key rotates fromPto-Pacross such a boundary, a valid verdict cached forPcan be returned for-P, allowing the old vote to survive revalidation and be advertised despite failing verification. Serialize the fingerprint key explicitly with the non-legacy scheme used by verification.AGENTS.md reference: AGENTS.md:L169-L170
Useful? React with 👍 / 👎.