diff options
-rw-r--r-- | build-aux/m4/bitcoin_qt.m4 | 27 | ||||
-rw-r--r-- | doc/dependencies.md | 2 | ||||
-rw-r--r-- | doc/release-notes-14481.md | 9 | ||||
-rw-r--r-- | doc/release-notes-15393.md | 4 | ||||
-rw-r--r-- | src/interfaces/wallet.cpp | 4 | ||||
-rw-r--r-- | src/interfaces/wallet.h | 3 | ||||
-rw-r--r-- | src/net_processing.cpp | 25 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 3 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 8 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 1 | ||||
-rw-r--r-- | src/qt/test/apptests.cpp | 2 | ||||
-rw-r--r-- | src/qt/test/rpcnestedtests.cpp | 2 | ||||
-rw-r--r-- | src/qt/walletcontroller.cpp | 16 | ||||
-rw-r--r-- | src/qt/walletcontroller.h | 1 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 21 | ||||
-rw-r--r-- | src/wallet/rpcwallet.cpp | 28 | ||||
-rwxr-xr-x | test/functional/rpc_signrawtransaction.py | 33 |
17 files changed, 141 insertions, 48 deletions
diff --git a/build-aux/m4/bitcoin_qt.m4 b/build-aux/m4/bitcoin_qt.m4 index 90a2cd9875..1a7c5d5f7d 100644 --- a/build-aux/m4/bitcoin_qt.m4 +++ b/build-aux/m4/bitcoin_qt.m4 @@ -116,24 +116,6 @@ AC_DEFUN([BITCOIN_QT_CONFIGURE],[ if test "x$bitcoin_cv_static_qt" = xyes; then _BITCOIN_QT_FIND_STATIC_PLUGINS AC_DEFINE(QT_STATICPLUGIN, 1, [Define this symbol if qt plugins are static]) - AC_CACHE_CHECK(for Qt < 5.4, bitcoin_cv_need_acc_widget,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - #include <QtCore/qconfig.h> - #ifndef QT_VERSION - # include <QtCore/qglobal.h> - #endif - ]], - [[ - #if QT_VERSION >= 0x050400 - choke - #endif - ]])], - [bitcoin_cv_need_acc_widget=yes], - [bitcoin_cv_need_acc_widget=no]) - ]) - if test "x$bitcoin_cv_need_acc_widget" = xyes; then - _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(AccessibleFactory)], [-lqtaccessiblewidgets]) - fi _BITCOIN_QT_CHECK_STATIC_PLUGINS([Q_IMPORT_PLUGIN(QMinimalIntegrationPlugin)],[-lqminimal]) AC_DEFINE(QT_QPA_PLATFORM_MINIMAL, 1, [Define this symbol if the minimal qt platform exists]) if test "x$TARGET_OS" = xwindows; then @@ -264,7 +246,7 @@ dnl All macros below are internal and should _not_ be used from the main dnl configure.ac. dnl ---- -dnl Internal. Check if the included version of Qt is Qt5. +dnl Internal. Check included version of Qt against minimum specified in doc/dependencies.md dnl Requires: INCLUDES must be populated as necessary. dnl Output: bitcoin_cv_qt5=yes|no AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[ @@ -276,7 +258,7 @@ AC_DEFUN([_BITCOIN_QT_CHECK_QT5],[ #endif ]], [[ - #if QT_VERSION < 0x050200 || QT_VERSION_MAJOR < 5 + #if QT_VERSION < 0x050501 choke #endif ]])], @@ -374,9 +356,7 @@ AC_DEFUN([_BITCOIN_QT_FIND_STATIC_PLUGINS],[ fi if test "x$TARGET_OS" = xlinux; then PKG_CHECK_MODULES([X11XCB], [x11-xcb], [QT_LIBS="$X11XCB_LIBS $QT_LIBS"]) - if ${PKG_CONFIG} --exists "Qt5Core >= 5.5" 2>/dev/null; then - PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) - fi + PKG_CHECK_MODULES([QTXCBQPA], [Qt5XcbQpa], [QT_LIBS="$QTXCBQPA_LIBS $QT_LIBS"]) elif test "x$TARGET_OS" = xdarwin; then PKG_CHECK_MODULES([QTCLIPBOARD], [Qt5ClipboardSupport], [QT_LIBS="-lQt5ClipboardSupport $QT_LIBS"]) PKG_CHECK_MODULES([QTGRAPHICS], [Qt5GraphicsSupport], [QT_LIBS="-lQt5GraphicsSupport $QT_LIBS"]) @@ -527,4 +507,3 @@ AC_DEFUN([_BITCOIN_QT_FIND_LIBS_WITHOUT_PKGCONFIG],[ CXXFLAGS="$TEMP_CXXFLAGS" LIBS="$TEMP_LIBS" ]) - diff --git a/doc/dependencies.md b/doc/dependencies.md index b833e9151f..235eeb28b6 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -23,7 +23,7 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | protobuf | [2.6.1](https://github.com/google/protobuf/releases) | | No | | | | Python (tests) | | [3.4](https://www.python.org/downloads) | | | | | qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | -| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.2](https://github.com/bitcoin/bitcoin/pull/14725) | No | | | +| Qt | [5.9.7](https://download.qt.io/official_releases/qt/) | [5.5.1](https://github.com/bitcoin/bitcoin/issues/13478) | No | | | | XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L87) (Linux only) | | xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L86) (Linux only) | | ZeroMQ | [4.3.1](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | | diff --git a/doc/release-notes-14481.md b/doc/release-notes-14481.md new file mode 100644 index 0000000000..ea8fc3c34e --- /dev/null +++ b/doc/release-notes-14481.md @@ -0,0 +1,9 @@ +Low-level RPC changes +---------------------- + +The `listunspent` RPC has been modified so that it also returns `witnessScript`, +the witness script in the case of a P2WSH or P2SH-P2WSH output. + +The `signrawtransactionwithkey` and `signrawtransactionwithwallet` RPCs have been +modified so that they also optionally accept a `witnessScript`, the witness script in the +case of a P2WSH or P2SH-P2WSH output. This is compatible with the change to `listunspent`. diff --git a/doc/release-notes-15393.md b/doc/release-notes-15393.md new file mode 100644 index 0000000000..f478dc798d --- /dev/null +++ b/doc/release-notes-15393.md @@ -0,0 +1,4 @@ +Dependencies +------------ + +- The minimum required version of QT has been increased from 5.2 to 5.5.1 (the [depends system](https://github.com/bitcoin/bitcoin/blob/master/depends/README.md) provides 5.9.7) diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 206227b101..5b5430037c 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -468,6 +468,10 @@ public: bool IsWalletFlagSet(uint64_t flag) override { return m_wallet.IsWalletFlagSet(flag); } OutputType getDefaultAddressType() override { return m_wallet.m_default_address_type; } OutputType getDefaultChangeType() override { return m_wallet.m_default_change_type; } + void remove() override + { + RemoveWallet(m_shared_wallet); + } std::unique_ptr<Handler> handleUnload(UnloadFn fn) override { return MakeHandler(m_wallet.NotifyUnload.connect(fn)); diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index a86212356c..a931e5fafb 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -247,6 +247,9 @@ public: // Get default change type. virtual OutputType getDefaultChangeType() = 0; + // Remove wallet. + virtual void remove() = 0; + //! Register handler for unload message. using UnloadFn = std::function<void()>; virtual std::unique_ptr<Handler> handleUnload(UnloadFn fn) = 0; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 5927a14a6e..18dd2f010c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -85,6 +85,7 @@ struct COrphanTx { CTransactionRef tx; NodeId fromPeer; int64_t nTimeExpire; + size_t list_pos; }; CCriticalSection g_cs_orphans; std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans); @@ -186,6 +187,8 @@ namespace { }; std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); + std::vector<std::map<uint256, COrphanTx>::iterator> g_orphan_list GUARDED_BY(g_cs_orphans); //! For random eviction + static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans); } // namespace @@ -837,8 +840,9 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE return false; } - auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); + auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, g_orphan_list.size()}); assert(ret.second); + g_orphan_list.push_back(ret.first); for (const CTxIn& txin : tx->vin) { mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); } @@ -864,6 +868,18 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) if (itPrev->second.empty()) mapOrphanTransactionsByPrev.erase(itPrev); } + + size_t old_pos = it->second.list_pos; + assert(g_orphan_list[old_pos] == it); + if (old_pos + 1 != g_orphan_list.size()) { + // Unless we're deleting the last entry in g_orphan_list, move the last + // entry to the position we're deleting. + auto it_last = g_orphan_list.back(); + g_orphan_list[old_pos] = it_last; + it_last->second.list_pos = old_pos; + } + g_orphan_list.pop_back(); + mapOrphanTransactions.erase(it); return 1; } @@ -914,11 +930,8 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: - uint256 randomhash = rng.rand256(); - std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash); - if (it == mapOrphanTransactions.end()) - it = mapOrphanTransactions.begin(); - EraseOrphanTx(it->first); + size_t randompos = rng.randrange(g_orphan_list.size()); + EraseOrphanTx(g_orphan_list[randompos]->first); ++nEvicted; } return nEvicted; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index d6c6fd6e98..1b063771ef 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -55,9 +55,6 @@ #if defined(QT_STATICPLUGIN) #include <QtPlugin> -#if QT_VERSION < 0x050400 -Q_IMPORT_PLUGIN(AccessibleFactory) -#endif #if defined(QT_QPA_PLATFORM_XCB) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f7a4bad916..bc88dd5e0a 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -338,6 +338,9 @@ void BitcoinGUI::createActions() m_open_wallet_action->setMenu(new QMenu(this)); m_open_wallet_action->setStatusTip(tr("Open a wallet")); + m_close_wallet_action = new QAction(tr("Close Wallet..."), this); + m_close_wallet_action->setStatusTip(tr("Close wallet")); + showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Bitcoin command-line options").arg(tr(PACKAGE_NAME))); @@ -396,6 +399,9 @@ void BitcoinGUI::createActions() }); } }); + connect(m_close_wallet_action, &QAction::triggered, [this] { + m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this); + }); } #endif // ENABLE_WALLET @@ -418,6 +424,7 @@ void BitcoinGUI::createMenuBar() if(walletFrame) { file->addAction(m_open_wallet_action); + file->addAction(m_close_wallet_action); file->addSeparator(); file->addAction(openAction); file->addAction(backupWalletAction); @@ -693,6 +700,7 @@ void BitcoinGUI::setWalletActionsEnabled(bool enabled) usedSendingAddressesAction->setEnabled(enabled); usedReceivingAddressesAction->setEnabled(enabled); openAction->setEnabled(enabled); + m_close_wallet_action->setEnabled(enabled); } void BitcoinGUI::createTrayIcon() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index c226494020..b58ccbb455 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -148,6 +148,7 @@ private: QAction* openAction = nullptr; QAction* showHelpMessageAction = nullptr; QAction* m_open_wallet_action{nullptr}; + QAction* m_close_wallet_action{nullptr}; QAction* m_wallet_selector_label_action = nullptr; QAction* m_wallet_selector_action = nullptr; diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index 2c477a2e98..da25d83175 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -27,9 +27,7 @@ #include <QTest> #include <QTextEdit> #include <QtGlobal> -#if QT_VERSION >= 0x050000 #include <QtTest/QtTestWidgets> -#endif #include <QtTest/QtTestGui> #include <new> #include <string> diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 173c814f1e..f8a9c25303 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -120,7 +120,6 @@ void RPCNestedTests::rpcNestedTests() RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest( abc , cba )"); QVERIFY(result == "[\"abc\",\"cba\"]"); -#if QT_VERSION >= 0x050300 // do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3) QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax @@ -131,5 +130,4 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , -#endif } diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 3483c75970..c532ffbbfe 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -63,6 +63,22 @@ OpenWalletActivity* WalletController::openWallet(const std::string& name, QWidge return activity; } +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(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); + if (box.exec() != QMessageBox::Yes) return; + + // First remove wallet from node. + wallet_model->wallet().remove(); + // Now release the model. + removeAndDeleteWallet(wallet_model); +} + WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wallet> wallet) { QMutexLocker locker(&m_mutex); diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h index f19c0e1f3d..19b3a82253 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -44,6 +44,7 @@ public: std::vector<std::string> getWalletsAvailableToOpen() const; OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr); + void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr); private Q_SLOTS: void addWallet(WalletModel* wallet_model); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 7fe73e56da..6ec05565e9 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -855,15 +855,25 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con RPCTypeCheckObj(prevOut, { {"redeemScript", UniValueType(UniValue::VSTR)}, - }); - UniValue v = find_value(prevOut, "redeemScript"); - if (!v.isNull()) { - std::vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); + {"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)); + } } } } @@ -948,7 +958,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"}, - {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"}, + {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"}, + {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"}, {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"}, }, }, diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9e362cf167..ee77739986 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2770,7 +2770,8 @@ static UniValue listunspent(const JSONRPCRequest& request) " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" - " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" + " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH" + " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n" " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" " \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n" " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n" @@ -2884,6 +2885,28 @@ static UniValue listunspent(const JSONRPCRequest& request) CScript redeemScript; if (pwallet->GetCScript(hash, redeemScript)) { entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); + // Now check if the redeemScript is actually a P2WSH script + CTxDestination witness_destination; + if (redeemScript.IsPayToWitnessScriptHash()) { + bool extracted = ExtractDestination(redeemScript, witness_destination); + assert(extracted); + // Also return the witness script + const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); + CScriptID id; + CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); + CScript witnessScript; + if (pwallet->GetCScript(id, witnessScript)) { + entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); + } + } + } + } else if (scriptPubKey.IsPayToWitnessScriptHash()) { + const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address); + CScriptID id; + CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); + CScript witnessScript; + if (pwallet->GetCScript(id, witnessScript)) { + entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); } } } @@ -3139,7 +3162,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"scriptPubKey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "script key"}, - {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH or P2WSH) redeem script"}, + {"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"}, + {"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"}, {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount spent"}, }, }, diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index 291538df64..56e2c73a90 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -5,14 +5,17 @@ """Test transaction signing using the signrawtransaction* RPCs.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.util import assert_equal, assert_raises_rpc_error, bytes_to_hex_str, hex_str_to_bytes +from test_framework.messages import sha256 +from test_framework.script import CScript, OP_0 +from decimal import Decimal class SignRawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 1 - self.extra_args = [["-deprecatedrpc=signrawtransaction"]] + self.num_nodes = 2 + self.extra_args = [["-deprecatedrpc=signrawtransaction"], []] def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -143,9 +146,33 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"]) assert not rawTxSigned['errors'][0]['witness'] + def witness_script_test(self): + # Now test signing transaction to P2SH-P2WSH addresses without wallet + # Create a new P2SH-P2WSH 1-of-1 multisig address: + embedded_address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress()) + embedded_privkey = self.nodes[1].dumpprivkey(embedded_address["address"]) + p2sh_p2wsh_address = self.nodes[1].addmultisigaddress(1, [embedded_address["pubkey"]], "", "p2sh-segwit") + # send transaction to P2SH-P2WSH 1-of-1 multisig address + self.nodes[0].generate(101) + self.nodes[0].sendtoaddress(p2sh_p2wsh_address["address"], 49.999) + self.nodes[0].generate(1) + self.sync_all() + # Find the UTXO for the transaction node[1] should have received, check witnessScript matches + unspent_output = self.nodes[1].listunspent(0, 999999, [p2sh_p2wsh_address["address"]])[0] + assert_equal(unspent_output["witnessScript"], p2sh_p2wsh_address["redeemScript"]) + p2sh_redeemScript = CScript([OP_0, sha256(hex_str_to_bytes(p2sh_p2wsh_address["redeemScript"]))]) + assert_equal(unspent_output["redeemScript"], bytes_to_hex_str(p2sh_redeemScript)) + # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys + spending_tx = self.nodes[0].createrawtransaction([unspent_output], {self.nodes[1].getnewaddress(): Decimal("49.998")}) + spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [unspent_output]) + # Check the signing completed successfully + assert 'complete' in spending_tx_signed + assert_equal(spending_tx_signed['complete'], True) + def run_test(self): self.successful_signing_test() self.script_verification_error_test() + self.witness_script_test() self.test_with_lock_outputs() |