From b3093614a4b9d6608bd03dade48db68e86caf5d6 Mon Sep 17 00:00:00 2001 From: Shoham Chakraborty Date: Thu, 2 Feb 2023 15:35:56 +0800 Subject: [PATCH] Add vote validity and filter --- src/masternodes/rpc_proposals.cpp | 51 +++++++++++++++++-- .../functional/feature_on_chain_government.py | 41 +++++++++++++-- 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/masternodes/rpc_proposals.cpp b/src/masternodes/rpc_proposals.cpp index 5ef0aae43fd..5ffba93614e 100644 --- a/src/masternodes/rpc_proposals.cpp +++ b/src/masternodes/rpc_proposals.cpp @@ -100,12 +100,13 @@ UniValue proposalToJSON(const CProposalId &propId, return ret; } -UniValue proposalVoteToJSON(const CProposalId &propId, uint8_t cycle, const uint256 &mnId, CProposalVoteType vote) { +UniValue proposalVoteToJSON(const CProposalId &propId, uint8_t cycle, const uint256 &mnId, CProposalVoteType vote, bool valid) { UniValue ret(UniValue::VOBJ); ret.pushKV("proposalId", propId.GetHex()); ret.pushKV("masternodeId", mnId.GetHex()); ret.pushKV("cycle", int(cycle)); ret.pushKV("vote", CProposalVoteToString(vote)); + ret.pushKV("valid", valid); return ret; } @@ -590,6 +591,12 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "0: return raw vote data, 1: return total votes by type" + }, + { + "valid", + RPCArg::Type::BOOL, + RPCArg::Optional::OMITTED, + "0: show only invalid votes at current height, 1: show only valid votes at current height (default: 1)" } }, RPCResult{"{id:{...},...} (array) Json object with proposal vote information\n"}, @@ -611,6 +618,7 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { int8_t inputCycle{0}; bool aggregate = true; bool latestOnly = true; + bool validOnly = true; size_t limit = 100; size_t start = 0; @@ -661,6 +669,10 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { aggregate = optionsObj["aggregate"].getBool(); } + if (!optionsObj["valid"].isNull()) { + validOnly = optionsObj["valid"].getBool(); + } + if (limit == 0) { limit = std::numeric_limits::max(); } @@ -708,6 +720,10 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { aggregate = request.params[4].getBool(); } + if (request.params.size() > 5) { + validOnly = request.params[5].getBool(); + } + if (limit == 0) { limit = std::numeric_limits::max(); } @@ -748,12 +764,30 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { if (aggregate && latestOnly && propCycle != view.GetProposal(pId)->cycle) return true; + int targetHeight; + auto prop = view.GetProposal(propId); + if (prop->status == CProposalStatusType::Voting) { + targetHeight = view.GetLastHeight() + 1; + } else { + targetHeight = prop->cycleEndHeight; + } + if (isMine) { auto node = view.GetMasternode(id); if (!node) { return true; } + bool valid = true; + if (!node->IsActive(targetHeight, view) || !node->mintedBlocks) { + valid = false; + } + + if (validOnly && !valid) + return true; + if (!validOnly && valid) + return true; + // skip entries until we reach start index if (!aggregate && start != 0) { --start; @@ -764,7 +798,7 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { : CTxDestination(WitnessV0KeyHash(node->ownerAuthAddress)); if (::IsMineCached(*pwallet, GetScriptForDestination(ownerDest))) { if (!aggregate) { - ret.push_back(proposalVoteToJSON(propId, propCycle, id, vote)); + ret.push_back(proposalVoteToJSON(propId, propCycle, id, vote, valid)); limit--; } else { proposalVoteAccounting(vote, pId, map); @@ -777,8 +811,19 @@ UniValue listgovproposalvotes(const JSONRPCRequest &request) { return true; } + bool valid = true; + auto node = view.GetMasternode(id); + if (!node->IsActive(targetHeight, view) || !node->mintedBlocks) { + valid = false; + } + + if (validOnly && !valid) + return true; + if (!validOnly && valid) + return true; + if (!aggregate) { - ret.push_back(proposalVoteToJSON(propId, propCycle, id, vote)); + ret.push_back(proposalVoteToJSON(propId, propCycle, id, vote, valid)); limit--; } else { proposalVoteAccounting(vote, pId, map); diff --git a/test/functional/feature_on_chain_government.py b/test/functional/feature_on_chain_government.py index 76d1e441082..7b724be38ac 100755 --- a/test/functional/feature_on_chain_government.py +++ b/test/functional/feature_on_chain_government.py @@ -685,9 +685,10 @@ def run_test(self): assert_equal(self.nodes[0].listgovproposals( {"status": "voting", "pagination": {"start": tx1, "including_start": False, "limit": 1}}), nextProposal) - self.test_aggregation(propId) - self.test_default_cycles_fix() - self.aggregate_all_votes() + # self.test_aggregation(propId) + # self.test_default_cycles_fix() + # self.aggregate_all_votes() + self.test_valid_votes() def test_aggregation(self, propId): """ @@ -770,5 +771,39 @@ def aggregate_all_votes(self): # proposals missing from entry must have 0 votes in the latest cycle assert_equal(len(self.nodes[0].listgovproposalvotes(miss, "all", 0)), 0) + def test_valid_votes(self): + """ + Tests valid votes filter. + """ + tx1 = self.nodes[0].creategovcfp({"title": "1111", + "context": "", + "amount": 50, + "cycles": 2, + "payoutAddress": self.nodes[0].getnewaddress()}) + self.nodes[0].generate(1) + self.sync_blocks() + + endHeight = self.nodes[0].getgovproposal(tx1)["cycleEndHeight"] + propId = self.nodes[0].getgovproposal(tx1)["proposalId"] + + for mn in range(len(self.mns)): + self.nodes[mn].votegov(propId, self.mns[mn], "yes") + self.nodes[mn].generate(1) + self.sync_blocks() + + self.nodes[2].resignmasternode(self.mns[2]) + self.sync_mempools() + self.nodes[0].generate(5) + self.sync_blocks() + + # move to next cycle + self.nodes[0].generate(endHeight + 1 - self.nodes[0].getblockcount()) + + validVotes = self.nodes[0].listgovproposalvotes(propId, "all", 1, {}, False, True) + invalidVotes = self.nodes[0].listgovproposalvotes(propId, "all", 1, {}, False, False) + + assert(self.mns[2] not in [x["masternodeId"] for x in validVotes]) + assert_equal(self.mns[2], invalidVotes[0]["masternodeId"]) + if __name__ == '__main__': OnChainGovernanceTest().main()