Skip to content

Commit

Permalink
Context for proposals (#1240)
Browse files Browse the repository at this point in the history
* Add context to proposal. Rename rpcs to include gov in title.

* throw error if title or context are over character limit

* update tests to check invalid title and context

Co-authored-by: Shoham Chakraborty <[email protected]>
  • Loading branch information
Mixa84 and shohamc1 authored May 19, 2022
1 parent 0364a90 commit 1a4fbae
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 62 deletions.
13 changes: 11 additions & 2 deletions src/masternodes/consensus/proposals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,17 @@ Res CProposalsConsensus::operator()(const CCreatePropMessage& obj) const {
if (obj.nAmount >= MAX_MONEY)
return Res::Err("proposal wants to gain all money");

if (obj.title.size() > 128)
return Res::Err("proposal title cannot be more than 128 bytes");
if (obj.title.empty())
return Res::Err("proposal title must not be empty");

if (obj.title.size() > MAX_PROP_TITLE_SIZE)
return Res::Err("proposal title cannot be more than %d bytes", MAX_PROP_TITLE_SIZE);

if (obj.context.empty())
return Res::Err("proposal context must not be empty");

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.nCycles < 1 || obj.nCycles > MAX_CYCLES)
return Res::Err("proposal cycles can be between 1 and %d", int(MAX_CYCLES));
Expand Down
4 changes: 4 additions & 0 deletions src/masternodes/proposals.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
using CPropId = uint256;
constexpr const uint8_t VOC_CYCLES = 2;
constexpr const uint8_t MAX_CYCLES = 3;
constexpr const uint16_t MAX_PROP_TITLE_SIZE = 128;
constexpr const uint16_t MAX_PROP_CONTEXT_SIZE = 512;

enum CPropType : uint8_t {
CommunityFundProposal = 0x01,
Expand Down Expand Up @@ -44,6 +46,7 @@ struct CCreatePropMessage {
CAmount nAmount;
uint8_t nCycles;
std::string title;
std::string context;

ADD_SERIALIZE_METHODS;

Expand All @@ -55,6 +58,7 @@ struct CCreatePropMessage {
READWRITE(nAmount);
READWRITE(nCycles);
READWRITE(title);
READWRITE(context);
}
};

Expand Down
95 changes: 60 additions & 35 deletions src/masternodes/rpc_proposals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@ UniValue propVoteToJSON(CPropId const& propId, uint8_t cycle, uint256 const & mn
/*
* Issued by: any
*/
UniValue createcfp(const JSONRPCRequest& request)
UniValue creategovcfp(const JSONRPCRequest& request)
{
auto pwallet = GetWallet(request);

RPCHelpMan{"createcfp",
RPCHelpMan{"creategovcfp",
"\nCreates a Community Fund Proposal" +
HelpRequiringPassphrase(pwallet) + "\n",
{
{"data", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG,
"data in json-form, containing cfp data",
{
{"title", RPCArg::Type::STR, RPCArg::Optional::NO, "The title of community fund request"},
{"context", RPCArg::Type::STR, RPCArg::Optional::NO, "The context field of community fund request"},
{"cycles", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "Defaulted to one cycle"},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "Amount in DFI to request"},
{"payoutAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "Any valid address for receiving"},
Expand All @@ -65,8 +66,8 @@ UniValue createcfp(const JSONRPCRequest& request)
"\"hash\" (string) The hex-encoded hash of broadcasted transaction\n"
},
RPCExamples{
HelpExampleCli("createcfp", "'{\"title\":\"The cfp title\",\"amount\":10,\"payoutAddress\":\"address\"}' '[{\"txid\":\"id\",\"vout\":0}]'")
+ HelpExampleRpc("createcfp", "'{\"title\":\"The cfp title\",\"amount\":10,\"payoutAddress\":\"address\"} '[{\"txid\":\"id\",\"vout\":0}]'")
HelpExampleCli("creategovcfp", "'{\"title\":\"The cfp title\",\"context\":\"The cfp context\",\"amount\":10,\"payoutAddress\":\"address\"}' '[{\"txid\":\"id\",\"vout\":0}]'")
+ HelpExampleRpc("creategovcfp", "'{\"title\":\"The cfp title\",\"context\":\"The cfp context\",\"amount\":10,\"payoutAddress\":\"address\"} '[{\"txid\":\"id\",\"vout\":0}]'")
},
}.Check(request);

Expand All @@ -80,16 +81,28 @@ UniValue createcfp(const JSONRPCRequest& request)

CAmount amount;
int cycles = 1;
std::string title, addressStr;
std::string title, context, addressStr;

const UniValue& data = request.params[0].get_obj();

if (!data["title"].isNull()) {
title = data["title"].get_str();
if (title.length() > MAX_PROP_TITLE_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("<title> must be %d characters or under", int(MAX_PROP_TITLE_SIZE)));
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "<title> is required");
}

if (!data["context"].isNull()) {
context = data["context"].get_str();
if (context.length() > MAX_PROP_CONTEXT_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("<context> must be %d characters or under", int(MAX_PROP_CONTEXT_SIZE)));
}
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "<context> is required");
}

if (!data["cycles"].isNull()) {
cycles = data["cycles"].get_int();
if (cycles > int(MAX_CYCLES) || cycles < 1) {
Expand Down Expand Up @@ -120,7 +133,8 @@ UniValue createcfp(const JSONRPCRequest& request)
pm.address = GetScriptForDestination(address);
pm.nAmount = amount;
pm.nCycles = cycles;
pm.title = title.substr(0, 128);
pm.title = title;
pm.context = context;

// encode
CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
Expand Down Expand Up @@ -159,15 +173,16 @@ UniValue createcfp(const JSONRPCRequest& request)
return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex();
}

UniValue createvoc(const JSONRPCRequest& request)
UniValue creategovvoc(const JSONRPCRequest& request)
{
auto pwallet = GetWallet(request);

RPCHelpMan{"createvoc",
RPCHelpMan{"creategovvoc",
"\nCreates a Vote of Confidence" +
HelpRequiringPassphrase(pwallet) + "\n",
{
{"title", RPCArg::Type::STR, RPCArg::Optional::NO, "The title of vote of confidence"},
{"context", RPCArg::Type::STR, RPCArg::Optional::NO, "The context field for vote of confidence"},
{"inputs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "A json array of json objects",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
Expand All @@ -183,8 +198,8 @@ UniValue createvoc(const JSONRPCRequest& request)
"\"hash\" (string) The hex-encoded hash of broadcasted transaction\n"
},
RPCExamples{
HelpExampleCli("createvoc", "'The voc title' '[{\"txid\":\"id\",\"vout\":0}]'")
+ HelpExampleRpc("createvoc", "'The voc title' '[{\"txid\":\"id\",\"vout\":0}]'")
HelpExampleCli("creategovvoc", "'The voc title' 'The voc context' '[{\"txid\":\"id\",\"vout\":0}]'")
+ HelpExampleRpc("creategovvoc", "'The voc title' 'The voc context' '[{\"txid\":\"id\",\"vout\":0}]'")
},
}.Check(request);

Expand All @@ -194,15 +209,25 @@ UniValue createvoc(const JSONRPCRequest& request)
}
pwallet->BlockUntilSyncedToCurrentChain();

RPCTypeCheck(request.params, { UniValue::VSTR, UniValue::VARR }, true);
RPCTypeCheck(request.params, { UniValue::VSTR, UniValue::VSTR, UniValue::VARR }, true);

const auto title = request.params[0].get_str();
const auto context = request.params[1].get_str();

if (title.length() > MAX_PROP_TITLE_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("<title> must be %d characters or under", int(MAX_PROP_TITLE_SIZE)));
}

if (context.length() > MAX_PROP_CONTEXT_SIZE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("<context> must be %d characters or under", int(MAX_PROP_CONTEXT_SIZE)));
}

CCreatePropMessage pm;
pm.type = CPropType::VoteOfConfidence;
pm.nAmount = 0;
pm.nCycles = VOC_CYCLES;
pm.title = title.substr(0, 128);
pm.title = title;
pm.context = context;

// encode
CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION);
Expand All @@ -218,7 +243,7 @@ UniValue createvoc(const JSONRPCRequest& request)

CTransactionRef optAuthTx;
std::set<CScript> auths;
rawTx.vin = GetAuthInputsSmart(pwallet, rawTx.nVersion, auths, false /*needFoundersAuth*/, optAuthTx, request.params[1]);
rawTx.vin = GetAuthInputsSmart(pwallet, rawTx.nVersion, auths, false /*needFoundersAuth*/, optAuthTx, request.params[2]);

CAmount cfpFee = GetPropsCreationFee(targetHeight, static_cast<CPropType>(pm.type));
rawTx.vout.emplace_back(CTxOut(cfpFee, scriptMeta));
Expand All @@ -241,11 +266,11 @@ UniValue createvoc(const JSONRPCRequest& request)
return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex();
}

UniValue vote(const JSONRPCRequest& request)
UniValue votegov(const JSONRPCRequest& request)
{
auto pwallet = GetWallet(request);

RPCHelpMan{"vote",
RPCHelpMan{"votegov",
"\nVote for community proposal" +
HelpRequiringPassphrase(pwallet) + "\n",
{
Expand All @@ -267,8 +292,8 @@ UniValue vote(const JSONRPCRequest& request)
"\"hash\" (string) The hex-encoded hash of broadcasted transaction\n"
},
RPCExamples{
HelpExampleCli("vote", "txid masternodeId yes")
+ HelpExampleRpc("vote", "txid masternodeId yes")
HelpExampleCli("votegov", "txid masternodeId yes")
+ HelpExampleRpc("votegov", "txid masternodeId yes")
},
}.Check(request);

Expand Down Expand Up @@ -349,11 +374,11 @@ UniValue vote(const JSONRPCRequest& request)
return signsend(rawTx, pwallet, optAuthTx)->GetHash().GetHex();
}

UniValue listvotes(const JSONRPCRequest& request)
UniValue listgovvotes(const JSONRPCRequest& request)
{
auto pwallet = GetWallet(request);

RPCHelpMan{"listvotes",
RPCHelpMan{"listgovvotes",
"\nReturns information about proposal votes.\n",
{
{"proposalId", RPCArg::Type::STR, RPCArg::Optional::NO, "The proposal id)"},
Expand All @@ -363,8 +388,8 @@ UniValue listvotes(const JSONRPCRequest& request)
"{id:{...},...} (array) Json object with proposal vote information\n"
},
RPCExamples{
HelpExampleCli("listvotes", "txid")
+ HelpExampleRpc("listvotes", "txid")
HelpExampleCli("listgovvotes", "txid")
+ HelpExampleRpc("listgovvotes", "txid")
},
}.Check(request);

Expand Down Expand Up @@ -410,9 +435,9 @@ UniValue listvotes(const JSONRPCRequest& request)
return ret;
}

UniValue getproposal(const JSONRPCRequest& request)
UniValue getgovproposal(const JSONRPCRequest& request)
{
RPCHelpMan{"getproposal",
RPCHelpMan{"getgovproposal",
"\nReturns real time information about proposal state.\n",
{
{"proposalId", RPCArg::Type::STR, RPCArg::Optional::NO, "The proposal id)"},
Expand All @@ -421,8 +446,8 @@ UniValue getproposal(const JSONRPCRequest& request)
"{id:{...},...} (obj) Json object with proposal vote information\n"
},
RPCExamples{
HelpExampleCli("getproposal", "txid")
+ HelpExampleRpc("getproposal", "txid")
HelpExampleCli("getgovproposal", "txid")
+ HelpExampleRpc("getgovproposal", "txid")
},
}.Check(request);

Expand Down Expand Up @@ -521,9 +546,9 @@ UniValue getproposal(const JSONRPCRequest& request)
return ret;
}

UniValue listproposals(const JSONRPCRequest& request)
UniValue listgovproposals(const JSONRPCRequest& request)
{
RPCHelpMan{"listproposals",
RPCHelpMan{"listgovproposals",
"\nReturns information about proposals.\n",
{
{"type", RPCArg::Type::STR, RPCArg::Optional::OMITTED,
Expand All @@ -535,8 +560,8 @@ UniValue listproposals(const JSONRPCRequest& request)
"{id:{...},...} (array) Json object with proposals information\n"
},
RPCExamples{
HelpExampleCli("listproposals", "")
+ HelpExampleRpc("listproposals", "")
HelpExampleCli("listgovproposals", "")
+ HelpExampleRpc("listgovproposals", "")
},
}.Check(request);

Expand Down Expand Up @@ -591,12 +616,12 @@ static const CRPCCommand commands[] =
{
// category name actor (function) params
// --------------- ---------------------- --------------------- ----------
{"proposals", "createcfp", &createcfp, {"data", "inputs"} },
{"proposals", "createvoc", &createvoc, {"title", "inputs"} },
{"proposals", "vote", &vote, {"proposalId", "masternodeId", "decision", "inputs"} },
{"proposals", "listvotes", &listvotes, {"proposalId", "masternode"} },
{"proposals", "getproposal", &getproposal, {"proposalId"} },
{"proposals", "listproposals", &listproposals, {"type", "status"} },
{"proposals", "creategovcfp", &creategovcfp, {"data", "inputs"} },
{"proposals", "creategovvoc", &creategovvoc, {"title", "inputs"} },
{"proposals", "votegov", &votegov, {"proposalId", "masternodeId", "decision", "inputs"} },
{"proposals", "listgovvotes", &listgovvotes, {"proposalId", "masternode"} },
{"proposals", "getgovproposal", &getgovproposal, {"proposalId"} },
{"proposals", "listgovproposals", &listgovproposals, {"type", "status"} },
};

void RegisterProposalRPCCommands(CRPCTable& tableRPC) {
Expand Down
8 changes: 4 additions & 4 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,10 @@ static const CRPCConvertParam vRPCConvertParams[] =

{ "setmockcheckpoint", 0, "height" },

{ "createcfp", 0, "data" },
{ "createcfp", 1, "inputs" },
{ "createvoc", 1, "inputs" },
{ "vote", 3, "inputs" },
{ "creategovcfp", 0, "data" },
{ "creategovcfp", 1, "inputs" },
{ "creategovvoc", 1, "inputs" },
{ "votegov", 3, "inputs" },
};
// clang-format on

Expand Down
Loading

0 comments on commit 1a4fbae

Please sign in to comment.