From 5d7ad931adc4903391b4ea6a62916a4553359d12 Mon Sep 17 00:00:00 2001
From: "James C. Owens" <jamesowens@optonline.net>
Date: Tue, 3 Oct 2023 17:27:08 -0400
Subject: [PATCH] Add description field for sidestake.

---
 src/gridcoin/sidestake.cpp | 10 +++++++--
 src/gridcoin/sidestake.h   | 15 +++++++++----
 src/rpc/blockchain.cpp     | 46 ++++++++++++++++++++++++++++++++++++--
 src/rpc/client.cpp         |  1 -
 4 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/src/gridcoin/sidestake.cpp b/src/gridcoin/sidestake.cpp
index c310c625c9..5378af713b 100644
--- a/src/gridcoin/sidestake.cpp
+++ b/src/gridcoin/sidestake.cpp
@@ -49,15 +49,17 @@ CBitcoinAddressForStorage::CBitcoinAddressForStorage(CBitcoinAddress address)
 SideStake::SideStake()
     : m_key()
       , m_allocation()
+      , m_description()
       , m_timestamp(0)
       , m_hash()
       , m_previous_hash()
       , m_status(SideStakeStatus::UNKNOWN)
 {}
 
-SideStake::SideStake(CBitcoinAddressForStorage address, double allocation)
+SideStake::SideStake(CBitcoinAddressForStorage address, double allocation, std::string description)
     : m_key(address)
       , m_allocation(allocation)
+      , m_description(description)
       , m_timestamp(0)
       , m_hash()
       , m_previous_hash()
@@ -66,11 +68,13 @@ SideStake::SideStake(CBitcoinAddressForStorage address, double allocation)
 
 SideStake::SideStake(CBitcoinAddressForStorage address,
                      double allocation,
+                     std::string description,
                      int64_t timestamp,
                      uint256 hash,
                      SideStakeStatus status)
     : m_key(address)
       , m_allocation(allocation)
+      , m_description(description)
       , m_timestamp(timestamp)
       , m_hash(hash)
       , m_previous_hash()
@@ -163,10 +167,11 @@ SideStakePayload::SideStakePayload(uint32_t version)
 SideStakePayload::SideStakePayload(const uint32_t version,
                                    CBitcoinAddressForStorage key,
                                    double value,
+                                   std::string description,
                                    SideStakeStatus status)
     : IContractPayload()
       , m_version(version)
-      , m_entry(SideStake(key, value, 0, uint256{}, status))
+      , m_entry(SideStake(key, value, description, 0, uint256{}, status))
 {
 }
 
@@ -603,6 +608,7 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig()
 
         SideStake sidestake(static_cast<CBitcoinAddressForStorage>(address),
                             dAllocation,
+                            std::string {},
                             0,
                             uint256{},
                             SideStakeStatus::ACTIVE);
diff --git a/src/gridcoin/sidestake.h b/src/gridcoin/sidestake.h
index 25d5de8574..afebd8faed 100644
--- a/src/gridcoin/sidestake.h
+++ b/src/gridcoin/sidestake.h
@@ -69,6 +69,8 @@ class SideStake
 
     double m_allocation;                //!< The allocation is a double precision floating point between 0.0 and 1.0 inclusive
 
+    std::string m_description;            //!< The description of the sidestake (optional)
+
     int64_t m_timestamp;                //!< Time of the sidestake contract transaction.
 
     uint256 m_hash;                     //!< The hash of the transaction that contains a mandatory sidestake.
@@ -88,17 +90,19 @@ class SideStake
     //!
     //! \param address
     //! \param allocation
+    //! \param description (optional)
     //!
-    SideStake(CBitcoinAddressForStorage address, double allocation);
+    SideStake(CBitcoinAddressForStorage address, double allocation, std::string description);
 
     //!
     //! \brief Initialize a sidestake instance with the provided parameters.
     //!
     //! \param address
     //! \param allocation
+    //! \param description (optional)
     //! \param status
     //!
-    SideStake(CBitcoinAddressForStorage address, double allocation, SideStakeStatus status);
+    SideStake(CBitcoinAddressForStorage address, double allocation, std::string description, SideStakeStatus status);
 
     //!
     //! \brief Initialize a sidestake instance with the provided parameters. This form is normally used to construct a
@@ -106,11 +110,12 @@ class SideStake
     //!
     //! \param address
     //! \param allocation
+    //! \param description (optional)
     //! \param timestamp
     //! \param hash
     //! \param status
     //!
-    SideStake(CBitcoinAddressForStorage address, double allocation, int64_t timestamp, uint256 hash, SideStakeStatus status);
+    SideStake(CBitcoinAddressForStorage address, double allocation, std::string description, int64_t timestamp, uint256 hash, SideStakeStatus status);
 
     //!
     //! \brief Determine whether a sidestake contains each of the required elements.
@@ -176,6 +181,7 @@ class SideStake
     {
         READWRITE(m_key);
         READWRITE(m_allocation);
+        READWRITE(m_description);
         READWRITE(m_timestamp);
         READWRITE(m_hash);
         READWRITE(m_previous_hash);
@@ -242,7 +248,8 @@ class SideStakePayload : public IContractPayload
     //! \param value. Value string for the sidestake entry
     //! \param status. Status of the sidestake entry
     //!
-    SideStakePayload(const uint32_t version, CBitcoinAddressForStorage key, double value, SideStakeStatus status);
+    SideStakePayload(const uint32_t version, CBitcoinAddressForStorage key, double value,
+                     std::string description, SideStakeStatus status);
 
     //!
     //! \brief Initialize a sidestake entry payload from the given sidestake entry
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index ee6581ef46..44bf74fcd4 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2269,6 +2269,17 @@ UniValue addkey(const UniValue& params, bool fHelp)
         }
     }
 
+    // For add a mandatory sidestake, the 4th parameter is the allocation and the description (5th parameter) is optional.
+    if (type == GRC::ContractType::SIDESTAKE) {
+        if (action == GRC::ContractAction::ADD) {
+            required_param_count = 4;
+            param_count_max = 5;
+        } else {
+            required_param_count = 3;
+            param_count_max = 3;
+        }
+    }
+
     if (fHelp || params.size() < required_param_count || params.size() > param_count_max) {
         std::string error_string;
 
@@ -2320,22 +2331,45 @@ UniValue addkey(const UniValue& params, bool fHelp)
     case GRC::ContractType::PROJECT:
     {
         if (action == GRC::ContractAction::ADD) {
+            bool gdpr_export_control = false;
+
             if (block_v13_enabled) {
+                // We must do our own conversion to boolean here, because the 5th parameter can either be
+                // a boolean for project or a string for sidestake, which means the client.cpp entry cannot contain a
+                // unicode type specifier for the 5th parameter.
+                if (ToLower(params[4].get_str()) == "true") {
+                    gdpr_export_control = true;
+                } else if (ToLower(params[4].get_str()) != "false") {
+                    // Neither true or false - throw an exception.
+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "GDPR export parameter invalid. Must be true or false.");
+                }
+
                 contract = GRC::MakeContract<GRC::Project>(
                             contract_version,
                             action,
                             uint32_t{3},          // Contract payload version number, 3
                             params[2].get_str(),  // Name
                             params[3].get_str(),  // URL
-                            params[4].getBool()); // GDPR stats export protection enforced boolean
+                            gdpr_export_control); // GDPR stats export protection enforced boolean
+
             } else if (project_v2_enabled) {
+                // We must do our own conversion to boolean here, because the 5th parameter can either be
+                // a boolean for project or a string for sidestake, which means the client.cpp entry cannot contain a
+                // unicode type specifier for the 5th parameter.
+                if (ToLower(params[4].get_str()) == "true") {
+                    gdpr_export_control = true;
+                } else if (ToLower(params[4].get_str()) != "false") {
+                    // Neither true or false - throw an exception.
+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "GDPR export parameter invalid. Must be true or false.");
+                }
+
                 contract = GRC::MakeContract<GRC::Project>(
                             contract_version,
                             action,
                             uint32_t{2},          // Contract payload version number, 2
                             params[2].get_str(),  // Name
                             params[3].get_str(),  // URL
-                            params[4].getBool()); // GDPR stats export protection enforced boolean
+                            gdpr_export_control); // GDPR stats export protection enforced boolean
 
             } else {
                 contract = GRC::MakeContract<GRC::Project>(
@@ -2443,6 +2477,13 @@ UniValue addkey(const UniValue& params, bool fHelp)
                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Address specified for the sidestake is invalid.");
             }
 
+            std::string description;
+            if (params.size() > 4) {
+                description = params[4].get_str();
+            }
+
+            // We have to do our own conversion here because the 4th parameter type specifier cannot be set other
+            // than string in the client.cpp file.
             double allocation = 0.0;
             if (!ParseDouble(params[3].get_str(), &allocation)) {
                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid allocation specified.");
@@ -2460,6 +2501,7 @@ UniValue addkey(const UniValue& params, bool fHelp)
                         uint32_t {1},                   // Contract payload version number
                         sidestake_address,              // Sidestake address
                         allocation,                     // Sidestake allocation
+                        description,                    // Sidestake description
                         GRC::SideStakeStatus::MANDATORY // sidestake status
                         );
         } else {
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 8bc4bbca8d..b1a76da8f8 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -201,7 +201,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
     { "superblocks"            , 1 },
 
     // Developer
-    { "addkey"                 , 4 },
     { "auditsnapshotaccrual"   , 1 },
     { "auditsnapshotaccruals"  , 0 },
     { "beaconaudit"            , 0 },