Skip to content

Commit

Permalink
Merge pull request #2704 from jamescowens/implement_sidestake_GUI
Browse files Browse the repository at this point in the history
gui, staking: Implement facilities for mandatory sidestakes and sidestake GUI
  • Loading branch information
jamescowens authored Jan 29, 2024
2 parents 270711d + 83d3f9e commit c31376c
Show file tree
Hide file tree
Showing 43 changed files with 5,062 additions and 308 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ add_library(gridcoin_util STATIC
gridcoin/scraper/scraper.cpp
gridcoin/scraper/scraper_net.cpp
gridcoin/scraper/scraper_registry.cpp
gridcoin/sidestake.cpp
gridcoin/staking/difficulty.cpp
gridcoin/staking/exceptions.cpp
gridcoin/staking/kernel.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ GRIDCOIN_CORE_H = \
gridcoin/scraper/scraper.h \
gridcoin/scraper/scraper_net.h \
gridcoin/scraper/scraper_registry.h \
gridcoin/sidestake.h \
gridcoin/staking/chain_trust.h \
gridcoin/staking/difficulty.h \
gridcoin/staking/exceptions.h \
Expand Down Expand Up @@ -258,6 +259,7 @@ GRIDCOIN_CORE_CPP = addrdb.cpp \
gridcoin/scraper/scraper.cpp \
gridcoin/scraper/scraper_net.cpp \
gridcoin/scraper/scraper_registry.cpp \
gridcoin/sidestake.cpp \
gridcoin/staking/difficulty.cpp \
gridcoin/staking/exceptions.cpp \
gridcoin/staking/kernel.cpp \
Expand Down
7 changes: 7 additions & 0 deletions src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ QT_FORMS_UI = \
qt/forms/consolidateunspentwizardsendpage.ui \
qt/forms/diagnosticsdialog.ui \
qt/forms/editaddressdialog.ui \
qt/forms/editsidestakedialog.ui \
qt/forms/favoritespage.ui \
qt/forms/intro.ui \
qt/forms/mrcrequestpage.ui \
Expand Down Expand Up @@ -143,6 +144,7 @@ QT_MOC_CPP = \
qt/moc_csvmodelwriter.cpp \
qt/moc_diagnosticsdialog.cpp \
qt/moc_editaddressdialog.cpp \
qt/moc_editsidestakedialog.cpp \
qt/moc_favoritespage.cpp \
qt/moc_guiutil.cpp \
qt/moc_intro.cpp \
Expand All @@ -161,6 +163,7 @@ QT_MOC_CPP = \
qt/moc_rpcconsole.cpp \
qt/moc_sendcoinsdialog.cpp \
qt/moc_sendcoinsentry.cpp \
qt/moc_sidestaketablemodel.cpp \
qt/moc_signverifymessagedialog.cpp \
qt/moc_trafficgraphwidget.cpp \
qt/moc_transactiondesc.cpp \
Expand Down Expand Up @@ -249,6 +252,7 @@ GRIDCOINRESEARCH_QT_H = \
qt/decoration.h \
qt/diagnosticsdialog.h \
qt/editaddressdialog.h \
qt/editsidestakedialog.h \
qt/favoritespage.h \
qt/guiconstants.h \
qt/guiutil.h \
Expand Down Expand Up @@ -285,6 +289,7 @@ GRIDCOINRESEARCH_QT_H = \
qt/rpcconsole.h \
qt/sendcoinsdialog.h \
qt/sendcoinsentry.h \
qt/sidestaketablemodel.h \
qt/signverifymessagedialog.h \
qt/trafficgraphwidget.h \
qt/transactiondesc.h \
Expand Down Expand Up @@ -340,6 +345,7 @@ GRIDCOINRESEARCH_QT_CPP = \
qt/decoration.cpp \
qt/diagnosticsdialog.cpp \
qt/editaddressdialog.cpp \
qt/editsidestakedialog.cpp \
qt/favoritespage.cpp \
qt/guiutil.cpp \
qt/intro.cpp \
Expand Down Expand Up @@ -372,6 +378,7 @@ GRIDCOINRESEARCH_QT_CPP = \
qt/rpcconsole.cpp \
qt/sendcoinsdialog.cpp \
qt/sendcoinsentry.cpp \
qt/sidestaketablemodel.cpp \
qt/signverifymessagedialog.cpp \
qt/trafficgraphwidget.cpp \
qt/transactiondesc.cpp \
Expand Down
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ GRIDCOIN_TESTS =\
test/gridcoin/protocol_tests.cpp \
test/gridcoin/researcher_tests.cpp \
test/gridcoin/scraper_registry_tests.cpp \
test/gridcoin/sidestake_tests.cpp \
test/gridcoin/superblock_tests.cpp \
test/key_tests.cpp \
test/merkle_tests.cpp \
Expand Down
4 changes: 4 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class CMainParams : public CChainParams {
consensus.InitialMRCFeeFractionPostZeroInterval = Fraction(2, 5);
// Zero day interval is 14 days on mainnet
consensus.MRCZeroPaymentInterval = 14 * 24 * 60 * 60;
// The maximum ratio of rewards that can be allocated to all of the mandatory sidestakes.
consensus.MaxMandatorySideStakeTotalAlloc = Fraction(1, 4);
// The "standard" contract replay lookback for those contract types
// that do not have a registry db.
consensus.StandardContractReplayLookback = 180 * 24 * 60 * 60;
Expand Down Expand Up @@ -187,6 +189,8 @@ class CTestNetParams : public CChainParams {
consensus.InitialMRCFeeFractionPostZeroInterval = Fraction(2, 5);
// Zero day interval is 10 minutes on testnet. The very short interval facilitates testing.
consensus.MRCZeroPaymentInterval = 10 * 60;
// The maximum ratio of rewards that can be allocated to all of the mandatory sidestakes.
consensus.MaxMandatorySideStakeTotalAlloc = Fraction(1, 4);
// The "standard" contract replay lookback for those contract types
// that do not have a registry db.
consensus.StandardContractReplayLookback = 180 * 24 * 60 * 60;
Expand Down
4 changes: 4 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ struct Params {
* forfeiture of fees to the staker and/or foundation. Only consensus critical at BlockV12Height or above.
*/
int64_t MRCZeroPaymentInterval;
/**
* @brief The maximum allocation (as a Fraction) that can be used by all of the mandatory sidestakes
*/
Fraction MaxMandatorySideStakeTotalAlloc;

int64_t StandardContractReplayLookback;

Expand Down
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
20 changes: 17 additions & 3 deletions src/gridcoin/contract/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "amount.h"
#include "gridcoin/contract/message.h"
#include "gridcoin/contract/contract.h"
#include "gridcoin/sidestake.h"
#include "script.h"
#include "wallet/wallet.h"

Expand Down Expand Up @@ -143,24 +144,37 @@ std::string SendContractTx(CWalletTx& wtx_new)

if (balance < COIN || balance < burn_fee + nTransactionFee) {
std::string strError = _("Balance too low to create a contract.");
LogPrintf("%s: %s", __func__, strError);
error("%s: %s", __func__, strError);
return strError;
}

if (!CreateContractTx(wtx_new, reserve_key, burn_fee)) {
std::string strError = _("Error: Transaction creation failed.");
LogPrintf("%s: %s", __func__, strError);
error("%s: %s", __func__, strError);
return strError;
}

for (const auto& pool_tx : mempool.mapTx) {
for (const auto& pool_tx_contract : pool_tx.second.GetContracts()) {
if (pool_tx_contract.m_type == GRC::ContractType::SIDESTAKE) {
std::string strError = _(
"Error: The mandatory sidestake transaction was rejected. "
"There is already a mandatory sidestake transaction in the mempool. "
"Wait until that transaction is bound in a block.");
error("%s: %s", __func__, strError);
return strError;
}
}
}

if (!pwalletMain->CommitTransaction(wtx_new, reserve_key)) {
std::string strError = _(
"Error: The transaction was rejected. This might happen if some of "
"the coins in your wallet were already spent, such as if you used "
"a copy of wallet.dat and coins were spent in the copy but not "
"marked as spent here.");

LogPrintf("%s: %s", __func__, strError);
error("%s: %s", __func__, strError);
return strError;
}

Expand Down
2 changes: 2 additions & 0 deletions src/gridcoin/contract/payload.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum class ContractType
SCRAPER, //!< Scraper node authorization grants and revocations.
VOTE, //!< A vote cast by a wallet for a poll.
MRC, //!< A manual rewards claim (MRC) request to pay rewards
SIDESTAKE, //!< Mandatory sidestakes
OUT_OF_BOUND, //!< Marker value for the end of the valid range.
};

Expand All @@ -82,6 +83,7 @@ static constexpr GRC::ContractType CONTRACT_TYPES[] = {
ContractType::SCRAPER,
ContractType::VOTE,
ContractType::MRC,
ContractType::SIDESTAKE,
ContractType::OUT_OF_BOUND
};

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
18 changes: 17 additions & 1 deletion 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 Expand Up @@ -154,8 +157,21 @@ class RegistryBookmarks
int lowest_height = std::numeric_limits<int>::max();

for (const auto& iter : m_db_heights) {
int db_height = iter.second;

//! When below the operational range of the sidestake contracts and registry, initialization of the sidestake
//! registry will report zero for height. It is undesirable to return this in the GetLowestRegistryBlockHeight()
//! method, because it will cause the contract replay clamp to go to the Fern mandatory blockheight. Setting
//! the db_height recorded in the bookmarks at V13 height for the sidestake registry for the purpose of contract
//! replay solves the problem.
//!
//! This code can be removed after the V13 mandatory blockheight has been reached.
if (iter.first == GRC::ContractType::SIDESTAKE and db_height < Params().GetConsensus().BlockV13Height) {
db_height = Params().GetConsensus().BlockV13Height;
}

if (iter.second < lowest_height) {
lowest_height = iter.second;
lowest_height = db_height;
}
}

Expand Down
Loading

0 comments on commit c31376c

Please sign in to comment.