Skip to content

Commit

Permalink
Wire up the rest of the sidestake contract machinery
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Oct 2, 2023
1 parent 64764db commit 24dc7c5
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 22 deletions.
46 changes: 36 additions & 10 deletions src/gridcoin/contract/contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "gridcoin/project.h"
#include "gridcoin/researcher.h"
#include "gridcoin/scraper/scraper_registry.h"
#include "gridcoin/sidestake.h"
#include "gridcoin/support/block_finder.h"
#include "gridcoin/support/xml.h"
#include "gridcoin/tx_message.h"
Expand Down Expand Up @@ -276,6 +277,7 @@ class Dispatcher
case ContractType::SCRAPER: return GetScraperRegistry();
case ContractType::VOTE: return GetPollRegistry();
case ContractType::MRC: return m_mrc_contract_handler;
case ContractType::SIDESTAKE: return GetSideStakeRegistry();
default: return m_unknown_handler;
}
}
Expand Down Expand Up @@ -677,12 +679,13 @@ bool Contract::RequiresMasterKey() const
// beacons by signing them with the original private key:
return m_version == 1 && m_action == ContractAction::REMOVE;

case ContractType::POLL: return m_action == ContractAction::REMOVE;
case ContractType::PROJECT: return true;
case ContractType::PROTOCOL: return true;
case ContractType::SCRAPER: return true;
case ContractType::VOTE: return m_action == ContractAction::REMOVE;
default: return false;
case ContractType::POLL: return m_action == ContractAction::REMOVE;
case ContractType::PROJECT: return true;
case ContractType::PROTOCOL: return true;
case ContractType::SCRAPER: return true;
case ContractType::VOTE: return m_action == ContractAction::REMOVE;
case ContractType::SIDESTAKE: return true;
default: return false;
}
}

Expand All @@ -693,10 +696,23 @@ CAmount Contract::RequiredBurnAmount() const

bool Contract::WellFormed() const
{
return m_version > 0 && m_version <= Contract::CURRENT_VERSION
&& m_type != ContractType::UNKNOWN
&& m_action != ContractAction::UNKNOWN
&& m_body.WellFormed(m_action.Value());
bool result = m_version > 0 && m_version <= Contract::CURRENT_VERSION
&& m_type != ContractType::UNKNOWN
&& m_action != ContractAction::UNKNOWN
&& m_body.WellFormed(m_action.Value());

if (!result) {
LogPrint(BCLog::LogFlags::CONTRACT, "WARN: %s: Contract was not well formed. m_version = %u, m_type = %s, "
"m_action = %s, m_body.Wellformed(m_action.Value()) = %u",
__func__,
m_version,
m_type.ToString(),
m_action.ToString(),
m_body.WellFormed(m_action.Value())
);
}

return result;
}

ContractPayload Contract::SharePayload() const
Expand Down Expand Up @@ -761,6 +777,7 @@ Contract::Type Contract::Type::Parse(std::string input)
if (input == "scraper") return ContractType::SCRAPER;
if (input == "protocol") return ContractType::PROTOCOL;
if (input == "message") return ContractType::MESSAGE;
if (input == "sidestake") return ContractType::SIDESTAKE;

return ContractType::UNKNOWN;
}
Expand All @@ -777,6 +794,7 @@ std::string Contract::Type::ToString() const
case ContractType::PROTOCOL: return "protocol";
case ContractType::SCRAPER: return "scraper";
case ContractType::VOTE: return "vote";
case ContractType::SIDESTAKE: return "sidestake";
default: return "";
}
}
Expand All @@ -793,6 +811,7 @@ std::string Contract::Type::ToString(ContractType contract_type)
case ContractType::PROTOCOL: return "protocol";
case ContractType::SCRAPER: return "scraper";
case ContractType::VOTE: return "vote";
case ContractType::SIDESTAKE: return "sidestake";
default: return "";
}
}
Expand All @@ -809,6 +828,7 @@ std::string Contract::Type::ToTranslatedString(ContractType contract_type)
case ContractType::PROTOCOL: return _("protocol");
case ContractType::SCRAPER: return _("scraper");
case ContractType::VOTE: return _("vote");
case ContractType::SIDESTAKE: return _("sidestake");
default: return "";
}
}
Expand Down Expand Up @@ -905,6 +925,9 @@ ContractPayload Contract::Body::ConvertFromLegacy(const ContractType type, uint3
case ContractType::VOTE:
return ContractPayload::Make<LegacyVote>(
LegacyVote::Parse(legacy.m_key, legacy.m_value));
case ContractType::SIDESTAKE:
// Sidestakes have no legacy representation as a contract.
assert(false && "Attempted to convert non-existent legacy sidestake contract.");
case ContractType::OUT_OF_BOUND:
assert(false);
}
Expand Down Expand Up @@ -961,6 +984,9 @@ void Contract::Body::ResetType(const ContractType type)
case ContractType::VOTE:
m_payload.Reset(new Vote());
break;
case ContractType::SIDESTAKE:
m_payload.Reset(new SideStakePayload());
break;
case ContractType::OUT_OF_BOUND:
assert(false);
}
Expand Down
2 changes: 2 additions & 0 deletions src/gridcoin/contract/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const std::vector<GRC::ContractType> RegistryBookmarks::CONTRACT_TYPES_WITH_REG_
ContractType::PROJECT,
ContractType::PROTOCOL,
ContractType::SCRAPER,
ContractType::SIDESTAKE
};

const std::vector<GRC::ContractType> RegistryBookmarks::CONTRACT_TYPES_SUPPORTING_REVERT = {
Expand All @@ -20,6 +21,7 @@ const std::vector<GRC::ContractType> RegistryBookmarks::CONTRACT_TYPES_SUPPORTIN
ContractType::PROTOCOL,
ContractType::SCRAPER,
ContractType::VOTE,
ContractType::SIDESTAKE
};

} // namespace GRC
3 changes: 3 additions & 0 deletions src/gridcoin/contract/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "gridcoin/beacon.h"
#include "gridcoin/project.h"
#include "gridcoin/protocol.h"
#include "gridcoin/sidestake.h"
#include "gridcoin/scraper/scraper_registry.h"
#include "gridcoin/voting/registry.h"

Expand Down Expand Up @@ -50,6 +51,7 @@ class RegistryBookmarks
case ContractType::PROJECT: return GetWhitelist();
case ContractType::PROTOCOL: return GetProtocolRegistry();
case ContractType::SCRAPER: return GetScraperRegistry();
case ContractType::SIDESTAKE: return GetSideStakeRegistry();
case ContractType::UNKNOWN:
[[fallthrough]];
case ContractType::CLAIM:
Expand Down Expand Up @@ -78,6 +80,7 @@ class RegistryBookmarks
case ContractType::PROTOCOL: return GetProtocolRegistry();
case ContractType::SCRAPER: return GetScraperRegistry();
case ContractType::VOTE: return GetPollRegistry();
case ContractType::SIDESTAKE: return GetSideStakeRegistry();
[[fallthrough]];
case ContractType::UNKNOWN:
[[fallthrough]];
Expand Down
36 changes: 27 additions & 9 deletions src/gridcoin/sidestake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,30 @@ const SideStakeRegistry::SideStakeMap& SideStakeRegistry::SideStakeEntries() con
const std::vector<SideStake_ptr> SideStakeRegistry::ActiveSideStakeEntries()
{
std::vector<SideStake_ptr> sidestakes;
double allocation_sum = 0.0;

// For right now refresh sidestakes from config file. This is about the same overhead as the original
// function in the miner. Perhaps replace with a signal to only refresh when r-w config file is
// actually changed.
LoadLocalSideStakesFromConfig();

// The loops below prevent sidestakes from being added that cause a total allocation above 1.0 (100%).

// Do mandatory sidestakes first.
for (const auto& entry : m_sidestake_entries)
{
if (entry.second->m_status == SideStakeStatus::MANDATORY && allocation_sum + entry.second->m_allocation <= 1.0) {
sidestakes.push_back(entry.second);
allocation_sum += entry.second->m_allocation;
}
}

// Followed by local active sidestakes
for (const auto& entry : m_sidestake_entries)
{
if (entry.second->m_status == SideStakeStatus::ACTIVE || entry.second->m_status == SideStakeStatus::MANDATORY) {
if (entry.second->m_status == SideStakeStatus::ACTIVE && allocation_sum + entry.second->m_allocation <= 1.0) {
sidestakes.push_back(entry.second);
allocation_sum += entry.second->m_allocation;
}
}

Expand Down Expand Up @@ -400,15 +414,11 @@ void SideStakeRegistry::Revert(const ContractContext& ctx)

bool SideStakeRegistry::Validate(const Contract& contract, const CTransaction& tx, int &DoS) const
{
if (contract.m_version < 1) {
return true;
}

const auto payload = contract.SharePayloadAs<SideStakePayload>();

if (contract.m_version >= 3 && payload->m_version < 2) {
if (contract.m_version < 3) {
DoS = 25;
error("%s: Legacy SideStake entry contract in contract v3", __func__);
error("%s: Sidestake entries only valid in contract v3 and above", __func__);
return false;
}

Expand Down Expand Up @@ -519,9 +529,17 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig()
}
}

// First, determine allocation already taken by mandatory sidestakes, because they must be allocated first.
for (const auto& entry : SideStakeEntries()) {
if (entry.second->m_status == SideStakeStatus::MANDATORY) {
dSumAllocation += entry.second->m_allocation;
}
}

for (const auto& entry : raw_vSideStakeAlloc)
{
std::string sAddress;

double dAllocation = 0.0;

sAddress = entry.first;
Expand Down Expand Up @@ -583,9 +601,9 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig()
auto iter = std::find(vSideStakes.begin(), vSideStakes.end(), *entry.second);

if (iter == vSideStakes.end()) {
// Entry in map is no longer found in config files, so mark map entry deleted.
// Entry in map is no longer found in config files, so mark map entry inactive.

entry.second->m_status = SideStakeStatus::DELETED;
entry.second->m_status = SideStakeStatus::INACTIVE;
}
}
}
Expand Down
32 changes: 30 additions & 2 deletions src/gridcoin/sidestake.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "gridcoin/contract/registry_db.h"
#include "gridcoin/support/enumbytes.h"
#include "serialize.h"
#include "logging.h"

namespace GRC {

Expand All @@ -29,6 +30,7 @@ class CBitcoinAddressForStorage : public CBitcoinAddress
// Note that (de)serializing the raw underlying vector char data for the address is safe here
// because this is only used in this module and validations were performed before serialization into
// storage.
READWRITE(nVersion);
READWRITE(vchData);
}
};
Expand Down Expand Up @@ -274,11 +276,35 @@ class SideStakePayload : public IContractPayload
//!
bool WellFormed(const ContractAction action) const override
{
if (m_version <= 0 || m_version > CURRENT_VERSION) {
bool valid = !(m_version <= 0 || m_version > CURRENT_VERSION);

if (!valid) {
LogPrint(BCLog::LogFlags::CONTRACT, "WARN: %s: Payload is not well formed. "
"m_version = %u, CURRENT_VERSION = %u",
__func__,
m_version,
CURRENT_VERSION);

return false;
}

valid = m_entry.WellFormed();

if (!valid) {
LogPrint(BCLog::LogFlags::CONTRACT, "WARN: %s: Sidestake entry is not well-formed. "
"m_entry.WellFormed = %u, m_entry.m_key = %s, m_entry.m_allocation = %f, "
"m_entry.StatusToString() = %s",
__func__,
valid,
m_entry.m_key.ToString(),
m_entry.m_allocation,
m_entry.StatusToString()
);

return false;
}

return m_entry.WellFormed();
return valid;
}

//!
Expand Down Expand Up @@ -372,6 +398,8 @@ class SideStakeRegistry : public IContractHandler
//! \brief Get the collection of active sidestake entries. This is presented as a vector of
//! smart pointers to the relevant sidestake entries in the database. The entries included have
//! the status of active (for local sidestakes) and/or mandatory (for contract sidestakes).
//! Mandatory sidestakes come before local ones, and the method ensures that the sidestakes
//! returned do not total an allocation greater than 1.0.
//!
//! \return A vector of smart pointers to sidestake entries.
//!
Expand Down
37 changes: 36 additions & 1 deletion src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "blockchain.h"
#include "gridcoin/protocol.h"
#include "gridcoin/scraper/scraper_registry.h"
#include "gridcoin/sidestake.h"
#include "node/blockstorage.h"
#include <util/string.h>
#include "gridcoin/mrc.h"
Expand Down Expand Up @@ -2304,7 +2305,8 @@ UniValue addkey(const UniValue& params, bool fHelp)

if (!(type == GRC::ContractType::PROJECT
|| type == GRC::ContractType::SCRAPER
|| type == GRC::ContractType::PROTOCOL)) {
|| type == GRC::ContractType::PROTOCOL
|| type == GRC::ContractType::SIDESTAKE)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid contract type for addkey.");
}

Expand Down Expand Up @@ -2433,6 +2435,39 @@ UniValue addkey(const UniValue& params, bool fHelp)
params[2].get_str(), // key
params[3].get_str()); // value
break;
case GRC::ContractType::SIDESTAKE:
{
if (block_v13_enabled) {
GRC::CBitcoinAddressForStorage sidestake_address;
if (!sidestake_address.SetString(params[2].get_str())) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address specified for the sidestake is invalid.");
}

double allocation = 0.0;
if (!ParseDouble(params[3].get_str(), &allocation)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid allocation specified.");
}

allocation /= 100.0;

if (allocation > 1.0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Allocation specified is greater than 100.0%.");
}

contract = GRC::MakeContract<GRC::SideStakePayload>(
contract_version, // Contract version number (3+)
action, // Contract action
uint32_t {1}, // Contract payload version number
sidestake_address, // Sidestake address
allocation, // Sidestake allocation
GRC::SideStakeStatus::MANDATORY // sidestake status
);
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Sidestake contracts are not valid for block version less than v13.");
}

break;
}
case GRC::ContractType::BEACON:
[[fallthrough]];
case GRC::ContractType::CLAIM:
Expand Down
2 changes: 2 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1934,6 +1934,7 @@ bool AcceptBlock(CBlock& block, bool generated_by_me) EXCLUSIVE_LOCKS_REQUIRED(c
|| (IsV10Enabled(nHeight) && block.nVersion < 10)
|| (IsV11Enabled(nHeight) && block.nVersion < 11)
|| (IsV12Enabled(nHeight) && block.nVersion < 12)
|| (IsV13Enabled(nHeight) && block.nVersion < 13)
) {
return block.DoS(20, error("%s: reject too old nVersion = %d", __func__, block.nVersion));
} else if ((!IsProtocolV2(nHeight) && block.nVersion >= 7)
Expand All @@ -1942,6 +1943,7 @@ bool AcceptBlock(CBlock& block, bool generated_by_me) EXCLUSIVE_LOCKS_REQUIRED(c
|| (!IsV10Enabled(nHeight) && block.nVersion >= 10)
|| (!IsV11Enabled(nHeight) && block.nVersion >= 11)
|| (!IsV12Enabled(nHeight) && block.nVersion >= 12)
|| (!IsV13Enabled(nHeight) && block.nVersion >= 13)
) {
return block.DoS(100, error("%s: reject too new nVersion = %d", __func__, block.nVersion));
}
Expand Down

0 comments on commit 24dc7c5

Please sign in to comment.