Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gui, rpc: Implement dynamic stakesplitting control, settings changes via rpc, and dynamic changes to sidestaking via rpc #2164

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/gridcoin/staking/status.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace {
//!
constexpr const char* STAKING_ERROR_STRINGS[] {
"None",
"Disabled by configuration",
"No Mature Coins",
"No coins",
"Entire balance reserved",
Expand Down
1 change: 1 addition & 0 deletions src/gridcoin/staking/status.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class MinerStatus
enum ReasonNotStakingCategory
{
NONE,
DISABLED_BY_CONFIGURATION,
NO_MATURE_COINS,
NO_COINS,
ENTIRE_BALANCE_RESERVED,
Expand Down
27 changes: 20 additions & 7 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,23 +357,36 @@ void SetupServerArgs()

// Staking
argsman.AddArg("-enablesidestaking", "Enable side staking functionality (default: 0)",
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-staking", "Allow wallet to stake if conditions to stake are met (default: 1)",
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-sidestake=<address,percent>", "Sidestake destination and allocation entry. There can be as many "
"specified as desired. Only six per stake can be sent. If more than "
"six are specified. Six are randomly chosen for each stake. Only active "
"if -enablesidestaking is set.",
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
"if -enablesidestaking is set. These settings are overridden if "
"-sidestakeaddresses and -stakestakeallocations are set.",
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-sidestakeaddresses=<address1,address2,...,addressN>", "Sidestake destination entry. There can be as many "
"specified as desired. Only six per stake can be sent. If more than "
"six are specified. Six are randomly chosen for each stake. Only active "
"if -enablesidestaking is set. If set along with -sidestakeallocations "
"overrides the -sidestake entries.",
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-sidestakeallocations=percent1,percent2,...,percentN>", "Sidestake allocation entry. There can be as many "
"specified as desired. Only six per stake can be sent. If more than "
"six are specified. Six are randomly chosen for each stake. Only active "
"if -enablesidestaking is set. If set along with -sidestakeaddresses "
"overrides the -sidestake entries.",
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-enablestakesplit", "Enable unspent output spitting when staking to optimize staking efficiency "
"(default: 0",
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-stakingefficiency=<percent>", "Specify target staking efficiency for stake splitting (default: 90, "
"clamped to [75, 98])",
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
argsman.AddArg("-minstakesplitvalue=<n>", strprintf("Specify minimum output value for post split output when stake "
"splitting (default: %" PRId64 "GRC)", MIN_STAKE_SPLIT_VALUE_GRC),
ArgsManager::ALLOW_ANY, OptionsCategory::STAKING);
ArgsManager::ALLOW_ANY | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);

// Scraper
argsman.AddArg("-scraper", "Activate scraper for statistics downloads. This will only work if the node has a wallet "
Expand Down
180 changes: 106 additions & 74 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,14 +1156,14 @@ bool IsMiningAllowed(CWallet *pwallet)
{
bool status = true;

if(pwallet->IsLocked())
if (pwallet->IsLocked())
{
LOCK(g_miner_status.lock);
g_miner_status.SetReasonNotStaking(GRC::MinerStatus::WALLET_LOCKED);
status = false;
}

if(fDevbuildCripple)
if (fDevbuildCripple)
{
LOCK(g_miner_status.lock);
g_miner_status.SetReasonNotStaking(GRC::MinerStatus::TESTNET_ONLY);
Expand All @@ -1177,92 +1177,120 @@ bool IsMiningAllowed(CWallet *pwallet)
status = false;
}

if (!gArgs.GetBoolArg("-staking", true))
{
LOCK(g_miner_status.lock);
g_miner_status.SetReasonNotStaking(GRC::MinerStatus::DISABLED_BY_CONFIGURATION);
status = false;
}

return status;
}

// This function parses the config file for the directives for side staking. It is used
// in StakeMiner for the miner loop and also called by rpc getmininginfo.
bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc)
SideStakeAlloc GetSideStakingStatusAndAlloc()
{
vector<string> vSubParam;
std::string sAddress;
double dAllocation = 0.0;
SideStakeAlloc vSideStakeAlloc;
std::vector<std::pair<std::string, std::string>> raw_vSideStakeAlloc;
double dSumAllocation = 0.0;

bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking");
LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableSideStaking = %u", fEnableSideStaking);
// Parse destinations and allocations. We don't need to worry about any that are rejected other than a warning
// message, because any unallocated rewards will go back into the coinstake output(s).

// If side staking is enabled, parse destinations and allocations. We don't need to worry about any that are rejected
// other than a warning message, because any unallocated rewards will go back into the coinstake output(s).
if (fEnableSideStaking)
// If -sidestakeaddresses and -sidestakeallocations is set in either the config file or the r-w settings file
// and the settings are not empty and they are the same size, this will take precedence over the multiple entry
// -sidestake format.
std::vector<std::string> addresses;
std::vector<std::string> allocations;

ParseString(gArgs.GetArg("-sidestakeaddresses", ""), ',', addresses);
ParseString(gArgs.GetArg("-sidestakeallocations", ""), ',', allocations);

if (addresses.size() != allocations.size())
{
if (gArgs.GetArgs("-sidestake").size())
LogPrintf("WARN: %s: Malformed new style sidestaking configuration entries. Reverting to original format.",
__func__);
}

if (addresses.size() && addresses.size() == allocations.size())
{
for (unsigned int i = 0; i < addresses.size(); ++i)
{
raw_vSideStakeAlloc.push_back(std::make_pair(addresses[i], allocations[i]));
}
}
else if (gArgs.GetArgs("-sidestake").size())
{
for (auto const& sSubParam : gArgs.GetArgs("-sidestake"))
{
for (auto const& sSubParam : gArgs.GetArgs("-sidestake"))
std::vector<std::string> vSubParam;

ParseString(sSubParam, ',', vSubParam);
if (vSubParam.size() != 2)
{
ParseString(sSubParam, ',', vSubParam);
if (vSubParam.size() != 2)
{
LogPrintf("WARN: StakeMiner: Incompletely SideStake Allocation specified. Skipping SideStake entry.");
vSubParam.clear();
continue;
}
LogPrintf("WARN: %s: Incomplete SideStake Allocation specified. Skipping SideStake entry.", __func__);
continue;
}

sAddress = vSubParam[0];
raw_vSideStakeAlloc.push_back(std::make_pair(vSubParam[0], vSubParam[1]));
}
}

CBitcoinAddress address(sAddress);
if (!address.IsValid())
{
LogPrintf("WARN: StakeMiner: ignoring sidestake invalid address %s.", sAddress.c_str());
vSubParam.clear();
continue;
}
for (auto const& entry : raw_vSideStakeAlloc)
{
std::string sAddress;
double dAllocation = 0.0;

try
{
dAllocation = stof(vSubParam[1]) / 100.0;
}
catch(...)
{
LogPrintf("WARN: StakeMiner: Invalid allocation provided. Skipping allocation.");
vSubParam.clear();
continue;
}
sAddress = entry.first;

if (dAllocation <= 0)
{
LogPrintf("WARN: StakeMiner: Negative or zero allocation provided. Skipping allocation.");
vSubParam.clear();
continue;
}
CBitcoinAddress address(sAddress);
if (!address.IsValid())
{
LogPrintf("WARN: %s: ignoring sidestake invalid address %s.", __func__, sAddress);
continue;
}

// The below will stop allocations if someone has made a mistake and the total adds up to more than 100%.
// Note this same check is also done in SplitCoinStakeOutput, but it needs to be done here for two reasons:
// 1. Early alertment in the debug log, rather than when the first kernel is found, and 2. When the UI is
// hooked up, the SideStakeAlloc vector will be filled in by other than reading the config file and will
// skip the above code.
dSumAllocation += dAllocation;
if (dSumAllocation > 1.0)
{
LogPrintf("WARN: StakeMiner: allocation percentage over 100\%, ending sidestake allocations.");
break;
}
try
{
dAllocation = stof(entry.second) / 100.0;
}
catch (...)
{
LogPrintf("WARN: %s: Invalid allocation provided. Skipping allocation.", __func__);
continue;
}

vSideStakeAlloc.push_back(std::pair<std::string, double>(sAddress, dAllocation));
LogPrint(BCLog::LogFlags::MINER, "StakeMiner: SideStakeAlloc Address %s, Allocation %f",
sAddress.c_str(), dAllocation);
if (dAllocation <= 0)
{
LogPrintf("WARN: %s: Negative or zero allocation provided. Skipping allocation.", __func__);
continue;
}

vSubParam.clear();
}
// The below will stop allocations if someone has made a mistake and the total adds up to more than 100%.
// Note this same check is also done in SplitCoinStakeOutput, but it needs to be done here for two reasons:
// 1. Early alertment in the debug log, rather than when the first kernel is found, and 2. When the UI is
// hooked up, the SideStakeAlloc vector will be filled in by other than reading the config file and will
// skip the above code.
dSumAllocation += dAllocation;
if (dSumAllocation > 1.0)
{
LogPrintf("WARN: %s: allocation percentage over 100\%, ending sidestake allocations.", __func__);
break;
}
// If we get here and dSumAllocation is zero then the enablesidestaking flag was set, but no VALID distribution
// was provided in the config file, so warn in the debug log.
if (!dSumAllocation)
LogPrintf("WARN: StakeMiner: enablesidestaking was set in config but nothing has been allocated for"
" distribution!");

vSideStakeAlloc.push_back(std::pair<std::string, double>(sAddress, dAllocation));
LogPrint(BCLog::LogFlags::MINER, "INFO: %s: SideStakeAlloc Address %s, Allocation %f",
__func__, sAddress, dAllocation);
}

return fEnableSideStaking;
// If we get here and dSumAllocation is zero then the enablesidestaking flag was set, but no VALID distribution
// was provided in the config file, so warn in the debug log.
if (!dSumAllocation)
LogPrintf("WARN: %s: enablesidestaking was set in config but nothing has been allocated for"
" distribution!");

return vSideStakeAlloc;
}

// This function parses the config file for the directives for stake splitting. It is used
Expand Down Expand Up @@ -1318,17 +1346,21 @@ void StakeMiner(CWallet *pwallet)
int64_t nMinStakeSplitValue = 0;
double dEfficiency = 0;
int64_t nDesiredStakeOutputValue = 0;
SideStakeAlloc vSideStakeAlloc = {};

// nMinStakeSplitValue and dEfficiency are out parameters.
bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeOutputValue);

// vSideStakeAlloc is an out parameter.
bool fEnableSideStaking = GetSideStakingStatusAndAlloc(vSideStakeAlloc);
SideStakeAlloc vSideStakeAlloc;

while (!fShutdown)
{
//wait for next round
// nMinStakeSplitValue and dEfficiency are out parameters.
bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeOutputValue);

bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking");

LogPrint(BCLog::LogFlags::MINER, "StakeMiner: fEnableSideStaking = %u", fEnableSideStaking);

// vSideStakeAlloc is an out parameter.
if (fEnableSideStaking) vSideStakeAlloc = GetSideStakingStatusAndAlloc();

// wait for next round
MilliSleep(nMinerSleep);

g_timer.InitTimer("miner", LogInstance().WillLogCategory(BCLog::LogFlags::MISC));
Expand Down
2 changes: 1 addition & 1 deletion src/miner.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ std::optional<CWalletTx> GetLastStake(CWallet& wallet);

void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStakeSplit, bool &fEnableSideStaking, SideStakeAlloc &vSideStakeAlloc, double &dEfficiency);
unsigned int GetNumberOfStakeOutputs(int64_t &nValue, int64_t &nMinStakeSplitValue, double &dEfficiency);
bool GetSideStakingStatusAndAlloc(SideStakeAlloc& vSideStakeAlloc);
SideStakeAlloc GetSideStakingStatusAndAlloc();
bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficiency, int64_t& nDesiredStakeOutputValue);

#endif // NOVACOIN_MINER_H
8 changes: 2 additions & 6 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2032,12 +2032,8 @@ void StartNode(void* parg)
if (!netThreads->createThread(ThreadDumpAddress,NULL,"ThreadDumpAddress"))
LogPrintf("Error: createThread(ThreadDumpAddress) failed");

// Mine proof-of-stake blocks in the background
if (!gArgs.GetBoolArg("-staking", true))
LogPrintf("Staking disabled");
else
if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner"))
LogPrintf("Error: createThread(ThreadStakeMiner) failed");
if (!netThreads->createThread(ThreadStakeMiner,pwalletMain,"ThreadStakeMiner"))
LogPrintf("Error: createThread(ThreadStakeMiner) failed");
}

bool StopNode()
Expand Down
18 changes: 12 additions & 6 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,16 +691,22 @@ void BitcoinGUI::createToolBars()
//12-21-2015 Prevent Lock from falling off the page
frameBlocksLayout->addStretch();

QTimer *timerStakingIcon = new QTimer(labelStakingIcon);
connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(updateStakingIcon()));
timerStakingIcon->start(MODEL_UPDATE_DELAY);

// Instead of calling updateStakingIcon here, simply set the icon to staking off.
// This is to prevent problems since this GUI code can initialize before the core.
labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet));

if (gArgs.GetBoolArg("-staking", true))
{
QTimer *timerStakingIcon = new QTimer(labelStakingIcon);
connect(timerStakingIcon, SIGNAL(timeout()), this, SLOT(updateStakingIcon()));
timerStakingIcon->start(MODEL_UPDATE_DELAY);
// Instead of calling updateStakingIcon here, simply set the icon to staking off.
// This is to prevent problems since this GUI code can initialize before the core.
labelStakingIcon->setPixmap(GRC::ScaleStatusIcon(this, ":/icons/status_staking_no_" + sSheet));
labelStakingIcon->setToolTip(tr("Not staking: Miner is not initialized."));
}
else
{
labelStakingIcon->setToolTip(tr("Not staking: Disabled by configuration."));
}

statusBar()->addPermanentWidget(frameBlocks);

Expand Down
Loading