aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compat.h2
-rw-r--r--src/init.cpp28
-rw-r--r--src/miner.cpp2
-rw-r--r--src/miner.h23
-rw-r--r--src/qt/addresstablemodel.cpp6
-rw-r--r--src/qt/addresstablemodel.h4
-rw-r--r--src/qt/editaddressdialog.cpp5
-rw-r--r--src/qt/forms/receivecoinsdialog.ui121
-rw-r--r--src/qt/guiutil.cpp12
-rw-r--r--src/qt/guiutil.h2
-rw-r--r--src/qt/receivecoinsdialog.cpp17
-rw-r--r--src/qt/receiverequestdialog.cpp8
-rw-r--r--src/qt/walletmodel.cpp5
-rw-r--r--src/qt/walletmodel.h4
-rw-r--r--src/rpc/blockchain.cpp17
-rw-r--r--src/rpc/misc.cpp13
-rw-r--r--src/test/mempool_tests.cpp46
-rw-r--r--src/txmempool.cpp4
-rw-r--r--src/txmempool.h74
-rw-r--r--src/util.cpp22
-rw-r--r--src/util.h1
-rw-r--r--src/wallet/db.cpp48
-rw-r--r--src/wallet/db.h2
-rw-r--r--src/wallet/init.cpp12
-rw-r--r--src/wallet/rpcwallet.cpp30
-rw-r--r--src/wallet/wallet.h2
-rw-r--r--src/wallet/walletutil.cpp2
27 files changed, 345 insertions, 167 deletions
diff --git a/src/compat.h b/src/compat.h
index 65e9683e2f..aae84b1181 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -33,7 +33,7 @@
#include <ws2tcpip.h>
#include <stdint.h>
#else
-#include <sys/fcntl.h>
+#include <fcntl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/socket.h>
diff --git a/src/init.cpp b/src/init.cpp
index 7215e87359..d1d733af94 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1143,23 +1143,10 @@ bool AppInitParameterInteraction()
static bool LockDataDirectory(bool probeOnly)
{
- std::string strDataDir = GetDataDir().string();
-
// Make sure only a single Bitcoin process is using the data directory.
- fs::path pathLockFile = GetDataDir() / ".lock";
- FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
- if (file) fclose(file);
-
- try {
- static boost::interprocess::file_lock lock(pathLockFile.string().c_str());
- if (!lock.try_lock()) {
- return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), strDataDir, _(PACKAGE_NAME)));
- }
- if (probeOnly) {
- lock.unlock();
- }
- } catch(const boost::interprocess::interprocess_exception& e) {
- return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.") + " %s.", strDataDir, _(PACKAGE_NAME), e.what()));
+ fs::path datadir = GetDataDir();
+ if (!LockDirectory(datadir, ".lock", probeOnly)) {
+ return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), _(PACKAGE_NAME)));
}
return true;
}
@@ -1223,6 +1210,15 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD);
+ // Warn about relative -datadir path.
+ if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) {
+ LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the "
+ "current working directory '%s'. This is fragile, because if bitcoin is started in the future "
+ "from a different location, it will be unable to locate the current data files. There could "
+ "also be data loss if bitcoin is started while in a temporary directory.\n",
+ gArgs.GetArg("-datadir", ""), fs::current_path().string());
+ }
+
InitSignatureCache();
InitScriptExecutionCache();
diff --git a/src/miner.cpp b/src/miner.cpp
index 4e63ab4df0..dda52790c6 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -352,7 +352,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
// Try to compare the mapTx entry to the mapModifiedTx entry
iter = mempool.mapTx.project<0>(mi);
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
- CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) {
+ CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
// The best entry in mapModifiedTx has higher score
// than the one from mapTx.
// Switch which transaction (package) to consider
diff --git a/src/miner.h b/src/miner.h
index 698b4a4788..9c086332d4 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -41,6 +41,12 @@ struct CTxMemPoolModifiedEntry {
nSigOpCostWithAncestors = entry->GetSigOpCostWithAncestors();
}
+ int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
+ uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
+ CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
+ size_t GetTxSize() const { return iter->GetTxSize(); }
+ const CTransaction& GetTx() const { return iter->GetTx(); }
+
CTxMemPool::txiter iter;
uint64_t nSizeWithAncestors;
CAmount nModFeesWithAncestors;
@@ -67,21 +73,6 @@ struct modifiedentry_iter {
}
};
-// This matches the calculation in CompareTxMemPoolEntryByAncestorFee,
-// except operating on CTxMemPoolModifiedEntry.
-// TODO: refactor to avoid duplication of this logic.
-struct CompareModifiedEntry {
- bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) const
- {
- double f1 = (double)a.nModFeesWithAncestors * b.nSizeWithAncestors;
- double f2 = (double)b.nModFeesWithAncestors * a.nSizeWithAncestors;
- if (f1 == f2) {
- return CTxMemPool::CompareIteratorByHash()(a.iter, b.iter);
- }
- return f1 > f2;
- }
-};
-
// A comparator that sorts transactions based on number of ancestors.
// This is sufficient to sort an ancestor package in an order that is valid
// to appear in a block.
@@ -106,7 +97,7 @@ typedef boost::multi_index_container<
// Reuse same tag from CTxMemPool's similar index
boost::multi_index::tag<ancestor_score>,
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
- CompareModifiedEntry
+ CompareTxMemPoolEntryByAncestorFee
>
>
> indexed_modified_transaction_set;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index a2521a1e9e..74f0db3520 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -341,7 +341,7 @@ void AddressTableModel::updateEntry(const QString &address,
priv->updateEntry(address, label, isMine, purpose, status);
}
-QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
+QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type)
{
std::string strLabel = label.toStdString();
std::string strAddress = address.toStdString();
@@ -384,8 +384,8 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
- wallet->LearnRelatedScripts(newKey, g_address_type);
- strAddress = EncodeDestination(GetDestinationForKey(newKey, g_address_type));
+ wallet->LearnRelatedScripts(newKey, address_type);
+ strAddress = EncodeDestination(GetDestinationForKey(newKey, address_type));
}
else
{
diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h
index d04b95ebae..11439e25d5 100644
--- a/src/qt/addresstablemodel.h
+++ b/src/qt/addresstablemodel.h
@@ -8,6 +8,8 @@
#include <QAbstractTableModel>
#include <QStringList>
+enum OutputType : int;
+
class AddressTablePriv;
class WalletModel;
@@ -61,7 +63,7 @@ public:
/* Add an address to the model.
Returns the added address on success, and an empty string otherwise.
*/
- QString addRow(const QString &type, const QString &label, const QString &address);
+ QString addRow(const QString &type, const QString &label, const QString &address, const OutputType address_type);
/* Look up label for address in address book, if not found return empty string.
*/
diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp
index 6b7df2f2b9..a945fc6aa0 100644
--- a/src/qt/editaddressdialog.cpp
+++ b/src/qt/editaddressdialog.cpp
@@ -11,6 +11,8 @@
#include <QDataWidgetMapper>
#include <QMessageBox>
+extern OutputType g_address_type;
+
EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) :
QDialog(parent),
ui(new Ui::EditAddressDialog),
@@ -77,7 +79,8 @@ bool EditAddressDialog::saveCurrentRow()
address = model->addRow(
mode == NewSendingAddress ? AddressTableModel::Send : AddressTableModel::Receive,
ui->labelEdit->text(),
- ui->addressEdit->text());
+ ui->addressEdit->text(),
+ g_address_type);
break;
case EditReceivingAddress:
case EditSendingAddress:
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 58f030ebf0..09fb435a58 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>776</width>
- <height>364</height>
+ <height>396</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
@@ -28,6 +28,22 @@
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout">
+ <item row="5" column="0">
+ <widget class="QLabel" name="label">
+ <property name="toolTip">
+ <string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
+ </property>
+ <property name="text">
+ <string>&amp;Amount:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="buddy">
+ <cstring>reqAmount</cstring>
+ </property>
+ </widget>
+ </item>
<item row="6" column="0">
<widget class="QLabel" name="label_3">
<property name="toolTip">
@@ -51,13 +67,6 @@
</property>
</widget>
</item>
- <item row="6" column="2">
- <widget class="QLineEdit" name="reqMessage">
- <property name="toolTip">
- <string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</string>
- </property>
- </widget>
- </item>
<item row="2" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
@@ -81,32 +90,10 @@
</property>
</widget>
</item>
- <item row="5" column="0">
- <widget class="QLabel" name="label">
- <property name="toolTip">
- <string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
- </property>
- <property name="text">
- <string>&amp;Amount:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- <property name="buddy">
- <cstring>reqAmount</cstring>
- </property>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="BitcoinAmountField" name="reqAmount">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
+ <item row="6" column="2">
+ <widget class="QLineEdit" name="reqMessage">
<property name="toolTip">
- <string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
+ <string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</string>
</property>
</widget>
</item>
@@ -174,6 +161,73 @@
</property>
</widget>
</item>
+ <item row="5" column="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="BitcoinAmountField" name="reqAmount">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1000</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>An optional amount to request. Leave this empty or zero to not request a specific amount.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="useBech32">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1000</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ <property name="toolTip">
+ <string>Bech32 addresses (BIP-173) are cheaper to spend from and offer better protection against typos. When unchecked a P2SH wrapped SegWit address will be created, compatible with older wallets.</string>
+ </property>
+ <property name="text">
+ <string>Generate Bech32 address</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
</layout>
</item>
</layout>
@@ -306,6 +360,7 @@
<tabstops>
<tabstop>reqLabel</tabstop>
<tabstop>reqAmount</tabstop>
+ <tabstop>useBech32</tabstop>
<tabstop>reqMessage</tabstop>
<tabstop>receiveButton</tabstop>
<tabstop>clearButton</tabstop>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index 670d6108db..558d4f108c 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -995,6 +995,18 @@ QString formatBytes(uint64_t bytes)
return QString(QObject::tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
}
+qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize, qreal font_size) {
+ while(font_size >= minPointSize) {
+ font.setPointSizeF(font_size);
+ QFontMetrics fm(font);
+ if (fm.width(text) < width) {
+ break;
+ }
+ font_size -= 0.5;
+ }
+ return font_size;
+}
+
void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
{
Q_EMIT clicked(event->pos());
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index ad0e22ccd6..71a69483f5 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -201,6 +201,8 @@ namespace GUIUtil
QString formatBytes(uint64_t bytes);
+ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);
+
class ClickableLabel : public QLabel
{
Q_OBJECT
diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp
index e07f2b74ee..7fd5285467 100644
--- a/src/qt/receivecoinsdialog.cpp
+++ b/src/qt/receivecoinsdialog.cpp
@@ -2,6 +2,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <wallet/wallet.h>
+
#include <qt/receivecoinsdialog.h>
#include <qt/forms/ui_receivecoinsdialog.h>
@@ -91,6 +93,15 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model)
SLOT(recentRequestsView_selectionChanged(QItemSelection, QItemSelection)));
// Last 2 columns are set by the columnResizingFixer, when the table geometry is ready.
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);
+
+ // configure bech32 checkbox, disable if launched with legacy as default:
+ if (model->getDefaultAddressType() == OUTPUT_TYPE_BECH32) {
+ ui->useBech32->setCheckState(Qt::Checked);
+ } else {
+ ui->useBech32->setCheckState(Qt::Unchecked);
+ }
+
+ ui->useBech32->setVisible(model->getDefaultAddressType() != OUTPUT_TYPE_LEGACY);
}
}
@@ -133,7 +144,11 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
QString address;
QString label = ui->reqLabel->text();
/* Generate new receiving address */
- address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "");
+ OutputType address_type = model->getDefaultAddressType();
+ if (address_type != OUTPUT_TYPE_LEGACY) {
+ address_type = ui->useBech32->isChecked() ? OUTPUT_TYPE_BECH32 : OUTPUT_TYPE_P2SH_SEGWIT;
+ }
+ address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", address_type);
SendCoinsRecipient info(address, label,
ui->reqAmount->value(), ui->reqMessage->text());
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index 209397ca0c..d4cb0e5ba2 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -183,9 +183,13 @@ void ReceiveRequestDialog::update()
QPainter painter(&qrAddrImage);
painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
QFont font = GUIUtil::fixedPitchFont();
- font.setPixelSize(12);
- painter.setFont(font);
QRect paddedRect = qrAddrImage.rect();
+
+ // calculate ideal font size
+ qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 20, info.address, font);
+ font.setPointSizeF(font_size);
+
+ painter.setFont(font);
paddedRect.setHeight(QR_IMAGE_SIZE+12);
painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, info.address);
painter.end();
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 494b46905d..4d7e977fcb 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -733,6 +733,11 @@ bool WalletModel::hdEnabled() const
return wallet->IsHDEnabled();
}
+OutputType WalletModel::getDefaultAddressType() const
+{
+ return g_address_type;
+}
+
int WalletModel::getDefaultConfirmTarget() const
{
return nTxConfirmTarget;
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 7d7e4f160e..9e13de79be 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -15,6 +15,8 @@
#include <QObject>
+enum OutputType : int;
+
class AddressTableModel;
class OptionsModel;
class PlatformStyle;
@@ -214,6 +216,8 @@ public:
bool hdEnabled() const;
+ OutputType getDefaultAddressType() const;
+
int getDefaultConfirmTarget() const;
private:
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 1b2c71c4a4..346672e45a 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -24,6 +24,7 @@
#include <util.h>
#include <utilstrencodings.h>
#include <hash.h>
+#include <validationinterface.h>
#include <warnings.h>
#include <stdint.h>
@@ -323,6 +324,21 @@ UniValue waitforblockheight(const JSONRPCRequest& request)
return ret;
}
+UniValue syncwithvalidationinterfacequeue(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() > 0) {
+ throw std::runtime_error(
+ "syncwithvalidationinterfacequeue\n"
+ "\nWaits for the validation interface queue to catch up on everything that was there when we entered this function.\n"
+ "\nExamples:\n"
+ + HelpExampleCli("syncwithvalidationinterfacequeue","")
+ + HelpExampleRpc("syncwithvalidationinterfacequeue","")
+ );
+ }
+ SyncWithValidationInterfaceQueue();
+ return NullUniValue;
+}
+
UniValue getdifficulty(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 0)
@@ -1628,6 +1644,7 @@ static const CRPCCommand commands[] =
{ "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} },
{ "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} },
{ "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} },
+ { "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} },
};
void RegisterBlockchainRPCCommands(CRPCTable &t)
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 95a27d474b..3bcad16316 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -668,6 +668,18 @@ UniValue echo(const JSONRPCRequest& request)
return request.params;
}
+static UniValue getinfo_deprecated(const JSONRPCRequest& request)
+{
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND,
+ "getinfo\n"
+ "\nThis call was removed in version 0.16.0. Use the appropriate fields from:\n"
+ "- getblockchaininfo: blocks, difficulty, chain\n"
+ "- getnetworkinfo: version, protocolversion, timeoffset, connections, proxy, relayfee, warnings\n"
+ "- getwalletinfo: balance, keypoololdest, keypoolsize, paytxfee, unlocked_until, walletversion\n"
+ "\nbitcoin-cli has the option -getinfo to collect and format these in the old format."
+ );
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
@@ -682,6 +694,7 @@ static const CRPCCommand commands[] =
{ "hidden", "setmocktime", &setmocktime, {"timestamp"}},
{ "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
{ "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}},
+ { "hidden", "getinfo", &getinfo_deprecated, {}},
};
void RegisterMiscRPCCommands(CRPCTable &t)
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index f56f341498..1766c6a093 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -287,35 +287,6 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
- /* Now check the sort on the mining score index.
- * Final order should be:
- *
- * tx7 (2M)
- * tx2 (20k)
- * tx4 (15000)
- * tx1/tx5 (10000)
- * tx3/6 (0)
- * (Ties resolved by hash)
- */
- sortedOrder.clear();
- sortedOrder.push_back(tx7.GetHash().ToString());
- sortedOrder.push_back(tx2.GetHash().ToString());
- sortedOrder.push_back(tx4.GetHash().ToString());
- if (tx1.GetHash() < tx5.GetHash()) {
- sortedOrder.push_back(tx5.GetHash().ToString());
- sortedOrder.push_back(tx1.GetHash().ToString());
- } else {
- sortedOrder.push_back(tx1.GetHash().ToString());
- sortedOrder.push_back(tx5.GetHash().ToString());
- }
- if (tx3.GetHash() < tx6.GetHash()) {
- sortedOrder.push_back(tx6.GetHash().ToString());
- sortedOrder.push_back(tx3.GetHash().ToString());
- } else {
- sortedOrder.push_back(tx3.GetHash().ToString());
- sortedOrder.push_back(tx6.GetHash().ToString());
- }
- CheckSort<mining_score>(pool, sortedOrder);
}
BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
@@ -427,6 +398,23 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
sortedOrder.erase(sortedOrder.end()-2);
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
CheckSort<ancestor_score>(pool, sortedOrder);
+
+ // High-fee parent, low-fee child
+ // tx7 -> tx8
+ CMutableTransaction tx8 = CMutableTransaction();
+ tx8.vin.resize(1);
+ tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
+ tx8.vin[0].scriptSig = CScript() << OP_11;
+ tx8.vout.resize(1);
+ tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
+ tx8.vout[0].nValue = 10*COIN;
+
+ // Check that we sort by min(feerate, ancestor_feerate):
+ // set the fee so that the ancestor feerate is above tx1/5,
+ // but the transaction's own feerate is lower
+ pool.addUnchecked(tx8.GetHash(), entry.Fee(5000LL).FromTx(tx8));
+ sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
+ CheckSort<ancestor_score>(pool, sortedOrder);
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index ffb024aef9..d1edde284f 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -906,8 +906,8 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
- // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
- return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
+ // Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
+ return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 12 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(mapLinks) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage;
}
void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) {
diff --git a/src/txmempool.h b/src/txmempool.h
index 512e70f8fa..d6f8e7094b 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -206,18 +206,14 @@ class CompareTxMemPoolEntryByDescendantScore
public:
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
{
- bool fUseADescendants = UseDescendantScore(a);
- bool fUseBDescendants = UseDescendantScore(b);
+ double a_mod_fee, a_size, b_mod_fee, b_size;
- double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee();
- double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize();
-
- double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee();
- double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize();
+ GetModFeeAndSize(a, a_mod_fee, a_size);
+ GetModFeeAndSize(b, b_mod_fee, b_size);
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
- double f1 = aModFee * bSize;
- double f2 = aSize * bModFee;
+ double f1 = a_mod_fee * b_size;
+ double f2 = a_size * b_mod_fee;
if (f1 == f2) {
return a.GetTime() >= b.GetTime();
@@ -225,12 +221,21 @@ public:
return f1 < f2;
}
- // Calculate which score to use for an entry (avoiding division).
- bool UseDescendantScore(const CTxMemPoolEntry &a) const
+ // Return the fee/size we're using for sorting this entry.
+ void GetModFeeAndSize(const CTxMemPoolEntry &a, double &mod_fee, double &size) const
{
+ // Compare feerate with descendants to feerate of the transaction, and
+ // return the fee/size for the max.
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
- return f2 > f1;
+
+ if (f2 > f1) {
+ mod_fee = a.GetModFeesWithDescendants();
+ size = a.GetSizeWithDescendants();
+ } else {
+ mod_fee = a.GetModifiedFee();
+ size = a.GetTxSize();
+ }
}
};
@@ -261,33 +266,53 @@ public:
}
};
+/** \class CompareTxMemPoolEntryByAncestorScore
+ *
+ * Sort an entry by min(score/size of entry's tx, score/size with all ancestors).
+ */
class CompareTxMemPoolEntryByAncestorFee
{
public:
- bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
+ template<typename T>
+ bool operator()(const T& a, const T& b) const
{
- double aFees = a.GetModFeesWithAncestors();
- double aSize = a.GetSizeWithAncestors();
+ double a_mod_fee, a_size, b_mod_fee, b_size;
- double bFees = b.GetModFeesWithAncestors();
- double bSize = b.GetSizeWithAncestors();
+ GetModFeeAndSize(a, a_mod_fee, a_size);
+ GetModFeeAndSize(b, b_mod_fee, b_size);
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
- double f1 = aFees * bSize;
- double f2 = aSize * bFees;
+ double f1 = a_mod_fee * b_size;
+ double f2 = a_size * b_mod_fee;
if (f1 == f2) {
return a.GetTx().GetHash() < b.GetTx().GetHash();
}
-
return f1 > f2;
}
+
+ // Return the fee/size we're using for sorting this entry.
+ template <typename T>
+ void GetModFeeAndSize(const T &a, double &mod_fee, double &size) const
+ {
+ // Compare feerate with ancestors to feerate of the transaction, and
+ // return the fee/size for the min.
+ double f1 = (double)a.GetModifiedFee() * a.GetSizeWithAncestors();
+ double f2 = (double)a.GetModFeesWithAncestors() * a.GetTxSize();
+
+ if (f1 > f2) {
+ mod_fee = a.GetModFeesWithAncestors();
+ size = a.GetSizeWithAncestors();
+ } else {
+ mod_fee = a.GetModifiedFee();
+ size = a.GetTxSize();
+ }
+ }
};
// Multi_index tag names
struct descendant_score {};
struct entry_time {};
-struct mining_score {};
struct ancestor_score {};
class CBlockPolicyEstimator;
@@ -356,7 +381,6 @@ public:
* - transaction hash
* - feerate [we use max(feerate of tx, feerate of tx with all descendants)]
* - time in mempool
- * - mining score (feerate modified by any fee deltas from PrioritiseTransaction)
*
* Note: the term "descendant" refers to in-mempool transactions that depend on
* this one, while "ancestor" refers to in-mempool transactions that a given
@@ -446,12 +470,6 @@ public:
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByEntryTime
>,
- // sorted by score (for mining prioritization)
- boost::multi_index::ordered_unique<
- boost::multi_index::tag<mining_score>,
- boost::multi_index::identity<CTxMemPoolEntry>,
- CompareTxMemPoolEntryByScore
- >,
// sorted by fee rate with ancestors
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<ancestor_score>,
diff --git a/src/util.cpp b/src/util.cpp
index 150bc503df..80eed24ffd 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -72,6 +72,7 @@
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
+#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/program_options/detail/config_file.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
@@ -375,6 +376,27 @@ int LogPrintStr(const std::string &str)
return ret;
}
+bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
+{
+ fs::path pathLockFile = directory / lockfile_name;
+ FILE* file = fsbridge::fopen(pathLockFile, "a"); // empty lock file; created if it doesn't exist.
+ if (file) fclose(file);
+
+ try {
+ static std::map<std::string, boost::interprocess::file_lock> locks;
+ boost::interprocess::file_lock& lock = locks.emplace(pathLockFile.string(), pathLockFile.string().c_str()).first->second;
+ if (!lock.try_lock()) {
+ return false;
+ }
+ if (probe_only) {
+ lock.unlock();
+ }
+ } catch (const boost::interprocess::interprocess_exception& e) {
+ return error("Error while attempting to lock directory %s: %s", directory.string(), e.what());
+ }
+ return true;
+}
+
/** Interpret string as boolean, for argument parsing */
static bool InterpretBool(const std::string& strValue)
{
diff --git a/src/util.h b/src/util.h
index 6a0d6a31e7..277b4c66af 100644
--- a/src/util.h
+++ b/src/util.h
@@ -173,6 +173,7 @@ bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);
bool RenameOver(fs::path src, fs::path dest);
+bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only=false);
bool TryCreateDirectories(const fs::path& p);
fs::path GetDefaultDataDir();
const fs::path &GetDataDir(bool fNetSpecific = true);
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index 35ff0e1eec..23c6279128 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -95,7 +95,7 @@ void CDBEnv::Close()
EnvShutdown();
}
-bool CDBEnv::Open(const fs::path& pathIn)
+bool CDBEnv::Open(const fs::path& pathIn, bool retry)
{
if (fDbEnvInit)
return true;
@@ -103,6 +103,11 @@ bool CDBEnv::Open(const fs::path& pathIn)
boost::this_thread::interruption_point();
strPath = pathIn.string();
+ if (!LockDirectory(pathIn, ".walletlock")) {
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ return false;
+ }
+
fs::path pathLogDir = pathIn / "database";
TryCreateDirectories(pathLogDir);
fs::path pathErrorFile = pathIn / "db.log";
@@ -134,7 +139,24 @@ bool CDBEnv::Open(const fs::path& pathIn)
S_IRUSR | S_IWUSR);
if (ret != 0) {
dbenv->close(0);
- return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
+ LogPrintf("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret));
+ if (retry) {
+ // try moving the database env out of the way
+ fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime());
+ try {
+ fs::rename(pathLogDir, pathDatabaseBak);
+ LogPrintf("Moved old %s to %s. Retrying.\n", pathLogDir.string(), pathDatabaseBak.string());
+ } catch (const fs::filesystem_error&) {
+ // failure is ok (well, not really, but it's not worse than what we started with)
+ }
+ // try opening it again one more time
+ if (!Open(pathIn, false)) {
+ // if it still fails, it probably means we can't even create the database env
+ return false;
+ }
+ } else {
+ return false;
+ }
}
fDbEnvInit = true;
@@ -269,25 +291,11 @@ bool CDB::VerifyEnvironment(const std::string& walletFile, const fs::path& walle
return false;
}
- if (!bitdb.Open(walletDir))
- {
- // try moving the database env out of the way
- fs::path pathDatabase = walletDir / "database";
- fs::path pathDatabaseBak = walletDir / strprintf("database.%d.bak", GetTime());
- try {
- fs::rename(pathDatabase, pathDatabaseBak);
- LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string());
- } catch (const fs::filesystem_error&) {
- // failure is ok (well, not really, but it's not worse than what we started with)
- }
-
- // try again
- if (!bitdb.Open(walletDir)) {
- // if it still fails, it probably means we can't even create the database env
- errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
- return false;
- }
+ if (!bitdb.Open(walletDir, true)) {
+ errorStr = strprintf(_("Error initializing wallet database environment %s!"), walletDir);
+ return false;
}
+
return true;
}
diff --git a/src/wallet/db.h b/src/wallet/db.h
index c6f317927f..787135e400 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -68,7 +68,7 @@ public:
typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
- bool Open(const fs::path& path);
+ bool Open(const fs::path& path, bool retry = 0);
void Close();
void Flush(bool fShutdown);
void CheckpointLSN(const std::string& strFile);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 0f453f154f..2d26f7ae0f 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -205,11 +205,15 @@ bool VerifyWallets()
return true;
}
- if (gArgs.IsArgSet("-walletdir") && !fs::is_directory(GetWalletDir())) {
- if (fs::exists(fs::system_complete(gArgs.GetArg("-walletdir", "")))) {
- return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), gArgs.GetArg("-walletdir", "").c_str()));
+ if (gArgs.IsArgSet("-walletdir")) {
+ fs::path wallet_dir = gArgs.GetArg("-walletdir", "");
+ if (!fs::exists(wallet_dir)) {
+ return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
+ } else if (!fs::is_directory(wallet_dir)) {
+ return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
+ } else if (!wallet_dir.is_absolute()) {
+ return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
}
- return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), gArgs.GetArg("-walletdir", "").c_str()));
}
LogPrintf("Using wallet directory %s\n", GetWalletDir().string());
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 7188bb40bc..7770b41e56 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -144,7 +144,7 @@ UniValue getnewaddress(const JSONRPCRequest& request)
"so payments received with the address will be credited to 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n"
- "2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh\", and \"bech32\". Default is set by -addresstype.\n"
+ "2. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -addresstype.\n"
"\nResult:\n"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n"
@@ -242,7 +242,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
"\nArguments:\n"
- "1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh\", and \"bech32\". Default is set by -changetype.\n"
+ "1. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is set by -changetype.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n"
@@ -1285,7 +1285,8 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
{
std::string msg = "addwitnessaddress \"address\" ( p2sh )\n"
- "\nAdd a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
+ "\nDEPRECATED: set the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"
+ "Add a witness address for a script (with pubkey or redeemscript known). Requires a new wallet backup.\n"
"It returns the witness script.\n"
"\nArguments:\n"
@@ -1299,6 +1300,12 @@ UniValue addwitnessaddress(const JSONRPCRequest& request)
throw std::runtime_error(msg);
}
+ if (!IsDeprecatedRPCEnabled("addwitnessaddress")) {
+ throw JSONRPCError(RPC_METHOD_DEPRECATED, "addwitnessaddress is deprecated and will be fully removed in v0.17. "
+ "To use addwitnessaddress in v0.16, restart bitcoind with -deprecatedrpc=addwitnessaddress.\n"
+ "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n");
+ }
+
{
LOCK(cs_main);
if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !gArgs.GetBoolArg("-walletprematurewitness", false)) {
@@ -2283,7 +2290,8 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
"This is needed prior to performing transactions related to private keys such as sending bitcoins\n"
"\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n"
- "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n"
+ "2. timeout (numeric, required) The time to keep the decryption key in seconds. Limited to at most 1073741824 (2^30) seconds.\n"
+ " Any value greater than 1073741824 seconds will be set to 1073741824 seconds.\n"
"\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n"
"time that overrides the old one.\n"
@@ -2312,6 +2320,17 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
strWalletPass = request.params[0].get_str().c_str();
+ // Get the timeout
+ int64_t nSleepTime = request.params[1].get_int64();
+ // Timeout cannot be negative, otherwise it will relock immediately
+ if (nSleepTime < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
+ }
+ // Clamp timeout to 2^30 seconds
+ if (nSleepTime > (int64_t)1 << 30) {
+ nSleepTime = (int64_t)1 << 30;
+ }
+
if (strWalletPass.length() > 0)
{
if (!pwallet->Unlock(strWalletPass)) {
@@ -2325,7 +2344,6 @@ UniValue walletpassphrase(const JSONRPCRequest& request)
pwallet->TopUpKeyPool();
- int64_t nSleepTime = request.params[1].get_int64();
pwallet->nRelockTime = GetTime() + nSleepTime;
RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), boost::bind(LockWallet, pwallet), nSleepTime);
@@ -3478,7 +3496,7 @@ static const CRPCCommand commands[] =
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} },
- { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
+ { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} },
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 70ced30e45..354f524821 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -99,7 +99,7 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
};
-enum OutputType
+enum OutputType : int
{
OUTPUT_TYPE_NONE,
OUTPUT_TYPE_LEGACY,
diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp
index f15e5de1e2..7c97b668ae 100644
--- a/src/wallet/walletutil.cpp
+++ b/src/wallet/walletutil.cpp
@@ -9,7 +9,7 @@ fs::path GetWalletDir()
fs::path path;
if (gArgs.IsArgSet("-walletdir")) {
- path = fs::system_complete(gArgs.GetArg("-walletdir", ""));
+ path = gArgs.GetArg("-walletdir", "");
if (!fs::is_directory(path)) {
// If the path specified doesn't exist, we return the deliberately
// invalid empty string.