Skip to content

Commit ceab53b

Browse files
committed
Merge pull request #3521
4d90102 [Qt] Add sorting feature to the requested payments table (Cozz Lovan) 8476d5d [Qt] Permanently store requested payments in wallet (Cozz Lovan) b10e147 wallet: add interface for storing generic data on destinations (Wladimir J. van der Laan)
2 parents 6586bc3 + 4d90102 commit ceab53b

10 files changed

+286
-6
lines changed

src/qt/forms/receivecoinsdialog.ui

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@
207207
</widget>
208208
</item>
209209
<item>
210-
<widget class="QTableView" name="recentRequestsView"/>
210+
<widget class="QTableView" name="recentRequestsView">
211+
<property name="sortingEnabled">
212+
<bool>true</bool>
213+
</property>
214+
</widget>
211215
</item>
212216
<item>
213217
<layout class="QHBoxLayout" name="horizontalLayout_2">

src/qt/receivecoinsdialog.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ void ReceiveCoinsDialog::setModel(WalletModel *model)
5555
ui->recentRequestsView->horizontalHeader()->setSectionResizeMode(RecentRequestsTableModel::Message, QHeaderView::Stretch);
5656
#endif
5757
ui->recentRequestsView->horizontalHeader()->resizeSection(RecentRequestsTableModel::Amount, 100);
58+
59+
model->getRecentRequestsTableModel()->sort(RecentRequestsTableModel::Date, Qt::DescendingOrder);
5860
}
5961
}
6062

src/qt/recentrequeststablemodel.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel
1212
walletModel(parent)
1313
{
1414
Q_UNUSED(wallet);
15+
nReceiveRequestsMaxId = 0;
16+
17+
// Load entries from wallet
18+
std::vector<std::string> vReceiveRequests;
19+
parent->loadReceiveRequests(vReceiveRequests);
20+
BOOST_FOREACH(const std::string& request, vReceiveRequests)
21+
addNewRequest(request);
1522

1623
/* These columns must match the indices in the ColumnIndex enumeration */
1724
columns << tr("Date") << tr("Label") << tr("Message") << tr("Amount");
@@ -104,6 +111,14 @@ bool RecentRequestsTableModel::removeRows(int row, int count, const QModelIndex
104111

105112
if(count > 0 && row >= 0 && (row+count) <= list.size())
106113
{
114+
const RecentRequestEntry *rec;
115+
for (int i = 0; i < count; ++i)
116+
{
117+
rec = &list[row+i];
118+
if (!walletModel->saveReceiveRequest(rec->recipient.address.toStdString(), rec->id, ""))
119+
return false;
120+
}
121+
107122
beginRemoveRows(parent, row, row + count - 1);
108123
list.erase(list.begin() + row, list.begin() + row + count);
109124
endRemoveRows();
@@ -118,12 +133,73 @@ Qt::ItemFlags RecentRequestsTableModel::flags(const QModelIndex &index) const
118133
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
119134
}
120135

136+
// called when adding a request from the GUI
121137
void RecentRequestsTableModel::addNewRequest(const SendCoinsRecipient &recipient)
122138
{
123139
RecentRequestEntry newEntry;
140+
newEntry.id = ++nReceiveRequestsMaxId;
124141
newEntry.date = QDateTime::currentDateTime();
125142
newEntry.recipient = recipient;
143+
144+
CDataStream ss(SER_DISK, CLIENT_VERSION);
145+
ss << newEntry;
146+
147+
if (!walletModel->saveReceiveRequest(recipient.address.toStdString(), newEntry.id, ss.str()))
148+
return;
149+
150+
addNewRequest(newEntry);
151+
}
152+
153+
// called from ctor when loading from wallet
154+
void RecentRequestsTableModel::addNewRequest(const std::string &recipient)
155+
{
156+
std::vector<char> data(recipient.begin(), recipient.end());
157+
CDataStream ss(data, SER_DISK, CLIENT_VERSION);
158+
159+
RecentRequestEntry entry;
160+
ss >> entry;
161+
162+
if (entry.id == 0) // should not happen
163+
return;
164+
165+
if (entry.id > nReceiveRequestsMaxId)
166+
nReceiveRequestsMaxId = entry.id;
167+
168+
addNewRequest(entry);
169+
}
170+
171+
// actually add to table in GUI
172+
void RecentRequestsTableModel::addNewRequest(RecentRequestEntry &recipient)
173+
{
126174
beginInsertRows(QModelIndex(), 0, 0);
127-
list.prepend(newEntry);
175+
list.prepend(recipient);
128176
endInsertRows();
129177
}
178+
179+
void RecentRequestsTableModel::sort(int column, Qt::SortOrder order)
180+
{
181+
qSort(list.begin(), list.end(), RecentRequestEntryLessThan(column, order));
182+
emit dataChanged(index(0, 0, QModelIndex()), index(list.size() - 1, NUMBER_OF_COLUMNS - 1, QModelIndex()));
183+
}
184+
185+
bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const
186+
{
187+
RecentRequestEntry *pLeft = &left;
188+
RecentRequestEntry *pRight = &right;
189+
if (order == Qt::DescendingOrder)
190+
std::swap(pLeft, pRight);
191+
192+
switch(column)
193+
{
194+
case RecentRequestsTableModel::Date:
195+
return pLeft->date.toTime_t() < pRight->date.toTime_t();
196+
case RecentRequestsTableModel::Label:
197+
return pLeft->recipient.label < pRight->recipient.label;
198+
case RecentRequestsTableModel::Message:
199+
return pLeft->recipient.message < pRight->recipient.message;
200+
case RecentRequestsTableModel::Amount:
201+
return pLeft->recipient.amount < pRight->recipient.amount;
202+
default:
203+
return pLeft->id < pRight->id;
204+
}
205+
}

src/qt/recentrequeststablemodel.h

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,44 @@
1313

1414
class CWallet;
1515

16-
struct RecentRequestEntry
16+
class RecentRequestEntry
1717
{
18+
public:
19+
RecentRequestEntry() : nVersion(RecentRequestEntry::CURRENT_VERSION), id(0) { }
20+
21+
static const int CURRENT_VERSION=1;
22+
int nVersion;
23+
int64_t id;
1824
QDateTime date;
1925
SendCoinsRecipient recipient;
26+
27+
IMPLEMENT_SERIALIZE
28+
(
29+
RecentRequestEntry* pthis = const_cast<RecentRequestEntry*>(this);
30+
31+
unsigned int nDate = date.toTime_t();
32+
33+
READWRITE(pthis->nVersion);
34+
nVersion = pthis->nVersion;
35+
READWRITE(id);
36+
READWRITE(nDate);
37+
READWRITE(recipient);
38+
39+
if (fRead)
40+
pthis->date = QDateTime::fromTime_t(nDate);
41+
)
42+
};
43+
44+
class RecentRequestEntryLessThan
45+
{
46+
public:
47+
RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder):
48+
column(nColumn), order(fOrder) {}
49+
bool operator()(RecentRequestEntry &left, RecentRequestEntry &right ) const;
50+
51+
private:
52+
int column;
53+
Qt::SortOrder order;
2054
};
2155

2256
/** Model for list of recently generated payment requests / bitcoin URIs.
@@ -34,7 +68,8 @@ class RecentRequestsTableModel: public QAbstractTableModel
3468
Date = 0,
3569
Label = 1,
3670
Message = 2,
37-
Amount = 3
71+
Amount = 3,
72+
NUMBER_OF_COLUMNS
3873
};
3974

4075
/** @name Methods overridden from QAbstractTableModel
@@ -51,11 +86,17 @@ class RecentRequestsTableModel: public QAbstractTableModel
5186

5287
const RecentRequestEntry &entry(int row) const { return list[row]; }
5388
void addNewRequest(const SendCoinsRecipient &recipient);
89+
void addNewRequest(const std::string &recipient);
90+
void addNewRequest(RecentRequestEntry &recipient);
91+
92+
public slots:
93+
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
5494

5595
private:
5696
WalletModel *walletModel;
5797
QStringList columns;
5898
QList<RecentRequestEntry> list;
99+
int64_t nReceiveRequestsMaxId;
59100
};
60101

61102
#endif

src/qt/walletmodel.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,27 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
554554
LOCK(wallet->cs_wallet);
555555
wallet->ListLockedCoins(vOutpts);
556556
}
557+
558+
void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
559+
{
560+
LOCK(wallet->cs_wallet);
561+
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
562+
BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata)
563+
if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request
564+
vReceiveRequests.push_back(item2.second);
565+
}
566+
567+
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
568+
{
569+
CTxDestination dest = CBitcoinAddress(sAddress).Get();
570+
571+
std::stringstream ss;
572+
ss << nId;
573+
std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata
574+
575+
LOCK(wallet->cs_wallet);
576+
if (sRequest.empty())
577+
return wallet->EraseDestData(dest, key);
578+
else
579+
return wallet->AddDestData(dest, key, sRequest);
580+
}

src/qt/walletmodel.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ QT_END_NAMESPACE
3636
class SendCoinsRecipient
3737
{
3838
public:
39-
explicit SendCoinsRecipient() : amount(0) { }
39+
explicit SendCoinsRecipient() : amount(0), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
4040
explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message):
41-
address(addr), label(label), amount(amount), message(message) {}
41+
address(addr), label(label), amount(amount), message(message), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
4242

4343
// If from an insecure payment request, this is used for storing
4444
// the addresses, e.g. address-A<br />address-B<br />address-C.
@@ -55,6 +55,41 @@ class SendCoinsRecipient
5555
PaymentRequestPlus paymentRequest;
5656
// Empty if no authentication or invalid signature/cert/etc.
5757
QString authenticatedMerchant;
58+
59+
static const int CURRENT_VERSION=1;
60+
int nVersion;
61+
62+
IMPLEMENT_SERIALIZE
63+
(
64+
SendCoinsRecipient* pthis = const_cast<SendCoinsRecipient*>(this);
65+
66+
std::string sAddress = pthis->address.toStdString();
67+
std::string sLabel = pthis->label.toStdString();
68+
std::string sMessage = pthis->message.toStdString();
69+
std::string sPaymentRequest;
70+
if (!fRead && pthis->paymentRequest.IsInitialized())
71+
pthis->paymentRequest.SerializeToString(&sPaymentRequest);
72+
std::string sAuthenticatedMerchant = pthis->authenticatedMerchant.toStdString();
73+
74+
READWRITE(pthis->nVersion);
75+
nVersion = pthis->nVersion;
76+
READWRITE(sAddress);
77+
READWRITE(sLabel);
78+
READWRITE(amount);
79+
READWRITE(sMessage);
80+
READWRITE(sPaymentRequest);
81+
READWRITE(sAuthenticatedMerchant);
82+
83+
if (fRead)
84+
{
85+
pthis->address = QString::fromStdString(sAddress);
86+
pthis->label = QString::fromStdString(sLabel);
87+
pthis->message = QString::fromStdString(sMessage);
88+
if (!sPaymentRequest.empty())
89+
pthis->paymentRequest.parse(QByteArray::fromRawData(sPaymentRequest.data(), sPaymentRequest.size()));
90+
pthis->authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
91+
}
92+
)
5893
};
5994

6095
/** Interface to Bitcoin wallet from Qt view code. */
@@ -152,6 +187,9 @@ class WalletModel : public QObject
152187
void unlockCoin(COutPoint& output);
153188
void listLockedCoins(std::vector<COutPoint>& vOutpts);
154189

190+
void loadReceiveRequests(std::vector<std::string>& vReceiveRequests);
191+
bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest);
192+
155193
private:
156194
CWallet *wallet;
157195

src/wallet.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,19 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam
15341534

15351535
bool CWallet::DelAddressBook(const CTxDestination& address)
15361536
{
1537+
15371538
AssertLockHeld(cs_wallet); // mapAddressBook
1539+
1540+
if(fFileBacked)
1541+
{
1542+
// Delete destdata tuples associated with address
1543+
std::string strAddress = CBitcoinAddress(address).ToString();
1544+
BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata)
1545+
{
1546+
CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);
1547+
}
1548+
}
1549+
15381550
mapAddressBook.erase(address);
15391551
NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), "", CT_DELETED);
15401552
if (!fFileBacked)
@@ -2008,3 +2020,45 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const {
20082020
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
20092021
mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
20102022
}
2023+
2024+
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
2025+
{
2026+
if (boost::get<CNoDestination>(&dest))
2027+
return false;
2028+
2029+
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
2030+
if (!fFileBacked)
2031+
return true;
2032+
return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);
2033+
}
2034+
2035+
bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)
2036+
{
2037+
if (!mapAddressBook[dest].destdata.erase(key))
2038+
return false;
2039+
if (!fFileBacked)
2040+
return true;
2041+
return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);
2042+
}
2043+
2044+
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
2045+
{
2046+
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
2047+
return true;
2048+
}
2049+
2050+
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
2051+
{
2052+
std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest);
2053+
if(i != mapAddressBook.end())
2054+
{
2055+
CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
2056+
if(j != i->second.destdata.end())
2057+
{
2058+
if(value)
2059+
*value = j->second;
2060+
return true;
2061+
}
2062+
}
2063+
return false;
2064+
}

src/wallet.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class CAddressBookData
8383
{
8484
purpose = "unknown";
8585
}
86+
87+
typedef std::map<std::string, std::string> StringMap;
88+
StringMap destdata;
8689
};
8790

8891
/** A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
@@ -189,6 +192,15 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
189192
bool AddCScript(const CScript& redeemScript);
190193
bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); }
191194

195+
/// Adds a destination data tuple to the store, and saves it to disk
196+
bool AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
197+
/// Erases a destination data tuple in the store and on disk
198+
bool EraseDestData(const CTxDestination &dest, const std::string &key);
199+
/// Adds a destination data tuple to the store, without saving it to disk
200+
bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
201+
/// Look up a destination data tuple in the store, return true if found false otherwise
202+
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
203+
192204
bool Unlock(const SecureString& strWalletPassphrase);
193205
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
194206
bool EncryptWallet(const SecureString& strWalletPassphrase);

0 commit comments

Comments
 (0)