Skip to content

Commit

Permalink
Changes to separate local and mandatory sidestakes classwise (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Dec 25, 2023
1 parent 5577e2f commit b6bd5d1
Show file tree
Hide file tree
Showing 8 changed files with 533 additions and 206 deletions.
326 changes: 237 additions & 89 deletions src/gridcoin/sidestake.cpp

Large diffs are not rendered by default.

260 changes: 205 additions & 55 deletions src/gridcoin/sidestake.h

Large diffs are not rendered by default.

26 changes: 14 additions & 12 deletions src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -921,26 +921,28 @@ void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStake
(iterSideStake != vSideStakeAlloc.end()) && (nOutputsUsed <= nMaxSideStakeOutputs);
++iterSideStake)
{
CBitcoinAddress& address = iterSideStake->get()->m_address;
CBitcoinAddress address = iterSideStake->get()->GetAddress();
double allocation = iterSideStake->get()->GetAllocation();

if (!address.IsValid())
{
LogPrintf("WARN: SplitCoinStakeOutput: ignoring sidestake invalid address %s.",
iterSideStake->get()->m_address.ToString());
address.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->get()->m_allocation < CENT)
if (nReward * allocation < CENT)
{
LogPrintf("WARN: SplitCoinStakeOutput: distribution %f too small to address %s.",
CoinToDouble(nReward * iterSideStake->get()->m_allocation),
iterSideStake->get()->m_address.ToString()
CoinToDouble(nReward * allocation),
address.ToString()
);
continue;
}

if (dSumAllocation + iterSideStake->get()->m_allocation > 1.0)
if (dSumAllocation + allocation > 1.0)
{
LogPrintf("WARN: SplitCoinStakeOutput: allocation percentage over 100 percent, "
"ending sidestake allocations.");
Expand All @@ -963,11 +965,11 @@ 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->get()->m_allocation < 1.0)
nSideStake = nReward * iterSideStake->get()->m_allocation;
if (dSumAllocation + allocation < 1.0)
nSideStake = nReward * 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->get()->m_allocation == 1.0)
else if (dSumAllocation + 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;
Expand All @@ -976,10 +978,10 @@ void SplitCoinStakeOutput(CBlock &blocknew, int64_t &nReward, bool &fEnableStake

LogPrintf("SplitCoinStakeOutput: create sidestake UTXO %i value %f to address %s",
nOutputsUsed,
CoinToDouble(nReward * iterSideStake->get()->m_allocation),
iterSideStake->get()->m_address.ToString()
CoinToDouble(nReward * allocation),
address.ToString()
);
dSumAllocation += iterSideStake->get()->m_allocation;
dSumAllocation += allocation;
nRemainingStakeOutputValue -= nSideStake;
nOutputsUsed++;
}
Expand Down
3 changes: 1 addition & 2 deletions src/qt/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,7 @@ void OptionsDialog::sidestakeSelectionChanged()
if (indexes.size() > 1) {
ui->pushButtonEditSideStake->setEnabled(false);
ui->pushButtonDeleteSideStake->setEnabled(false);
} else if (static_cast<GRC::SideStake*>(indexes.at(0).internalPointer())->m_status
== GRC::SideStakeStatus::MANDATORY) {
} else if (static_cast<GRC::SideStake*>(indexes.at(0).internalPointer())->IsMandatory()) {
ui->pushButtonEditSideStake->setEnabled(false);
ui->pushButtonDeleteSideStake->setEnabled(false);
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/qt/researcher/researchermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ BeaconStatus MapAdvertiseBeaconError(const BeaconError error)
case BeaconError::PENDING: return BeaconStatus::PENDING;
case BeaconError::TX_FAILED: return BeaconStatus::ERROR_TX_FAILED;
case BeaconError::WALLET_LOCKED: return BeaconStatus::ERROR_WALLET_LOCKED;
}
case BeaconError::ALEADY_IN_MEMPOOL: break;
}

assert(false); // Suppress warning
}
Expand Down
101 changes: 64 additions & 37 deletions src/qt/sidestaketablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,35 @@ bool SideStakeLessThan::operator()(const GRC::SideStake& left, const GRC::SideSt
std::swap(pLeft, pRight);
}

int left_status, right_status;

if (pLeft->IsMandatory()) {
left_status = static_cast<int>(pLeft->m_mandatory_sidestake_ptr->m_status.Value());
} else {
// For purposes of comparison, the enum value for local sidestake is shifted by the max entry of the mandatory
// status enum.
left_status = static_cast<int>(pLeft->m_local_sidestake_ptr->m_status.Value())
+ static_cast<int>(GRC::MandatorySideStake::MandatorySideStakeStatus::OUT_OF_BOUND);
}

if (pRight->IsMandatory()) {
right_status = static_cast<int>(pRight->m_mandatory_sidestake_ptr->m_status.Value());
} else {
// For purposes of comparison, the enum value for local sidestake is shifted by the max entry of the mandatory
// status enum.
right_status = static_cast<int>(pRight->m_local_sidestake_ptr->m_status.Value())
+ static_cast<int>(GRC::MandatorySideStake::MandatorySideStakeStatus::OUT_OF_BOUND);
}

switch (static_cast<SideStakeTableModel::ColumnIndex>(m_column)) {
case SideStakeTableModel::Address:
return pLeft->m_address < pRight->m_address;
return pLeft->GetAddress() < pRight->GetAddress();
case SideStakeTableModel::Allocation:
return pLeft->m_allocation < pRight->m_allocation;
return pLeft->GetAllocation() < pRight->GetAllocation();
case SideStakeTableModel::Description:
return pLeft->m_description.compare(pRight->m_description) < 0;
return pLeft->GetDescription().compare(pRight->GetDescription()) < 0;
case SideStakeTableModel::Status:
return pLeft->m_status < pRight->m_status;
return left_status < right_status;
} // no default case, so the compiler can warn about missing cases
assert(false);
}
Expand Down Expand Up @@ -84,7 +104,6 @@ class SideStakeTablePriv

return nullptr;
}

};

SideStakeTableModel::SideStakeTableModel(OptionsModel* parent)
Expand Down Expand Up @@ -131,11 +150,11 @@ QVariant SideStakeTableModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (column) {
case Address:
return QString::fromStdString(rec->m_address.ToString());
return QString::fromStdString(rec->GetAddress().ToString());
case Allocation:
return rec->m_allocation * 100.0;
return rec->GetAllocation() * 100.0;
case Description:
return QString::fromStdString(rec->m_description);
return QString::fromStdString(rec->GetDescription());
case Status:
return QString::fromStdString(rec->StatusToString());
} // no default case, so the compiler can warn about missing cases
Expand Down Expand Up @@ -181,8 +200,7 @@ bool SideStakeTableModel::setData(const QModelIndex &index, const QVariant &valu
CBitcoinAddress address;
address.SetString(value.toString().toStdString());


if (rec->m_address == address) {
if (rec->GetAddress() == address) {
m_edit_status = NO_CHANGES;
return false;
} else if (!address.IsValid()) {
Expand All @@ -209,15 +227,29 @@ bool SideStakeTableModel::setData(const QModelIndex &index, const QVariant &valu
// Save the original local sidestake (also in the core).
GRC::SideStake orig_sidestake = *rec;

CBitcoinAddress orig_address = rec->GetAddress();
double orig_allocation = rec->GetAllocation();
std::string orig_description = rec->GetDescription();

// We really only need the local sidestake status enum here
GRC::LocalSideStake::Status orig_status;

if (!rec->IsMandatory()) {
orig_status = rec->m_local_sidestake_ptr->m_status;
}

for (const auto& entry : registry.ActiveSideStakeEntries(false, true)) {
if (entry->m_address == orig_sidestake.m_address) {
CBitcoinAddress address = entry->GetAddress();
double allocation = entry->GetAllocation();

if (address == orig_address) {
continue;
}

prior_total_allocation += entry->m_allocation * 100.0;
prior_total_allocation += allocation * 100.0;
}

if (rec->m_allocation * 100.0 == value.toDouble()) {
if (orig_allocation * 100.0 == value.toDouble()) {
m_edit_status = NO_CHANGES;
return false;
}
Expand All @@ -233,15 +265,13 @@ bool SideStakeTableModel::setData(const QModelIndex &index, const QVariant &valu
}

// Delete the original sidestake
registry.NonContractDelete(orig_sidestake.m_address, false);
registry.NonContractDelete(orig_address, false);

// Add back the sidestake with the modified allocation
registry.NonContractAdd(GRC::SideStake(orig_sidestake.m_address,
registry.NonContractAdd(GRC::LocalSideStake(orig_address,
value.toDouble() / 100.0,
orig_sidestake.m_description,
int64_t {0},
uint256 {},
orig_sidestake.m_status.Value()), true);
orig_description,
orig_status.Value()), true);

break;
}
Expand All @@ -250,7 +280,7 @@ bool SideStakeTableModel::setData(const QModelIndex &index, const QVariant &valu
std::string orig_value = value.toString().toStdString();
std::string san_value = SanitizeString(orig_value, SAFE_CHARS_CSV);

if (rec->m_description == orig_value) {
if (rec->GetDescription() == orig_value) {
m_edit_status = NO_CHANGES;
return false;
}
Expand All @@ -264,15 +294,13 @@ bool SideStakeTableModel::setData(const QModelIndex &index, const QVariant &valu
GRC::SideStake orig_sidestake = *rec;

// Delete the original sidestake
registry.NonContractDelete(orig_sidestake.m_address, false);
registry.NonContractDelete(orig_sidestake.GetAddress(), false);

// Add back the sidestake with the modified allocation
registry.NonContractAdd(GRC::SideStake(orig_sidestake.m_address,
orig_sidestake.m_allocation,
san_value,
int64_t {0},
uint256 {},
orig_sidestake.m_status.Value()), true);
registry.NonContractAdd(GRC::LocalSideStake(orig_sidestake.GetAddress(),
orig_sidestake.GetAllocation(),
san_value,
orig_sidestake.m_local_sidestake_ptr->m_status.Value()), true);

break;
}
Expand Down Expand Up @@ -308,7 +336,8 @@ Qt::ItemFlags SideStakeTableModel::flags(const QModelIndex &index) const

Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;

if (rec->m_status == GRC::SideStakeStatus::ACTIVE
if (!rec->IsMandatory()
&& rec->m_local_sidestake_ptr->m_status == GRC::LocalSideStake::LocalSideStakeStatus::ACTIVE
&& (index.column() == Allocation || index.column() == Description)) {
retval |= Qt::ItemIsEditable;
}
Expand Down Expand Up @@ -350,7 +379,7 @@ QString SideStakeTableModel::addRow(const QString &address, const QString &alloc

// Get total allocation of all active/mandatory sidestake entries
for (const auto& entry : registry.ActiveSideStakeEntries(false, true)) {
prior_total_allocation += entry->m_allocation * 100.0;
prior_total_allocation += entry->GetAllocation() * 100.0;
}

if (!core_local_sidestake.empty()) {
Expand All @@ -376,12 +405,10 @@ QString SideStakeTableModel::addRow(const QString &address, const QString &alloc
return QString();
}

registry.NonContractAdd(GRC::SideStake(sidestake_address,
sidestake_allocation,
sanitized_description,
int64_t {0},
uint256 {},
GRC::SideStakeStatus::ACTIVE));
registry.NonContractAdd(GRC::LocalSideStake(sidestake_address,
sidestake_allocation,
sanitized_description,
GRC::LocalSideStake::LocalSideStakeStatus::ACTIVE));

updateSideStakeTableModel();

Expand All @@ -393,14 +420,14 @@ bool SideStakeTableModel::removeRows(int row, int count, const QModelIndex &pare
Q_UNUSED(parent);
GRC::SideStake* rec = m_priv->index(row);

if(count != 1 || !rec || rec->m_status == GRC::SideStakeStatus::MANDATORY)
if (count != 1 || !rec || rec->IsMandatory())
{
// Can only remove one row at a time, and cannot remove rows not in model.
// Also refuse to remove mandatory sidestakes.
return false;
}

GRC::GetSideStakeRegistry().NonContractDelete(rec->m_address);
GRC::GetSideStakeRegistry().NonContractDelete(rec->GetAddress());

updateSideStakeTableModel();

Expand Down
16 changes: 8 additions & 8 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2496,14 +2496,14 @@ UniValue addkey(const UniValue& params, bool fHelp)
}

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
description, // Sidestake description
GRC::SideStakeStatus::MANDATORY // sidestake status
);
contract_version, // Contract version number (3+)
action, // Contract action
uint32_t {1}, // Contract payload version number
sidestake_address, // Sidestake address
allocation, // Sidestake allocation
description, // Sidestake description
GRC::MandatorySideStake::MandatorySideStakeStatus::MANDATORY // sidestake status
);
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Sidestake contracts are not valid for block version less than v13.");
}
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ UniValue getstakinginfo(const UniValue& params, bool fHelp)
// sidestakes are always included.
for (const auto& alloc : vSideStakeAlloc)
{
sidestakingalloc.pushKV("address", alloc->m_address.ToString());
sidestakingalloc.pushKV("allocation_pct", alloc->m_allocation * 100);
sidestakingalloc.pushKV("address", alloc->GetAddress().ToString());
sidestakingalloc.pushKV("allocation_pct", alloc->GetAllocation() * 100);
sidestakingalloc.pushKV("status", alloc->StatusToString());

vsidestakingalloc.push_back(sidestakingalloc);
Expand Down

0 comments on commit b6bd5d1

Please sign in to comment.