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, poll: Implement poll expiration reminders #2716

Merged
61 changes: 55 additions & 6 deletions src/gridcoin/voting/result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -864,18 +864,38 @@ class VoteCounter
{
CTransaction tx;

if (!m_txdb.ReadDiskTx(txid, tx)) {
LogPrint(LogFlags::VOTE, "%s: failed to read vote tx", __func__);
bool read_tx_success = false;

// This is very ugly. In testing for implement poll expiration reminders PR2716, there is an issue with ReadDiskTx
// on very fast machines, where upon receipt of a vote on an existing poll, the poll builder tests for the transaction
// BEFORE it is committed to disk. This retry loop is essentially zero overhead for an immediate success, but does
// up to 10 tries over up to 2.5 seconds total to "wait" for the transaction to appear in leveldb.
for (unsigned int i = 0; i < 10; ++i) {
if (m_txdb.ReadDiskTx(txid, tx)) {
read_tx_success = true;
break;
} else {
LogPrintf("WARN: %s: failed to read vote tx in try %u", __func__, i + 1);
}

if (!MilliSleep(250)) {
// Interrupt with throw if MilliSleep interrupted by op sys signal.
throw InvalidVoteError();
}
}

if (!read_tx_success) {
LogPrintf("WARN: %s: failed to read vote tx after 10 tries", __func__);
jamescowens marked this conversation as resolved.
Show resolved Hide resolved
throw InvalidVoteError();
}

if (tx.nTime < m_poll.m_timestamp) {
LogPrint(LogFlags::VOTE, "%s: tx earlier than poll", __func__);
LogPrintf("WARN: %s: tx earlier than poll", __func__);
throw InvalidVoteError();
}

if (m_poll.Expired(tx.nTime)) {
LogPrint(LogFlags::VOTE, "%s: tx exceeds expiration", __func__);
LogPrintf("WARN: %s: tx exceeds expiration", __func__);
throw InvalidVoteError();
}

Expand All @@ -885,7 +905,7 @@ class VoteCounter
}

if (!contract.WellFormed()) {
LogPrint(LogFlags::VOTE, "%s: skipped bad contract", __func__);
LogPrintf("WARN: %s: skipped bad contract", __func__);
continue;
}

Expand Down Expand Up @@ -1228,7 +1248,11 @@ void PollResult::TallyVote(VoteDetail detail)

if (detail.m_ismine != ISMINE_NO) {
m_self_voted = true;
m_self_vote_detail = detail;

m_self_vote_detail.m_amount += detail.m_amount;
m_self_vote_detail.m_mining_id = detail.m_mining_id;
m_self_vote_detail.m_magnitude = detail.m_magnitude;
m_self_vote_detail.m_ismine = detail.m_ismine;
}

for (const auto& response_pair : detail.m_responses) {
Expand All @@ -1238,6 +1262,22 @@ void PollResult::TallyVote(VoteDetail detail)
m_responses[response_offset].m_weight += response_weight;
m_responses[response_offset].m_votes += 1.0 / detail.m_responses.size();
m_total_weight += response_weight;

if (detail.m_ismine != ISMINE_NO) {
bool choice_found = false;

for (auto& choice : m_self_vote_detail.m_responses) {
if (choice.first == response_offset) {
choice.second += response_weight;
choice_found = true;
break;
}
}

if (!choice_found) {
m_self_vote_detail.m_responses.push_back(std::make_pair(response_offset, response_weight));
}
}
}

m_votes.emplace_back(std::move(detail));
Expand All @@ -1259,6 +1299,15 @@ VoteDetail::VoteDetail() : m_amount(0), m_magnitude(Magnitude::Zero()), m_ismine
{
}

VoteDetail::VoteDetail(const VoteDetail &original_votedetail)
: m_amount(original_votedetail.m_amount)
, m_mining_id(original_votedetail.m_mining_id)
, m_magnitude(original_votedetail.m_magnitude)
, m_ismine(original_votedetail.m_ismine)
, m_responses(original_votedetail.m_responses)
{
}

bool VoteDetail::Empty() const
{
return m_amount == 0 && m_magnitude == 0;
Expand Down
7 changes: 7 additions & 0 deletions src/gridcoin/voting/result.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ class PollResult
//!
VoteDetail();

//!
//! \brief User copy constructor.
//!
//! \param original_votedetail
//!
VoteDetail(const VoteDetail& original_votedetail);

//!
//! \brief Determine whether a vote contributes no weight.
//!
Expand Down
44 changes: 42 additions & 2 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "signverifymessagedialog.h"
#include "optionsdialog.h"
#include "aboutdialog.h"
#include "voting/polltab.h"
#include "voting/votingpage.h"
#include "clientmodel.h"
#include "walletmodel.h"
Expand All @@ -43,6 +44,7 @@
#include "univalue.h"
#include "upgradeqt.h"
#include "voting/votingmodel.h"
#include "voting/polltablemodel.h"

#ifdef Q_OS_MAC
#include "macdockiconhandler.h"
Expand Down Expand Up @@ -1932,18 +1934,56 @@ void BitcoinGUI::handleNewPoll()
overviewPage->setCurrentPollTitle(votingModel->getCurrentPollTitle());
}

//!
//! \brief BitcoinGUI::extracted. Helper function to avoid container detach on range loop warning.
//! \param expiring_polls
//! \param notification
//!
void BitcoinGUI::extracted(QStringList& expiring_polls, QString& notification)
{
for (const auto& expiring_poll : expiring_polls) {
notification += expiring_poll + "\n";
}
}

void BitcoinGUI::handleExpiredPoll()
{
// The only difference between this and handleNewPoll() is no call to the event notifier.
if (!clientModel) {
return;
}

if (!clientModel || !clientModel->getOptionsModel()) {
if (!clientModel->getOptionsModel()) {
return;
}

if (!votingModel) {
return;
}

// Only do if in sync.
if (researcherModel && !researcherModel->outOfSync() && votingPage->getActiveTab()) {

// First refresh the active poll tab and underlying table
votingPage->getActiveTab()->refresh();

if (!clientModel->getOptionsModel()->getDisablePollNotifications()) {
QStringList expiring_polls = votingModel->getExpiringPollsNotNotified();

if (!expiring_polls.isEmpty()) {
QString notification = tr("The following poll(s) are about to expire:\n");

extracted(expiring_polls, notification);

notification += tr("Open Gridcoin to vote.");

notificator->notify(
Notificator::Information,
tr("Poll(s) about to expire"),
notification);
}
}
}

overviewPage->setCurrentPollTitle(votingModel->getCurrentPollTitle());
}

Expand Down
2 changes: 2 additions & 0 deletions src/qt/bitcoingui.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class WalletModel;
class ResearcherModel;
class MRCModel;
class VotingModel;
class PollTableModel;
class TransactionView;
class OverviewPage;
class FavoritesPage;
Expand Down Expand Up @@ -295,6 +296,7 @@ private slots:
QString GetEstimatedStakingFrequency(unsigned int nEstimateTime);

void handleNewPoll();
void extracted(QStringList& expiring_polls, QString& notification);
void handleExpiredPoll();
};

Expand Down
33 changes: 32 additions & 1 deletion src/qt/forms/optionsdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,37 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="pollExpireNotifyLabel">
<property name="text">
<string>Hours before poll expiry reminder</string>
</property>
</widget>
</item>
<item>
<widget class="QValidatedLineEdit" name="pollExpireNotifyLineEdit">
<property name="toolTip">
<string>Valid values are between 0.25 and 24.0 hours.</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_Window">
<property name="orientation">
Expand Down Expand Up @@ -585,7 +616,7 @@
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header>
<header location="global">qvalidatedlineedit.h</header>
</customwidget>
<customwidget>
<class>QValueComboBox</class>
Expand Down
4 changes: 2 additions & 2 deletions src/qt/forms/voting/pollcard.ui
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,14 @@
<item row="2" column="0">
<widget class="QLabel" name="myLastVoteAnswerTextLabel">
<property name="text">
<string>Your Last Vote:</string>
<string>Your Vote(s):</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="myVoteWeightTextLabel">
<property name="text">
<string>Your Vote Weight:</string>
<string>Your Vote Weight(s):</string>
</property>
</widget>
</item>
Expand Down
Loading