@@ -51,6 +51,25 @@ UniValue PollChoicesToJson(const Poll::ChoiceList& choices)
5151 return json;
5252}
5353
54+ UniValue PollAdditionalFieldsToJson (const Poll::AdditionalFieldList fields)
55+ {
56+ UniValue json (UniValue::VARR);
57+
58+ for (size_t i = 0 ; i < fields.size (); ++i) {
59+ UniValue field (UniValue::VOBJ);
60+ UniValue field_value (UniValue::VOBJ);
61+
62+ field_value.pushKV (" value" , fields.At (i)->m_value );
63+ field_value.pushKV (" required" , fields.At (i)->m_required );
64+
65+ field.pushKV (fields.At (i)->m_name , field_value);
66+
67+ json.push_back (field);
68+ }
69+
70+ return json;
71+ }
72+
5473UniValue PollToJson (const Poll& poll, const uint256 txid)
5574{
5675 UniValue json (UniValue::VOBJ);
@@ -59,6 +78,7 @@ UniValue PollToJson(const Poll& poll, const uint256 txid)
5978 json.pushKV (" id" , txid.ToString ());
6079 json.pushKV (" question" , poll.m_question );
6180 json.pushKV (" url" , poll.m_url );
81+ json.pushKV (" additional_fields" , PollAdditionalFieldsToJson (poll.AdditionalFields ()));
6282 json.pushKV (" poll_type" , poll.PollTypeToString ());
6383 json.pushKV (" poll_type_id" , (int )poll.m_type .Raw ());
6484 json.pushKV (" weight_type" , poll.WeightTypeToString ());
@@ -318,29 +338,31 @@ UniValue addpoll(const UniValue& params, bool fHelp)
318338 types_ss << ToLower (Poll::PollTypeToString (type, false ));
319339 }
320340
321- if (fHelp || params.size () != 8 ) {
341+ if (params.size () == 0 ) {
322342 std::string e = strprintf (
323- " addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url>\n "
343+ " addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url> "
344+ " <required_field_name1=value1;required_field_name2=value2...>\n "
324345 " \n "
325- " <type> ---------> Type of poll. Valid types are: %s.\n "
326- " <title> --------> Title for the poll\n "
327- " <days> ---------> Number of days that the poll will run\n "
328- " <question> -----> Prompt that voters shall answer\n "
329- " <answers> ------> Answers for voters to choose from. Separate answers with semicolons (;)\n "
330- " <weighttype> ---> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n "
331- " <responsetype> -> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n "
332- " <url> ----------> Discussion web page URL for the poll\n "
346+ " <type> -----------> Type of poll. Valid types are: %s.\n "
347+ " <title> ----------> Title for the poll\n "
348+ " <days> -----------> Number of days that the poll will run\n "
349+ " <question> -------> Prompt that voters shall answer\n "
350+ " <answers> --------> Answers for voters to choose from. Separate answers with semicolons (;)\n "
351+ " <weighttype> -----> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n "
352+ " <responsetype> ---> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n "
353+ " <url> ------------> Discussion web page URL for the poll\n "
354+ " <required fields>-> Required additional field(s) if any (see below)\n "
333355 " \n "
334356 " Add a poll to the network.\n "
335357 " Requires 100K GRC balance. Costs 50 GRC.\n "
336- " Provide an empty string for <answers> when choosing \" yes/no/abstain\" for <responsetype>.\n " ,
358+ " Provide an empty string for <answers> when choosing \" yes/no/abstain\" for <responsetype>.\n "
359+ " Certain poll types may require additional fields. You can see these with addpoll <type> \n "
360+ " with no other parameters." ,
337361 types_ss.str ());
338362
339363 throw std::runtime_error (e);
340364 }
341365
342- EnsureWalletIsUnlocked ();
343-
344366 std::string type_string = ToLower (params[0 ].get_str ());
345367
346368 PollType poll_type;
@@ -361,6 +383,55 @@ UniValue addpoll(const UniValue& params, bool fHelp)
361383 throw JSONRPCError (RPC_INVALID_PARAMETER, e);
362384 }
363385
386+ const std::vector<std::string>& required_fields = Poll::POLL_TYPE_RULES[(int ) poll_type].m_required_fields ;
387+ std::stringstream required_fields_ss;
388+
389+ for (const auto & required_field : required_fields) {
390+ if (required_fields_ss.str () != std::string{}) {
391+ required_fields_ss << " , " ;
392+ }
393+
394+ required_fields_ss << required_field;
395+ }
396+
397+ if (params.size () == 1 ) {
398+ std::string e = strprintf (
399+ " For addpoll %s, the required fields are the following: %s.\n " ,
400+ ToLower (params[0 ].get_str ()),
401+ required_fields.empty () ? " none" : required_fields_ss.str ());
402+
403+ throw std::runtime_error (e);
404+ }
405+
406+ size_t required_number_of_params = required_fields.empty () ? 8 : 9 ;
407+
408+ if (fHelp || params.size () < required_number_of_params) {
409+ std::string e = strprintf (
410+ " addpoll <type> <title> <days> <question> <answer1;answer2...> <weighttype> <responsetype> <url> "
411+ " <required_field_name1=value1;required_field_name2=value2...>\n "
412+ " \n "
413+ " <type> -----------> Type of poll. Valid types are: %s.\n "
414+ " <title> ----------> Title for the poll\n "
415+ " <days> -----------> Number of days that the poll will run\n "
416+ " <question> -------> Prompt that voters shall answer\n "
417+ " <answers> --------> Answers for voters to choose from. Separate answers with semicolons (;)\n "
418+ " <weighttype> -----> Weighing method for the poll: 1 = Balance, 2 = Magnitude + Balance\n "
419+ " <responsetype> ---> 1 = yes/no/abstain, 2 = single-choice, 3 = multiple-choice\n "
420+ " <url> ------------> Discussion web page URL for the poll\n "
421+ " <required fields>-> Required additional field(s) if any (see below)\n "
422+ " \n "
423+ " Add a poll to the network.\n "
424+ " Requires 100K GRC balance. Costs 50 GRC.\n "
425+ " Provide an empty string for <answers> when choosing \" yes/no/abstain\" for <responsetype>.\n "
426+ " Certain poll types may require additional fields. You can see these with addpoll <type> \n "
427+ " with no other parameters." ,
428+ types_ss.str ());
429+
430+ throw std::runtime_error (e);
431+ }
432+
433+ EnsureWalletIsUnlocked ();
434+
364435 PollBuilder builder = PollBuilder ()
365436 .SetPayloadVersion (payload_version)
366437 .SetType (poll_type)
@@ -375,6 +446,39 @@ UniValue addpoll(const UniValue& params, bool fHelp)
375446 builder = builder.SetChoices (split (params[4 ].get_str (), " ;" ));
376447 }
377448
449+ if (params.size () == 9 && !params[8 ].isNull () && !params[8 ].get_str ().empty ()) {
450+ std::vector<std::string> name_value_pairs = split (params[8 ].get_str (), " ;" );
451+ Poll::AdditionalFieldList fields;
452+
453+ for (const auto & name_value_pair : name_value_pairs) {
454+ std::vector v_field = split (name_value_pair, " =" );
455+ bool required = true ;
456+
457+ if (v_field.size () != 2 ) {
458+ throw std::runtime_error (" Required fields parameter for poll is malformed." );
459+ }
460+
461+ std::string field_name = TrimString (v_field[0 ]);
462+ std::string field_value = TrimString (v_field[1 ]);
463+
464+ if (std::find (required_fields.begin (), required_fields.end (), field_name) == required_fields.end ()) {
465+ required = false ;
466+ }
467+
468+ Poll::AdditionalField field (field_name, field_value, required);
469+
470+ fields.Add (field);
471+ }
472+
473+ // TODO: Extend Wellformed to do a duplicate check on the field name? This is done in the builder anyway. This
474+ // makes sure that at least the required fields have been provided and that they are well formed.
475+ if (!fields.WellFormed (poll_type)) {
476+ throw std::runtime_error (" Required field list is malformed." );
477+ }
478+
479+ builder = builder.AddAdditionalFields (fields);
480+ }
481+
378482 std::pair<CWalletTx, std::string> result_pair;
379483
380484 {
0 commit comments