Skip to content

Commit

Permalink
Implement dynamic sidestaking entries
Browse files Browse the repository at this point in the history
This commit introduces two new settings for sidestaking,
-sidestakeaddresses=address1,address2,..., addressN
-sidestakeallocations=alloc1,alloc2,...,allocN

These may be used in both the config file and with changesettings as an alternative to the original
sidestake=address,allocation entries. If the new entries are present, are not empty, and are correctly
filled out then they will take precedence over the older entries. The reason this is necessary is that
the read-write settings file does not support multiple instances of the same setting, so a restructure
was required to support the r-w override.
  • Loading branch information
jamescowens committed Jun 13, 2021
1 parent ead9e7f commit cfd1321
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 77 deletions.
17 changes: 15 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,21 @@ void SetupServerArgs()
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 | ArgsManager::IMMEDIATE_EFFECT, OptionsCategory::STAKING);
Expand Down
169 changes: 97 additions & 72 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,87 +1189,108 @@ bool IsMiningAllowed(CWallet *pwallet)

// 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())
{
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())
{
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 (std::exception& e)
{
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 @@ -1325,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
5 changes: 3 additions & 2 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ UniValue getmininginfo(const UniValue& params, bool fHelp)
// nMinStakeSplitValue, dEfficiency, and nDesiredStakeSplitValue are out parameters.
bool fEnableStakeSplit = GetStakeSplitStatusAndParams(nMinStakeSplitValue, dEfficiency, nDesiredStakeSplitValue);

// vSideStakeAlloc is an out parameter.
bool fEnableSideStaking = GetSideStakingStatusAndAlloc(vSideStakeAlloc);
bool fEnableSideStaking = gArgs.GetBoolArg("-enablesidestaking");

vSideStakeAlloc = GetSideStakingStatusAndAlloc();

stakesplitting.pushKV("stake-splitting-enabled", fEnableStakeSplit);
if (fEnableStakeSplit)
Expand Down

0 comments on commit cfd1321

Please sign in to comment.