diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/addrman.h | 2 | ||||
-rw-r--r-- | src/bitcoin-tx.cpp | 8 | ||||
-rw-r--r-- | src/chainparams.cpp | 1 | ||||
-rw-r--r-- | src/netbase.cpp | 125 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 9 | ||||
-rw-r--r-- | src/qt/forms/sendcoinsdialog.ui | 9 | ||||
-rw-r--r-- | src/qt/guiutil.cpp | 26 | ||||
-rw-r--r-- | src/qt/guiutil.h | 5 | ||||
-rw-r--r-- | src/qt/modaloverlay.cpp | 11 | ||||
-rw-r--r-- | src/qt/optionsdialog.cpp | 2 | ||||
-rw-r--r-- | src/qt/optionsmodel.cpp | 22 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 11 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 24 | ||||
-rw-r--r-- | src/qt/transactionview.cpp | 27 | ||||
-rw-r--r-- | src/qt/transactionview.h | 4 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 4 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 2 | ||||
-rw-r--r-- | src/rpc/client.cpp | 4 | ||||
-rw-r--r-- | src/rpc/protocol.cpp | 21 | ||||
-rw-r--r-- | src/rpc/protocol.h | 2 | ||||
-rw-r--r-- | src/validationinterface.cpp | 2 | ||||
-rw-r--r-- | src/versionbits.cpp | 2 | ||||
-rw-r--r-- | src/wallet/db.cpp | 12 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 20 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 19 | ||||
-rw-r--r-- | src/wallet/test/wallet_tests.cpp | 17 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 12 |
27 files changed, 252 insertions, 151 deletions
diff --git a/src/addrman.h b/src/addrman.h index 547088aedf..18f3062287 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -472,6 +472,8 @@ public: nTried = 0; nNew = 0; nLastGood = 1; //Initially at 1 so that "never" is strictly worse. + mapInfo.clear(); + mapAddr.clear(); } CAddrMan() diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index cff90d13af..0f2c19bd5d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -310,6 +310,9 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str } if (bSegWit) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } @@ -377,6 +380,11 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { + for (CPubKey& pubkey : pubkeys) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2021ec51db..872626f160 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -228,7 +228,6 @@ public: vSeeds.emplace_back("testnet-seed.bitcoin.jonasschnelli.ch", true); vSeeds.emplace_back("seed.tbtc.petertodd.org", true); vSeeds.emplace_back("testnet-seed.bluematt.me", false); - vSeeds.emplace_back("testnet-seed.bitcoin.schildbach.de", false); base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111); base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196); diff --git a/src/netbase.cpp b/src/netbase.cpp index 05f9f6961c..914124cb17 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout) return timeout; } +/** SOCKS version */ +enum SOCKSVersion: uint8_t { + SOCKS4 = 0x04, + SOCKS5 = 0x05 +}; + +/** Values defined for METHOD in RFC1928 */ +enum SOCKS5Method: uint8_t { + NOAUTH = 0x00, //! No authentication required + GSSAPI = 0x01, //! GSSAPI + USER_PASS = 0x02, //! Username/password + NO_ACCEPTABLE = 0xff, //! No acceptable methods +}; + +/** Values defined for CMD in RFC1928 */ +enum SOCKS5Command: uint8_t { + CONNECT = 0x01, + BIND = 0x02, + UDP_ASSOCIATE = 0x03 +}; + +/** Values defined for REP in RFC1928 */ +enum SOCKS5Reply: uint8_t { + SUCCEEDED = 0x00, //! Succeeded + GENFAILURE = 0x01, //! General failure + NOTALLOWED = 0x02, //! Connection not allowed by ruleset + NETUNREACHABLE = 0x03, //! Network unreachable + HOSTUNREACHABLE = 0x04, //! Network unreachable + CONNREFUSED = 0x05, //! Connection refused + TTLEXPIRED = 0x06, //! TTL expired + CMDUNSUPPORTED = 0x07, //! Command not supported + ATYPEUNSUPPORTED = 0x08, //! Address type not supported +}; + +/** Values defined for ATYPE in RFC1928 */ +enum SOCKS5Atyp: uint8_t { + IPV4 = 0x01, + DOMAINNAME = 0x03, + IPV6 = 0x04, +}; + +/** Status codes that can be returned by InterruptibleRecv */ enum class IntrRecvError { OK, Timeout, @@ -203,7 +245,7 @@ enum class IntrRecvError { * * @note This function requires that hSocket is in non-blocking mode. */ -static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket) +static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket) { int64_t curTime = GetTimeMillis(); int64_t endTime = curTime + timeout; @@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons // to break off in case of an interruption. const int64_t maxWait = 1000; while (len > 0 && curTime < endTime) { - ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first + ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first if (ret > 0) { len -= ret; data += ret; @@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } +/** Credentials for proxy authentication */ struct ProxyCredentials { std::string username; std::string password; }; -std::string Socks5ErrorString(int err) +/** Convert SOCKS5 reply to a an error message */ +std::string Socks5ErrorString(uint8_t err) { switch(err) { - case 0x01: return "general failure"; - case 0x02: return "connection not allowed"; - case 0x03: return "network unreachable"; - case 0x04: return "host unreachable"; - case 0x05: return "connection refused"; - case 0x06: return "TTL expired"; - case 0x07: return "protocol error"; - case 0x08: return "address type not supported"; - default: return "unknown"; + case SOCKS5Reply::GENFAILURE: + return "general failure"; + case SOCKS5Reply::NOTALLOWED: + return "connection not allowed"; + case SOCKS5Reply::NETUNREACHABLE: + return "network unreachable"; + case SOCKS5Reply::HOSTUNREACHABLE: + return "host unreachable"; + case SOCKS5Reply::CONNREFUSED: + return "connection refused"; + case SOCKS5Reply::TTLEXPIRED: + return "TTL expired"; + case SOCKS5Reply::CMDUNSUPPORTED: + return "protocol error"; + case SOCKS5Reply::ATYPEUNSUPPORTED: + return "address type not supported"; + default: + return "unknown"; } } @@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials } // Accepted authentication methods std::vector<uint8_t> vSocks5Init; - vSocks5Init.push_back(0x05); + vSocks5Init.push_back(SOCKSVersion::SOCKS5); if (auth) { - vSocks5Init.push_back(0x02); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED - vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929) + vSocks5Init.push_back(0x02); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); + vSocks5Init.push_back(SOCKS5Method::USER_PASS); } else { - vSocks5Init.push_back(0x01); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + vSocks5Init.push_back(0x01); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); } ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL); if (ret != (ssize_t)vSocks5Init.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet1[2]; + uint8_t pchRet1[2]; if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); return false; } - if (pchRet1[0] != 0x05) { + if (pchRet1[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to initialize"); } - if (pchRet1[1] == 0x02 && auth) { + if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) { // Perform username/password authentication (as described in RFC1929) std::vector<uint8_t> vAuth; - vAuth.push_back(0x01); + vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation if (auth->username.size() > 255 || auth->password.size() > 255) return error("Proxy username or password too long"); vAuth.push_back(auth->username.size()); @@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error sending authentication to proxy"); } LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); - char pchRetA[2]; + uint8_t pchRetA[2]; if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); return error("Error reading proxy authentication response"); @@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Proxy authentication unsuccessful"); } - } else if (pchRet1[1] == 0x00) { + } else if (pchRet1[1] == SOCKS5Method::NOAUTH) { // Perform no authentication } else { CloseSocket(hSocket); return error("Proxy requested wrong authentication method %02x", pchRet1[1]); } std::vector<uint8_t> vSocks5; - vSocks5.push_back(0x05); // VER protocol version - vSocks5.push_back(0x01); // CMD CONNECT - vSocks5.push_back(0x00); // RSV Reserved - vSocks5.push_back(0x03); // ATYP DOMAINNAME + vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version + vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT + vSocks5.push_back(0x00); // RSV Reserved must be 0 + vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); vSocks5.push_back((port >> 8) & 0xFF); @@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet2[4]; + uint8_t pchRet2[4]; if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); if (recvr == IntrRecvError::Timeout) { @@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error while reading proxy response"); } } - if (pchRet2[0] != 0x05) { + if (pchRet2[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to accept request"); } - if (pchRet2[1] != 0x00) { + if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) { // Failures to connect to a peer that are not proxy errors CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); return false; } - if (pchRet2[2] != 0x00) { + if (pchRet2[2] != 0x00) { // Reserved field must be 0 CloseSocket(hSocket); return error("Error: malformed proxy response"); } - char pchRet3[256]; + uint8_t pchRet3[256]; switch (pchRet2[3]) { - case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x03: + case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::DOMAINNAME: { recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); if (recvr != IntrRecvError::OK) { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e3970298e6..56373563b8 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -123,7 +123,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); + QSettings settings; + if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET @@ -261,7 +265,8 @@ BitcoinGUI::~BitcoinGUI() // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); - GUIUtil::saveWindowGeometry("nWindow", this); + QSettings settings; + settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); #ifdef Q_OS_MAC diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 1e2f2302b9..a0e48334c1 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -846,19 +846,13 @@ <item> <layout class="QHBoxLayout" name="horizontalLayoutFee13"> <item> - <widget class="QRadioButton" name="radioCustomPerKilobyte"> + <widget class="QLabel" name="labelCustomPerKilobyte"> <property name="toolTip"> <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string> </property> <property name="text"> <string>per kilobyte</string> </property> - <property name="checked"> - <bool>true</bool> - </property> - <attribute name="buttonGroup"> - <string notr="true">groupCustomFee</string> - </attribute> </widget> </item> <item> @@ -1285,6 +1279,5 @@ <connections/> <buttongroups> <buttongroup name="groupFee"/> - <buttongroup name="groupCustomFee"/> </buttongroups> </ui> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebec..69c3e06869 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -842,32 +842,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif -void saveWindowGeometry(const QString& strSetting, QWidget *parent) -{ - QSettings settings; - settings.setValue(strSetting + "Pos", parent->pos()); - settings.setValue(strSetting + "Size", parent->size()); -} - -void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) -{ - QSettings settings; - QPoint pos = settings.value(strSetting + "Pos").toPoint(); - QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - - parent->resize(size); - parent->move(pos); - - if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) - { - QRect screen = QApplication::desktop()->screenGeometry(); - QPoint defaultPos((screen.width() - defaultSize.width()) / 2, - (screen.height() - defaultSize.height()) / 2); - parent->resize(defaultSize); - parent->move(defaultPos); - } -} - void setClipboard(const QString& str) { QApplication::clipboard()->setText(str, QClipboard::Clipboard); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea6..d10818d0c8 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -179,11 +179,6 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /** Save window size and position */ - void saveWindowGeometry(const QString& strSetting, QWidget *parent); - /** Restore window size and position */ - void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); - /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index a83f285034..4f357e297d 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -99,15 +99,18 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri progressDelta = progressStart-sample.second; timeDelta = blockProcessTime[0].first - sample.first; progressPerHour = progressDelta/(double)timeDelta*1000*3600; - remainingMSecs = remainingProgress / progressDelta * timeDelta; + remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); - // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); + if(remainingMSecs >= 0) { + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); + } else { + ui->expectedTimeLeft->setText(QObject::tr("unknown")); + } static const int MAX_SAMPLES = 5000; if (blockProcessTime.count() > MAX_SAMPLES) @@ -169,4 +172,4 @@ void ModalOverlay::closeClicked() { showHide(true); userClosed = true; -} +}
\ No newline at end of file diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b80b6541dd..550884ed8e 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -80,6 +80,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME))); ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME))); + ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME))); + ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME))); ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); for (const QString &langStr : translations.entryList()) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 77efef3e3c..a2dd52d9cb 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -151,10 +151,32 @@ void OptionsModel::Init(bool resetSettings) language = settings.value("language").toString(); } +/** Helper function to copy contents from one QSettings to another. + * By using allKeys this also covers nested settings in a hierarchy. + */ +static void CopySettings(QSettings& dst, const QSettings& src) +{ + for (const QString& key : src.allKeys()) { + dst.setValue(key, src.value(key)); + } +} + +/** Back up a QSettings to an ini-formatted file. */ +static void BackupSettings(const fs::path& filename, const QSettings& src) +{ + qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); + QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); + dst.clear(); + CopySettings(dst, src); +} + void OptionsModel::Reset() { QSettings settings; + // Backup old settings to chain-specific datadir for troubleshooting + BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98efa..d895fc1663 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,6 +28,7 @@ #include <wallet/wallet.h> #endif +#include <QDesktopWidget> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> @@ -428,7 +429,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : consoleFontSize(0) { ui->setupUi(this); - GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); + QSettings settings; + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME))); @@ -466,14 +471,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); - QSettings settings; consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt(); clear(); } RPCConsole::~RPCConsole() { - GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); + QSettings settings; + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 38a475227e..96adae73da 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -114,10 +114,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p settings.setValue("nFeeRadio", 1); // custom if (!settings.contains("nFeeRadio")) settings.setValue("nFeeRadio", 0); // recommended - if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility - settings.setValue("nCustomFeeRadio", 1); // total at least - if (!settings.contains("nCustomFeeRadio")) - settings.setValue("nCustomFeeRadio", 0); // per kilobyte if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) @@ -127,8 +123,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); - ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button(0)->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); @@ -178,7 +172,6 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); @@ -214,7 +207,6 @@ SendCoinsDialog::~SendCoinsDialog() QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); - settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -609,7 +601,6 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { - ui->radioCustomPerKilobyte->setChecked(true); ui->customFee->setValue(CWallet::GetRequiredFee(1000)); } @@ -622,7 +613,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); - ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -634,8 +625,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel() if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { - ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + - ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); + ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB"); } } @@ -789,10 +779,8 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) } else // Valid address { - CKeyID keyid; - addr.GetKeyID(keyid); - if (!model->havePrivKey(keyid)) // Unknown change address - { + const CTxDestination dest = addr.Get(); + if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); // confirmation dialog @@ -800,7 +788,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -819,7 +807,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; } } } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9db..39dfdb587c 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -33,6 +33,7 @@ #include <QScrollBar> #include <QSignalMapper> #include <QTableView> +#include <QTimer> #include <QUrl> #include <QVBoxLayout> @@ -112,6 +113,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); + // Delay before filtering transactions in ms + static const int input_filter_delay = 200; + + QTimer* amount_typing_delay = new QTimer(this); + amount_typing_delay->setSingleShot(true); + amount_typing_delay->setInterval(input_filter_delay); + + QTimer* prefix_typing_delay = new QTimer(this); + prefix_typing_delay->setSingleShot(true); + prefix_typing_delay->setInterval(input_filter_delay); + QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); @@ -173,8 +185,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start())); + connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount())); + connect(addressWidget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start())); + connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedPrefix())); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -312,20 +326,19 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } -void TransactionView::changedPrefix(const QString &prefix) +void TransactionView::changedPrefix() { if(!transactionProxyModel) return; - transactionProxyModel->setAddressPrefix(prefix); + transactionProxyModel->setAddressPrefix(addressWidget->text()); } -void TransactionView::changedAmount(const QString &amount) +void TransactionView::changedAmount() { if(!transactionProxyModel) return; CAmount amount_parsed = 0; - if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) - { + if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } else diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 52e57cae4c..5b4cfd4a88 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -112,8 +112,8 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); - void changedPrefix(const QString &prefix); - void changedAmount(const QString &amount); + void changedAmount(); + void changedPrefix(); void exportClicked(); void focusTransaction(const QModelIndex&); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 3ca726d0f9..aedbf22e4d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -561,9 +561,9 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return wallet->GetPubKey(address, vchPubKeyOut); } -bool WalletModel::havePrivKey(const CKeyID &address) const +bool WalletModel::IsSpendable(const CTxDestination& dest) const { - return wallet->HaveKey(address); + return IsMine(*wallet, dest) & ISMINE_SPENDABLE; } bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e2..05733f8272 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -190,7 +190,7 @@ public: UnlockContext requestUnlock(); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool havePrivKey(const CKeyID &address) const; + bool IsSpendable(const CTxDestination& dest) const; bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); bool isSpent(const COutPoint& outpoint) const; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4179453782..406ad2f6ec 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -115,8 +115,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, - { "estimatesmartfee", 0, "nblocks" }, - { "estimaterawfee", 0, "nblocks" }, + { "estimatesmartfee", 0, "conf_target" }, + { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, { "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 2, "fee_delta" }, diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index db0626b5e1..dc6bcec382 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -66,9 +66,14 @@ static const std::string COOKIEAUTH_USER = "__cookie__"; /** Default name for auth cookie file */ static const std::string COOKIEAUTH_FILE = ".cookie"; -fs::path GetAuthCookieFile() +/** Get name of RPC authentication cookie file */ +static fs::path GetAuthCookieFile(bool temp=false) { - fs::path path(gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + std::string arg = gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE); + if (temp) { + arg += ".tmp"; + } + fs::path path(arg); if (!path.is_complete()) path = GetDataDir() / path; return path; } @@ -84,14 +89,20 @@ bool GenerateAuthCookie(std::string *cookie_out) * these are set to 077 in init.cpp unless overridden with -sysperms. */ std::ofstream file; - fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + fs::path filepath_tmp = GetAuthCookieFile(true); + file.open(filepath_tmp.string().c_str()); if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); + LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; } file << cookie; file.close(); + + fs::path filepath = GetAuthCookieFile(false); + if (!RenameOver(filepath_tmp, filepath)) { + LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string()); + return false; + } LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); if (cookie_out) diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 4bd4702d62..5c9c64f67d 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -91,8 +91,6 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCError(int code, const std::string& message); -/** Get name of RPC authentication cookie file */ -fs::path GetAuthCookieFile(); /** Generate a new RPC authentication cookie and write it to disk */ bool GenerateAuthCookie(std::string *cookie_out); /** Read the RPC authentication cookie from disk */ diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index bf20d606f8..cd2fecd224 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -4,7 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "validationinterface.h" + #include "init.h" +#include "primitives/block.h" #include "scheduler.h" #include "sync.h" #include "util.h" diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 04a692d826..59588023ad 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -107,7 +107,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* // return the numerical statistics of blocks signalling the specified BIP9 condition in this current period BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const { - BIP9Stats stats; + BIP9Stats stats = {}; stats.period = Period(params); stats.threshold = Threshold(params); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index b12d46e40a..d2fe4866fa 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -101,8 +101,10 @@ bool CDBEnv::Open(const fs::path& pathIn) DB_RECOVER | nEnvFlags, S_IRUSR | S_IWUSR); - if (ret != 0) + if (ret != 0) { + dbenv->close(0); return error("CDBEnv::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + } fDbEnvInit = true; fMockDb = false; @@ -196,9 +198,9 @@ bool CDB::Recover(const std::string& filename, void *callbackDataIn, bool (*reco DB_BTREE, // Database type DB_CREATE, // Flags 0); - if (ret > 0) - { + if (ret > 0) { LogPrintf("Cannot create database file %s\n", filename); + pdbCopy->close(0); return false; } @@ -536,8 +538,10 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) env->CloseDb(strFile); if (pdbCopy->close(0)) fSuccess = false; - delete pdbCopy; + } else { + pdbCopy->close(0); } + delete pdbCopy; } if (fSuccess) { Db dbA(env->dbenv, 0); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 67c6d9ec64..6c8ff7fc3b 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -79,10 +79,10 @@ UniValue importprivkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n" + "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" "\nArguments:\n" - "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" + "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is true.\n" @@ -595,7 +595,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" - "\nDumps all wallet keys in a human-readable format.\n" + "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n" "\nResult:\n" @@ -611,9 +611,19 @@ UniValue dumpwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::ofstream file; boost::filesystem::path filepath = request.params[0].get_str(); filepath = boost::filesystem::absolute(filepath); + + /* Prevent arbitrary files from being overwritten. There have been reports + * that users have overwritten wallet files this way: + * https://github.com/bitcoin/bitcoin/issues/9934 + * It may also avoid other security issues. + */ + if (boost::filesystem::exists(filepath)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first"); + } + + std::ofstream file; file.open(filepath.string().c_str()); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -950,7 +960,7 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 pwallet->SetAddressBook(vchAddress, label, "receive"); if (pwallet->HaveKey(vchAddress)) { - return false; + throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); } pwallet->mapKeyMetadata[vchAddress].nCreateTime = timestamp; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a6176c3485..6efbc23281 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1823,19 +1823,20 @@ UniValue listsinceblock(const JSONRPCRequest& request) int target_confirms = 1; isminefilter filter = ISMINE_SPENDABLE; - if (!request.params[0].isNull()) { + if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { uint256 blockId; blockId.SetHex(request.params[0].get_str()); BlockMap::iterator it = mapBlockIndex.find(blockId); - if (it != mapBlockIndex.end()) { - paltindex = pindex = it->second; - if (chainActive[pindex->nHeight] != pindex) { - // the block being asked for is a part of a deactivated chain; - // we don't want to depend on its perceived height in the block - // chain, we want to instead use the last common ancestor - pindex = chainActive.FindFork(pindex); - } + if (it == mapBlockIndex.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + paltindex = pindex = it->second; + if (chainActive[pindex->nHeight] != pindex) { + // the block being asked for is a part of a deactivated chain; + // we don't want to depend on its perceived height in the block + // chain, we want to instead use the last common ancestor + pindex = chainActive.FindFork(pindex); } } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 4a2cc9a139..5ebacd57d3 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -364,6 +364,12 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) empty_wallet(); } +static void AddKey(CWallet& wallet, const CKey& key) +{ + LOCK(wallet.cs_wallet); + wallet.AddKeyPubKey(key, key.GetPubKey()); +} + BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); @@ -379,8 +385,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // and new block files. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -393,8 +398,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // file. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -599,8 +603,7 @@ public: wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat")))); bool firstRun; wallet->LoadWallet(firstRun); - LOCK(wallet->cs_wallet); - wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(*wallet, coinbaseKey); wallet->ScanForWalletTransactions(chainActive.Genesis()); } @@ -635,7 +638,7 @@ public: BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) { std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString(); - LOCK(wallet->cs_wallet); + LOCK2(cs_main, wallet->cs_wallet); // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 318c159cfb..5e833ebee7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -969,6 +969,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } + // If we have a witness-stripped version of this transaction, and we + // see a new version with a witness, then we must be upgrading a pre-segwit + // wallet. Store the new version of the transaction with the witness, + // as the stripped-version must be invalid. + // TODO: Store all versions of the transaction, instead of just one. + if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) { + wtx.SetTx(wtxIn.tx); + fUpdated = true; + } } //// debug print @@ -3101,13 +3110,14 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_c DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { + LOCK2(cs_main, cs_wallet); + fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { - LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); m_pool_key_to_index.clear(); |