diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 9fdd5176e1..d1c6b29604 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -4247,7 +4247,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (obj.context.size() > MAX_PROP_CONTEXT_SIZE) return Res::Err("proposal context cannot be more than %d bytes", MAX_PROP_CONTEXT_SIZE); - if (obj.contexthash.size() > MAX_PROP_CONTEXT_SIZE) + if (obj.contextHash.size() > MAX_PROP_CONTEXT_SIZE) return Res::Err("proposal context hash cannot be more than %d bytes", MAX_PROP_CONTEXT_SIZE); if (obj.nCycles < 1 || obj.nCycles > MAX_CYCLES) diff --git a/src/masternodes/proposals.cpp b/src/masternodes/proposals.cpp index 63d54740c8..c5f555073c 100644 --- a/src/masternodes/proposals.cpp +++ b/src/masternodes/proposals.cpp @@ -72,7 +72,7 @@ Res CPropsView::CreateProp(const CPropId& propId, uint32_t height, const CCreate WriteBy(keyPair, i); } } - prop.finalHeight = height; + prop.proposalEndHeight = height; WriteBy(propId, prop); return Res::Ok(); } @@ -88,6 +88,7 @@ std::optional CPropsView::GetProp(const CPropId& propId) if (auto cycle = ReadBy(key)) { prop->cycle = *cycle; prop->status = status; + prop->cycleEndHeight = prop->creationHeight + (prop->votingPeriod - prop->creationHeight % prop->votingPeriod) + prop->votingPeriod * *cycle; return true; } return false; @@ -143,8 +144,8 @@ Res CPropsView::UpdatePropStatus(const CPropId& propId, uint32_t height, CPropSt } } - if (p_prop->finalHeight != height) { - p_prop->finalHeight = height; + if (p_prop->proposalEndHeight != height) { + p_prop->proposalEndHeight = height; WriteBy(propId, *p_prop); } return Res::Ok(); diff --git a/src/masternodes/proposals.h b/src/masternodes/proposals.h index 53e9c1e671..2afbcebe2f 100644 --- a/src/masternodes/proposals.h +++ b/src/masternodes/proposals.h @@ -51,7 +51,7 @@ struct CCreatePropMessage { uint8_t nCycles; std::string title; std::string context; - std::string contexthash; + std::string contextHash; uint8_t options; ADD_SERIALIZE_METHODS; @@ -65,7 +65,7 @@ struct CCreatePropMessage { READWRITE(nCycles); READWRITE(title); READWRITE(context); - READWRITE(contexthash); + READWRITE(contextHash); READWRITE(options); } }; @@ -91,7 +91,7 @@ struct CPropObject : public CCreatePropMessage { explicit CPropObject(const CCreatePropMessage& other) : CCreatePropMessage(other) {} uint32_t creationHeight{}; - uint32_t finalHeight{}; + uint32_t proposalEndHeight{}; uint32_t votingPeriod; CAmount majority; @@ -103,6 +103,7 @@ struct CPropObject : public CCreatePropMessage { // memory only CPropStatusType status{}; uint8_t cycle{}; + uint32_t cycleEndHeight{}; ADD_SERIALIZE_METHODS; @@ -111,7 +112,7 @@ struct CPropObject : public CCreatePropMessage { { READWRITEAS(CCreatePropMessage, *this); READWRITE(creationHeight); - READWRITE(finalHeight); + READWRITE(proposalEndHeight); READWRITE(votingPeriod); READWRITE(majority); READWRITE(quorum); diff --git a/src/masternodes/rpc_customtx.cpp b/src/masternodes/rpc_customtx.cpp index 896fd151e1..bae9d51f0a 100644 --- a/src/masternodes/rpc_customtx.cpp +++ b/src/masternodes/rpc_customtx.cpp @@ -519,18 +519,18 @@ class CCustomTxRpcVisitor rpcInfo.pushKV("context", obj.context); rpcInfo.pushKV("amount", ValueFromAmount(obj.nAmount)); rpcInfo.pushKV("cycles", int(obj.nCycles)); - auto finalHeight = height; + auto proposalEndHeight = height; bool emergency = obj.options & CPropOption::Emergency; if (auto prop = mnview.GetProp(propId)) { - finalHeight = prop->finalHeight; + proposalEndHeight = prop->proposalEndHeight; } else { auto votingPeriod = (emergency ? mnview.GetEmergencyPeriodFromAttributes(type) : mnview.GetVotingPeriodFromAttributes()); - finalHeight = height + (votingPeriod - height % votingPeriod); + proposalEndHeight = height + (votingPeriod - height % votingPeriod); for (uint8_t i = 1; i <= obj.nCycles; ++i) { - finalHeight += votingPeriod; + proposalEndHeight += votingPeriod; } } - rpcInfo.pushKV("finalizeAfter", int64_t(finalHeight)); + rpcInfo.pushKV("proposalEndHeight", int64_t(proposalEndHeight)); rpcInfo.pushKV("payoutAddress", ScriptToString(obj.address)); if (obj.options) { diff --git a/src/masternodes/rpc_proposals.cpp b/src/masternodes/rpc_proposals.cpp index a207bee9e2..2ff2ae221a 100644 --- a/src/masternodes/rpc_proposals.cpp +++ b/src/masternodes/rpc_proposals.cpp @@ -1,4 +1,3 @@ - #include #include @@ -10,15 +9,16 @@ UniValue propToJSON(CPropId const& propId, CPropObject const& prop) ret.pushKV("proposalId", propId.GetHex()); ret.pushKV("title", prop.title); ret.pushKV("context", prop.context); - ret.pushKV("contexthash", prop.contexthash); + ret.pushKV("contextHash", prop.contextHash); auto type = static_cast(prop.type); ret.pushKV("type", CPropTypeToString(type)); auto status = static_cast(prop.status); ret.pushKV("status", CPropStatusToString(status)); ret.pushKV("amount", ValueFromAmount(prop.nAmount)); - ret.pushKV("nextCycle", static_cast(prop.cycle)); + ret.pushKV("currentCycle", static_cast(prop.cycle)); ret.pushKV("totalCycles", static_cast(prop.nCycles)); - ret.pushKV("finalizeAfter", static_cast(prop.finalHeight)); + ret.pushKV("cycleEndHeight", static_cast(prop.cycleEndHeight)); + ret.pushKV("proposalEndHeight", static_cast(prop.proposalEndHeight)); ret.pushKV("payoutAddress", ScriptToString(prop.address)); if (prop.options) { @@ -92,7 +92,7 @@ UniValue creategovcfp(const JSONRPCRequest& request) CAmount amount; int cycles = 1; - std::string title, context, contexthash, addressStr; + std::string title, context, contextHash, addressStr; const UniValue& data = request.params[0].get_obj(); @@ -109,7 +109,7 @@ UniValue creategovcfp(const JSONRPCRequest& request) } if (!data["contextHash"].isNull()) - contexthash = data["contextHash"].get_str(); + contextHash = data["contextHash"].get_str(); if (!data["cycles"].isNull()) cycles = data["cycles"].get_int(); @@ -139,7 +139,7 @@ UniValue creategovcfp(const JSONRPCRequest& request) pm.nCycles = cycles; pm.title = title; pm.context = context; - pm.contexthash = contexthash; + pm.contextHash = contextHash; pm.options = 0; // encode @@ -223,7 +223,7 @@ UniValue creategovvoc(const JSONRPCRequest& request) RPCTypeCheck(request.params, { UniValue::VOBJ, UniValue::VARR }, true); - std::string title, context, contexthash; + std::string title, context, contextHash; bool emergency = false; const UniValue& data = request.params[0].get_obj(); @@ -241,7 +241,7 @@ UniValue creategovvoc(const JSONRPCRequest& request) } if (!data["contextHash"].isNull()) - contexthash = data["contextHash"].get_str(); + contextHash = data["contextHash"].get_str(); if (!data["emergency"].isNull()) { @@ -254,7 +254,7 @@ UniValue creategovvoc(const JSONRPCRequest& request) pm.nCycles = (emergency ? 1 : VOC_CYCLES); pm.title = title; pm.context = context; - pm.contexthash = contexthash; + pm.contextHash = contextHash; pm.options = (emergency ? CPropOption::Emergency : 0); // encode @@ -493,7 +493,12 @@ UniValue getgovproposal(const JSONRPCRequest& request) return propToJSON(propId, *prop); } - auto targetHeight = view.GetLastHeight() + 1; + int targetHeight; + if (prop->status == CPropStatusType::Voting) { + targetHeight = view.GetLastHeight() + 1; + } else { + targetHeight = prop->cycleEndHeight; + } std::set activeMasternodes; view.ForEachMasternode([&](uint256 const & mnId, CMasternode node) { @@ -554,10 +559,19 @@ UniValue getgovproposal(const JSONRPCRequest& request) ret.pushKV("proposalId", propId.GetHex()); ret.pushKV("title", prop->title); ret.pushKV("context", prop->context); - if (!prop->contexthash.empty()) - ret.pushKV("contexthash", prop->contexthash); + ret.pushKV("contextHash", prop->contextHash); auto type = static_cast(prop->type); ret.pushKV("type", CPropTypeToString(type)); + if (valid && votes >= majorityThreshold) { + ret.pushKV("status", "Approved"); + } else { + ret.pushKV("status", "Rejected"); + } + ret.pushKV("currentCycle", static_cast(prop->cycle)); + ret.pushKV("totalCycles", static_cast(prop->nCycles)); + ret.pushKV("cycleEndHeight", static_cast(prop->cycleEndHeight)); + ret.pushKV("proposalEndHeight", static_cast(prop->proposalEndHeight)); + ret.pushKV("payoutAddress", ScriptToString(prop->address)); if (prop->options) { @@ -571,28 +585,9 @@ UniValue getgovproposal(const JSONRPCRequest& request) ret.pushKV("options", array); } - if (valid && votes >= majorityThreshold) { - ret.pushKV("status", "Approved"); - } else { - ret.pushKV("status", "Rejected"); - } - - if (valid) { - ret.pushKV("approval", strprintf("%d.%02d of %d.%02d%%", votes / 100, votes % 100, majorityThreshold / 100, majorityThreshold % 100)); - } else { - ret.pushKV("validity", strprintf("%d.%02d of %d.%02d%%", allVotes / 100, allVotes % 100, quorum / 100, quorum % 100)); - } - - auto target = Params().GetConsensus().pos.nTargetSpacing; - auto blocks = prop->finalHeight - targetHeight; + ret.pushKV("votes", strprintf("%d.%02d of %d.%02d%%", votes / 100, votes % 100, majorityThreshold / 100, majorityThreshold % 100)); + ret.pushKV("votingPercent", strprintf("%d.%02d of %d.%02d%%", allVotes / 100, allVotes % 100, quorum / 100, quorum % 100)); - if (blocks > Params().GetConsensus().blocksPerDay()) { - ret.pushKV("ends", strprintf("%d days", blocks * target / 60 / 60 / 24)); - } else if (blocks > Params().GetConsensus().blocksPerDay() / 24) { - ret.pushKV("ends", strprintf("%d hours", blocks * target / 60 / 60)); - } else { - ret.pushKV("ends", strprintf("%d minutes", blocks * target / 60)); - } return ret; } diff --git a/test/functional/feature_on_chain_government.py b/test/functional/feature_on_chain_government.py index fc41eec65b..3e04fafc50 100755 --- a/test/functional/feature_on_chain_government.py +++ b/test/functional/feature_on_chain_government.py @@ -148,7 +148,7 @@ def run_test(self): # Calculate cycle cycle1 = creationHeight + (votingPeriod - creationHeight % votingPeriod) + votingPeriod - finalHeight = cycle1 + votingPeriod + proposalEndHeight = cycle1 + votingPeriod # Check proposal and votes result = self.nodes[0].listgovproposals() @@ -159,10 +159,11 @@ def run_test(self): assert_equal(result[0]["type"], "CommunityFundProposal") assert_equal(result[0]["status"], "Voting") assert_equal(result[0]["amount"], Decimal("100")) - assert_equal(result[0]["nextCycle"], 1) + assert_equal(result[0]["currentCycle"], 1) assert_equal(result[0]["totalCycles"], 2) assert_equal(result[0]["payoutAddress"], address) - assert_equal(result[0]["finalizeAfter"], finalHeight) + assert_equal(result[0]["proposalEndHeight"], proposalEndHeight) + assert_equal(result[0]["cycleEndHeight"], cycle1) # Check individual MN votes results = self.nodes[1].listgovvotes(tx, mn0) @@ -192,7 +193,7 @@ def run_test(self): # Check first cycle length result = self.nodes[0].listgovproposals() - assert_equal(result[0]['nextCycle'], 1) + assert_equal(result[0]['currentCycle'], 1) # Move to cycle payout self.nodes[0].generate(1) @@ -202,7 +203,7 @@ def run_test(self): blockcount = self.nodes[0].getblockcount() # Actually moved to next cycle at cycle1 - assert_equal(result[0]['nextCycle'], 2) + assert_equal(result[0]['currentCycle'], 2) assert_equal(blockcount, cycle1) # First cycle should last for at least a votingPeriod @@ -215,10 +216,10 @@ def run_test(self): assert_equal(self.nodes[1].getaccount(address), ['100.00000000@DFI']) result = self.nodes[0].listgovproposals()[0] assert_equal(result["status"], "Voting") - assert_equal(result["nextCycle"], 2) + assert_equal(result["currentCycle"], 2) # Move to just before final height - self.nodes[0].generate(finalHeight - self.nodes[0].getblockcount() - 1) + self.nodes[0].generate(proposalEndHeight - self.nodes[0].getblockcount() - 1) bal = self.nodes[0].listcommunitybalances()['CommunityDevelopmentFunds'] # Move to final height @@ -248,6 +249,7 @@ def run_test(self): self.nodes[3].sendrawtransaction(raw_tx) self.nodes[3].generate(1) self.sync_blocks() + creationHeight = self.nodes[0].getblockcount() # Check burn fee increment assert_equal(self.nodes[0].getburninfo()['feeburn'], Decimal('7.50000000')) @@ -269,6 +271,8 @@ def run_test(self): self.nodes[3].generate(1) self.sync_blocks() + cycle1 = creationHeight + (votingPeriod - creationHeight % votingPeriod) + votingPeriod + proposalEndHeight = cycle1 + votingPeriod # Check results result = self.nodes[0].getgovproposal(tx) assert_equal(result["proposalId"], tx) @@ -276,8 +280,14 @@ def run_test(self): assert_equal(result["context"], context) assert_equal(result["type"], "VoteOfConfidence") assert_equal(result["status"], "Approved") - assert_equal(result["approval"], "75.00 of 66.67%") - assert_equal(result["ends"], "1 days") + assert_equal(result["votes"], "75.00 of 66.67%") + assert_equal(result["contextHash"], "") + assert_equal(result["currentCycle"], 1) + assert_equal(result["cycleEndHeight"], cycle1) + assert_equal(result["payoutAddress"], '') + assert_equal(result["totalCycles"], 2) + assert_equal(result["votingPercent"], "100.00 of 1.00%") + assert_equal(result["proposalEndHeight"], proposalEndHeight) assert_equal(len(self.nodes[0].listgovproposals("all", "voting")), 1) assert_equal(self.nodes[0].listgovproposals("all", "completed"), []) @@ -288,6 +298,7 @@ def run_test(self): 'v0/gov/proposals/cfp_fee':'0.25', 'v0/gov/proposals/voting_period':'100', }}) + votingPeriod = 100 self.nodes[0].generate(1) self.sync_blocks() @@ -327,7 +338,7 @@ def run_test(self): # Calculate cycle votingPeriod = 100 cycle1 = creationHeight + (votingPeriod - creationHeight % votingPeriod) + votingPeriod - finalHeight = cycle1 + votingPeriod + proposalEndHeight = cycle1 + votingPeriod # Check proposal and votes result = self.nodes[0].listgovproposals("cfp","voting") @@ -337,10 +348,11 @@ def run_test(self): assert_equal(result[0]["type"], "CommunityFundProposal") assert_equal(result[0]["status"], "Voting") assert_equal(result[0]["amount"], Decimal("50")) - assert_equal(result[0]["nextCycle"], 1) + assert_equal(result[0]["currentCycle"], 1) assert_equal(result[0]["totalCycles"], 2) assert_equal(result[0]["payoutAddress"], address) - assert_equal(result[0]["finalizeAfter"], finalHeight) + assert_equal(result[0]["proposalEndHeight"], proposalEndHeight) + assert_equal(result[0]["cycleEndHeight"], cycle1) # Check individual MN votes results = self.nodes[1].listgovvotes(propId, mn0) @@ -376,13 +388,15 @@ def run_test(self): assert_equal(self.nodes[0].listcommunitybalances()['CommunityDevelopmentFunds'], bal + Decimal("19.23346268")) # payout address + cycle2 = cycle1 + votingPeriod assert_equal(self.nodes[1].getaccount(address), ['100.00000000@DFI']) result = self.nodes[0].getgovproposal(propId) assert_equal(result["status"], "Voting") - assert_equal(result["nextCycle"], 2) + assert_equal(result["currentCycle"], 2) + assert_equal(result["cycleEndHeight"], cycle2) # Move to just before final height - self.nodes[0].generate(finalHeight - self.nodes[0].getblockcount() - 1) + self.nodes[0].generate(proposalEndHeight - self.nodes[0].getblockcount() - 1) bal = self.nodes[0].listcommunitybalances()['CommunityDevelopmentFunds'] # Move to final height @@ -422,6 +436,7 @@ def run_test(self): tx = self.nodes[0].creategovvoc({"title": title, "context": context, "emergency": True}) self.nodes[0].generate(1) self.sync_blocks() + creationHeight = self.nodes[0].getblockcount() # Check burn fee increment assert_equal(self.nodes[0].getburninfo()['feeburn'], Decimal('23.750000000')) @@ -443,6 +458,8 @@ def run_test(self): self.nodes[3].generate(1) self.sync_blocks() + cycle1 = creationHeight + (emergencyPeriod - creationHeight % emergencyPeriod) + emergencyPeriod + proposalEndHeight = creationHeight + emergencyPeriod # Check results result = self.nodes[0].getgovproposal(tx) assert_equal(result["proposalId"], tx) @@ -450,8 +467,15 @@ def run_test(self): assert_equal(result["context"], context) assert_equal(result["type"], "VoteOfConfidence") assert_equal(result["status"], "Approved") - assert_equal(result["approval"], "50.00 of 49.99%") - assert_equal(result["ends"], "3 hours") + assert_equal(result["votes"], "50.00 of 49.99%") + assert_equal(result["contextHash"], "") + assert_equal(result["currentCycle"], 1) + assert_equal(result["cycleEndHeight"], cycle1) + assert_equal(result["payoutAddress"], '') + assert_equal(result["totalCycles"], 1) + assert_equal(result["votingPercent"], "100.00 of 1.00%") + assert_equal(result["options"], ["Emergency"]) + assert_equal(result["proposalEndHeight"], proposalEndHeight) assert_equal(len(self.nodes[0].listgovproposals("all", "voting")), 1) assert_equal(len(self.nodes[0].listgovproposals("all", "completed")), 0)