Skip to content

Commit 2bcb984

Browse files
Merge #7061: docs(rpc): introduce more RPC help text and deduplicate governance RPC logic to resolve pending definitions
562aeb7 rpc: add JSON help defs for `CGovernanceObject` fragments (Kittywhiskers Van Gogh) 57edf31 rpc: deduplicate routines used to generate governance RPC results (Kittywhiskers Van Gogh) 5866061 rpc: add `GetJsonHelp()` defs for `CGovernance{Manager,Object},Object` (Kittywhiskers Van Gogh) 0efaad3 move-only: consolidate governance `ToJson()` defs to `core_write.cpp` (Kittywhiskers Van Gogh) 6063d79 rpc: add help text for `masternode list` (Kittywhiskers Van Gogh) ae40575 rpc: add help text for `protx listdiff`, `masternode status` (Kittywhiskers Van Gogh) 291b8f6 rpc: add help text for `quorum dkgstatus` (Kittywhiskers Van Gogh) 56b95d4 rpc: add `GetJsonHelp()` defs for `CDKGDebug{,Session}Status` (Kittywhiskers Van Gogh) Pull request description: ## Motivation [dash#7062](#7062) introduces breaking changes. To convey the change in return object parameter's optional status, we need to be able to emit complete documentation to begin with. This pull request aims to fill up the remaining gaps in Dash-specific RPC documentation (which were thankfully annotated with TODOs courtesy of [dash#6886](#6886), thanks knst!). This is a continuation of the work started in [dash#6872](#6872). ## Additional Information * Dependency for #7062 * `quorum dkgstatus` requires additional work to generate its help text as the RPC does something unusual, it takes the object returned by CDKGDebugStatus ([source](https://github.com/dashpay/dash/blob/bac6bfadffe6fcf323e5e0208c8619e1e68401fe/src/rpc/quorums.cpp#L356C10-L356C13)) and _modifies_ it ([source](https://github.com/dashpay/dash/blob/bac6bfadffe6fcf323e5e0208c8619e1e68401fe/src/rpc/quorums.cpp#L425)) instead of treating it as a distinct value paired to a key. This necessitated defining a separate function to generate the help text for `quorum dkgstatus`, `quorum_dkgstatus_help()`, that has to mirror this mutating behavior due to the `const`-only structure of `RPCResult` ([source](https://github.com/dashpay/dash/blob/bac6bfadffe6fcf323e5e0208c8619e1e68401fe/src/rpc/util.h#L265-L270)). * This has also been done for `gobject get` though it is a product of deduplication done in this PR and not a decision made when the RPC was first introduced. * Two governance RPCs, `gobject {diff,list}` and `gobject get`, refer to the same value, local validity status, with different keys, `fBlockchainValidity` and `fLocalValidity` respectively. This required a workaround to maintain deduplication by allowing to overwrite the key name after fetching the description using the key in `GetRpcResult` by specifying `override_name`. ## Breaking Changes None expected. ## 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: UdjinM6: utACK 562aeb7 Tree-SHA512: 5497ad7aff0c07900d9775fe9f52af0661bceb76f48ee709de63e742b2e767f9fdc968717001dccb63cbebf75249a43931704f352a08a8e4dc14e8b5a7b47c4b
2 parents ea73b83 + 562aeb7 commit 2bcb984

18 files changed

Lines changed: 409 additions & 275 deletions

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,7 @@ libbitcoin_common_a_SOURCES = \
920920
evo/netinfo.cpp \
921921
external_signer.cpp \
922922
governance/common.cpp \
923+
governance/core_write.cpp \
923924
init/common.cpp \
924925
key.cpp \
925926
key_io.cpp \

src/core_io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true,
5858
void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, const CSpentIndexTxInfo* ptxSpentInfo = nullptr);
5959

6060
// evo/core_write.cpp
61-
RPCResult GetRpcResult(const std::string& key, bool optional = false);
61+
RPCResult GetRpcResult(const std::string& key, bool optional = false, const std::string& override_name = "");
6262

6363
#endif // BITCOIN_CORE_IO_H

src/evo/core_write.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,20 @@ const std::map<std::string, RPCResult> RPCRESULT_MAP{{
3737
{RPCResult::Type::ARR, "platform_https", /*optional=*/true, "Addresses used for Platform HTTPS API",
3838
{{RPCResult::Type::STR, "address", ""}}},
3939
}}},
40+
RESULT_MAP_ENTRY("collateralAddress", RPCResult::Type::STR, "Dash address used for collateral"),
4041
RESULT_MAP_ENTRY("collateralHash", RPCResult::Type::STR_HEX, "Collateral transaction hash"),
4142
RESULT_MAP_ENTRY("collateralIndex", RPCResult::Type::NUM, "Collateral transaction output index"),
4243
RESULT_MAP_ENTRY("consecutivePayments", RPCResult::Type::NUM, "Consecutive payments masternode has received in payment cycle"),
4344
RESULT_MAP_ENTRY("height", RPCResult::Type::NUM, "Block height"),
4445
RESULT_MAP_ENTRY("inputsHash", RPCResult::Type::STR_HEX, "Hash of all the outpoints of the transaction inputs"),
4546
RESULT_MAP_ENTRY("lastPaidHeight", RPCResult::Type::NUM, "Height masternode was last paid"),
4647
RESULT_MAP_ENTRY("llmqType", RPCResult::Type::NUM, "Quorum type"),
48+
RESULT_MAP_ENTRY("memberIndex", RPCResult::Type::NUM, "Quorum member index"),
4749
RESULT_MAP_ENTRY("merkleRootMNList", RPCResult::Type::STR_HEX, "Merkle root of the masternode list"),
4850
RESULT_MAP_ENTRY("merkleRootQuorums", RPCResult::Type::STR_HEX, "Merkle root of the quorum list"),
4951
RESULT_MAP_ENTRY("operatorPayoutAddress", RPCResult::Type::STR, "Dash address used for operator reward payments"),
5052
RESULT_MAP_ENTRY("operatorReward", RPCResult::Type::NUM, "Fraction in %% of reward shared with the operator between 0 and 10000"),
53+
RESULT_MAP_ENTRY("outpoint", RPCResult::Type::STR_HEX,"The outpoint of the masternode"),
5154
RESULT_MAP_ENTRY("ownerAddress", RPCResult::Type::STR, "Dash address used for payee updates and proposal voting"),
5255
RESULT_MAP_ENTRY("payoutAddress", RPCResult::Type::STR, "Dash address used for masternode reward payments"),
5356
RESULT_MAP_ENTRY("platformHTTPPort", RPCResult::Type::NUM, "(DEPRECATED) TCP port of Platform HTTP API"),
@@ -65,17 +68,18 @@ const std::map<std::string, RPCResult> RPCRESULT_MAP{{
6568
RESULT_MAP_ENTRY("revocationReason", RPCResult::Type::NUM, "Reason for ProUpRegTx revocation"),
6669
RESULT_MAP_ENTRY("service", RPCResult::Type::STR, "(DEPRECATED) IP address and port of the masternode"),
6770
RESULT_MAP_ENTRY("type", RPCResult::Type::NUM, "Masternode type"),
71+
RESULT_MAP_ENTRY("type_str", RPCResult::Type::STR, "Masternode type (human-readable string)"),
6872
RESULT_MAP_ENTRY("version", RPCResult::Type::NUM, "Special transaction version"),
6973
RESULT_MAP_ENTRY("votingAddress", RPCResult::Type::STR, "Dash address used for voting"),
7074
}};
7175
#undef RESULT_MAP_ENTRY
7276
} // anonymous namespace
7377

74-
RPCResult GetRpcResult(const std::string& key, bool optional)
78+
RPCResult GetRpcResult(const std::string& key, bool optional, const std::string& override_name)
7579
{
7680
if (const auto it = RPCRESULT_MAP.find(key); it != RPCRESULT_MAP.end()) {
7781
const auto& ret{it->second};
78-
return RPCResult{ret.m_type, ret.m_key_name, optional, ret.m_description, ret.m_inner};
82+
return RPCResult{ret.m_type, override_name.empty() ? ret.m_key_name : override_name, optional, ret.m_description, ret.m_inner};
7983
}
8084
throw NonFatalCheckError(strprintf("Requested invalid RPCResult for nonexistent key \"%s\"", key).c_str(),
8185
__FILE__, __LINE__, __func__);
@@ -178,11 +182,11 @@ RPCResult CDeterministicMN::GetJsonHelp(const std::string& key, bool optional)
178182
{
179183
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The masternode's details",
180184
{
181-
{RPCResult::Type::STR, "type", "Masternode type"},
185+
GetRpcResult("type_str", /*optional=*/false, /*override_name=*/"type"),
182186
GetRpcResult("proTxHash"),
183187
GetRpcResult("collateralHash"),
184188
GetRpcResult("collateralIndex"),
185-
{RPCResult::Type::STR, "collateralAddress", /*optional=*/true, "Dash address used for collateral"},
189+
GetRpcResult("collateralAddress", /*optional=*/true),
186190
GetRpcResult("operatorReward"),
187191
CDeterministicMNState::GetJsonHelp(/*key=*/"state", /*optional=*/false),
188192
}};
@@ -501,7 +505,7 @@ RPCResult CSimplifiedMNListEntry::GetJsonHelp(const std::string& key, bool optio
501505
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "The simplified masternode list entry",
502506
{
503507
{RPCResult::Type::NUM, "nVersion", "Version of the entry"},
504-
{RPCResult::Type::NUM, "nType", "Masternode type"},
508+
GetRpcResult("type", /*optional=*/false, /*override_name=*/"nType"),
505509
{RPCResult::Type::STR_HEX, "proRegTxHash", "Hash of the ProRegTx identifying the masternode"},
506510
{RPCResult::Type::STR_HEX, "confirmedHash", "Hash of the block where the masternode was confirmed"},
507511
GetRpcResult("service"),

src/governance/common.cpp

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
#include <hash.h>
99
#include <univalue.h>
1010

11-
namespace Governance
12-
{
13-
11+
namespace Governance {
1412
Object::Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex) :
1513
hashParent{nHashParent},
1614
revision{nRevision},
@@ -40,25 +38,6 @@ uint256 Object::GetHash() const
4038
return ss.GetHash();
4139
}
4240

43-
UniValue Object::ToJson() const
44-
{
45-
UniValue obj(UniValue::VOBJ);
46-
obj.pushKV("objectHash", GetHash().ToString());
47-
obj.pushKV("parentHash", hashParent.ToString());
48-
obj.pushKV("collateralHash", collateralHash.ToString());
49-
obj.pushKV("createdAt", time);
50-
obj.pushKV("revision", revision);
51-
UniValue data;
52-
if (!data.read(GetDataAsPlainString())) {
53-
data.clear();
54-
data.setObject();
55-
data.pushKV("plain", GetDataAsPlainString());
56-
}
57-
data.pushKV("hex", GetDataAsHexString());
58-
obj.pushKV("data", data);
59-
return obj;
60-
}
61-
6241
std::string Object::GetDataAsHexString() const
6342
{
6443
return HexStr(vchData);
@@ -68,5 +47,4 @@ std::string Object::GetDataAsPlainString() const
6847
{
6948
return std::string(vchData.begin(), vchData.end());
7049
}
71-
7250
} // namespace Governance

src/governance/common.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,32 @@
1313
#include <string>
1414
#include <vector>
1515

16+
struct RPCResult;
17+
18+
class UniValue;
19+
1620
/**
1721
* This module is a public interface of governance module that can be used
1822
* in other components such as wallet
1923
*/
2024

21-
class UniValue;
22-
2325
enum class GovernanceObject : int {
2426
UNKNOWN = 0,
2527
PROPOSAL,
2628
TRIGGER
2729
};
2830
template<> struct is_serializable_enum<GovernanceObject> : std::true_type {};
2931

30-
namespace Governance
31-
{
32+
namespace Governance {
3233
class Object
3334
{
3435
public:
3536
Object() = default;
3637

3738
Object(const uint256& nHashParent, int nRevision, int64_t nTime, const uint256& nCollateralHash, const std::string& strDataHex);
3839

39-
UniValue ToJson() const;
40+
[[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
41+
[[nodiscard]] UniValue ToJson() const;
4042

4143
uint256 GetHash() const;
4244

src/governance/core_write.cpp

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright (c) 2025 The Dash Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <governance/common.h>
6+
#include <governance/governance.h>
7+
8+
#include <rpc/util.h>
9+
#include <util/check.h>
10+
11+
#include <univalue.h>
12+
13+
RPCResult CGovernanceManager::GetJsonHelp(const std::string& key, bool optional)
14+
{
15+
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Count of governance objects and votes",
16+
{
17+
{RPCResult::Type::NUM, "objects_total", "Total number of all governance objects"},
18+
{RPCResult::Type::NUM, "proposals", "Number of governance proposals"},
19+
{RPCResult::Type::NUM, "triggers", "Number of triggers"},
20+
{RPCResult::Type::NUM, "other", "Total number of unknown governance objects"},
21+
{RPCResult::Type::NUM, "erased", "Number of removed (expired) objects"},
22+
{RPCResult::Type::NUM, "votes", "Total number of votes"},
23+
}};
24+
}
25+
26+
UniValue CGovernanceManager::ToJson() const
27+
{
28+
LOCK(cs_store);
29+
30+
int nProposalCount = 0;
31+
int nTriggerCount = 0;
32+
int nOtherCount = 0;
33+
34+
for (const auto& [_, govobj] : mapObjects) {
35+
switch (Assert(govobj)->GetObjectType()) {
36+
case GovernanceObject::PROPOSAL:
37+
nProposalCount++;
38+
break;
39+
case GovernanceObject::TRIGGER:
40+
nTriggerCount++;
41+
break;
42+
default:
43+
nOtherCount++;
44+
break;
45+
}
46+
}
47+
48+
UniValue jsonObj(UniValue::VOBJ);
49+
jsonObj.pushKV("objects_total", mapObjects.size());
50+
jsonObj.pushKV("proposals", nProposalCount);
51+
jsonObj.pushKV("triggers", nTriggerCount);
52+
jsonObj.pushKV("other", nOtherCount);
53+
jsonObj.pushKV("erased", mapErasedGovernanceObjects.size());
54+
jsonObj.pushKV("votes", cmapVoteToObject.GetSize());
55+
return jsonObj;
56+
}
57+
58+
RPCResult CGovernanceObject::GetInnerJsonHelp(const std::string& key, bool optional)
59+
{
60+
return Governance::Object::GetJsonHelp(key, optional);
61+
}
62+
63+
UniValue CGovernanceObject::GetInnerJson() const
64+
{
65+
return m_obj.ToJson();
66+
}
67+
68+
// CGovernanceObject::GetStateJson() defined in governance/object.cpp
69+
RPCResult CGovernanceObject::GetStateJsonHelp(const std::string& key, bool optional, const std::string& local_valid_key)
70+
{
71+
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object state info",
72+
{
73+
{RPCResult::Type::STR_HEX, "DataHex", "Governance object (hex)"},
74+
{RPCResult::Type::STR, "DataString", "Governance object (string)"},
75+
{RPCResult::Type::STR_HEX, "Hash", "Hash of governance object"},
76+
GetRpcResult("collateralHash", /*optional=*/false, /*override_name=*/"CollateralHash"),
77+
{RPCResult::Type::NUM, "ObjectType", "Object types"},
78+
{RPCResult::Type::NUM, "CreationTime", "Object creation timestamp"},
79+
{RPCResult::Type::STR_HEX, "SigningMasternode", /*optional=*/true, "Signing masternode’s vin (for triggers only)"},
80+
{RPCResult::Type::BOOL, local_valid_key, "Returns true if valid"},
81+
{RPCResult::Type::STR, "IsValidReason", strprintf("%s error (human-readable string, empty if %s true)", local_valid_key, local_valid_key)},
82+
{RPCResult::Type::BOOL, "fCachedValid", "Returns true if minimum support has been reached flagging object as valid"},
83+
{RPCResult::Type::BOOL, "fCachedFunding", "Returns true if minimum support has been reached flagging object as fundable"},
84+
{RPCResult::Type::BOOL, "fCachedDelete", "Returns true if minimum support has been reached flagging object as marked for deletion"},
85+
{RPCResult::Type::BOOL, "fCachedEndorsed", "Returns true if minimum support has been reached flagging object as endorsed"},
86+
}};
87+
}
88+
89+
RPCResult CGovernanceObject::GetVotesJsonHelp(const std::string& key, bool optional)
90+
{
91+
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object vote counts",
92+
{
93+
{RPCResult::Type::NUM, "AbsoluteYesCount", "Number of Yes votes minus number of No votes"},
94+
{RPCResult::Type::NUM, "YesCount", "Number of Yes votes"},
95+
{RPCResult::Type::NUM, "NoCount", "Number of No votes"},
96+
{RPCResult::Type::NUM, "AbstainCount", "Number of Abstain votes"},
97+
}};
98+
}
99+
100+
UniValue CGovernanceObject::GetVotesJson(const CDeterministicMNList& tip_mn_list, vote_signal_enum_t signal) const
101+
{
102+
UniValue obj(UniValue::VOBJ);
103+
obj.pushKV("AbsoluteYesCount", GetAbsoluteYesCount(tip_mn_list, signal));
104+
obj.pushKV("YesCount", GetYesCount(tip_mn_list, signal));
105+
obj.pushKV("NoCount", GetNoCount(tip_mn_list, signal));
106+
obj.pushKV("AbstainCount", GetAbstainCount(tip_mn_list, signal));
107+
return obj;
108+
}
109+
110+
namespace Governance {
111+
RPCResult Object::GetJsonHelp(const std::string& key, bool optional)
112+
{
113+
return {RPCResult::Type::OBJ, key, optional, key.empty() ? "" : "Object info",
114+
{
115+
{RPCResult::Type::STR_HEX, "objectHash", "Hash of proposal object"},
116+
{RPCResult::Type::STR_HEX, "parentHash", "Hash of the parent object (root node has a hash of 0)"},
117+
GetRpcResult("collateralHash"),
118+
{RPCResult::Type::NUM, "createdAt", "Proposal creation timestamp"},
119+
{RPCResult::Type::NUM, "revision", "Proposal revision number"},
120+
{RPCResult::Type::OBJ, "data", "", {
121+
// Fields emitted through GetDataAsPlainString(), read by CProposalValidator
122+
{RPCResult::Type::STR, "end_epoch", /*optional=*/true, "Proposal end timestamp"},
123+
{RPCResult::Type::STR, "name", /*optional=*/true, "Proposal name"},
124+
{RPCResult::Type::STR, "payment_address", /*optional=*/true, "Proposal payment address"},
125+
{RPCResult::Type::STR, "payment_amount", /*optional=*/true, "Proposal payment amount"},
126+
{RPCResult::Type::STR, "start_epoch", /*optional=*/true, "Proposal start timestamp"},
127+
{RPCResult::Type::STR, "type", /*optional=*/true, "Object type"},
128+
{RPCResult::Type::STR, "url", /*optional=*/true, "Proposal URL"},
129+
// Failure case for GetDataAsPlainString()
130+
{RPCResult::Type::STR, "plain", /*optional=*/true, "Governance object data as string"},
131+
// Always emitted by ToJson()
132+
{RPCResult::Type::STR_HEX, "hex", "Governance object data as hex"},
133+
}},
134+
}};
135+
}
136+
137+
UniValue Object::ToJson() const
138+
{
139+
UniValue obj(UniValue::VOBJ);
140+
obj.pushKV("objectHash", GetHash().ToString());
141+
obj.pushKV("parentHash", hashParent.ToString());
142+
obj.pushKV("collateralHash", collateralHash.ToString());
143+
obj.pushKV("createdAt", time);
144+
obj.pushKV("revision", revision);
145+
UniValue data;
146+
if (!data.read(GetDataAsPlainString())) {
147+
data.clear();
148+
data.setObject();
149+
data.pushKV("plain", GetDataAsPlainString());
150+
}
151+
data.pushKV("hex", GetDataAsHexString());
152+
obj.pushKV("data", data);
153+
return obj;
154+
}
155+
} // namespace Governance

src/governance/governance.cpp

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,38 +1108,6 @@ std::string CGovernanceManager::ToString() const
11081108
return strprintf("%s, Votes: %d", GovernanceStore::ToString(), (int)cmapVoteToObject.GetSize());
11091109
}
11101110

1111-
UniValue CGovernanceManager::ToJson() const
1112-
{
1113-
LOCK(cs_store);
1114-
1115-
int nProposalCount = 0;
1116-
int nTriggerCount = 0;
1117-
int nOtherCount = 0;
1118-
1119-
for (const auto& [_, govobj] : mapObjects) {
1120-
switch (Assert(govobj)->GetObjectType()) {
1121-
case GovernanceObject::PROPOSAL:
1122-
nProposalCount++;
1123-
break;
1124-
case GovernanceObject::TRIGGER:
1125-
nTriggerCount++;
1126-
break;
1127-
default:
1128-
nOtherCount++;
1129-
break;
1130-
}
1131-
}
1132-
1133-
UniValue jsonObj(UniValue::VOBJ);
1134-
jsonObj.pushKV("objects_total", mapObjects.size());
1135-
jsonObj.pushKV("proposals", nProposalCount);
1136-
jsonObj.pushKV("triggers", nTriggerCount);
1137-
jsonObj.pushKV("other", nOtherCount);
1138-
jsonObj.pushKV("erased", mapErasedGovernanceObjects.size());
1139-
jsonObj.pushKV("votes", cmapVoteToObject.GetSize());
1140-
return jsonObj;
1141-
}
1142-
11431111
void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex)
11441112
{
11451113
AssertLockNotHeld(cs_store);

src/governance/governance.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ template<typename T>
3131
class CFlatDB;
3232
class CInv;
3333
class CNode;
34+
struct RPCResult;
3435

3536
class CDeterministicMNList;
3637
class CDeterministicMNManager;
@@ -282,9 +283,10 @@ class CGovernanceManager : public GovernanceStore, public GovernanceSignerParent
282283
bool IsValid() const override { return is_valid; }
283284
bool LoadCache(bool load_cache)
284285
EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
286+
[[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional);
285287
std::string ToString() const
286288
EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
287-
UniValue ToJson() const
289+
[[nodiscard]] UniValue ToJson() const
288290
EXCLUSIVE_LOCKS_REQUIRED(!cs_store);
289291
void Clear()
290292
EXCLUSIVE_LOCKS_REQUIRED(!cs_store);

0 commit comments

Comments
 (0)