-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: update masternode rate limit on failed governance trigger path #7521
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 |
|---|---|---|
|
|
@@ -344,18 +344,25 @@ void CGovernanceManager::AddGovernanceObjectInternal(CGovernanceObject& insert_o | |
| LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- Before trigger block, GetDataAsPlainString = %s, nObjectType = %d\n", | ||
| Assert(govobj)->GetDataAsPlainString(), std23::to_underlying(govobj->GetObjectType())); | ||
|
|
||
| // Count the attempt against the per-masternode rate buffer before any early | ||
| // return. Failed AddTrigger paths used to skip this, so a single operator | ||
| // key could flood mapObjects with unparseable triggers. | ||
| MasternodeRateUpdate(*govobj); | ||
|
|
||
| if (govobj->GetObjectType() == GovernanceObject::TRIGGER && !m_superblocks.AddTrigger(govobj, nCachedBlockHeight)) { | ||
| LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- undo adding invalid trigger object: hash = %s\n", nHash.ToString()); | ||
| govobj->PrepareDeletion(GetTime<std::chrono::seconds>().count()); | ||
| return; | ||
| } | ||
|
|
||
| // Only objects we keep may be announced. Scheduling this before the AddTrigger | ||
| // check would make us re-announce, and serve on GETDATA, a trigger we just | ||
| // undid and marked for deletion. | ||
| ScheduleTriggerRelay(*govobj); | ||
|
|
||
| LogPrint(BCLog::GOBJECT, "CGovernanceManager::AddGovernanceObject -- %s new, received from peer %s\n", strHash, peer_str); | ||
| RelayObject(*govobj); | ||
|
|
||
| // Update the rate buffer | ||
| MasternodeRateUpdate(*govobj); | ||
|
|
||
| m_mn_sync.BumpAssetLastTime("CGovernanceManager::AddGovernanceObject"); | ||
|
|
||
| // WE MIGHT HAVE PENDING/ORPHAN VOTES FOR THIS OBJECT | ||
|
|
@@ -701,15 +708,23 @@ void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj) | |
| it = mapLastMasternodeObject.insert(txout_m_t::value_type(masternodeOutpoint, last_object_rec(true))).first; | ||
| } | ||
|
|
||
| int64_t nTimestamp = govobj.GetCreationTime(); | ||
| it->second.triggerBuffer.AddTimestamp(nTimestamp); | ||
| it->second.triggerBuffer.AddTimestamp(govobj.GetCreationTime()); | ||
|
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. 🔴 Blocking: Throttle failed triggers using non-attacker-controlled time The failed-trigger limiter records the operator-supplied creation timestamp, so the PR's flood protection remains bypassable on short-cycle networks. CRateCheckBuffer::GetRate() returns 5 / (max_timestamp - min_timestamp), while MasternodeRateCheck() accepts timestamps from now - 2 * cycle through now + 3600 and permits rates below 2.2 / cycle. On default testnet and devnet, cycle = 24 * 150 = 3600 seconds, making the accepted span 3 cycles and the endpoint-spaced rate 5 / (3 * cycle), which is below 2.2 / cycle. On regtest, cycle = 20 * 150 = 3000 seconds and the accepted span is 3.2 cycles, which also passes. By alternating unique, signature-valid malformed triggers between the accepted endpoints, every rolling five-entry buffer contains both endpoints, so every submission reaches BLS verification, AddTrigger, and mapObjects insertion indefinitely. Mainnet's roughly 2.001-cycle window is too narrow for this bypass, but this implementation and its stated protection are network-generic. Account failed attempts using receipt time or another non-signer-controlled value, and add an endpoint-spaced regression test rather than testing only consecutive creation timestamps. source: ['codex'] |
||
| it->second.fStatusOK = true; | ||
| } | ||
|
|
||
| void CGovernanceManager::ScheduleTriggerRelay(const CGovernanceObject& govobj) | ||
| { | ||
| AssertLockHeld(cs_store); | ||
|
|
||
| if (nTimestamp > GetTime() + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME)) { | ||
| // schedule additional relay for the object | ||
| if (govobj.GetObjectType() != GovernanceObject::TRIGGER) return; | ||
|
|
||
| // A trigger created this close to the future-deviation limit is still too new for | ||
| // peers with a lagging clock to accept, so re-announce it once it has aged past | ||
| // RELIABLE_PROPAGATION_TIME (see CheckPostponedObjects). | ||
| if (govobj.GetCreationTime() > | ||
| GetTime() + count_seconds(MAX_TIME_FUTURE_DEVIATION) - count_seconds(RELIABLE_PROPAGATION_TIME)) { | ||
| setAdditionalRelayObjects.insert(govobj.GetHash()); | ||
| } | ||
|
|
||
| it->second.fStatusOK = true; | ||
| } | ||
|
|
||
| bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus) | ||
|
|
||
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.
On testnet/devnet, where the superblock cycle is one hour, the accepted creation-time window spans three cycles (
now - 2 * cyclethroughnow + 1h), while the rate check permits a full buffer whenever its timestamp span exceeds5 * cycle / 2.2, or about 2.27 cycles. A valid operator can therefore alternate malformed signed triggers between the window endpoints; every five-entry buffer retains both endpoints,GetRate()stays belowdMaxRate, and every failedAddTriggercontinues enteringmapObjects. Regtest is similarly affected, so the consecutive-timestamp test passes while the flood remains unbounded on these networks. Record a non-attacker-controlled receipt time for failed attempts, or otherwise constrain this buffer, and test endpoint-spaced timestamps.AGENTS.md reference: AGENTS.md:L166-L166
Useful? React with 👍 / 👎.