aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bitcoin-qt.pro6
-rw-r--r--contrib/misc/example-linearize.cfg12
-rwxr-xr-xcontrib/misc/linearize.py129
-rw-r--r--src/bitcoinrpc.cpp1
-rw-r--r--src/bitcoinrpc.h1
-rw-r--r--src/init.cpp5
-rw-r--r--src/qt/addresstablemodel.cpp65
-rw-r--r--src/qt/addresstablemodel.h2
-rw-r--r--src/qt/bitcoin.cpp22
-rw-r--r--src/qt/bitcoinaddressvalidator.h4
-rw-r--r--src/qt/bitcoingui.cpp5
-rw-r--r--src/qt/intro.cpp7
-rw-r--r--src/qt/intro.h2
-rw-r--r--src/qt/paymentserver.cpp24
-rw-r--r--src/qt/paymentserver.h18
-rw-r--r--src/qt/sendcoinsdialog.cpp113
-rw-r--r--src/qt/sendcoinsdialog.h1
-rw-r--r--src/qt/test/paymentservertests.cpp3
-rw-r--r--src/qt/transactiondesc.cpp26
-rw-r--r--src/qt/transactiondesc.h2
-rw-r--r--src/qt/transactiontablemodel.cpp24
-rw-r--r--src/qt/walletmodel.cpp76
-rw-r--r--src/qt/walletmodel.h16
-rw-r--r--src/qt/walletmodeltransaction.cpp56
-rw-r--r--src/qt/walletmodeltransaction.h37
-rw-r--r--src/rpcwallet.cpp27
-rw-r--r--src/wallet.cpp17
-rw-r--r--src/wallet.h10
28 files changed, 535 insertions, 176 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index 9baa03a4e3..00dd02da58 100644
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -240,7 +240,8 @@ HEADERS += src/qt/bitcoingui.h \
src/threadsafety.h \
src/limitedmap.h \
src/qt/splashscreen.h \
- src/qt/intro.h
+ src/qt/intro.h \
+ src/qt/walletmodeltransaction.h
SOURCES += src/qt/bitcoin.cpp \
src/qt/bitcoingui.cpp \
@@ -313,7 +314,8 @@ SOURCES += src/qt/bitcoin.cpp \
src/leveldb.cpp \
src/txdb.cpp \
src/qt/splashscreen.cpp \
- src/qt/intro.cpp
+ src/qt/intro.cpp \
+ src/qt/walletmodeltransaction.cpp
RESOURCES += src/qt/bitcoin.qrc
diff --git a/contrib/misc/example-linearize.cfg b/contrib/misc/example-linearize.cfg
new file mode 100644
index 0000000000..9e5aa404c2
--- /dev/null
+++ b/contrib/misc/example-linearize.cfg
@@ -0,0 +1,12 @@
+
+# bitcoind RPC settings
+rpcuser=someuser
+rpcpass=somepassword
+host=127.0.0.1
+port=8332
+
+# bootstrap.dat settings
+netmagic=f9beb4d9
+max_height=250000
+output=bootstrap.dat
+
diff --git a/contrib/misc/linearize.py b/contrib/misc/linearize.py
new file mode 100755
index 0000000000..2d8509f83c
--- /dev/null
+++ b/contrib/misc/linearize.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+#
+# linearize.py: Construct a linear, no-fork, best version of the blockchain.
+#
+#
+# Copyright (c) 2013 The Bitcoin developers
+# Distributed under the MIT/X11 software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#
+
+import json
+import struct
+import re
+import base64
+import httplib
+import sys
+
+ERR_SLEEP = 15
+MAX_NONCE = 1000000L
+
+settings = {}
+
+class BitcoinRPC:
+ OBJID = 1
+
+ def __init__(self, host, port, username, password):
+ authpair = "%s:%s" % (username, password)
+ self.authhdr = "Basic %s" % (base64.b64encode(authpair))
+ self.conn = httplib.HTTPConnection(host, port, False, 30)
+ def rpc(self, method, params=None):
+ self.OBJID += 1
+ obj = { 'version' : '1.1',
+ 'method' : method,
+ 'id' : self.OBJID }
+ if params is None:
+ obj['params'] = []
+ else:
+ obj['params'] = params
+ self.conn.request('POST', '/', json.dumps(obj),
+ { 'Authorization' : self.authhdr,
+ 'Content-type' : 'application/json' })
+
+ resp = self.conn.getresponse()
+ if resp is None:
+ print "JSON-RPC: no response"
+ return None
+
+ body = resp.read()
+ resp_obj = json.loads(body)
+ if resp_obj is None:
+ print "JSON-RPC: cannot JSON-decode body"
+ return None
+ if 'error' in resp_obj and resp_obj['error'] != None:
+ return resp_obj['error']
+ if 'result' not in resp_obj:
+ print "JSON-RPC: no result in object"
+ return None
+
+ return resp_obj['result']
+ def getblock(self, hash, verbose=True):
+ return self.rpc('getblock', [hash, verbose])
+ def getblockhash(self, index):
+ return self.rpc('getblockhash', [index])
+
+def getblock(rpc, settings, n):
+ hash = rpc.getblockhash(n)
+ hexdata = rpc.getblock(hash, False)
+ data = hexdata.decode('hex')
+
+ return data
+
+def get_blocks(settings):
+ rpc = BitcoinRPC(settings['host'], settings['port'],
+ settings['rpcuser'], settings['rpcpass'])
+
+ outf = open(settings['output'], 'wb')
+
+ for height in xrange(settings['max_height']+1):
+ data = getblock(rpc, settings, height)
+
+ outhdr = settings['netmagic']
+ outhdr += struct.pack("<i", len(data))
+
+ outf.write(outhdr)
+ outf.write(data)
+
+ if (height % 1000) == 0:
+ sys.stdout.write("Wrote block " + str(height) + "\n")
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2:
+ print "Usage: linearize.py CONFIG-FILE"
+ sys.exit(1)
+
+ f = open(sys.argv[1])
+ for line in f:
+ # skip comment lines
+ m = re.search('^\s*#', line)
+ if m:
+ continue
+
+ # parse key=value lines
+ m = re.search('^(\w+)\s*=\s*(\S.*)$', line)
+ if m is None:
+ continue
+ settings[m.group(1)] = m.group(2)
+ f.close()
+
+ if 'netmagic' not in settings:
+ settings['netmagic'] = 'f9beb4d9'
+ if 'output' not in settings:
+ settings['output'] = 'bootstrap.dat'
+ if 'host' not in settings:
+ settings['host'] = '127.0.0.1'
+ if 'port' not in settings:
+ settings['port'] = 8332
+ if 'max_height' not in settings:
+ settings['max_height'] = 250000
+ if 'rpcuser' not in settings or 'rpcpass' not in settings:
+ print "Missing username and/or password in cfg file"
+ sys.exit(1)
+
+ settings['netmagic'] = settings['netmagic'].decode('hex')
+ settings['port'] = int(settings['port'])
+ settings['max_height'] = int(settings['max_height'])
+
+ get_blocks(settings)
+
+
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index d22809ce69..47c7383564 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -208,6 +208,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getmininginfo", &getmininginfo, true, false },
{ "getnewaddress", &getnewaddress, true, false },
{ "getaccountaddress", &getaccountaddress, true, false },
+ { "getrawchangeaddress", &getrawchangeaddress, true, false },
{ "setaccount", &setaccount, true, false },
{ "getaccount", &getaccount, false, false },
{ "getaddressesbyaccount", &getaddressesbyaccount, true, false },
diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h
index 4d5599be84..1aa2e70d26 100644
--- a/src/bitcoinrpc.h
+++ b/src/bitcoinrpc.h
@@ -161,6 +161,7 @@ extern json_spirit::Value submitblock(const json_spirit::Array& params, bool fHe
extern json_spirit::Value getnewaddress(const json_spirit::Array& params, bool fHelp); // in rpcwallet.cpp
extern json_spirit::Value getaccountaddress(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value getrawchangeaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value setaccount(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getaccount(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getaddressesbyaccount(const json_spirit::Array& params, bool fHelp);
diff --git a/src/init.cpp b/src/init.cpp
index db368c7f53..57e20523b1 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -377,8 +377,6 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 2: parameter interactions
- Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
-
if (mapArgs.count("-bind")) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
@@ -427,6 +425,7 @@ bool AppInit2(boost::thread_group& threadGroup)
fDebug = GetBoolArg("-debug", false);
fBenchmark = GetBoolArg("-benchmark", false);
mempool.fChecks = GetBoolArg("-checkmempool", RegTest());
+ Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = GetArg("-par", 0);
@@ -898,7 +897,7 @@ bool AppInit2(boost::thread_group& threadGroup)
RandAddSeedPerfmon();
CPubKey newDefaultKey;
- if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) {
+ if (pwalletMain->GetKeyFromPool(newDefaultKey)) {
pwalletMain->SetDefaultKey(newDefaultKey);
if (!pwalletMain->SetAddressBook(pwalletMain->vchDefaultKey.GetID(), "", "receive"))
strErrors << _("Cannot write default address") << "\n";
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index dcc70222cc..be31b03749 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -15,7 +15,8 @@ struct AddressTableEntry
{
enum Type {
Sending,
- Receiving
+ Receiving,
+ Hidden /* QSortFilterProxyModel will filter these out */
};
Type type;
@@ -43,6 +44,20 @@ struct AddressTableEntryLessThan
}
};
+/* Determine address type from address purpose */
+static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
+{
+ AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
+ // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
+ if (strPurpose == "send")
+ addressType = AddressTableEntry::Sending;
+ else if (strPurpose == "receive")
+ addressType = AddressTableEntry::Receiving;
+ else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
+ addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
+ return addressType;
+}
+
// Private implementation
class AddressTablePriv
{
@@ -62,17 +77,9 @@ public:
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
-
- AddressTableEntry::Type addressType;
- const std::string& strPurpose = item.second.purpose;
- if (strPurpose == "send") addressType = AddressTableEntry::Sending;
- else if (strPurpose == "receive") addressType = AddressTableEntry::Receiving;
- else if (strPurpose == "unknown") {
- bool fMine = IsMine(*wallet, address.Get());
- addressType = (fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
- }
- else continue; // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
-
+ bool fMine = IsMine(*wallet, address.Get());
+ AddressTableEntry::Type addressType = translateTransactionType(
+ QString::fromStdString(item.second.purpose), fMine);
const std::string& strName = item.second.name;
cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(strName),
@@ -80,10 +87,12 @@ public:
}
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
+ // Even though the map is already sorted this re-sorting step is needed because the originating map
+ // is sorted by binary address, not by base58() address.
qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
}
- void updateEntry(const QString &address, const QString &label, bool isMine, int status)
+ void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
{
// Find address / label in model
QList<AddressTableEntry>::iterator lower = qLowerBound(
@@ -93,7 +102,7 @@ public:
int lowerIndex = (lower - cachedAddressTable.begin());
int upperIndex = (upper - cachedAddressTable.begin());
bool inModel = (lower != upper);
- AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending;
+ AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);
switch(status)
{
@@ -322,10 +331,11 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &par
}
}
-void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
+void AddressTableModel::updateEntry(const QString &address,
+ const QString &label, bool isMine, const QString &purpose, int status)
{
// Update address book model from Bitcoin core
- priv->updateEntry(address, label, isMine, status);
+ priv->updateEntry(address, label, isMine, purpose, status);
}
QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
@@ -355,18 +365,21 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
else if(type == Receive)
{
// Generate a new address to associate with given label
- WalletModel::UnlockContext ctx(walletModel->requestUnlock());
- if(!ctx.isValid())
- {
- // Unlock wallet failed or was cancelled
- editStatus = WALLET_UNLOCK_FAILURE;
- return QString();
- }
CPubKey newKey;
- if(!wallet->GetKeyFromPool(newKey, true))
+ if(!wallet->GetKeyFromPool(newKey))
{
- editStatus = KEY_GENERATION_FAILURE;
- return QString();
+ WalletModel::UnlockContext ctx(walletModel->requestUnlock());
+ if(!ctx.isValid())
+ {
+ // Unlock wallet failed or was cancelled
+ editStatus = WALLET_UNLOCK_FAILURE;
+ return QString();
+ }
+ if(!wallet->GetKeyFromPool(newKey))
+ {
+ editStatus = KEY_GENERATION_FAILURE;
+ return QString();
+ }
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
}
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index 48baff5e54..6f532087fe 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -85,7 +85,7 @@ signals:
public slots:
/* Update address list from core.
*/
- void updateEntry(const QString &address, const QString &label, bool isMine, int status);
+ void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
friend class AddressTablePriv;
};
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index e7cf440044..a4d589e167 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -155,12 +155,12 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans
#if QT_VERSION < 0x050000
void DebugMessageHandler(QtMsgType type, const char * msg)
{
- OutputDebugStringF("%s\n", msg);
+ OutputDebugStringF("Bitcoin-Qt: %s\n", msg);
}
#else
void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
{
- OutputDebugStringF("%s\n", qPrintable(msg));
+ OutputDebugStringF("Bitcoin-Qt: %s\n", qPrintable(msg));
}
#endif
@@ -232,10 +232,16 @@ int main(int argc, char *argv[])
PaymentServer* paymentServer = new PaymentServer(&app);
// User language is set up: pick a data directory
- Intro::pickDataDirectory();
+ Intro::pickDataDirectory(TestNet());
// Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
+ // Install qDebug() message handler to route to debug.log
+#if QT_VERSION < 0x050000
+ qInstallMsgHandler(DebugMessageHandler);
+#else
+ qInstallMessageHandler(DebugMessageHandler);
+#endif
// ... now GUI settings:
OptionsModel optionsModel;
@@ -255,13 +261,6 @@ int main(int argc, char *argv[])
return 1;
}
- // Install qDebug() message handler to route to debug.log:
-#if QT_VERSION < 0x050000
- qInstallMsgHandler(DebugMessageHandler);
-#else
- qInstallMessageHandler(DebugMessageHandler);
-#endif
-
SplashScreen splash(QPixmap(), 0);
if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false))
{
@@ -300,7 +299,8 @@ int main(int argc, char *argv[])
optionsModel.Upgrade(); // Must be done after AppInit2
PaymentServer::LoadRootCAs();
- paymentServer->initNetManager(optionsModel);
+ paymentServer->setOptionsModel(&optionsModel);
+ paymentServer->initNetManager();
if (splashref)
splash.finish(&window);
diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h
index 2b6a59367d..b7f4dfee96 100644
--- a/src/qt/bitcoinaddressvalidator.h
+++ b/src/qt/bitcoinaddressvalidator.h
@@ -3,8 +3,8 @@
#include <QValidator>
-/** Base48 entry widget validator.
- Corrects near-miss characters and refuses characters that are no part of base48.
+/** Base58 entry widget validator.
+ Corrects near-miss characters and refuses characters that are not part of base58.
*/
class BitcoinAddressValidator : public QValidator
{
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ad32c9ea68..bb9eb60e5b 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -670,9 +670,12 @@ void BitcoinGUI::closeEvent(QCloseEvent *event)
void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee)
{
+ if (!clientModel || !clientModel->getOptionsModel())
+ return;
+
QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, "
"which goes to the nodes that process your transaction and helps to support the network. "
- "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired));
+ "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired));
QMessageBox::StandardButton retval = QMessageBox::question(
this, tr("Confirm transaction fee"), strMessage,
QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes);
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 99db141c94..4a02ff89e7 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -142,7 +142,7 @@ QString Intro::getDefaultDataDirectory()
return QString::fromStdString(GetDefaultDataDir().string());
}
-void Intro::pickDataDirectory()
+void Intro::pickDataDirectory(bool fIsTestnet)
{
namespace fs = boost::filesystem;;
QSettings settings;
@@ -160,6 +160,11 @@ void Intro::pickDataDirectory()
/* If current default data directory does not exist, let the user choose one */
Intro intro;
intro.setDataDirectory(dataDir);
+ if (!fIsTestnet)
+ intro.setWindowIcon(QIcon(":icons/bitcoin"));
+ else
+ intro.setWindowIcon(QIcon(":icons/bitcoin_testnet"));
+
while(true)
{
if(!intro.exec())
diff --git a/src/qt/intro.h b/src/qt/intro.h
index 788799b7b0..8b09847abd 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -31,7 +31,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static void pickDataDirectory();
+ static void pickDataDirectory(bool fIsTestnet);
/**
* Determine default data directory for operating system.
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 75a8c03c01..b153decd1f 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -94,7 +94,7 @@ static void ReportInvalidCertificate(const QSslCertificate& cert)
}
//
-// Load openSSL's list of root certificate authorities
+// Load OpenSSL's list of root certificate authorities
//
void PaymentServer::LoadRootCAs(X509_STORE* _store)
{
@@ -149,7 +149,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
const unsigned char *data = (const unsigned char *)certData.data();
X509* x509 = d2i_X509(0, &data, certData.size());
- if (x509 && X509_STORE_add_cert( PaymentServer::certStore, x509))
+ if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509))
{
// Note: X509_STORE_free will free the X509* objects when
// the PaymentServer is destroyed
@@ -305,18 +305,20 @@ bool PaymentServer::eventFilter(QObject *, QEvent *event)
return false;
}
-void PaymentServer::initNetManager(const OptionsModel& options)
+void PaymentServer::initNetManager()
{
+ if (!optionsModel)
+ return;
if (netManager != NULL)
delete netManager;
// netManager is used to fetch paymentrequests given in bitcoin: URI's
netManager = new QNetworkAccessManager(this);
- // Use proxy settings from options:
+ // Use proxy settings from optionsModel:
QString proxyIP;
quint16 proxyPort;
- if (options.getProxySettings(proxyIP, proxyPort))
+ if (optionsModel->getProxySettings(proxyIP, proxyPort))
{
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::Socks5Proxy);
@@ -437,13 +439,16 @@ bool
PaymentServer::processPaymentRequest(PaymentRequestPlus& request,
QList<SendCoinsRecipient>& recipients)
{
+ if (!optionsModel)
+ return false;
+
QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo();
qint64 totalAmount = 0;
foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) {
CTxOut txOut(sendingTo.second, sendingTo.first);
if (txOut.IsDust(CTransaction::nMinRelayTxFee)) {
QString message = QObject::tr("Requested payment amount (%1) too small")
- .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendingTo.second));
+ .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second));
qDebug() << message;
emit reportError(tr("Payment request error"), message, CClientUIInterface::MODAL);
return false;
@@ -535,7 +540,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB
}
else {
CPubKey newKey;
- if (wallet->GetKeyFromPool(newKey, false)) {
+ if (wallet->GetKeyFromPool(newKey)) {
CKeyID keyID = newKey.GetID();
wallet->SetAddressBook(keyID, strAccount, "refund");
@@ -618,3 +623,8 @@ PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &err
}
emit reportError(tr("Network request error"), errString, CClientUIInterface::MODAL);
}
+
+void PaymentServer::setOptionsModel(OptionsModel *optionsModel)
+{
+ this->optionsModel = optionsModel;
+}
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 7c6b2eabf0..131ede518e 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -17,7 +17,7 @@
// received at or during startup in a list.
//
// When startup is finished and the main window is
-// show, a signal is sent to slot uiReady(), which
+// shown, a signal is sent to slot uiReady(), which
// emits a receivedURL() signal for any payment
// requests that happened during startup.
//
@@ -70,13 +70,16 @@ public:
// Return certificate store
static X509_STORE* getCertStore() { return certStore; }
- // Setup networking (options is used to get proxy settings)
- void initNetManager(const OptionsModel& options);
+ // Setup networking
+ void initNetManager();
// Constructor registers this on the parent QApplication to
// receive QEvent::FileOpen events
bool eventFilter(QObject *object, QEvent *event);
+ // OptionsModel is used for getting proxy settings and display unit
+ void setOptionsModel(OptionsModel *optionsModel);
+
signals:
// Fired when a valid payment request is received
void receivedPaymentRequest(SendCoinsRecipient);
@@ -106,12 +109,15 @@ private:
void handleURIOrFile(const QString& s);
void fetchRequest(const QUrl& url);
- bool saveURIs; // true during startup
+ bool saveURIs; // true during startup
QLocalServer* uriServer;
- static X509_STORE* certStore; // Trusted root certificates
+
+ static X509_STORE* certStore; // Trusted root certificates
static void freeCertStore();
- QNetworkAccessManager* netManager; // Used to fetch payment requests
+ QNetworkAccessManager* netManager; // Used to fetch payment requests
+
+ OptionsModel *optionsModel;
};
#endif // PAYMENTSERVER_H
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 9086f6614e..809eff9c27 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -63,12 +63,12 @@ SendCoinsDialog::~SendCoinsDialog()
void SendCoinsDialog::on_sendButton_clicked()
{
+ if(!model || !model->getOptionsModel())
+ return;
+
QList<SendCoinsRecipient> recipients;
bool valid = true;
- if(!model)
- return;
-
for(int i = 0; i < ui->entries->count(); ++i)
{
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
@@ -94,40 +94,31 @@ void SendCoinsDialog::on_sendButton_clicked()
QStringList formatted;
foreach(const SendCoinsRecipient &rcp, recipients)
{
- QString amount = BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount);
+ QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (rcp.authenticatedMerchant.isEmpty())
{
- QString address = rcp.address;
-#if QT_VERSION < 0x050000
- QString to = Qt::escape(rcp.label);
-#else
- QString to = rcp.label.toHtmlEscaped();
-#endif
- formatted.append(tr("<b>%1</b> to %2 (%3)").arg(amount, to, address));
+ QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount));
+ recipientElement.append(tr("to"));
+
+ if(rcp.label.length() > 0)
+ {
+ recipientElement.append(QString(" %1 <span style='font-size:8px;'>%2</span><br />").arg(GUIUtil::HtmlEscape(rcp.label), rcp.address)); // add address with label
+ }
+ else
+ {
+ recipientElement.append(QString(" %1<br />").arg(rcp.address)); // add address WITHOUT label
+ }
+ formatted.append(recipientElement);
}
else
{
-#if QT_VERSION < 0x050000
- QString merchant = Qt::escape(rcp.authenticatedMerchant);
-#else
- QString merchant = rcp.authenticatedMerchant.toHtmlEscaped();
-#endif
+ QString merchant = GUIUtil::HtmlEscape(rcp.authenticatedMerchant);
formatted.append(tr("<b>%1</b> to %2").arg(amount, merchant));
}
}
fNewRecipientAllowed = false;
- QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
- tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
- QMessageBox::Yes|QMessageBox::Cancel,
- QMessageBox::Cancel);
-
- if(retval != QMessageBox::Yes)
- {
- fNewRecipientAllowed = true;
- return;
- }
WalletModel::UnlockContext ctx(model->requestUnlock());
if(!ctx.isValid())
@@ -137,8 +128,11 @@ void SendCoinsDialog::on_sendButton_clicked()
return;
}
- WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients);
- switch(sendstatus.status)
+ // prepare transaction for getting txFee earlier
+ WalletModelTransaction currentTransaction(recipients);
+ WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction);
+
+ switch(prepareStatus.status)
{
case WalletModel::InvalidAddress:
QMessageBox::warning(this, tr("Send Coins"),
@@ -158,7 +152,7 @@ void SendCoinsDialog::on_sendButton_clicked()
case WalletModel::AmountWithFeeExceedsBalance:
QMessageBox::warning(this, tr("Send Coins"),
tr("The total exceeds your balance when the %1 transaction fee is included.").
- arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)),
+ arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())),
QMessageBox::Ok, QMessageBox::Ok);
break;
case WalletModel::DuplicateAddress:
@@ -171,6 +165,51 @@ void SendCoinsDialog::on_sendButton_clicked()
tr("Error: Transaction creation failed!"),
QMessageBox::Ok, QMessageBox::Ok);
break;
+ case WalletModel::Aborted: // User aborted, nothing to do
+ case WalletModel::OK:
+ case WalletModel::TransactionCommitFailed:
+ break;
+ }
+
+ if(prepareStatus.status != WalletModel::OK) {
+ fNewRecipientAllowed = true;
+ return;
+ }
+
+ qint64 txFee = currentTransaction.getTransactionFee();
+ QString questionString = tr("Are you sure you want to send?");
+ questionString.append("<br /><br />%1");
+
+ if(txFee > 0)
+ {
+ // append fee string if a fee is required
+ questionString.append("<hr /><span style='color:#aa0000;'>");
+ questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
+ questionString.append("</span> ");
+ questionString.append(tr("added as transaction fee"));
+ }
+ if(txFee > 0 || recipients.count() > 1)
+ {
+ // add total amount string if there are more then one recipients or a fee is required
+ questionString.append("<hr />");
+ questionString.append(tr("Total Amount %1").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTotalTransactionAmount()+txFee)));
+ }
+
+ QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
+ questionString.arg(formatted.join("<br />")),
+ QMessageBox::Yes|QMessageBox::Cancel,
+ QMessageBox::Cancel);
+
+ if(retval != QMessageBox::Yes)
+ {
+ fNewRecipientAllowed = true;
+ return;
+ }
+
+ // now send the prepared transaction
+ WalletModel::SendCoinsReturn sendstatus = model->sendCoins(currentTransaction);
+ switch(sendstatus.status)
+ {
case WalletModel::TransactionCommitFailed:
QMessageBox::warning(this, tr("Send Coins"),
tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."),
@@ -181,6 +220,8 @@ void SendCoinsDialog::on_sendButton_clicked()
case WalletModel::OK:
accept();
break;
+ default:
+ break;
}
fNewRecipientAllowed = true;
}
@@ -338,18 +379,14 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint
{
Q_UNUSED(unconfirmedBalance);
Q_UNUSED(immatureBalance);
- if(!model || !model->getOptionsModel())
- return;
- int unit = model->getOptionsModel()->getDisplayUnit();
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance));
+ if(model && model->getOptionsModel())
+ {
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
+ }
}
void SendCoinsDialog::updateDisplayUnit()
{
- if(model && model->getOptionsModel())
- {
- // Update labelBalance with the current balance and the current unit
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance()));
- }
+ setBalance(model->getBalance(), 0, 0);
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index e75a003ba1..f4bffedc9b 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -10,6 +10,7 @@ namespace Ui {
class WalletModel;
class SendCoinsEntry;
class SendCoinsRecipient;
+class OptionsModel;
QT_BEGIN_NAMESPACE
class QUrl;
diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp
index 2e26ab0c9b..6c8ad62b2b 100644
--- a/src/qt/test/paymentservertests.cpp
+++ b/src/qt/test/paymentservertests.cpp
@@ -58,7 +58,8 @@ void PaymentServerTests::paymentServerTests()
X509_STORE* caStore = X509_STORE_new();
X509_STORE_add_cert(caStore, parse_b64der_cert(caCert_BASE64));
PaymentServer::LoadRootCAs(caStore);
- server->initNetManager(optionsModel);
+ server->setOptionsModel(&optionsModel);
+ server->initNetManager();
server->uiReady();
// Now feed PaymentRequests to server, and observe signals it produces:
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 25ff3623c0..55875c2e46 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -32,7 +32,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
}
}
-QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
+QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int unit)
{
QString strHTML;
@@ -129,7 +129,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
nUnmatured += wallet->GetCredit(txout);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
- strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
+ strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
else
strHTML += "(" + tr("not accepted") + ")";
strHTML += "<br>";
@@ -139,7 +139,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
//
// Credit
//
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>";
}
else
{
@@ -175,7 +175,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
}
}
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -txout.nValue) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>";
}
if (fAllToMe)
@@ -183,13 +183,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
// Payment to self
int64 nChange = wtx.GetChange();
int64 nValue = nCredit - nChange;
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nValue) + "<br>";
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
}
int64 nTxFee = nDebit - GetValueOut(wtx);
if (nTxFee > 0)
- strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "<br>";
+ strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>";
}
else
{
@@ -198,14 +198,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
//
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if (wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
}
}
- strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet, true) + "<br>";
+ strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "<br>";
//
// Message
@@ -243,10 +243,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if(wallet->IsMine(txin))
- strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>";
+ strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
- strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>";
+ strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
@@ -274,7 +274,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
}
- strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue);
+ strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "</li>";
}
}
diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h
index cb0dda5b58..a659281dd2 100644
--- a/src/qt/transactiondesc.h
+++ b/src/qt/transactiondesc.h
@@ -14,7 +14,7 @@ class TransactionDesc: public QObject
Q_OBJECT
public:
- static QString toHTML(CWallet *wallet, CWalletTx &wtx);
+ static QString toHTML(CWallet *wallet, CWalletTx &wtx, int unit);
private:
TransactionDesc() {}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index baf1e16483..9c040d5b63 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -20,11 +20,11 @@
// Amount column is right-aligned it contains numbers
static int column_alignments[] = {
- Qt::AlignLeft|Qt::AlignVCenter,
- Qt::AlignLeft|Qt::AlignVCenter,
- Qt::AlignLeft|Qt::AlignVCenter,
- Qt::AlignLeft|Qt::AlignVCenter,
- Qt::AlignRight|Qt::AlignVCenter
+ Qt::AlignLeft|Qt::AlignVCenter, /* status */
+ Qt::AlignLeft|Qt::AlignVCenter, /* date */
+ Qt::AlignLeft|Qt::AlignVCenter, /* type */
+ Qt::AlignLeft|Qt::AlignVCenter, /* address */
+ Qt::AlignRight|Qt::AlignVCenter /* amount */
};
// Comparison operator for sort/binary search of model tx list
@@ -48,11 +48,12 @@ struct TxLessThan
class TransactionTablePriv
{
public:
- TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
- wallet(wallet),
- parent(parent)
+ TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent) :
+ wallet(wallet),
+ parent(parent)
{
}
+
CWallet *wallet;
TransactionTableModel *parent;
@@ -200,19 +201,18 @@ public:
}
}
- QString describe(TransactionRecord *rec)
+ QString describe(TransactionRecord *rec, int unit)
{
{
LOCK(wallet->cs_wallet);
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
- return TransactionDesc::toHTML(wallet, mi->second);
+ return TransactionDesc::toHTML(wallet, mi->second, unit);
}
}
return QString("");
}
-
};
TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent):
@@ -561,7 +561,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const
case DateRole:
return QDateTime::fromTime_t(static_cast<uint>(rec->time));
case LongDescriptionRole:
- return priv->describe(rec);
+ return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit());
case AddressRole:
return QString::fromStdString(rec->address);
case LabelRole:
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 61357647b7..2d3a6975e4 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -5,7 +5,6 @@
#include "transactiontablemodel.h"
#include "ui_interface.h"
-#include "wallet.h"
#include "walletdb.h" // for BackupWallet
#include "base58.h"
@@ -112,10 +111,11 @@ void WalletModel::updateTransaction(const QString &hash, int status)
}
}
-void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, int status)
+void WalletModel::updateAddressBook(const QString &address, const QString &label,
+ bool isMine, const QString &purpose, int status)
{
if(addressTableModel)
- addressTableModel->updateEntry(address, label, isMine, status);
+ addressTableModel->updateEntry(address, label, isMine, purpose, status);
}
bool WalletModel::validateAddress(const QString &address)
@@ -124,11 +124,11 @@ bool WalletModel::validateAddress(const QString &address)
return addressParsed.IsValid();
}
-WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients)
+WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction)
{
qint64 total = 0;
+ QList<SendCoinsRecipient> recipients = transaction.getRecipients();
std::vector<std::pair<CScript, int64> > vecSend;
- QByteArray transaction;
if(recipients.empty())
{
@@ -192,58 +192,70 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
if((total + nTransactionFee) > getBalance())
{
- return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee);
+ transaction.setTransactionFee(nTransactionFee);
+ return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
{
LOCK2(cs_main, wallet->cs_wallet);
- CReserveKey keyChange(wallet);
+ transaction.newPossibleKeyChange(wallet);
int64 nFeeRequired = 0;
std::string strFailReason;
- CWalletTx wtx;
- bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
+
+ CWalletTx *newTx = transaction.getTransaction();
+ CReserveKey *keyChange = transaction.getPossibleKeyChange();
+ bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason);
+ transaction.setTransactionFee(nFeeRequired);
if(!fCreated)
{
if((total + nFeeRequired) > wallet->GetBalance())
{
- return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired);
+ return SendCoinsReturn(AmountWithFeeExceedsBalance);
}
emit message(tr("Send Coins"), QString::fromStdString(strFailReason),
CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed;
}
+ }
+
+ return SendCoinsReturn(OK);
+}
+
+WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction)
+{
+ QByteArray transaction_array; /* store serialized transaction */
+
+ {
+ LOCK2(cs_main, wallet->cs_wallet);
+ CWalletTx *newTx = transaction.getTransaction();
+
// Store PaymentRequests in wtx.vOrderForm in wallet.
- foreach(const SendCoinsRecipient &rcp, recipients)
+ foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
{
if (rcp.paymentRequest.IsInitialized())
{
std::string key("PaymentRequest");
std::string value;
rcp.paymentRequest.SerializeToString(&value);
- wtx.vOrderForm.push_back(make_pair(key, value));
+ newTx->vOrderForm.push_back(make_pair(key, value));
}
- }
-
- if(!uiInterface.ThreadSafeAskFee(nFeeRequired))
- {
- return Aborted;
}
- if(!wallet->CommitTransaction(wtx, keyChange))
- {
+
+ CReserveKey *keyChange = transaction.getPossibleKeyChange();
+ if(!wallet->CommitTransaction(*newTx, *keyChange))
return TransactionCommitFailed;
- }
- CTransaction* t = (CTransaction*)&wtx;
+ CTransaction* t = (CTransaction*)newTx;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *t;
- transaction.append(&(ssTx[0]), ssTx.size());
+ transaction_array.append(&(ssTx[0]), ssTx.size());
}
// Add addresses / update labels that we've sent to to the address book,
- // and emit coinsSent signal
- foreach(const SendCoinsRecipient &rcp, recipients)
+ // and emit coinsSent signal for each recipient
+ foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
{
std::string strAddress = rcp.address.toStdString();
CTxDestination dest = CBitcoinAddress(strAddress).Get();
@@ -263,10 +275,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose
}
}
- emit coinsSent(wallet, rcp, transaction);
+ emit coinsSent(wallet, rcp, transaction_array);
}
- return SendCoinsReturn(OK, 0);
+ return SendCoinsReturn(OK);
}
OptionsModel *WalletModel::getOptionsModel()
@@ -351,13 +363,17 @@ static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStor
QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection);
}
-static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status)
+static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
+ const CTxDestination &address, const std::string &label, bool isMine,
+ const std::string &purpose, ChangeType status)
{
- OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i status=%i\n", CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, status);
+ OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i purpose=%s status=%i\n",
+ CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, purpose.c_str(), status);
QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection,
Q_ARG(QString, QString::fromStdString(CBitcoinAddress(address).ToString())),
Q_ARG(QString, QString::fromStdString(label)),
Q_ARG(bool, isMine),
+ Q_ARG(QString, QString::fromStdString(purpose)),
Q_ARG(int, status));
}
@@ -373,7 +389,7 @@ void WalletModel::subscribeToCoreSignals()
{
// Connect signals to wallet
wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
- wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
+ wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
}
@@ -381,7 +397,7 @@ void WalletModel::unsubscribeFromCoreSignals()
{
// Disconnect signals from wallet
wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1));
- wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5));
+ wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6));
wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3));
}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 8cba10f5d2..6abcdaf8cb 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -4,12 +4,15 @@
#include <QObject>
#include "allocators.h" /* for SecureString */
+#include "wallet.h"
+#include "walletmodeltransaction.h"
#include "paymentrequestplus.h"
class OptionsModel;
class AddressTableModel;
class TransactionTableModel;
class CWallet;
+class WalletModelTransaction;
QT_BEGIN_NAMESPACE
class QTimer;
@@ -74,15 +77,16 @@ public:
// Return status record for SendCoins, contains error id + information
struct SendCoinsReturn
{
- SendCoinsReturn(StatusCode status,
- qint64 fee=0):
- status(status), fee(fee) {}
+ SendCoinsReturn(StatusCode status):
+ status(status) {}
StatusCode status;
- qint64 fee; // is used in case status is "AmountWithFeeExceedsBalance"
};
+ // prepare transaction for getting txfee before sending coins
+ SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction);
+
// Send coins to a list of recipients
- SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients);
+ SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
// Wallet encryption
bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
@@ -165,7 +169,7 @@ public slots:
/* New transaction, or transaction changed status */
void updateTransaction(const QString &hash, int status);
/* New, updated or removed address book entry */
- void updateAddressBook(const QString &address, const QString &label, bool isMine, int status);
+ void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status);
/* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */
void pollBalanceChanged();
};
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
new file mode 100644
index 0000000000..96fc3edbb2
--- /dev/null
+++ b/src/qt/walletmodeltransaction.cpp
@@ -0,0 +1,56 @@
+#include "walletmodeltransaction.h"
+
+WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) :
+ recipients(recipients),
+ walletTransaction(0),
+ keyChange(0),
+ fee(0)
+{
+ walletTransaction = new CWalletTx();
+}
+
+WalletModelTransaction::~WalletModelTransaction()
+{
+ delete keyChange;
+ delete walletTransaction;
+}
+
+QList<SendCoinsRecipient> WalletModelTransaction::getRecipients()
+{
+ return recipients;
+}
+
+CWalletTx *WalletModelTransaction::getTransaction()
+{
+ return walletTransaction;
+}
+
+qint64 WalletModelTransaction::getTransactionFee()
+{
+ return fee;
+}
+
+void WalletModelTransaction::setTransactionFee(qint64 newFee)
+{
+ fee=newFee;
+}
+
+qint64 WalletModelTransaction::getTotalTransactionAmount()
+{
+ qint64 totalTransactionAmount = 0;
+ foreach(const SendCoinsRecipient &rcp, recipients)
+ {
+ totalTransactionAmount+=rcp.amount;
+ }
+ return totalTransactionAmount;
+}
+
+void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet)
+{
+ keyChange = new CReserveKey(wallet);
+}
+
+CReserveKey *WalletModelTransaction::getPossibleKeyChange()
+{
+ return keyChange;
+}
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
new file mode 100644
index 0000000000..c4848fb12d
--- /dev/null
+++ b/src/qt/walletmodeltransaction.h
@@ -0,0 +1,37 @@
+#ifndef WALLETMODELTRANSACTION_H
+#define WALLETMODELTRANSACTION_H
+
+#include "walletmodel.h"
+
+class SendCoinsRecipient;
+
+/** Data model for a walletmodel transaction. */
+class WalletModelTransaction
+{
+public:
+ explicit WalletModelTransaction(const QList<SendCoinsRecipient> &recipients);
+ ~WalletModelTransaction();
+
+ QList<SendCoinsRecipient> getRecipients();
+
+ CWalletTx *getTransaction();
+
+ void setTransactionFee(qint64 newFee);
+ qint64 getTransactionFee();
+
+ qint64 getTotalTransactionAmount();
+
+ void newPossibleKeyChange(CWallet *wallet);
+ CReserveKey *getPossibleKeyChange();
+
+private:
+ const QList<SendCoinsRecipient> recipients;
+ CWalletTx *walletTransaction;
+ CReserveKey *keyChange;
+ qint64 fee;
+
+public slots:
+
+};
+
+#endif // WALLETMODELTRANSACTION_H
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index d07d3408b9..14b4956a15 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -110,7 +110,7 @@ Value getnewaddress(const Array& params, bool fHelp)
// Generate a new key that is added to wallet
CPubKey newKey;
- if (!pwalletMain->GetKeyFromPool(newKey, false))
+ if (!pwalletMain->GetKeyFromPool(newKey))
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
CKeyID keyID = newKey.GetID();
@@ -148,7 +148,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false)
// Generate a new key
if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed)
{
- if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false))
+ if (!pwalletMain->GetKeyFromPool(account.vchPubKey))
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
@@ -176,6 +176,29 @@ Value getaccountaddress(const Array& params, bool fHelp)
}
+Value getrawchangeaddress(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "getrawchangeaddress\n"
+ "Returns a new Bitcoin address, for receiving change. "
+ "This is for use with raw transactions, NOT normal use.");
+
+ if (!pwalletMain->IsLocked())
+ pwalletMain->TopUpKeyPool();
+
+ CReserveKey reservekey(pwalletMain);
+ CPubKey vchPubKey;
+ if (!reservekey.GetReservedKey(vchPubKey))
+ throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change");
+
+ reservekey.KeepKey();
+
+ CKeyID keyID = vchPubKey.GetID();
+
+ return CBitcoinAddress(keyID).ToString();
+}
+
Value setaccount(const Array& params, bool fHelp)
{
diff --git a/src/wallet.cpp b/src/wallet.cpp
index ddfd71efda..54ede12a50 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -493,7 +493,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
if (txout.scriptPubKey == scriptDefaultKey)
{
CPubKey newDefaultKey;
- if (GetKeyFromPool(newDefaultKey, false))
+ if (GetKeyFromPool(newDefaultKey))
{
SetDefaultKey(newDefaultKey);
SetAddressBook(vchDefaultKey.GetID(), "", "receive");
@@ -1461,7 +1461,11 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam
{
std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);
mapAddressBook[address].name = strName;
- NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
+ if (!strPurpose.empty()) /* update purpose only if requested */
+ mapAddressBook[address].purpose = strPurpose;
+ NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address),
+ mapAddressBook[address].purpose,
+ (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED);
if (!fFileBacked)
return false;
if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))
@@ -1472,7 +1476,7 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam
bool CWallet::DelAddressBook(const CTxDestination& address)
{
mapAddressBook.erase(address);
- NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED);
+ NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), "", CT_DELETED);
if (!fFileBacked)
return false;
CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());
@@ -1647,7 +1651,7 @@ void CWallet::ReturnKey(int64 nIndex)
printf("keypool return %"PRI64d"\n", nIndex);
}
-bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse)
+bool CWallet::GetKeyFromPool(CPubKey& result)
{
int64 nIndex = 0;
CKeyPool keypool;
@@ -1656,11 +1660,6 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse)
ReserveKeyFromKeyPool(nIndex, keypool);
if (nIndex == -1)
{
- if (fAllowReuse && vchDefaultKey.IsValid())
- {
- result = vchDefaultKey;
- return true;
- }
if (IsLocked()) return false;
result = GenerateNewKey();
return true;
diff --git a/src/wallet.h b/src/wallet.h
index d47416d272..b529d5f28b 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -220,7 +220,7 @@ public:
void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool);
void KeepKey(int64 nIndex);
void ReturnKey(int64 nIndex);
- bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true);
+ bool GetKeyFromPool(CPubKey &key);
int64 GetOldestKeyPoolTime();
void GetAllReserveKeys(std::set<CKeyID>& setAddress) const;
@@ -335,12 +335,16 @@ public:
/** Address book entry changed.
* @note called with lock cs_wallet held.
*/
- boost::signals2::signal<void (CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status)> NotifyAddressBookChanged;
+ boost::signals2::signal<void (CWallet *wallet, const CTxDestination
+ &address, const std::string &label, bool isMine,
+ const std::string &purpose,
+ ChangeType status)> NotifyAddressBookChanged;
/** Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
- boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, ChangeType status)> NotifyTransactionChanged;
+ boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx,
+ ChangeType status)> NotifyTransactionChanged;
};
/** A key allocated from the key pool. */