Skip to content

Commit f16025f

Browse files
Merge #6094: feat: support descriptor wallets for RPC governance votemany, votealias
c72ec70 feat: implement governance RPCs votealias and votemany for descriptor wallets (Konstantin Akimov) 4908329 refactor: new method to generate a signing message in CGovernanceVote (Konstantin Akimov) Pull request description: ## Issue being fixed or feature implemented RPCs `governance votemany` and `governance votealias` use forcely LegacyScriptPubKeyMan instead using CWallet's interface. It causes a failures such as ``` test_framework.authproxy.JSONRPCException: This type of wallet does not support this command (-4) ``` See dashpay/dash-issues#59 to track progress ## What was done? Use CWallet's interfaces instead LegacyScriptPubKeyMan ## How Has This Been Tested? Functional tests `feature_governance.py` and `feature_governance_cl.py` to run by both ways - legacy and descriptor wallets. Run unit and functional tests. Extra test done locally: ```diff --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -242,10 +242,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if self.options.descriptors is None: # Prefer BDB unless it isn't available - if self.is_bdb_compiled(): - self.options.descriptors = False - elif self.is_sqlite_compiled(): + if self.is_sqlite_compiled(): self.options.descriptors = True + elif self.is_bdb_compiled(): + self.options.descriptors = False ``` to flip flag descriptor wallets/legacy wallets for all functional tests. ## Breaking Changes N/A ## 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 - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: utACK c72ec70 PastaPastaPasta: utACK c72ec70 Tree-SHA512: 2c18f0d4acb1c4d57da81bf54f0d155682f558eeb7271df7e6fe75c126ef7f047562794a6730e3ca5351abc4e2daded06b874c2ab77f9c47b840c89d8a158c9f
2 parents 1394c41 + c72ec70 commit f16025f

File tree

4 files changed

+65
-65
lines changed

4 files changed

+65
-65
lines changed

src/governance/vote.cpp

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -162,41 +162,6 @@ uint256 CGovernanceVote::GetSignatureHash() const
162162
return SerializeHash(*this);
163163
}
164164

165-
bool CGovernanceVote::Sign(const CKey& key, const CKeyID& keyID)
166-
{
167-
std::string strError;
168-
169-
// Harden Spork6 so that it is active on testnet and no other networks
170-
if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
171-
uint256 signatureHash = GetSignatureHash();
172-
173-
if (!CHashSigner::SignHash(signatureHash, key, vchSig)) {
174-
LogPrintf("CGovernanceVote::Sign -- SignHash() failed\n");
175-
return false;
176-
}
177-
178-
if (!CHashSigner::VerifyHash(signatureHash, keyID, vchSig, strError)) {
179-
LogPrintf("CGovernanceVote::Sign -- VerifyHash() failed, error: %s\n", strError);
180-
return false;
181-
}
182-
} else {
183-
std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
184-
::ToString(nVoteSignal) + "|" + ::ToString(nVoteOutcome) + "|" + ::ToString(nTime);
185-
186-
if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
187-
LogPrintf("CGovernanceVote::Sign -- SignMessage() failed\n");
188-
return false;
189-
}
190-
191-
if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
192-
LogPrintf("CGovernanceVote::Sign -- VerifyMessage() failed, error: %s\n", strError);
193-
return false;
194-
}
195-
}
196-
197-
return true;
198-
}
199-
200165
bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const
201166
{
202167
std::string strError;
@@ -208,12 +173,7 @@ bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const
208173
return false;
209174
}
210175
} else {
211-
std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
212-
::ToString(nVoteSignal) + "|" +
213-
::ToString(nVoteOutcome) + "|" +
214-
::ToString(nTime);
215-
216-
if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
176+
if (!CMessageSigner::VerifyMessage(keyID, vchSig, GetSignatureString(), strError)) {
217177
LogPrint(BCLog::GOBJECT, "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError);
218178
return false;
219179
}
@@ -275,6 +235,14 @@ bool CGovernanceVote::IsValid(const CDeterministicMNList& tip_mn_list, bool useV
275235
}
276236
}
277237

238+
std::string CGovernanceVote::GetSignatureString() const
239+
{
240+
return masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
241+
::ToString(nVoteSignal) + "|" +
242+
::ToString(nVoteOutcome) + "|" +
243+
::ToString(nTime);
244+
}
245+
278246
bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
279247
{
280248
bool fResult = ((vote1.masternodeOutpoint == vote2.masternodeOutpoint) &&

src/governance/vote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class CGovernanceVote
105105
bool Sign(const CActiveMasternodeManager& mn_activeman);
106106
bool CheckSignature(const CBLSPublicKey& pubKey) const;
107107
bool IsValid(const CDeterministicMNList& tip_mn_list, bool useVotingKey) const;
108+
std::string GetSignatureString() const;
108109
void Relay(PeerManager& peerman, const CMasternodeSync& mn_sync, const CDeterministicMNList& tip_mn_list) const;
109110

110111
const COutPoint& GetMasternodeOutpoint() const { return masternodeOutpoint; }

src/rpc/governance.cpp

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,37 @@ static RPCHelpMan gobject_submit()
404404
};
405405
}
406406

407-
static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const std::map<uint256, CKey>& keys,
407+
#ifdef ENABLE_WALLET
408+
static bool SignVote(const CWallet& wallet, const CKeyID& keyID, CGovernanceVote& vote)
409+
{
410+
// Special implementation for testnet (Harden Spork6 that has not been deployed to other networks)
411+
if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
412+
std::vector<unsigned char> signature;
413+
if (!wallet.SignSpecialTxPayload(vote.GetSignatureHash(), keyID, signature)) {
414+
LogPrintf("SignVote -- SignHash() failed\n");
415+
return false;
416+
}
417+
vote.SetSignature(signature);
418+
return true;
419+
} // end of testnet implementation
420+
421+
std::string strMessage{vote.GetSignatureString()};
422+
std::string signature;
423+
SigningResult err = wallet.SignMessage(strMessage, PKHash{keyID}, signature);
424+
if (err != SigningResult::OK) {
425+
LogPrintf("SignVote failed due to: %s\n", SigningResultString(err));
426+
return false;
427+
}
428+
bool ret = true;
429+
const auto decoded = DecodeBase64(signature, &ret);
430+
assert(!ret); // it should not fail
431+
432+
vote.SetSignature(std::vector<unsigned char>(decoded.data(), decoded.data() + decoded.size()));
433+
return true;
434+
}
435+
436+
static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const CWallet& wallet,
437+
const std::map<uint256, CKeyID>& votingKeys,
408438
const uint256& hash, vote_signal_enum_t eVoteSignal,
409439
vote_outcome_enum_t eVoteOutcome)
410440
{
@@ -425,9 +455,9 @@ static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const std::ma
425455

426456
UniValue resultsObj(UniValue::VOBJ);
427457

428-
for (const auto& p : keys) {
458+
for (const auto& p : votingKeys) {
429459
const auto& proTxHash = p.first;
430-
const auto& key = p.second;
460+
const auto& keyID = p.second;
431461

432462
UniValue statusObj(UniValue::VOBJ);
433463

@@ -441,7 +471,8 @@ static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const std::ma
441471
}
442472

443473
CGovernanceVote vote(dmn->collateralOutpoint, hash, eVoteSignal, eVoteOutcome);
444-
if (!vote.Sign(key, key.GetPubKey().GetID())) {
474+
475+
if (!SignVote(wallet, keyID, vote) || !vote.CheckSignature(keyID)) {
445476
nFailed++;
446477
statusObj.pushKV("result", "failed");
447478
statusObj.pushKV("errorMessage", "Failure to sign.");
@@ -471,7 +502,14 @@ static UniValue VoteWithMasternodes(const JSONRPCRequest& request, const std::ma
471502
return returnObj;
472503
}
473504

474-
#ifdef ENABLE_WALLET
505+
static bool CheckWalletOwnsKey(const CWallet& wallet, const CKeyID& keyid)
506+
{
507+
const CScript script{GetScriptForDestination(PKHash(keyid))};
508+
LOCK(wallet.cs_wallet);
509+
510+
return wallet.IsMine(script) == isminetype::ISMINE_SPENDABLE;
511+
}
512+
475513
static RPCHelpMan gobject_vote_many()
476514
{
477515
return RPCHelpMan{"gobject vote-many",
@@ -510,22 +548,17 @@ static RPCHelpMan gobject_vote_many()
510548

511549
EnsureWalletIsUnlocked(wallet.get());
512550

513-
LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan();
514-
if (!spk_man) {
515-
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
516-
}
517-
518-
std::map<uint256, CKey> votingKeys;
551+
std::map<uint256, CKeyID> votingKeys;
519552

520553
auto mnList = node.dmnman->GetListAtChainTip();
521554
mnList.ForEachMN(true, [&](auto& dmn) {
522-
CKey votingKey;
523-
if (spk_man->GetKey(dmn.pdmnState->keyIDVoting, votingKey)) {
524-
votingKeys.emplace(dmn.proTxHash, votingKey);
555+
const bool is_mine = CheckWalletOwnsKey(*wallet, dmn.pdmnState->keyIDVoting);
556+
if (is_mine) {
557+
votingKeys.emplace(dmn.proTxHash, dmn.pdmnState->keyIDVoting);
525558
}
526559
});
527560

528-
return VoteWithMasternodes(request, votingKeys, hash, eVoteSignal, eVoteOutcome);
561+
return VoteWithMasternodes(request, *wallet, votingKeys, hash, eVoteSignal, eVoteOutcome);
529562
},
530563
};
531564
}
@@ -575,20 +608,16 @@ static RPCHelpMan gobject_vote_alias()
575608
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid or unknown proTxHash");
576609
}
577610

578-
LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan();
579-
if (!spk_man) {
580-
throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command");
581-
}
582611

583-
CKey votingKey;
584-
if (!spk_man->GetKey(dmn->pdmnState->keyIDVoting, votingKey)) {
612+
const bool is_mine = CheckWalletOwnsKey(*wallet, dmn->pdmnState->keyIDVoting);
613+
if (!is_mine) {
585614
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Private key for voting address %s not known by wallet", EncodeDestination(PKHash(dmn->pdmnState->keyIDVoting))));
586615
}
587616

588-
std::map<uint256, CKey> votingKeys;
589-
votingKeys.emplace(proTxHash, votingKey);
617+
std::map<uint256, CKeyID> votingKeys;
618+
votingKeys.emplace(proTxHash, dmn->pdmnState->keyIDVoting);
590619

591-
return VoteWithMasternodes(request, votingKeys, hash, eVoteSignal, eVoteOutcome);
620+
return VoteWithMasternodes(request, *wallet, votingKeys, hash, eVoteSignal, eVoteOutcome);
592621
},
593622
};
594623
}

test/functional/test_runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@
288288
'feature_new_quorum_type_activation.py',
289289
'feature_governance_objects.py',
290290
'feature_governance.py --legacy-wallet',
291+
'feature_governance.py --descriptors',
291292
'feature_governance_cl.py --legacy-wallet',
293+
'feature_governance_cl.py --descriptors',
292294
'rpc_uptime.py',
293295
'feature_discover.py',
294296
'wallet_resendwallettransactions.py --legacy-wallet',

0 commit comments

Comments
 (0)