aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bitcoin-wallet.cpp2
-rw-r--r--src/interfaces/node.cpp1
-rw-r--r--src/interfaces/node.h3
-rw-r--r--src/qt/askpassphrasedialog.cpp4
-rw-r--r--src/qt/bitcoin.cpp15
-rw-r--r--src/qt/bitcoin.h2
-rw-r--r--src/qt/bitcoingui.cpp4
-rw-r--r--src/qt/createwalletdialog.cpp9
-rw-r--r--src/qt/createwalletdialog.h6
-rw-r--r--src/qt/forms/askpassphrasedialog.ui2
-rw-r--r--src/qt/forms/createwalletdialog.ui2
-rw-r--r--src/qt/forms/intro.ui10
-rw-r--r--src/qt/intro.cpp18
-rw-r--r--src/qt/intro.h3
-rw-r--r--src/qt/optionsmodel.cpp22
-rw-r--r--src/qt/optionsmodel.h3
-rw-r--r--src/qt/paymentserver.cpp8
-rw-r--r--src/qt/walletcontroller.cpp13
-rw-r--r--src/qt/walletcontroller.h2
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/rawtransaction_util.cpp76
-rw-r--r--src/validation.cpp23
-rw-r--r--src/validation.h19
-rw-r--r--src/wallet/rpcwallet.cpp15
24 files changed, 193 insertions, 71 deletions
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 361fedf35a..eb7f0098ec 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -27,7 +27,7 @@ static void SetupWalletToolArgs()
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("info", "Get wallet info", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
gArgs.AddArg("create", "Create new wallet file", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp
index ccafc3ac8c..c80a8789fc 100644
--- a/src/interfaces/node.cpp
+++ b/src/interfaces/node.cpp
@@ -62,6 +62,7 @@ public:
return gArgs.ParseParameters(argc, argv, error);
}
bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error, true); }
+ void forceSetArg(const std::string& arg, const std::string& value) override { gArgs.ForceSetArg(arg, value); }
bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); }
bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); }
void selectParams(const std::string& network) override { SelectParams(network); }
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index e8c3d0b721..2f4f396e72 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -46,6 +46,9 @@ public:
//! Set command line arguments.
virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0;
+ //! Set a command line argument
+ virtual void forceSetArg(const std::string& arg, const std::string& value) = 0;
+
//! Set a command line argument if it doesn't already have a value
virtual bool softSetArg(const std::string& arg, const std::string& value) = 0;
diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp
index c9f17d12ec..2ababb5e1e 100644
--- a/src/qt/askpassphrasedialog.cpp
+++ b/src/qt/askpassphrasedialog.cpp
@@ -44,7 +44,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri
switch(mode)
{
case Encrypt: // Ask passphrase x2
- ui->warningLabel->setText(tr("Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
+ ui->warningLabel->setText(tr("Enter the new passphrase for the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>."));
ui->passLabel1->hide();
ui->passEdit1->hide();
setWindowTitle(tr("Encrypt wallet"));
@@ -67,7 +67,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent, SecureStri
break;
case ChangePass: // Ask old passphrase + new passphrase x2
setWindowTitle(tr("Change passphrase"));
- ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase to the wallet."));
+ ui->warningLabel->setText(tr("Enter the old passphrase and new passphrase for the wallet."));
break;
}
textChanged();
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index adc4827e63..46f8deee57 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -282,6 +282,10 @@ void BitcoinApplication::parameterSetup()
m_node.initParameterInteraction();
}
+void BitcoinApplication::SetPrune(bool prune, bool force) {
+ optionsModel->SetPrune(prune, force);
+}
+
void BitcoinApplication::requestInitialize()
{
qDebug() << __func__ << ": Requesting initialize";
@@ -487,8 +491,10 @@ int GuiMain(int argc, char* argv[])
/// 5. Now that settings and translations are available, ask user for data directory
// User language is set up: pick a data directory
- if (!Intro::pickDataDirectory(*node))
- return EXIT_SUCCESS;
+ bool did_show_intro = false;
+ bool prune = false; // Intro dialog prune check box
+ // Gracefully exit if the user cancels
+ if (!Intro::showIfNeeded(*node, did_show_intro, prune)) return EXIT_SUCCESS;
/// 6. Determine availability of data directory and parse bitcoin.conf
/// - Do not call GetDataDir(true) before this step finishes
@@ -562,6 +568,11 @@ int GuiMain(int argc, char* argv[])
// Load GUI settings from QSettings
app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false));
+ if (did_show_intro) {
+ // Store intro dialog settings other than datadir (network specific)
+ app.SetPrune(prune, true);
+ }
+
if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
diff --git a/src/qt/bitcoin.h b/src/qt/bitcoin.h
index 3869193a3a..8c77fd8a7d 100644
--- a/src/qt/bitcoin.h
+++ b/src/qt/bitcoin.h
@@ -67,6 +67,8 @@ public:
void parameterSetup();
/// Create options model
void createOptionsModel(bool resetSettings);
+ /// Update prune value
+ void SetPrune(bool prune, bool force = false);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
/// Create splash screen
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index c672171cfb..7671fde705 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -375,7 +375,9 @@ void BitcoinGUI::createActions()
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
const std::string& path = i.first;
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
- // Menu items remove single &. Single & are shown when && is in the string, but only the first occurrence. So replace only the first & with &&
+ // Menu items remove single &. Single & are shown when && is in
+ // the string, but only the first occurrence. So replace only
+ // the first & with &&.
name.replace(name.indexOf(QChar('&')), 1, QString("&&"));
QAction* action = m_open_wallet_menu->addAction(name);
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index 10262c37c3..8e6474b0d4 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -25,7 +25,8 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
});
connect(ui->encrypt_wallet_checkbox, &QCheckBox::toggled, [this](bool checked) {
- // Disable disable_privkeys_checkbox when encrypt is set to true, enable it when encrypt is false
+ // Disable the disable_privkeys_checkbox when isEncryptWalletChecked is
+ // set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
// When the disable_privkeys_checkbox is disabled, uncheck it.
@@ -45,17 +46,17 @@ QString CreateWalletDialog::walletName() const
return ui->wallet_name_line_edit->text();
}
-bool CreateWalletDialog::encrypt() const
+bool CreateWalletDialog::isEncryptWalletChecked() const
{
return ui->encrypt_wallet_checkbox->isChecked();
}
-bool CreateWalletDialog::disablePrivateKeys() const
+bool CreateWalletDialog::isDisablePrivateKeysChecked() const
{
return ui->disable_privkeys_checkbox->isChecked();
}
-bool CreateWalletDialog::blank() const
+bool CreateWalletDialog::isMakeBlankWalletChecked() const
{
return ui->blank_wallet_checkbox->isChecked();
}
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index a1365b5969..30766107b9 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -24,9 +24,9 @@ public:
virtual ~CreateWalletDialog();
QString walletName() const;
- bool encrypt() const;
- bool disablePrivateKeys() const;
- bool blank() const;
+ bool isEncryptWalletChecked() const;
+ bool isDisablePrivateKeysChecked() const;
+ bool isMakeBlankWalletChecked() const;
private:
Ui::CreateWalletDialog *ui;
diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui
index 69803989cd..e74d183818 100644
--- a/src/qt/forms/askpassphrasedialog.ui
+++ b/src/qt/forms/askpassphrasedialog.ui
@@ -95,7 +95,7 @@
<item row="3" column="1">
<widget class="QCheckBox" name="toggleShowPasswordButton">
<property name="text">
- <string>Show password</string>
+ <string>Show passphrase</string>
</property>
</widget>
</item>
diff --git a/src/qt/forms/createwalletdialog.ui b/src/qt/forms/createwalletdialog.ui
index 1fbaeeaaab..e49bab8f3b 100644
--- a/src/qt/forms/createwalletdialog.ui
+++ b/src/qt/forms/createwalletdialog.ui
@@ -62,7 +62,7 @@
</rect>
</property>
<property name="toolTip">
- <string>Encrypt the wallet. The wallet will be encrypted with a password of your choice.</string>
+ <string>Encrypt the wallet. The wallet will be encrypted with a passphrase of your choice.</string>
</property>
<property name="text">
<string>Encrypt Wallet</string>
diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui
index cfdd8482e3..f27a4ebe44 100644
--- a/src/qt/forms/intro.ui
+++ b/src/qt/forms/intro.ui
@@ -211,6 +211,16 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="prune">
+ <property name="toolTip">
+ <string>Reverting this setting requires re-downloading the entire blockchain. It is faster to download the full chain first and prune it later. Disables some advanced features.</string>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QLabel" name="lblExplanation2">
<property name="text">
<string>This initial synchronisation is very demanding, and may expose hardware problems with your computer that had previously gone unnoticed. Each time you run %1, it will continue downloading where it left off.</string>
diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp
index 102e37e471..9e05c63aa0 100644
--- a/src/qt/intro.cpp
+++ b/src/qt/intro.cpp
@@ -131,6 +131,11 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz
ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
uint64_t pruneTarget = std::max<int64_t>(0, gArgs.GetArg("-prune", 0));
+ if (pruneTarget > 1) { // -prune=1 means enabled, above that it's a size in MB
+ ui->prune->setChecked(true);
+ ui->prune->setEnabled(false);
+ }
+ ui->prune->setText(tr("Discard blocks after verification, except most recent %1 GB (prune)").arg(pruneTarget ? pruneTarget / 1000 : 2));
requiredSpace = m_blockchain_size;
QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
if (pruneTarget) {
@@ -180,8 +185,10 @@ void Intro::setDataDirectory(const QString &dataDir)
}
}
-bool Intro::pickDataDirectory(interfaces::Node& node)
+bool Intro::showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune)
{
+ did_show_intro = false;
+
QSettings settings;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
@@ -205,6 +212,7 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
Intro intro(0, node.getAssumedBlockchainSize(), node.getAssumedChainStateSize());
intro.setDataDirectory(dataDir);
intro.setWindowIcon(QIcon(":icons/bitcoin"));
+ did_show_intro = true;
while(true)
{
@@ -227,6 +235,9 @@ bool Intro::pickDataDirectory(interfaces::Node& node)
}
}
+ // Additional preferences:
+ prune = intro.ui->prune->isChecked();
+
settings.setValue("strDataDir", dataDir);
settings.setValue("fReset", false);
}
@@ -263,6 +274,11 @@ void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable
{
freeString += " " + tr("(of %n GB needed)", "", requiredSpace);
ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
+ ui->prune->setChecked(true);
+ } else if (bytesAvailable / GB_BYTES - requiredSpace < 10) {
+ freeString += " " + tr("(%n GB needed for full chain)", "", requiredSpace);
+ ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
+ ui->prune->setChecked(true);
} else {
ui->freeSpace->setStyleSheet("");
}
diff --git a/src/qt/intro.h b/src/qt/intro.h
index c3b26808d4..aca7e71642 100644
--- a/src/qt/intro.h
+++ b/src/qt/intro.h
@@ -39,6 +39,7 @@ public:
/**
* Determine data directory. Let the user choose if the current one doesn't exist.
+ * Let the user configure additional preferences such as pruning.
*
* @returns true if a data directory was selected, false if the user cancelled the selection
* dialog.
@@ -46,7 +47,7 @@ public:
* @note do NOT call global GetDataDir() before calling this function, this
* will cause the wrong path to be cached.
*/
- static bool pickDataDirectory(interfaces::Node& node);
+ static bool showIfNeeded(interfaces::Node& node, bool& did_show_intro, bool& prune);
Q_SIGNALS:
void requestCheck();
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index f3974b1c85..d047a82475 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -92,11 +92,7 @@ void OptionsModel::Init(bool resetSettings)
settings.setValue("bPrune", false);
if (!settings.contains("nPruneSize"))
settings.setValue("nPruneSize", 2);
- // Convert prune size from GB to MiB:
- const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20;
- if (!m_node.softSetArg("-prune", settings.value("bPrune").toBool() ? std::to_string(nPruneSizeMiB) : "0")) {
- addOverriddenOption("-prune");
- }
+ SetPrune(settings.value("bPrune").toBool());
if (!settings.contains("nDatabaseCache"))
settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache);
@@ -240,6 +236,22 @@ static const QString GetDefaultProxyAddress()
return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT);
}
+void OptionsModel::SetPrune(bool prune, bool force)
+{
+ QSettings settings;
+ settings.setValue("bPrune", prune);
+ // Convert prune size from GB to MiB:
+ const uint64_t nPruneSizeMiB = (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20;
+ std::string prune_val = prune ? std::to_string(nPruneSizeMiB) : "0";
+ if (force) {
+ m_node.forceSetArg("-prune", prune_val);
+ return;
+ }
+ if (!m_node.softSetArg("-prune", prune_val)) {
+ addOverriddenOption("-prune");
+ }
+}
+
// read QSettings values and return them
QVariant OptionsModel::data(const QModelIndex & index, int role) const
{
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index 1af3a72b92..b1231b7c7d 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -77,6 +77,9 @@ public:
bool getCoinControlFeatures() const { return fCoinControlFeatures; }
const QString& getOverriddenByCommandLine() { return strOverriddenByCommandLine; }
+ /* Explicit setters */
+ void SetPrune(bool prune, bool force = false);
+
/* Restart flag helper */
void setRestartRequired(bool fRequired);
bool isRestartRequired() const;
diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp
index 0bb87742e9..00d83d23dd 100644
--- a/src/qt/paymentserver.cpp
+++ b/src/qt/paymentserver.cpp
@@ -328,7 +328,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
#ifndef ENABLE_BIP70
if (uri.hasQueryItem("r")) { // payment request
Q_EMIT message(tr("URI handling"),
- tr("Cannot process payment request because BIP70 support was not compiled in."),
+ tr("Cannot process payment request because BIP70 support was not compiled in.")+
+ tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+
+ tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
CClientUIInterface::ICON_WARNING);
}
#endif
@@ -364,7 +366,9 @@ void PaymentServer::handleURIOrFile(const QString& s)
return;
#else
Q_EMIT message(tr("Payment request file handling"),
- tr("Cannot process payment request because BIP70 support was not compiled in."),
+ tr("Cannot process payment request because BIP70 support was not compiled in.")+
+ tr("Due to widespread security flaws in BIP70 it's strongly recommended that any merchant instructions to switch wallets be ignored.")+
+ tr("If you are receiving this error you should request the merchant provide a BIP21 compatible URI."),
CClientUIInterface::ICON_WARNING);
#endif
}
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 8b8283d3d8..fa6f9f3f16 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -75,7 +75,7 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
{
QMessageBox box(parent);
box.setWindowTitle(tr("Close wallet"));
- box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
+ box.setText(tr("Are you sure you wish to close the wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
box.setDefaultButton(QMessageBox::Yes);
@@ -179,9 +179,10 @@ CreateWalletActivity::~CreateWalletActivity()
delete m_passphrase_dialog;
}
-void CreateWalletActivity::askPasshprase()
+void CreateWalletActivity::askPassphrase()
{
m_passphrase_dialog = new AskPassphraseDialog(AskPassphraseDialog::Encrypt, m_parent_widget, &m_passphrase);
+ m_passphrase_dialog->setWindowModality(Qt::ApplicationModal);
m_passphrase_dialog->show();
connect(m_passphrase_dialog, &QObject::destroyed, [this] {
@@ -201,10 +202,10 @@ void CreateWalletActivity::createWallet()
std::string name = m_create_wallet_dialog->walletName().toStdString();
uint64_t flags = 0;
- if (m_create_wallet_dialog->disablePrivateKeys()) {
+ if (m_create_wallet_dialog->isDisablePrivateKeysChecked()) {
flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
}
- if (m_create_wallet_dialog->blank()) {
+ if (m_create_wallet_dialog->isMakeBlankWalletChecked()) {
flags |= WALLET_FLAG_BLANK_WALLET;
}
@@ -246,8 +247,8 @@ void CreateWalletActivity::create()
Q_EMIT finished();
});
connect(m_create_wallet_dialog, &QDialog::accepted, [this] {
- if (m_create_wallet_dialog->encrypt()) {
- askPasshprase();
+ if (m_create_wallet_dialog->isEncryptWalletChecked()) {
+ askPassphrase();
} else {
createWallet();
}
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 4e1a772f3a..fb37b7292c 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -118,7 +118,7 @@ Q_SIGNALS:
void created(WalletModel* wallet_model);
private:
- void askPasshprase();
+ void askPassphrase();
void createWallet();
void finish();
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 93fca5a6de..c2714f9c83 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -85,7 +85,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getblockheader", 1, "verbose" },
{ "getchaintxstats", 0, "nblocks" },
{ "gettransaction", 1, "include_watchonly" },
- { "gettransaction", 2, "decode" },
+ { "gettransaction", 2, "verbose" },
{ "getrawtransaction", 1, "verbose" },
{ "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" },
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 697c6d45c4..f1d176ba4d 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -196,32 +196,73 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
}
// if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
- if (keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) {
+ const bool is_p2sh = scriptPubKey.IsPayToScriptHash();
+ const bool is_p2wsh = scriptPubKey.IsPayToWitnessScriptHash();
+ if (keystore && (is_p2sh || is_p2wsh)) {
RPCTypeCheckObj(prevOut,
{
{"redeemScript", UniValueType(UniValue::VSTR)},
{"witnessScript", UniValueType(UniValue::VSTR)},
}, true);
UniValue rs = find_value(prevOut, "redeemScript");
- if (!rs.isNull()) {
- std::vector<unsigned char> rsData(ParseHexV(rs, "redeemScript"));
- CScript redeemScript(rsData.begin(), rsData.end());
- keystore->AddCScript(redeemScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- // This is only for compatibility, it is encouraged to use the explicit witnessScript field instead.
- keystore->AddCScript(GetScriptForWitness(redeemScript));
- }
UniValue ws = find_value(prevOut, "witnessScript");
- if (!ws.isNull()) {
- std::vector<unsigned char> wsData(ParseHexV(ws, "witnessScript"));
- CScript witnessScript(wsData.begin(), wsData.end());
- keystore->AddCScript(witnessScript);
- // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
- keystore->AddCScript(GetScriptForWitness(witnessScript));
- }
if (rs.isNull() && ws.isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript");
}
+
+ // work from witnessScript when possible
+ std::vector<unsigned char> scriptData(!ws.isNull() ? ParseHexV(ws, "witnessScript") : ParseHexV(rs, "redeemScript"));
+ CScript script(scriptData.begin(), scriptData.end());
+ keystore->AddCScript(script);
+ // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
+ // This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.
+ CScript witness_output_script{GetScriptForWitness(script)};
+ keystore->AddCScript(witness_output_script);
+
+ if (!ws.isNull() && !rs.isNull()) {
+ // if both witnessScript and redeemScript are provided,
+ // they should either be the same (for backwards compat),
+ // or the redeemScript should be the encoded form of
+ // the witnessScript (ie, for p2sh-p2wsh)
+ if (ws.get_str() != rs.get_str()) {
+ std::vector<unsigned char> redeemScriptData(ParseHexV(rs, "redeemScript"));
+ CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end());
+ if (redeemScript != witness_output_script) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not correspond to witnessScript");
+ }
+ }
+ }
+
+ if (is_p2sh) {
+ const CTxDestination p2sh{ScriptHash(script)};
+ const CTxDestination p2sh_p2wsh{ScriptHash(witness_output_script)};
+ if (scriptPubKey == GetScriptForDestination(p2sh)) {
+ // traditional p2sh; arguably an error if
+ // we got here with rs.IsNull(), because
+ // that means the p2sh script was specified
+ // via witnessScript param, but for now
+ // we'll just quietly accept it
+ } else if (scriptPubKey == GetScriptForDestination(p2sh_p2wsh)) {
+ // p2wsh encoded as p2sh; ideally the witness
+ // script was specified in the witnessScript
+ // param, but also support specifying it via
+ // redeemScript param for backwards compat
+ // (in which case ws.IsNull() == true)
+ } else {
+ // otherwise, can't generate scriptPubKey from
+ // either script, so we got unusable parameters
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
+ }
+ } else if (is_p2wsh) {
+ // plain p2wsh; could throw an error if script
+ // was specified by redeemScript rather than
+ // witnessScript (ie, ws.IsNull() == true), but
+ // accept it for backwards compat
+ const CTxDestination p2wsh{WitnessV0ScriptHash(script)};
+ if (scriptPubKey != GetScriptForDestination(p2wsh)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey");
+ }
+ }
}
}
}
@@ -268,6 +309,9 @@ UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keysto
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)");
+ } else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
+ // Verification failed (possibly due to insufficient signatures).
+ TxInErrorToJSON(txin, vErrors, "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
} else {
TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror));
}
diff --git a/src/validation.cpp b/src/validation.cpp
index d470fd5b6e..6a9b0c95fb 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2240,14 +2240,12 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar
if (nUpgraded > 0)
AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded));
}
- LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)", __func__, /* Continued */
+ LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__,
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion,
log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx,
FormatISO8601DateTime(pindexNew->GetBlockTime()),
- GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize());
- if (!warningMessages.empty())
- LogPrintf(" warning='%s'", warningMessages); /* Continued */
- LogPrintf("\n");
+ GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(),
+ !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : "");
}
@@ -2510,6 +2508,8 @@ void CChainState::PruneBlockIndexCandidates() {
/**
* Try to make some progress towards making pindexMostWork the active block.
* pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork.
+ *
+ * @returns true unless a system error occurred
*/
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
{
@@ -2629,15 +2629,6 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
-/**
- * Make the best chain active, in multiple steps. The result is either failure
- * or an activated best chain. pblock is either nullptr or a pointer to a block
- * that is already loaded (to avoid loading it again from disk).
- *
- * ActivateBestChain is split into steps (see ActivateBestChainStep) so that
- * we avoid holding cs_main for an extended period of time; the length of this
- * call may be quite long during reindexing or a substantial reorg.
- */
bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) {
// Note that while we're often called here from ProcessNewBlock, this is
// far from a guarantee. Things in the P2P/RPC will often end up calling
@@ -2685,8 +2676,10 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
bool fInvalidFound = false;
std::shared_ptr<const CBlock> nullBlockPtr;
- if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
+ if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
+ // A system error occurred
return false;
+ }
blocks_connected = true;
if (fInvalidFound) {
diff --git a/src/validation.h b/src/validation.h
index 99850f71d9..06252b6bc8 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -211,7 +211,7 @@ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
* @param[in] pblock The block we want to process.
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers.
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
- * @return True if state.IsValid()
+ * @returns If the block was processed, independently of block validity
*/
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
@@ -653,6 +653,8 @@ public:
*
* If FlushStateMode::NONE is used, then FlushStateToDisk(...) won't do anything
* besides checking if we need to prune.
+ *
+ * @returns true unless a system error occurred
*/
bool FlushStateToDisk(
const CChainParams& chainparams,
@@ -667,7 +669,20 @@ public:
//! if we pruned.
void PruneAndFlush();
- bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
+ /**
+ * Make the best chain active, in multiple steps. The result is either failure
+ * or an activated best chain. pblock is either nullptr or a pointer to a block
+ * that is already loaded (to avoid loading it again from disk).
+ *
+ * ActivateBestChain is split into steps (see ActivateBestChainStep) so that
+ * we avoid holding cs_main for an extended period of time; the length of this
+ * call may be quite long during reindexing or a substantial reorg.
+ *
+ * @returns true unless a system error occurred
+ */
+ bool ActivateBestChain(CValidationState& state,
+ const CChainParams& chainparams,
+ std::shared_ptr<const CBlock> pblock) LOCKS_EXCLUDED(cs_main);
bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index b88aabd0fa..216205ed61 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1648,8 +1648,10 @@ static UniValue gettransaction(const JSONRPCRequest& request)
"\nGet detailed information about in-wallet transaction <txid>\n",
{
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
- {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses in balance calculation and details[]"},
- {"decode", RPCArg::Type::BOOL, /* default */ "false", "Whether to add a field with the decoded transaction"},
+ {"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false",
+ "Whether to include watch-only addresses in balance calculation and details[]"},
+ {"verbose", RPCArg::Type::BOOL, /* default */ "false",
+ "Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
},
RPCResult{
"{\n"
@@ -1685,7 +1687,8 @@ static UniValue gettransaction(const JSONRPCRequest& request)
" ,...\n"
" ],\n"
" \"hex\" : \"data\" (string) Raw data for transaction\n"
- " \"decoded\" : transaction (json object) Optional, the decoded transaction\n"
+ " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n"
+ " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n"
"}\n"
},
RPCExamples{
@@ -1711,7 +1714,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
filter |= ISMINE_WATCH_ONLY;
}
- bool decode_tx = request.params[2].isNull() ? false : request.params[2].get_bool();
+ bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
UniValue entry(UniValue::VOBJ);
auto it = pwallet->mapWallet.find(hash);
@@ -1738,7 +1741,7 @@ static UniValue gettransaction(const JSONRPCRequest& request)
std::string strHex = EncodeHexTx(*wtx.tx, pwallet->chain().rpcSerializationFlags());
entry.pushKV("hex", strHex);
- if (decode_tx) {
+ if (verbose) {
UniValue decoded(UniValue::VOBJ);
TxToUniv(*wtx.tx, uint256(), decoded, false);
entry.pushKV("decoded", decoded);
@@ -4189,7 +4192,7 @@ static const CRPCCommand commands[] =
{ "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} },
{ "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} },
- { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","decode"} },
+ { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly","verbose"} },
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} },
{ "wallet", "getbalances", &getbalances, {} },
{ "wallet", "getwalletinfo", &getwalletinfo, {} },