Skip to content

Commit

Permalink
Change miner to use ActiveSideStakeEntries()
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Oct 2, 2023
1 parent a8024cf commit 64764db
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 128 deletions.
28 changes: 22 additions & 6 deletions src/gridcoin/sidestake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,17 @@ SideStake::SideStake(CBitcoinAddressForStorage address, double allocation)
, m_status(SideStakeStatus::UNKNOWN)
{}

SideStake::SideStake(CBitcoinAddressForStorage address, double allocation, int64_t timestamp, uint256 hash)
SideStake::SideStake(CBitcoinAddressForStorage address,
double allocation,
int64_t timestamp,
uint256 hash,
SideStakeStatus status)
: m_key(address)
, m_allocation(allocation)
, m_timestamp(timestamp)
, m_hash(hash)
, m_previous_hash()
, m_status(SideStakeStatus::UNKNOWN)
, m_status(status)
{}

bool SideStake::WellFormed() const
Expand Down Expand Up @@ -145,10 +149,13 @@ SideStakePayload::SideStakePayload(uint32_t version)
{
}

SideStakePayload::SideStakePayload(const uint32_t version, CBitcoinAddressForStorage key, double value, SideStakeStatus status)
SideStakePayload::SideStakePayload(const uint32_t version,
CBitcoinAddressForStorage key,
double value,
SideStakeStatus status)
: IContractPayload()
, m_version(version)
, m_entry(SideStake(key, value, status))
, m_entry(SideStake(key, value, 0, uint256{}, status))
{
}

Expand All @@ -172,10 +179,15 @@ const SideStakeRegistry::SideStakeMap& SideStakeRegistry::SideStakeEntries() con
return m_sidestake_entries;
}

const std::vector<SideStake_ptr> SideStakeRegistry::ActiveSideStakeEntries() const
const std::vector<SideStake_ptr> SideStakeRegistry::ActiveSideStakeEntries()
{
std::vector<SideStake_ptr> sidestakes;

// 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();

for (const auto& entry : m_sidestake_entries)
{
if (entry.second->m_status == SideStakeStatus::ACTIVE || entry.second->m_status == SideStakeStatus::MANDATORY) {
Expand Down Expand Up @@ -547,7 +559,11 @@ void SideStakeRegistry::LoadLocalSideStakesFromConfig()
break;
}

SideStake sidestake(static_cast<CBitcoinAddressForStorage>(address), dAllocation, 0, uint256{});
SideStake sidestake(static_cast<CBitcoinAddressForStorage>(address),
dAllocation,
0,
uint256{},
SideStakeStatus::ACTIVE);

// This will add or update (replace) a non-contract entry in the registry for the local sidestake.
NonContractAdd(sidestake);
Expand Down
5 changes: 3 additions & 2 deletions src/gridcoin/sidestake.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,9 @@ class SideStake
//! \param allocation
//! \param timestamp
//! \param hash
//! \param status
//!
SideStake(CBitcoinAddressForStorage address, double allocation, int64_t timestamp, uint256 hash);
SideStake(CBitcoinAddressForStorage address, double allocation, int64_t timestamp, uint256 hash, SideStakeStatus status);

//!
//! \brief Determine whether a sidestake contains each of the required elements.
Expand Down Expand Up @@ -374,7 +375,7 @@ class SideStakeRegistry : public IContractHandler
//!
//! \return A vector of smart pointers to sidestake entries.
//!
const std::vector<SideStake_ptr> ActiveSideStakeEntries() const;
const std::vector<SideStake_ptr> ActiveSideStakeEntries();

//!
//! \brief Get the current sidestake entry for the specified key string.
Expand Down
131 changes: 16 additions & 115 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,24 +922,26 @@ void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStake
(iterSideStake != vSideStakeAlloc.end()) && (nOutputsUsed <= nMaxSideStakeOutputs);
++iterSideStake)
{
CBitcoinAddress address(iterSideStake->first);
CBitcoinAddress& address = iterSideStake->get()->m_key;
if (!address.IsValid())
{
LogPrintf("WARN: SplitCoinStakeOutput: ignoring sidestake invalid address %s.",
iterSideStake->first.c_str());
iterSideStake->get()->m_key.ToString());
continue;
}

// Do not process a distribution that would result in an output less than 1 CENT. This will flow back into
// the coinstake below. Prevents dust build-up.
if (nReward * iterSideStake->second < CENT)
if (nReward * iterSideStake->get()->m_allocation < CENT)
{
LogPrintf("WARN: SplitCoinStakeOutput: distribution %f too small to address %s.",
CoinToDouble(nReward * iterSideStake->second), iterSideStake->first.c_str());
CoinToDouble(nReward * iterSideStake->get()->m_allocation),
iterSideStake->get()->m_key.ToString()
);
continue;
}

if (dSumAllocation + iterSideStake->second > 1.0)
if (dSumAllocation + iterSideStake->get()->m_allocation > 1.0)
{
LogPrintf("WARN: SplitCoinStakeOutput: allocation percentage over 100 percent, "
"ending sidestake allocations.");
Expand All @@ -962,20 +964,23 @@ void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStake
int64_t nSideStake = 0;

// For allocations ending less than 100% assign using sidestake allocation.
if (dSumAllocation + iterSideStake->second < 1.0)
nSideStake = nReward * iterSideStake->second;
if (dSumAllocation + iterSideStake->get()->m_allocation < 1.0)
nSideStake = nReward * iterSideStake->get()->m_allocation;
// We need to handle the final sidestake differently in the case it brings the total allocation up to 100%,
// because testing showed in corner cases the output return to the staking address could be off by one Halford.
else if (dSumAllocation + iterSideStake->second == 1.0)
else if (dSumAllocation + iterSideStake->get()->m_allocation == 1.0)
// Simply assign the special case final nSideStake the remaining output value minus input value to ensure
// a match on the output flowing down.
nSideStake = nRemainingStakeOutputValue - nInputValue;

blocknew.vtx[1].vout.push_back(CTxOut(nSideStake, SideStakeScriptPubKey));

LogPrintf("SplitCoinStakeOutput: create sidestake UTXO %i value %f to address %s",
nOutputsUsed, CoinToDouble(nReward * iterSideStake->second), iterSideStake->first.c_str());
dSumAllocation += iterSideStake->second;
nOutputsUsed,
CoinToDouble(nReward * iterSideStake->get()->m_allocation),
iterSideStake->get()->m_key.ToString()
);
dSumAllocation += iterSideStake->get()->m_allocation;
nRemainingStakeOutputValue -= nSideStake;
nOutputsUsed++;
}
Expand Down Expand Up @@ -1249,110 +1254,6 @@ bool IsMiningAllowed(CWallet *pwallet)
return g_miner_status.StakingEnabled();
}

// 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 getstakinginfo.
SideStakeAlloc GetSideStakingStatusAndAlloc()
{
SideStakeAlloc vSideStakeAlloc;
std::vector<std::pair<std::string, std::string>> raw_vSideStakeAlloc;
double dSumAllocation = 0.0;

// 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 -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())
{
for (auto const& sSubParam : gArgs.GetArgs("-sidestake"))
{
std::vector<std::string> vSubParam;

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

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

for (auto const& entry : raw_vSideStakeAlloc)
{
std::string sAddress;
double dAllocation = 0.0;

sAddress = entry.first;

CBitcoinAddress address(sAddress);
if (!address.IsValid())
{
LogPrintf("WARN: %s: ignoring sidestake invalid address %s.", __func__, sAddress);
continue;
}

if (!ParseDouble(entry.second, &dAllocation))
{
LogPrintf("WARN: %s: Invalid allocation %s provided. Skipping allocation.", __func__, entry.second);
continue;
}

dAllocation /= 100.0;

if (dAllocation <= 0)
{
LogPrintf("WARN: %s: Negative or zero allocation provided. Skipping allocation.", __func__);
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: %s: allocation percentage over 100 percent, ending sidestake allocations.", __func__);
break;
}

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

// 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!", __func__);

return vSideStakeAlloc;
}

// This function parses the config file for the directives for stake splitting. It is used
// in StakeMiner for the miner loop and also called by rpc getstakinginfo.
bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficiency, int64_t& nDesiredStakeOutputValue)
Expand Down Expand Up @@ -1421,7 +1322,7 @@ void StakeMiner(CWallet *pwallet)
LogPrint(BCLog::LogFlags::MINER, "INFO: %s: fEnableSideStaking = %u", __func__, fEnableSideStaking);

// vSideStakeAlloc is an out parameter.
if (fEnableSideStaking) vSideStakeAlloc = GetSideStakingStatusAndAlloc();
if (fEnableSideStaking) vSideStakeAlloc = GRC::GetSideStakeRegistry().ActiveSideStakeEntries();

// wait for next round
if (!MilliSleep(nMinerSleep)) return;
Expand Down
5 changes: 3 additions & 2 deletions src/miner.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
#define BITCOIN_MINER_H

#include "main.h"
#include "gridcoin/sidestake.h"


class CWallet;
class CWalletTx;

typedef std::vector< std::pair<std::string, double> > SideStakeAlloc;
typedef std::vector<GRC::SideStake_ptr> SideStakeAlloc;

extern unsigned int nMinerSleep;

Expand All @@ -24,7 +26,6 @@ static const int64_t MIN_STAKE_SPLIT_VALUE_GRC = 800;
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);
SideStakeAlloc GetSideStakingStatusAndAlloc();
bool GetStakeSplitStatusAndParams(int64_t& nMinStakeSplitValue, double& dEfficiency, int64_t& nDesiredStakeOutputValue);

bool CreateMRCRewards(CBlock &blocknew,
Expand Down
6 changes: 3 additions & 3 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ UniValue getstakinginfo(const UniValue& params, bool fHelp)

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

if (fEnableSideStaking) vSideStakeAlloc = GetSideStakingStatusAndAlloc();
if (fEnableSideStaking) vSideStakeAlloc = GRC::GetSideStakeRegistry().ActiveSideStakeEntries();

stakesplitting.pushKV("stake-splitting-enabled", fEnableStakeSplit);
if (fEnableStakeSplit)
Expand All @@ -115,8 +115,8 @@ UniValue getstakinginfo(const UniValue& params, bool fHelp)
{
for (const auto& alloc : vSideStakeAlloc)
{
sidestakingalloc.pushKV("address", alloc.first);
sidestakingalloc.pushKV("allocation-pct", alloc.second * 100);
sidestakingalloc.pushKV("address", alloc->m_key.ToString());
sidestakingalloc.pushKV("allocation-pct", alloc->m_allocation * 100);

vsidestakingalloc.push_back(sidestakingalloc);
}
Expand Down

0 comments on commit 64764db

Please sign in to comment.