Skip to content

Commit

Permalink
Initial machinery in project.h/cpp and blockchain.cpp for greylist st…
Browse files Browse the repository at this point in the history
…atus
  • Loading branch information
jamescowens committed May 6, 2024
1 parent 5ac7984 commit 97d99e9
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 18 deletions.
78 changes: 72 additions & 6 deletions src/gridcoin/project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ std::string ProjectEntry::StatusToString(const ProjectEntryStatus& status, const
switch(status) {
case ProjectEntryStatus::UNKNOWN: return _("Unknown");
case ProjectEntryStatus::DELETED: return _("Deleted");
case ProjectEntryStatus::MAN_GREYLISTED: return _("Manually Greylisted");
case ProjectEntryStatus::AUTO_GREYLISTED: return _("Automatically Greylisted");
case ProjectEntryStatus::ACTIVE: return _("Active");
case ProjectEntryStatus::OUT_OF_BOUND: break;
}
Expand All @@ -107,6 +109,8 @@ std::string ProjectEntry::StatusToString(const ProjectEntryStatus& status, const
switch(status) {
case ProjectEntryStatus::UNKNOWN: return "Unknown";
case ProjectEntryStatus::DELETED: return "Deleted";
case ProjectEntryStatus::MAN_GREYLISTED: return "Manually Greylisted";
case ProjectEntryStatus::AUTO_GREYLISTED: return "Automatically Greylisted";
case ProjectEntryStatus::ACTIVE: return "Active";
case ProjectEntryStatus::OUT_OF_BOUND: break;
}
Expand Down Expand Up @@ -196,6 +200,14 @@ Project::Project(uint32_t version, std::string name, std::string url, bool gdpr_
{
}

Project::Project(uint32_t version, std::string name, std::string url, bool gdpr_controls, bool manual_greylist)
: ProjectEntry(version, name, url, gdpr_controls, ProjectEntryStatus::UNKNOWN, int64_t {0})
{
if (manual_greylist) {
m_status = ProjectEntryStatus::MAN_GREYLISTED;
}
}

Project::Project(uint32_t version, std::string name, std::string url, bool gdpr_controls, int64_t timestamp)
: ProjectEntry(version, name, url, gdpr_controls, ProjectEntryStatus::UNKNOWN, timestamp)
{
Expand All @@ -215,8 +227,9 @@ Project::Project(ProjectEntry entry)
// Class: WhitelistSnapshot
// -----------------------------------------------------------------------------

WhitelistSnapshot::WhitelistSnapshot(ProjectListPtr projects)
WhitelistSnapshot::WhitelistSnapshot(ProjectListPtr projects, const ProjectEntry::ProjectFilterFlag& filter_used)
: m_projects(std::move(projects))
, m_filter(filter_used)
{
}

Expand Down Expand Up @@ -258,6 +271,11 @@ bool WhitelistSnapshot::Contains(const std::string& name) const
return false;
}

ProjectEntry::ProjectFilterFlag WhitelistSnapshot::FilterUsed() const
{
return m_filter;
}

WhitelistSnapshot WhitelistSnapshot::Sorted() const
{
ProjectList sorted(m_projects->begin(), m_projects->end());
Expand All @@ -275,26 +293,70 @@ WhitelistSnapshot WhitelistSnapshot::Sorted() const

std::sort(sorted.begin(), sorted.end(), ascending_by_name);

return WhitelistSnapshot(std::make_shared<ProjectList>(sorted));
return WhitelistSnapshot(std::make_shared<ProjectList>(sorted), m_filter);
}

// -----------------------------------------------------------------------------
// Class: Whitelist (Registry)
// -----------------------------------------------------------------------------

WhitelistSnapshot Whitelist::Snapshot() const
WhitelistSnapshot Whitelist::Snapshot(const ProjectEntry::ProjectFilterFlag& filter) const
{
LOCK(cs_lock);

ProjectList projects;

for (const auto& iter : m_project_entries) {
if (iter.second->m_status == ProjectEntryStatus::ACTIVE) {
switch (filter) {
case ProjectEntry::ProjectFilterFlag::ACTIVE:
if (iter.second->m_status == ProjectEntryStatus::ACTIVE) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::MAN_GREYLISTED:
if (iter.second->m_status == ProjectEntryStatus::MAN_GREYLISTED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::AUTO_GREYLISTED:
if (iter.second->m_status == ProjectEntryStatus::AUTO_GREYLISTED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::GREYLISTED:
if (iter.second->m_status == ProjectEntryStatus::MAN_GREYLISTED
|| iter.second->m_status == ProjectEntryStatus::AUTO_GREYLISTED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::DELETED:
if (iter.second->m_status == ProjectEntryStatus::DELETED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::NOT_ACTIVE:
if (iter.second->m_status == ProjectEntryStatus::MAN_GREYLISTED
|| iter.second->m_status == ProjectEntryStatus::AUTO_GREYLISTED
|| iter.second->m_status == ProjectEntryStatus::DELETED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::ALL_BUT_DELETED:
if (iter.second->m_status == ProjectEntryStatus::ACTIVE
|| iter.second->m_status == ProjectEntryStatus::MAN_GREYLISTED
|| iter.second->m_status == ProjectEntryStatus::AUTO_GREYLISTED) {
projects.push_back(*iter.second);
}
break;
case ProjectEntry::ProjectFilterFlag::ALL:
projects.push_back(*iter.second);
break;
case ProjectEntry::ProjectFilterFlag::NONE:
break;
}
}

return WhitelistSnapshot(std::make_shared<ProjectList>(projects));
return WhitelistSnapshot(std::make_shared<ProjectList>(projects), filter);
}

void Whitelist::Reset()
Expand Down Expand Up @@ -325,7 +387,12 @@ void Whitelist::AddDelete(const ContractContext& ctx)
// If the contract status is ADD, then ProjectEntryStatus will be ACTIVE. If contract status
// is REMOVE then ProjectEntryStatus will be DELETED.
if (ctx->m_action == ContractAction::ADD) {
// Normal add/delete contracts are constructed with "unknown" status. The status is recorded on the local client
// based on the add/delete action. The manual greylist status is specified in the contract as constructed, and will carry
// through here for the contract add with the greylist flag set.
if (payload.m_status == ProjectEntryStatus::UNKNOWN) {
payload.m_status = ProjectEntryStatus::ACTIVE;
}
} else if (ctx->m_action == ContractAction::REMOVE) {
payload.m_status = ProjectEntryStatus::DELETED;
}
Expand Down Expand Up @@ -545,4 +612,3 @@ template<> const std::string Whitelist::ProjectEntryDB::KeyType()
{
return std::string("project");
}

51 changes: 47 additions & 4 deletions src/gridcoin/project.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@ namespace GRC
//!
//! ACTIVE corresponds to an active entry.
//!
//! GREYLISTED means that the project temporarily does not meet the whitelist qualification criteria.
//!
//! OUT_OF_BOUND must go at the end and be retained for the EnumBytes wrapper.
//!
enum class ProjectEntryStatus
{
UNKNOWN,
DELETED,
ACTIVE,
MAN_GREYLISTED,
AUTO_GREYLISTED,
OUT_OF_BOUND
};

Expand All @@ -49,6 +53,23 @@ class ProjectEntry
//!
using Status = EnumByte<ProjectEntryStatus>;

//!
//! \brief Project filter flag enumeration.
//!
//! This controls what project entries by status are in the project whitelist snapshot.
//!
enum ProjectFilterFlag : uint8_t {
NONE = 0b0000,
DELETED = 0b0001,
MAN_GREYLISTED = 0b0010,
AUTO_GREYLISTED = 0b0100,
GREYLISTED = MAN_GREYLISTED | AUTO_GREYLISTED,
ACTIVE = 0b1000,
NOT_ACTIVE = 0b0111,
ALL_BUT_DELETED = 0b1110,
ALL = 0b1111
};

//!
//! \brief Version number of the current format for a serialized project.
//!
Expand Down Expand Up @@ -284,6 +305,17 @@ class Project : public IContractPayload, public ProjectEntry
//!
Project(uint32_t version, std::string name, std::string url, bool gdpr_controls);

//!
//! \brief Initialize a \c Project using data from the contract.
//!
//! \param version Project payload version.
//! \param name Project name from contract message key.
//! \param url Project URL from contract message value.
//! \param gdpr_controls Boolean to indicate gdpr stats export controls enforced
//! \param manual_greylist Boolean to force manual greylisting of project
//!
Project(uint32_t version, std::string name, std::string url, bool gdpr_controls, bool manual_greylist);

//!
//! \brief Initialize a \c Project using data from the contract.
//!
Expand All @@ -307,7 +339,7 @@ class Project : public IContractPayload, public ProjectEntry
Project(std::string name, std::string url, int64_t timestamp, uint32_t version, bool gdpr_controls);

//!
//! \brief Initialize a \c Project payloard from a provided project entry
//! \brief Initialize a \c Project payload from a provided project entry
//! \param version. Project Payload version.
//! \param entry. Project entry to place in the payload.
//!
Expand Down Expand Up @@ -426,8 +458,10 @@ class WhitelistSnapshot
//! \brief Initialize a snapshot for the provided project list from the project entry map.
//!
//! \param projects A copy of the smart pointer to the project list.
//! \param filter_used The project status filter used for the project list.
//!
WhitelistSnapshot(ProjectListPtr projects);
WhitelistSnapshot(ProjectListPtr projects,
const ProjectEntry::ProjectFilterFlag& filter_used = ProjectEntry::ProjectFilterFlag::ACTIVE);

//!
//! \brief Returns an iterator to the beginning.
Expand Down Expand Up @@ -463,6 +497,13 @@ class WhitelistSnapshot
//!
bool Contains(const std::string& name) const;

//!
//! \brief Returns the project status filter flag used to populate the snapshot.
//!
//! \return ProjectFilterFlag used for whitelist snapshot.
//!
ProjectEntry::ProjectFilterFlag FilterUsed() const;

//!
//! \brief Create a snapshot copy sorted alphabetically by project name.
//!
Expand All @@ -472,6 +513,7 @@ class WhitelistSnapshot

private:
const ProjectListPtr m_projects; //!< The vector of whitelisted projects.
const ProjectEntry::ProjectFilterFlag m_filter; //!< The filter used to populate the readonly list.
};

//!
Expand Down Expand Up @@ -513,9 +555,10 @@ class Whitelist : public IContractHandler
typedef std::map<uint256, ProjectEntry_ptr> HistoricalProjectEntryMap;

//!
//! \brief Get a read-only view of the projects in the whitelist.
//! \brief Get a read-only view of the projects in the whitelist. The default filter is ACTIVE, which
//! provides the original ACTIVE project only view.
//!
WhitelistSnapshot Snapshot() const;
WhitelistSnapshot Snapshot(const ProjectEntry::ProjectFilterFlag& filter = ProjectEntry::ProjectFilterFlag::ACTIVE) const;

//!
//! \brief Destroy the contract handler state to prepare for historical
Expand Down
38 changes: 30 additions & 8 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2194,23 +2194,35 @@ UniValue superblocks(const UniValue& params, bool fHelp)
//!
//! To whitelist a project:
//!
//! addkey add project projectname url
//! addkey add project projectname url (< v2)
//!
//! To de-whitelist a project:
//! addkey add project projectname url gdpr_controls (v2)
//!
//! addkey add project projectname url gdpr_controls manual_greylist (v3)
//!
//! addkey delete project projectname
//! To de-whitelist a project:
//!
//! or
//! addkey delete project projectname 1 (<= v2)
//!
//! addkey delete project projectname 1
//! addkey delete project projectname (>= v3)
//!
//! Key examples:
//!
//! To add:
//!
//! v1: addkey add project milkyway@home http://milkyway.cs.rpi.edu/milkyway/@
//! v2: addkey add project milkyway@home http://milkyway.cs.rpi.edu/milkyway/@ true
//!
//! To manually greylist:
//!
//! v3: addkey add project milkyway@home http://milkyway.cs.rpi.edu/milkyway/@ true true
//!
//! To delete:
//!
//! v1 or v2: addkey delete project milkyway@home
//! v1 or v2: addkey delete project milkyway@home 1 (last parameter is a dummy)
//! v2: addkey delete project milkyway@home 1 false (last two parameters are dummies)
//! v3: addkey delete project milkyway@home (the dummy parameters are no longer needed for v3 deletes)
//!
//! GRC will only memorize the *last* value it finds for a key in the highest
//! block.
Expand Down Expand Up @@ -2254,7 +2266,8 @@ UniValue addkey(const UniValue& params, bool fHelp)
&& action == GRC::ContractAction::ADD
&& type == GRC::ContractType::PROJECT) {
required_param_count = 5;
param_count_max = 5;

block_v13_enabled ? param_count_max = 6 : param_count_max = 5;
}

if ((type == GRC::ContractType::PROJECT || type == GRC::ContractType::SCRAPER)
Expand Down Expand Up @@ -2332,6 +2345,7 @@ UniValue addkey(const UniValue& params, bool fHelp)
{
if (action == GRC::ContractAction::ADD) {
bool gdpr_export_control = false;
bool manually_greylist = false;

if (block_v13_enabled) {
// We must do our own conversion to boolean here, because the 5th parameter can either be
Expand All @@ -2344,13 +2358,20 @@ UniValue addkey(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "GDPR export parameter invalid. Must be true or false.");
}

if (params.size() == 6) {
if (ToLower(params[4].get_str()) == "true") {
manually_greylist = true;
}
}

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
gdpr_export_control); // GDPR stats export protection enforced boolean
gdpr_export_control, // GDPR stats export protection enforced boolean
manually_greylist); // manual greylist flag

} else if (project_v2_enabled) {
// We must do our own conversion to boolean here, because the 5th parameter can either be
Expand Down Expand Up @@ -2386,7 +2407,8 @@ UniValue addkey(const UniValue& params, bool fHelp)
uint32_t{3}, // Contract payload version number, 3
params[2].get_str(), // Name
std::string{}, // URL ignored
false); // GDPR status irrelevant
false, // GDPR status irrelevant
false); // manual greylisting irrelevant

} else if (project_v2_enabled) {
contract = GRC::MakeContract<GRC::Project>(
Expand Down

0 comments on commit 97d99e9

Please sign in to comment.