diff options
42 files changed, 1331 insertions, 983 deletions
diff --git a/.travis.yml b/.travis.yml index d5086b084a..41532b9260 100644 --- a/.travis.yml +++ b/.travis.yml @@ -133,6 +133,15 @@ jobs: BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++" - stage: test + name: 'x86_64 Linux [GOAL: install] [no BIP70]' + env: >- + HOST=x86_64-unknown-linux-gnu + PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libqrencode-dev" + NO_DEPENDS=1 + GOAL="install" + BITCOIN_CONFIG="--enable-zmq --disable-bip70 --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++" + + - stage: test name: 'x86_64 Linux [GOAL: install] [bionic] [no wallet]' env: >- HOST=x86_64-unknown-linux-gnu diff --git a/Makefile.am b/Makefile.am index 9a6e15f0dd..287ba3ab3a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,6 +20,7 @@ BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT) BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT) +BITCOIN_WALLET_BIN=$(top_builddir)/src/$(BITCOIN_WALLET_TOOL_NAME)$(EXEEXT) BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) empty := @@ -76,6 +77,7 @@ $(BITCOIN_WIN_INSTALLER): all-recursive STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release + STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_WALLET_BIN) $(top_builddir)/release @test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \ echo error: could not build $@ @echo built $@ @@ -172,6 +174,9 @@ $(BITCOIN_CLI_BIN): FORCE $(BITCOIN_TX_BIN): FORCE $(MAKE) -C src $(@F) +$(BITCOIN_WALLET_BIN): FORCE + $(MAKE) -C src $(@F) + if USE_LCOV LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1" 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/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj index 367e68ee51..12fda65904 100644 --- a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj +++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj @@ -56,6 +56,9 @@ <ProjectReference Include="..\libsecp256k1\libsecp256k1.vcxproj"> <Project>{bb493552-3b8c-4a8c-bf69-a6e7a51d2ea6}</Project> </ProjectReference> + <ProjectReference Include="..\libleveldb\libleveldb.vcxproj"> + <Project>{18430fef-6b61-4c53-b396-718e02850f1b}</Project> + </ProjectReference> </ItemGroup> <PropertyGroup Label="Globals"> <VCProjectVersion>15.0</VCProjectVersion> 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-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/share/setup.nsi.in b/share/setup.nsi.in index 6542370f97..046deed9ea 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -81,6 +81,7 @@ Section -Main SEC0000 File @abs_top_srcdir@/release/@BITCOIN_DAEMON_NAME@@EXEEXT@ File @abs_top_srcdir@/release/@BITCOIN_CLI_NAME@@EXEEXT@ File @abs_top_srcdir@/release/@BITCOIN_TX_NAME@@EXEEXT@ + File @abs_top_srcdir@/release/@BITCOIN_WALLET_TOOL_NAME@@EXEEXT@ SetOutPath $INSTDIR\doc File /r /x Makefile* @abs_top_srcdir@/doc\*.* SetOutPath $INSTDIR diff --git a/src/Makefile.am b/src/Makefile.am index a57fcb0711..7490d8b790 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -152,6 +152,7 @@ BITCOIN_CORE_H = \ netaddress.h \ netbase.h \ netmessagemaker.h \ + node/transaction.h \ noui.h \ optional.h \ outputtype.h \ @@ -161,6 +162,7 @@ BITCOIN_CORE_H = \ policy/rbf.h \ pow.h \ protocol.h \ + psbt.h \ random.h \ reverse_iterator.h \ reverselock.h \ @@ -209,6 +211,7 @@ BITCOIN_CORE_H = \ wallet/db.h \ wallet/feebumper.h \ wallet/fees.h \ + wallet/psbtwallet.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ @@ -255,6 +258,7 @@ libbitcoin_server_a_SOURCES = \ miner.cpp \ net.cpp \ net_processing.cpp \ + node/transaction.cpp \ noui.cpp \ outputtype.cpp \ policy/fees.cpp \ @@ -308,6 +312,7 @@ libbitcoin_wallet_a_SOURCES = \ wallet/feebumper.cpp \ wallet/fees.cpp \ wallet/init.cpp \ + wallet/psbtwallet.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ @@ -421,6 +426,7 @@ libbitcoin_common_a_SOURCES = \ netaddress.cpp \ netbase.cpp \ policy/feerate.cpp \ + psbt.cpp \ protocol.cpp \ scheduler.cpp \ script/descriptor.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d3972fdb89..97b0e05b20 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -107,7 +107,7 @@ public: pchMessageStart[3] = 0xd9; nDefaultPort = 8333; nPruneAfterHeight = 100000; - m_assumed_blockchain_size = 200; + m_assumed_blockchain_size = 240; m_assumed_chain_state_size = 3; genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN); @@ -219,7 +219,7 @@ public: pchMessageStart[3] = 0x07; nDefaultPort = 18333; nPruneAfterHeight = 1000; - m_assumed_blockchain_size = 20; + m_assumed_blockchain_size = 30; m_assumed_chain_state_size = 2; genesis = CreateGenesisBlock(1296688602, 414098458, 0x1d00ffff, 1, 50 * COIN); diff --git a/src/core_io.h b/src/core_io.h index 6f87161f46..ae377eb6e8 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -37,7 +37,11 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); */ bool ParseHashStr(const std::string& strHex, uint256& result); std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName); -NODISCARD bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error); + +//! Decode a base64ed PSBT into a PartiallySignedTransaction +NODISCARD bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error); +//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction +NODISCARD bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error); int ParseSighashString(const UniValue& sighash); // core_write.cpp diff --git a/src/core_read.cpp b/src/core_read.cpp index 3b82b2853c..536a7f4f17 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -4,6 +4,7 @@ #include <core_io.h> +#include <psbt.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <script/script.h> @@ -176,10 +177,20 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) return true; } -bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error) +bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error) { - std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str()); - CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION); + bool invalid; + std::string tx_data = DecodeBase64(base64_tx, &invalid); + if (invalid) { + error = "invalid base64"; + return false; + } + return DecodeRawPSBT(psbt, tx_data, error); +} + +bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error) +{ + CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION); try { ss_data >> psbt; if (!ss_data.empty()) { 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/node/transaction.cpp b/src/node/transaction.cpp new file mode 100644 index 0000000000..c9cdd0d1cd --- /dev/null +++ b/src/node/transaction.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <net.h> +#include <txmempool.h> +#include <validation.h> +#include <validationinterface.h> +#include <node/transaction.h> + +#include <future> + +const char* TransactionErrorString(const TransactionError err) +{ + switch (err) { + case TransactionError::OK: + return "No error"; + case TransactionError::MISSING_INPUTS: + return "Missing inputs"; + case TransactionError::ALREADY_IN_CHAIN: + return "Transaction already in block chain"; + case TransactionError::P2P_DISABLED: + return "Peer-to-peer functionality missing or disabled"; + case TransactionError::MEMPOOL_REJECTED: + return "Transaction rejected by AcceptToMemoryPool"; + case TransactionError::MEMPOOL_ERROR: + return "AcceptToMemoryPool failed"; + case TransactionError::INVALID_PSBT: + return "PSBT is not sane"; + case TransactionError::PSBT_MISMATCH: + return "PSBTs not compatible (different transactions)"; + case TransactionError::SIGHASH_MISMATCH: + return "Specified sighash value does not match existing value"; + + case TransactionError::UNKNOWN_ERROR: + default: break; + } + return "Unknown error"; +} + +bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, TransactionError& error, std::string& err_string, const bool allowhighfees) +{ + std::promise<void> promise; + hashTx = tx->GetHash(); + + CAmount nMaxRawTxFee = maxTxFee; + if (allowhighfees) + nMaxRawTxFee = 0; + + { // cs_main scope + LOCK(cs_main); + CCoinsViewCache &view = *pcoinsTip; + bool fHaveChain = false; + for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { + const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); + fHaveChain = !existingCoin.IsSpent(); + } + bool fHaveMempool = mempool.exists(hashTx); + if (!fHaveMempool && !fHaveChain) { + // push to local node and sync with wallets + CValidationState state; + bool fMissingInputs; + if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { + if (state.IsInvalid()) { + err_string = FormatStateMessage(state); + error = TransactionError::MEMPOOL_REJECTED; + return false; + } else { + if (fMissingInputs) { + error = TransactionError::MISSING_INPUTS; + return false; + } + err_string = FormatStateMessage(state); + error = TransactionError::MEMPOOL_ERROR; + return false; + } + } else { + // If wallet is enabled, ensure that the wallet has been made aware + // of the new transaction prior to returning. This prevents a race + // where a user might call sendrawtransaction with a transaction + // to/from their wallet, immediately call some wallet RPC, and get + // a stale result because callbacks have not yet been processed. + CallFunctionInValidationInterfaceQueue([&promise] { + promise.set_value(); + }); + } + } else if (fHaveChain) { + error = TransactionError::ALREADY_IN_CHAIN; + return false; + } else { + // Make sure we don't block forever if re-sending + // a transaction already in mempool. + promise.set_value(); + } + + } // cs_main + + promise.get_future().wait(); + + if(!g_connman) { + error = TransactionError::P2P_DISABLED; + return false; + } + + CInv inv(MSG_TX, hashTx); + g_connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); + + return true; + } diff --git a/src/node/transaction.h b/src/node/transaction.h new file mode 100644 index 0000000000..3b0cbba98b --- /dev/null +++ b/src/node/transaction.h @@ -0,0 +1,43 @@ +// Copyright (c) 2017-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_NODE_TRANSACTION_H +#define BITCOIN_NODE_TRANSACTION_H + +#include <primitives/transaction.h> +#include <uint256.h> + +enum class TransactionError { + OK = 0, + UNKNOWN_ERROR, + + MISSING_INPUTS, + ALREADY_IN_CHAIN, + P2P_DISABLED, + MEMPOOL_REJECTED, + MEMPOOL_ERROR, + INVALID_PSBT, + PSBT_MISMATCH, + SIGHASH_MISMATCH, + + ERROR_COUNT +}; + +#define TRANSACTION_ERR_LAST TransactionError::ERROR_COUNT + +const char* TransactionErrorString(const TransactionError error); + +/** + * Broadcast a transaction + * + * @param[in] tx the transaction to broadcast + * @param[out] &txid the txid of the transaction, if successfully broadcast + * @param[out] &error reference to UniValue to fill with error info on failure + * @param[out] &err_string reference to std::string to fill with error string if available + * @param[in] allowhighfees whether to allow fees exceeding maxTxFee + * return true on success, false on error (and fills in `error`) + */ +bool BroadcastTransaction(CTransactionRef tx, uint256& txid, TransactionError& error, std::string& err_string, bool allowhighfees = false); + +#endif // BITCOIN_NODE_TRANSACTION_H diff --git a/src/psbt.cpp b/src/psbt.cpp new file mode 100644 index 0000000000..81633c0cc7 --- /dev/null +++ b/src/psbt.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <psbt.h> +#include <util/strencodings.h> + +PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx) +{ + inputs.resize(tx.vin.size()); + outputs.resize(tx.vout.size()); +} + +bool PartiallySignedTransaction::IsNull() const +{ + return !tx && inputs.empty() && outputs.empty() && unknown.empty(); +} + +bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt) +{ + // Prohibited to merge two PSBTs over different transactions + if (tx->GetHash() != psbt.tx->GetHash()) { + return false; + } + + for (unsigned int i = 0; i < inputs.size(); ++i) { + inputs[i].Merge(psbt.inputs[i]); + } + for (unsigned int i = 0; i < outputs.size(); ++i) { + outputs[i].Merge(psbt.outputs[i]); + } + unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); + + return true; +} + +bool PartiallySignedTransaction::IsSane() const +{ + for (PSBTInput input : inputs) { + if (!input.IsSane()) return false; + } + return true; +} + +bool PSBTInput::IsNull() const +{ + return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty(); +} + +void PSBTInput::FillSignatureData(SignatureData& sigdata) const +{ + if (!final_script_sig.empty()) { + sigdata.scriptSig = final_script_sig; + sigdata.complete = true; + } + if (!final_script_witness.IsNull()) { + sigdata.scriptWitness = final_script_witness; + sigdata.complete = true; + } + if (sigdata.complete) { + return; + } + + sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end()); + if (!redeem_script.empty()) { + sigdata.redeem_script = redeem_script; + } + if (!witness_script.empty()) { + sigdata.witness_script = witness_script; + } + for (const auto& key_pair : hd_keypaths) { + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); + } +} + +void PSBTInput::FromSignatureData(const SignatureData& sigdata) +{ + if (sigdata.complete) { + partial_sigs.clear(); + hd_keypaths.clear(); + redeem_script.clear(); + witness_script.clear(); + + if (!sigdata.scriptSig.empty()) { + final_script_sig = sigdata.scriptSig; + } + if (!sigdata.scriptWitness.IsNull()) { + final_script_witness = sigdata.scriptWitness; + } + return; + } + + partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end()); + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + if (witness_script.empty() && !sigdata.witness_script.empty()) { + witness_script = sigdata.witness_script; + } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } +} + +void PSBTInput::Merge(const PSBTInput& input) +{ + if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo; + if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) { + witness_utxo = input.witness_utxo; + non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one. + } + + partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end()); + hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end()); + unknown.insert(input.unknown.begin(), input.unknown.end()); + + if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script; + if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script; + if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig; + if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness; +} + +bool PSBTInput::IsSane() const +{ + // Cannot have both witness and non-witness utxos + if (!witness_utxo.IsNull() && non_witness_utxo) return false; + + // If we have a witness_script or a scriptWitness, we must also have a witness utxo + if (!witness_script.empty() && witness_utxo.IsNull()) return false; + if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false; + + return true; +} + +void PSBTOutput::FillSignatureData(SignatureData& sigdata) const +{ + if (!redeem_script.empty()) { + sigdata.redeem_script = redeem_script; + } + if (!witness_script.empty()) { + sigdata.witness_script = witness_script; + } + for (const auto& key_pair : hd_keypaths) { + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); + } +} + +void PSBTOutput::FromSignatureData(const SignatureData& sigdata) +{ + if (redeem_script.empty() && !sigdata.redeem_script.empty()) { + redeem_script = sigdata.redeem_script; + } + if (witness_script.empty() && !sigdata.witness_script.empty()) { + witness_script = sigdata.witness_script; + } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } +} + +bool PSBTOutput::IsNull() const +{ + return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty(); +} + +void PSBTOutput::Merge(const PSBTOutput& output) +{ + hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end()); + unknown.insert(output.unknown.begin(), output.unknown.end()); + + if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script; + if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; +} + +bool PSBTInputSigned(PSBTInput& input) +{ + return !input.final_script_sig.empty() || !input.final_script_witness.IsNull(); +} + +bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash) +{ + PSBTInput& input = psbt.inputs.at(index); + const CMutableTransaction& tx = *psbt.tx; + + if (PSBTInputSigned(input)) { + return true; + } + + // Fill SignatureData with input info + SignatureData sigdata; + input.FillSignatureData(sigdata); + + // Get UTXO + bool require_witness_sig = false; + CTxOut utxo; + + // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided. + if (!input.IsSane()) { + return false; + } + + if (input.non_witness_utxo) { + // If we're taking our information from a non-witness UTXO, verify that it matches the prevout. + COutPoint prevout = tx.vin[index].prevout; + if (input.non_witness_utxo->GetHash() != prevout.hash) { + return false; + } + utxo = input.non_witness_utxo->vout[prevout.n]; + } else if (!input.witness_utxo.IsNull()) { + utxo = input.witness_utxo; + // When we're taking our information from a witness UTXO, we can't verify it is actually data from + // the output being spent. This is safe in case a witness signature is produced (which includes this + // information directly in the hash), but not for non-witness signatures. Remember that we require + // a witness signature in this situation. + require_witness_sig = true; + } else { + return false; + } + + MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash); + sigdata.witness = false; + bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata); + // Verify that a witness signature was produced in case one was required. + if (require_witness_sig && !sigdata.witness) return false; + input.FromSignatureData(sigdata); + + // If we have a witness signature, use the smaller witness UTXO. + if (sigdata.witness) { + input.witness_utxo = utxo; + input.non_witness_utxo = nullptr; + } + + return sig_complete; +} + +bool FinalizePSBT(PartiallySignedTransaction& psbtx) +{ + // Finalize input signatures -- in case we have partial signatures that add up to a complete + // signature, but have not combined them yet (e.g. because the combiner that created this + // PartiallySignedTransaction did not understand them), this will combine them into a final + // script. + bool complete = true; + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL); + } + + return complete; +} + +bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result) +{ + // It's not safe to extract a PSBT that isn't finalized, and there's no easy way to check + // whether a PSBT is finalized without finalizing it, so we just do this. + if (!FinalizePSBT(psbtx)) { + return false; + } + + result = *psbtx.tx; + for (unsigned int i = 0; i < result.vin.size(); ++i) { + result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig; + result.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness; + } + return true; +} + +bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs) +{ + out = psbtxs[0]; // Copy the first one + + // Merge + for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) { + if (!out.Merge(*it)) { + error = TransactionError::PSBT_MISMATCH; + return false; + } + } + if (!out.IsSane()) { + error = TransactionError::INVALID_PSBT; + return false; + } + + return true; +} diff --git a/src/psbt.h b/src/psbt.h new file mode 100644 index 0000000000..e18790322b --- /dev/null +++ b/src/psbt.h @@ -0,0 +1,574 @@ +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_PSBT_H +#define BITCOIN_PSBT_H + +#include <attributes.h> +#include <node/transaction.h> +#include <primitives/transaction.h> +#include <pubkey.h> +#include <script/sign.h> + +// Magic bytes +static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff}; + +// Global types +static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00; + +// Input types +static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00; +static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01; +static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02; +static constexpr uint8_t PSBT_IN_SIGHASH = 0x03; +static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04; +static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05; +static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06; +static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07; +static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08; + +// Output types +static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00; +static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01; +static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02; + +// The separator is 0x00. Reading this in means that the unserializer can interpret it +// as a 0 length key which indicates that this is the separator. The separator has no value. +static constexpr uint8_t PSBT_SEPARATOR = 0x00; + +/** A structure for PSBTs which contain per-input information */ +struct PSBTInput +{ + CTransactionRef non_witness_utxo; + CTxOut witness_utxo; + CScript redeem_script; + CScript witness_script; + CScript final_script_sig; + CScriptWitness final_script_witness; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; + std::map<CKeyID, SigPair> partial_sigs; + std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; + int sighash_type = 0; + + bool IsNull() const; + void FillSignatureData(SignatureData& sigdata) const; + void FromSignatureData(const SignatureData& sigdata); + void Merge(const PSBTInput& input); + bool IsSane() const; + PSBTInput() {} + + template <typename Stream> + inline void Serialize(Stream& s) const { + // Write the utxo + // If there is a non-witness utxo, then don't add the witness one. + if (non_witness_utxo) { + SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO); + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + SerializeToVector(os, non_witness_utxo); + } else if (!witness_utxo.IsNull()) { + SerializeToVector(s, PSBT_IN_WITNESS_UTXO); + SerializeToVector(s, witness_utxo); + } + + if (final_script_sig.empty() && final_script_witness.IsNull()) { + // Write any partial signatures + for (auto sig_pair : partial_sigs) { + SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first)); + s << sig_pair.second.second; + } + + // Write the sighash type + if (sighash_type > 0) { + SerializeToVector(s, PSBT_IN_SIGHASH); + SerializeToVector(s, sighash_type); + } + + // Write the redeem script + if (!redeem_script.empty()) { + SerializeToVector(s, PSBT_IN_REDEEMSCRIPT); + s << redeem_script; + } + + // Write the witness script + if (!witness_script.empty()) { + SerializeToVector(s, PSBT_IN_WITNESSSCRIPT); + s << witness_script; + } + + // Write any hd keypaths + SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION); + } + + // Write script sig + if (!final_script_sig.empty()) { + SerializeToVector(s, PSBT_IN_SCRIPTSIG); + s << final_script_sig; + } + // write script witness + if (!final_script_witness.IsNull()) { + SerializeToVector(s, PSBT_IN_SCRIPTWITNESS); + SerializeToVector(s, final_script_witness.stack); + } + + // Write unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + s << PSBT_SEPARATOR; + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read loop + bool found_sep = false; + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) { + found_sep = true; + break; + } + + // First byte of key is the type + unsigned char type = key[0]; + + // Do stuff based on type + switch(type) { + case PSBT_IN_NON_WITNESS_UTXO: + { + if (non_witness_utxo) { + throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Non-witness utxo key is more than one byte type"); + } + // Set the stream to unserialize with witness since this is always a valid network transaction + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS); + UnserializeFromVector(os, non_witness_utxo); + break; + } + case PSBT_IN_WITNESS_UTXO: + if (!witness_utxo.IsNull()) { + throw std::ios_base::failure("Duplicate Key, input witness utxo already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Witness utxo key is more than one byte type"); + } + UnserializeFromVector(s, witness_utxo); + break; + case PSBT_IN_PARTIAL_SIG: + { + // Make sure that the key is the size of pubkey + 1 + if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { + throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey"); + } + // Read in the pubkey from key + CPubKey pubkey(key.begin() + 1, key.end()); + if (!pubkey.IsFullyValid()) { + throw std::ios_base::failure("Invalid pubkey"); + } + if (partial_sigs.count(pubkey.GetID()) > 0) { + throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided"); + } + + // Read in the signature from value + std::vector<unsigned char> sig; + s >> sig; + + // Add to list + partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig))); + break; + } + case PSBT_IN_SIGHASH: + if (sighash_type > 0) { + throw std::ios_base::failure("Duplicate Key, input sighash type already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Sighash type key is more than one byte type"); + } + UnserializeFromVector(s, sighash_type); + break; + case PSBT_IN_REDEEMSCRIPT: + { + if (!redeem_script.empty()) { + throw std::ios_base::failure("Duplicate Key, input redeemScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input redeemScript key is more than one byte type"); + } + s >> redeem_script; + break; + } + case PSBT_IN_WITNESSSCRIPT: + { + if (!witness_script.empty()) { + throw std::ios_base::failure("Duplicate Key, input witnessScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Input witnessScript key is more than one byte type"); + } + s >> witness_script; + break; + } + case PSBT_IN_BIP32_DERIVATION: + { + DeserializeHDKeypaths(s, key, hd_keypaths); + break; + } + case PSBT_IN_SCRIPTSIG: + { + if (!final_script_sig.empty()) { + throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Final scriptSig key is more than one byte type"); + } + s >> final_script_sig; + break; + } + case PSBT_IN_SCRIPTWITNESS: + { + if (!final_script_witness.IsNull()) { + throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Final scriptWitness key is more than one byte type"); + } + UnserializeFromVector(s, final_script_witness.stack); + break; + } + // Unknown stuff + default: + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + break; + } + } + + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of an input map"); + } + } + + template <typename Stream> + PSBTInput(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + +/** A structure for PSBTs which contains per output information */ +struct PSBTOutput +{ + CScript redeem_script; + CScript witness_script; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; + std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; + + bool IsNull() const; + void FillSignatureData(SignatureData& sigdata) const; + void FromSignatureData(const SignatureData& sigdata); + void Merge(const PSBTOutput& output); + bool IsSane() const; + PSBTOutput() {} + + template <typename Stream> + inline void Serialize(Stream& s) const { + // Write the redeem script + if (!redeem_script.empty()) { + SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT); + s << redeem_script; + } + + // Write the witness script + if (!witness_script.empty()) { + SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT); + s << witness_script; + } + + // Write any hd keypaths + SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION); + + // Write unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + s << PSBT_SEPARATOR; + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read loop + bool found_sep = false; + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) { + found_sep = true; + break; + } + + // First byte of key is the type + unsigned char type = key[0]; + + // Do stuff based on type + switch(type) { + case PSBT_OUT_REDEEMSCRIPT: + { + if (!redeem_script.empty()) { + throw std::ios_base::failure("Duplicate Key, output redeemScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output redeemScript key is more than one byte type"); + } + s >> redeem_script; + break; + } + case PSBT_OUT_WITNESSSCRIPT: + { + if (!witness_script.empty()) { + throw std::ios_base::failure("Duplicate Key, output witnessScript already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Output witnessScript key is more than one byte type"); + } + s >> witness_script; + break; + } + case PSBT_OUT_BIP32_DERIVATION: + { + DeserializeHDKeypaths(s, key, hd_keypaths); + break; + } + // Unknown stuff + default: { + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + break; + } + } + } + + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of an output map"); + } + } + + template <typename Stream> + PSBTOutput(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + +/** A version of CTransaction with the PSBT format*/ +struct PartiallySignedTransaction +{ + boost::optional<CMutableTransaction> tx; + std::vector<PSBTInput> inputs; + std::vector<PSBTOutput> outputs; + std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; + + bool IsNull() const; + + /** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the + * same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */ + NODISCARD bool Merge(const PartiallySignedTransaction& psbt); + bool IsSane() const; + PartiallySignedTransaction() {} + PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {} + explicit PartiallySignedTransaction(const CMutableTransaction& tx); + + template <typename Stream> + inline void Serialize(Stream& s) const { + + // magic bytes + s << PSBT_MAGIC_BYTES; + + // unsigned tx flag + SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX); + + // Write serialized tx to a stream + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + SerializeToVector(os, *tx); + + // Write the unknown things + for (auto& entry : unknown) { + s << entry.first; + s << entry.second; + } + + // Separator + s << PSBT_SEPARATOR; + + // Write inputs + for (const PSBTInput& input : inputs) { + s << input; + } + // Write outputs + for (const PSBTOutput& output : outputs) { + s << output; + } + } + + + template <typename Stream> + inline void Unserialize(Stream& s) { + // Read the magic bytes + uint8_t magic[5]; + s >> magic; + if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) { + throw std::ios_base::failure("Invalid PSBT magic bytes"); + } + + // Read global data + bool found_sep = false; + while(!s.empty()) { + // Read + std::vector<unsigned char> key; + s >> key; + + // the key is empty if that was actually a separator byte + // This is a special case for key lengths 0 as those are not allowed (except for separator) + if (key.empty()) { + found_sep = true; + break; + } + + // First byte of key is the type + unsigned char type = key[0]; + + // Do stuff based on type + switch(type) { + case PSBT_GLOBAL_UNSIGNED_TX: + { + if (tx) { + throw std::ios_base::failure("Duplicate Key, unsigned tx already provided"); + } else if (key.size() != 1) { + throw std::ios_base::failure("Global unsigned tx key is more than one byte type"); + } + CMutableTransaction mtx; + // Set the stream to serialize with non-witness since this should always be non-witness + OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); + UnserializeFromVector(os, mtx); + tx = std::move(mtx); + // Make sure that all scriptSigs and scriptWitnesses are empty + for (const CTxIn& txin : tx->vin) { + if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) { + throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses."); + } + } + break; + } + // Unknown stuff + default: { + if (unknown.count(key) > 0) { + throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); + } + // Read in the value + std::vector<unsigned char> val_bytes; + s >> val_bytes; + unknown.emplace(std::move(key), std::move(val_bytes)); + } + } + } + + if (!found_sep) { + throw std::ios_base::failure("Separator is missing at the end of the global map"); + } + + // Make sure that we got an unsigned tx + if (!tx) { + throw std::ios_base::failure("No unsigned transcation was provided"); + } + + // Read input data + unsigned int i = 0; + while (!s.empty() && i < tx->vin.size()) { + PSBTInput input; + s >> input; + inputs.push_back(input); + + // Make sure the non-witness utxo matches the outpoint + if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) { + throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash"); + } + ++i; + } + // Make sure that the number of inputs matches the number of inputs in the transaction + if (inputs.size() != tx->vin.size()) { + throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction."); + } + + // Read output data + i = 0; + while (!s.empty() && i < tx->vout.size()) { + PSBTOutput output; + s >> output; + outputs.push_back(output); + ++i; + } + // Make sure that the number of outputs matches the number of outputs in the transaction + if (outputs.size() != tx->vout.size()) { + throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction."); + } + // Sanity check + if (!IsSane()) { + throw std::ios_base::failure("PSBT is not sane."); + } + } + + template <typename Stream> + PartiallySignedTransaction(deserialize_type, Stream& s) { + Unserialize(s); + } +}; + +/** Checks whether a PSBTInput is already signed. */ +bool PSBTInputSigned(PSBTInput& input); + +/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ +bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL); + +/** + * Finalizes a PSBT if possible, combining partial signatures. + * + * @param[in,out] &psbtx reference to PartiallySignedTransaction to finalize + * return True if the PSBT is now complete, false otherwise + */ +bool FinalizePSBT(PartiallySignedTransaction& psbtx); + +/** + * Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized. + * + * @param[in] &psbtx reference to PartiallySignedTransaction + * @param[out] result CMutableTransaction representing the complete transaction, if successful + * @return True if we successfully extracted the transaction, false otherwise + */ +bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransaction& result); + +/** + * Combines PSBTs with the same underlying transaction, resulting in a single PSBT with all partial signatures from each input. + * + * @param[out] &out the combined PSBT, if successful + * @param[out] &error reference to TransactionError to fill with error info on failure + * @param[in] psbtxs the PSBTs to combine + * @return True if we successfully combined the transactions, false if they were not compatible + */ +bool CombinePSBTs(PartiallySignedTransaction& out, TransactionError& error, const std::vector<PartiallySignedTransaction>& psbtxs); + +#endif // BITCOIN_PSBT_H 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/paymentserver.cpp b/src/qt/paymentserver.cpp index b1ec90ad36..43dccec4ea 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -292,9 +292,9 @@ void PaymentServer::handleURIOrFile(const QString& s) else if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI { QUrlQuery uri((QUrl(s))); +#ifdef ENABLE_BIP70 if (uri.hasQueryItem("r")) // payment request URI { -#ifdef ENABLE_BIP70 Q_EMIT message(tr("URI handling"), tr("You are using a BIP70 URL which will be unsupported in the future."), CClientUIInterface::ICON_WARNING); @@ -315,19 +315,23 @@ void PaymentServer::handleURIOrFile(const QString& s) tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()), CClientUIInterface::ICON_WARNING); } -#else - Q_EMIT message(tr("URI handling"), - tr("Cannot process payment request because BIP70 support was not compiled in."), - CClientUIInterface::ICON_WARNING); -#endif return; } - else // normal URI + else +#endif + // normal URI { SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { if (!IsValidDestinationString(recipient.address.toStdString())) { +#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."), + CClientUIInterface::ICON_WARNING); + } +#endif Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), CClientUIInterface::MSG_ERROR); } @@ -343,9 +347,9 @@ void PaymentServer::handleURIOrFile(const QString& s) } } -#ifdef ENABLE_BIP70 if (QFile::exists(s)) // payment request file { +#ifdef ENABLE_BIP70 PaymentRequestPlus request; SendCoinsRecipient recipient; if (!readPaymentRequestFromFile(s, request)) @@ -358,8 +362,12 @@ void PaymentServer::handleURIOrFile(const QString& s) Q_EMIT receivedPaymentRequest(recipient); return; - } +#else + Q_EMIT message(tr("Payment request file handling"), + tr("Cannot process payment request because BIP70 support was not compiled in."), + CClientUIInterface::ICON_WARNING); #endif + } } void PaymentServer::handleURIConnection() 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 1126fa3d2c..6ec05565e9 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -13,10 +13,11 @@ #include <key_io.h> #include <keystore.h> #include <merkleblock.h> -#include <net.h> +#include <node/transaction.h> #include <policy/policy.h> #include <policy/rbf.h> #include <primitives/transaction.h> +#include <psbt.h> #include <rpc/rawtransaction.h> #include <rpc/server.h> #include <rpc/util.h> @@ -24,13 +25,11 @@ #include <script/script_error.h> #include <script/sign.h> #include <script/standard.h> -#include <txmempool.h> #include <uint256.h> #include <util/strencodings.h> #include <validation.h> #include <validationinterface.h> -#include <future> #include <stdint.h> #include <univalue.h> @@ -1052,8 +1051,6 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) }, }.ToString()); - std::promise<void> promise; - RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); // parse hex string from parameter @@ -1061,67 +1058,17 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) if (!DecodeHexTx(mtx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); CTransactionRef tx(MakeTransactionRef(std::move(mtx))); - const uint256& hashTx = tx->GetHash(); - - CAmount nMaxRawTxFee = maxTxFee; - if (!request.params[1].isNull() && request.params[1].get_bool()) - nMaxRawTxFee = 0; - { // cs_main scope - LOCK(cs_main); - CCoinsViewCache &view = *pcoinsTip; - bool fHaveChain = false; - for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) { - const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o)); - fHaveChain = !existingCoin.IsSpent(); + bool allowhighfees = false; + if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool(); + uint256 txid; + TransactionError err; + std::string err_string; + if (!BroadcastTransaction(tx, txid, err, err_string, allowhighfees)) { + throw JSONRPCTransactionError(err, err_string); } - bool fHaveMempool = mempool.exists(hashTx); - if (!fHaveMempool && !fHaveChain) { - // push to local node and sync with wallets - CValidationState state; - bool fMissingInputs; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, - nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { - if (state.IsInvalid()) { - throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state)); - } else { - if (fMissingInputs) { - throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); - } - throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state)); - } - } else { - // If wallet is enabled, ensure that the wallet has been made aware - // of the new transaction prior to returning. This prevents a race - // where a user might call sendrawtransaction with a transaction - // to/from their wallet, immediately call some wallet RPC, and get - // a stale result because callbacks have not yet been processed. - CallFunctionInValidationInterfaceQueue([&promise] { - promise.set_value(); - }); - } - } else if (fHaveChain) { - throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); - } else { - // Make sure we don't block forever if re-sending - // a transaction already in mempool. - promise.set_value(); - } - - } // cs_main - - promise.get_future().wait(); - - if(!g_connman) - throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - CInv inv(MSG_TX, hashTx); - g_connman->ForEachNode([&inv](CNode* pnode) - { - pnode->PushInventory(inv); - }); - - return hashTx.GetHex(); + return txid.GetHex(); } static UniValue testmempoolaccept(const JSONRPCRequest& request) @@ -1334,7 +1281,7 @@ UniValue decodepsbt(const JSONRPCRequest& request) // Unserialize the transactions PartiallySignedTransaction psbtx; std::string error; - if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { + if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); } @@ -1535,23 +1482,16 @@ UniValue combinepsbt(const JSONRPCRequest& request) for (unsigned int i = 0; i < txs.size(); ++i) { PartiallySignedTransaction psbtx; std::string error; - if (!DecodePSBT(psbtx, txs[i].get_str(), error)) { + if (!DecodeBase64PSBT(psbtx, txs[i].get_str(), error)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); } psbtxs.push_back(psbtx); } - PartiallySignedTransaction merged_psbt(psbtxs[0]); // Copy the first one - - // Merge - for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) { - if (*it != merged_psbt) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs do not refer to the same transactions."); - } - merged_psbt.Merge(*it); - } - if (!merged_psbt.IsSane()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Merged PSBT is inconsistent"); + PartiallySignedTransaction merged_psbt; + TransactionError error; + if (!CombinePSBTs(merged_psbt, error, psbtxs)) { + throw JSONRPCTransactionError(error); } UniValue result(UniValue::VOBJ); @@ -1592,33 +1532,27 @@ UniValue finalizepsbt(const JSONRPCRequest& request) // Unserialize the transactions PartiallySignedTransaction psbtx; std::string error; - if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { + if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); } - // Finalize input signatures -- in case we have partial signatures that add up to a complete - // signature, but have not combined them yet (e.g. because the combiner that created this - // PartiallySignedTransaction did not understand them), this will combine them into a final - // script. - bool complete = true; - for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL); - } + bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()); + + CMutableTransaction mtx; + bool complete = FinalizeAndExtractPSBT(psbtx, mtx); UniValue result(UniValue::VOBJ); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - bool extract = request.params[1].isNull() || (!request.params[1].isNull() && request.params[1].get_bool()); + std::string result_str; + if (complete && extract) { - CMutableTransaction mtx(*psbtx.tx); - for (unsigned int i = 0; i < mtx.vin.size(); ++i) { - mtx.vin[i].scriptSig = psbtx.inputs[i].final_script_sig; - mtx.vin[i].scriptWitness = psbtx.inputs[i].final_script_witness; - } ssTx << mtx; - result.pushKV("hex", HexStr(ssTx.str())); + result_str = HexStr(ssTx.str()); + result.pushKV("hex", result_str); } else { ssTx << psbtx; - result.pushKV("psbt", EncodeBase64(ssTx.str())); + result_str = EncodeBase64(ssTx.str()); + result.pushKV("psbt", result_str); } result.pushKV("complete", complete); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index ee7f3ca0dc..023b4b6746 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -141,6 +141,34 @@ unsigned int ParseConfirmTarget(const UniValue& value) return (unsigned int)target; } +RPCErrorCode RPCErrorFromTransactionError(TransactionError terr) +{ + switch (terr) { + case TransactionError::MEMPOOL_REJECTED: + return RPC_TRANSACTION_REJECTED; + case TransactionError::ALREADY_IN_CHAIN: + return RPC_TRANSACTION_ALREADY_IN_CHAIN; + case TransactionError::P2P_DISABLED: + return RPC_CLIENT_P2P_DISABLED; + case TransactionError::INVALID_PSBT: + case TransactionError::PSBT_MISMATCH: + return RPC_INVALID_PARAMETER; + case TransactionError::SIGHASH_MISMATCH: + return RPC_DESERIALIZATION_ERROR; + default: break; + } + return RPC_TRANSACTION_ERROR; +} + +UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string) +{ + if (err_string.length() > 0) { + return JSONRPCError(RPCErrorFromTransactionError(terr), err_string); + } else { + return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr)); + } +} + struct Section { Section(const std::string& left, const std::string& right) : m_left{left}, m_right{right} {} diff --git a/src/rpc/util.h b/src/rpc/util.h index d895e99c7e..1c9ddcdf44 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_RPC_UTIL_H #define BITCOIN_RPC_UTIL_H +#include <node/transaction.h> #include <pubkey.h> #include <script/standard.h> #include <univalue.h> @@ -33,6 +34,9 @@ UniValue DescribeAddress(const CTxDestination& dest); //! Parse a confirm target option and raise an RPC error if it is invalid. unsigned int ParseConfirmTarget(const UniValue& value); +RPCErrorCode RPCErrorFromTransactionError(TransactionError terr); +UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = ""); + struct RPCArg { enum class Type { OBJ, diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 792fb2997f..9d6a390ea2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -232,67 +232,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } -bool PSBTInputSigned(PSBTInput& input) -{ - return !input.final_script_sig.empty() || !input.final_script_witness.IsNull(); -} - -bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash) -{ - PSBTInput& input = psbt.inputs.at(index); - const CMutableTransaction& tx = *psbt.tx; - - if (PSBTInputSigned(input)) { - return true; - } - - // Fill SignatureData with input info - SignatureData sigdata; - input.FillSignatureData(sigdata); - - // Get UTXO - bool require_witness_sig = false; - CTxOut utxo; - - // Verify input sanity, which checks that at most one of witness or non-witness utxos is provided. - if (!input.IsSane()) { - return false; - } - - if (input.non_witness_utxo) { - // If we're taking our information from a non-witness UTXO, verify that it matches the prevout. - COutPoint prevout = tx.vin[index].prevout; - if (input.non_witness_utxo->GetHash() != prevout.hash) { - return false; - } - utxo = input.non_witness_utxo->vout[prevout.n]; - } else if (!input.witness_utxo.IsNull()) { - utxo = input.witness_utxo; - // When we're taking our information from a witness UTXO, we can't verify it is actually data from - // the output being spent. This is safe in case a witness signature is produced (which includes this - // information directly in the hash), but not for non-witness signatures. Remember that we require - // a witness signature in this situation. - require_witness_sig = true; - } else { - return false; - } - - MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash); - sigdata.witness = false; - bool sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata); - // Verify that a witness signature was produced in case one was required. - if (require_witness_sig && !sigdata.witness) return false; - input.FromSignatureData(sigdata); - - // If we have a witness signature, use the smaller witness UTXO. - if (sigdata.witness) { - input.witness_utxo = utxo; - input.non_witness_utxo = nullptr; - } - - return sig_complete; -} - class SignatureExtractorChecker final : public BaseSignatureChecker { private: @@ -509,166 +448,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script) return false; } -PartiallySignedTransaction::PartiallySignedTransaction(const CMutableTransaction& tx) : tx(tx) -{ - inputs.resize(tx.vin.size()); - outputs.resize(tx.vout.size()); -} - -bool PartiallySignedTransaction::IsNull() const -{ - return !tx && inputs.empty() && outputs.empty() && unknown.empty(); -} - -void PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt) -{ - for (unsigned int i = 0; i < inputs.size(); ++i) { - inputs[i].Merge(psbt.inputs[i]); - } - for (unsigned int i = 0; i < outputs.size(); ++i) { - outputs[i].Merge(psbt.outputs[i]); - } - unknown.insert(psbt.unknown.begin(), psbt.unknown.end()); -} - -bool PartiallySignedTransaction::IsSane() const -{ - for (PSBTInput input : inputs) { - if (!input.IsSane()) return false; - } - return true; -} - -bool PSBTInput::IsNull() const -{ - return !non_witness_utxo && witness_utxo.IsNull() && partial_sigs.empty() && unknown.empty() && hd_keypaths.empty() && redeem_script.empty() && witness_script.empty(); -} - -void PSBTInput::FillSignatureData(SignatureData& sigdata) const -{ - if (!final_script_sig.empty()) { - sigdata.scriptSig = final_script_sig; - sigdata.complete = true; - } - if (!final_script_witness.IsNull()) { - sigdata.scriptWitness = final_script_witness; - sigdata.complete = true; - } - if (sigdata.complete) { - return; - } - - sigdata.signatures.insert(partial_sigs.begin(), partial_sigs.end()); - if (!redeem_script.empty()) { - sigdata.redeem_script = redeem_script; - } - if (!witness_script.empty()) { - sigdata.witness_script = witness_script; - } - for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); - } -} - -void PSBTInput::FromSignatureData(const SignatureData& sigdata) -{ - if (sigdata.complete) { - partial_sigs.clear(); - hd_keypaths.clear(); - redeem_script.clear(); - witness_script.clear(); - - if (!sigdata.scriptSig.empty()) { - final_script_sig = sigdata.scriptSig; - } - if (!sigdata.scriptWitness.IsNull()) { - final_script_witness = sigdata.scriptWitness; - } - return; - } - - partial_sigs.insert(sigdata.signatures.begin(), sigdata.signatures.end()); - if (redeem_script.empty() && !sigdata.redeem_script.empty()) { - redeem_script = sigdata.redeem_script; - } - if (witness_script.empty() && !sigdata.witness_script.empty()) { - witness_script = sigdata.witness_script; - } - for (const auto& entry : sigdata.misc_pubkeys) { - hd_keypaths.emplace(entry.second); - } -} - -void PSBTInput::Merge(const PSBTInput& input) -{ - if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo; - if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) { - witness_utxo = input.witness_utxo; - non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one. - } - - partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end()); - hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end()); - unknown.insert(input.unknown.begin(), input.unknown.end()); - - if (redeem_script.empty() && !input.redeem_script.empty()) redeem_script = input.redeem_script; - if (witness_script.empty() && !input.witness_script.empty()) witness_script = input.witness_script; - if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig; - if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness; -} - -bool PSBTInput::IsSane() const -{ - // Cannot have both witness and non-witness utxos - if (!witness_utxo.IsNull() && non_witness_utxo) return false; - - // If we have a witness_script or a scriptWitness, we must also have a witness utxo - if (!witness_script.empty() && witness_utxo.IsNull()) return false; - if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false; - - return true; -} - -void PSBTOutput::FillSignatureData(SignatureData& sigdata) const -{ - if (!redeem_script.empty()) { - sigdata.redeem_script = redeem_script; - } - if (!witness_script.empty()) { - sigdata.witness_script = witness_script; - } - for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); - } -} - -void PSBTOutput::FromSignatureData(const SignatureData& sigdata) -{ - if (redeem_script.empty() && !sigdata.redeem_script.empty()) { - redeem_script = sigdata.redeem_script; - } - if (witness_script.empty() && !sigdata.witness_script.empty()) { - witness_script = sigdata.witness_script; - } - for (const auto& entry : sigdata.misc_pubkeys) { - hd_keypaths.emplace(entry.second); - } -} - -bool PSBTOutput::IsNull() const -{ - return redeem_script.empty() && witness_script.empty() && hd_keypaths.empty() && unknown.empty(); -} - -void PSBTOutput::Merge(const PSBTOutput& output) -{ - hd_keypaths.insert(output.hd_keypaths.begin(), output.hd_keypaths.end()); - unknown.insert(output.unknown.begin(), output.unknown.end()); - - if (redeem_script.empty() && !output.redeem_script.empty()) redeem_script = output.redeem_script; - if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; -} - bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return m_provider->GetCScript(scriptid, script); diff --git a/src/script/sign.h b/src/script/sign.h index e884f4c480..64eb3eb8e5 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -123,32 +123,6 @@ struct SignatureData { void MergeSignatureData(SignatureData sigdata); }; -// Magic bytes -static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff}; - -// Global types -static constexpr uint8_t PSBT_GLOBAL_UNSIGNED_TX = 0x00; - -// Input types -static constexpr uint8_t PSBT_IN_NON_WITNESS_UTXO = 0x00; -static constexpr uint8_t PSBT_IN_WITNESS_UTXO = 0x01; -static constexpr uint8_t PSBT_IN_PARTIAL_SIG = 0x02; -static constexpr uint8_t PSBT_IN_SIGHASH = 0x03; -static constexpr uint8_t PSBT_IN_REDEEMSCRIPT = 0x04; -static constexpr uint8_t PSBT_IN_WITNESSSCRIPT = 0x05; -static constexpr uint8_t PSBT_IN_BIP32_DERIVATION = 0x06; -static constexpr uint8_t PSBT_IN_SCRIPTSIG = 0x07; -static constexpr uint8_t PSBT_IN_SCRIPTWITNESS = 0x08; - -// Output types -static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00; -static constexpr uint8_t PSBT_OUT_WITNESSSCRIPT = 0x01; -static constexpr uint8_t PSBT_OUT_BIP32_DERIVATION = 0x02; - -// The separator is 0x00. Reading this in means that the unserializer can interpret it -// as a 0 length key which indicates that this is the separator. The separator has no value. -static constexpr uint8_t PSBT_SEPARATOR = 0x00; - // Takes a stream and multiple arguments and serializes them as if first serialized into a vector and then into the stream // The resulting output into the stream has the total serialized length of all of the objects followed by all objects concatenated with each other. template<typename Stream, typename... X> @@ -223,514 +197,6 @@ void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_k } } -/** A structure for PSBTs which contain per-input information */ -struct PSBTInput -{ - CTransactionRef non_witness_utxo; - CTxOut witness_utxo; - CScript redeem_script; - CScript witness_script; - CScript final_script_sig; - CScriptWitness final_script_witness; - std::map<CPubKey, KeyOriginInfo> hd_keypaths; - std::map<CKeyID, SigPair> partial_sigs; - std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; - int sighash_type = 0; - - bool IsNull() const; - void FillSignatureData(SignatureData& sigdata) const; - void FromSignatureData(const SignatureData& sigdata); - void Merge(const PSBTInput& input); - bool IsSane() const; - PSBTInput() {} - - template <typename Stream> - inline void Serialize(Stream& s) const { - // Write the utxo - // If there is a non-witness utxo, then don't add the witness one. - if (non_witness_utxo) { - SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO); - OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); - SerializeToVector(os, non_witness_utxo); - } else if (!witness_utxo.IsNull()) { - SerializeToVector(s, PSBT_IN_WITNESS_UTXO); - SerializeToVector(s, witness_utxo); - } - - if (final_script_sig.empty() && final_script_witness.IsNull()) { - // Write any partial signatures - for (auto sig_pair : partial_sigs) { - SerializeToVector(s, PSBT_IN_PARTIAL_SIG, MakeSpan(sig_pair.second.first)); - s << sig_pair.second.second; - } - - // Write the sighash type - if (sighash_type > 0) { - SerializeToVector(s, PSBT_IN_SIGHASH); - SerializeToVector(s, sighash_type); - } - - // Write the redeem script - if (!redeem_script.empty()) { - SerializeToVector(s, PSBT_IN_REDEEMSCRIPT); - s << redeem_script; - } - - // Write the witness script - if (!witness_script.empty()) { - SerializeToVector(s, PSBT_IN_WITNESSSCRIPT); - s << witness_script; - } - - // Write any hd keypaths - SerializeHDKeypaths(s, hd_keypaths, PSBT_IN_BIP32_DERIVATION); - } - - // Write script sig - if (!final_script_sig.empty()) { - SerializeToVector(s, PSBT_IN_SCRIPTSIG); - s << final_script_sig; - } - // write script witness - if (!final_script_witness.IsNull()) { - SerializeToVector(s, PSBT_IN_SCRIPTWITNESS); - SerializeToVector(s, final_script_witness.stack); - } - - // Write unknown things - for (auto& entry : unknown) { - s << entry.first; - s << entry.second; - } - - s << PSBT_SEPARATOR; - } - - - template <typename Stream> - inline void Unserialize(Stream& s) { - // Read loop - bool found_sep = false; - while(!s.empty()) { - // Read - std::vector<unsigned char> key; - s >> key; - - // the key is empty if that was actually a separator byte - // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) { - found_sep = true; - break; - } - - // First byte of key is the type - unsigned char type = key[0]; - - // Do stuff based on type - switch(type) { - case PSBT_IN_NON_WITNESS_UTXO: - { - if (non_witness_utxo) { - throw std::ios_base::failure("Duplicate Key, input non-witness utxo already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Non-witness utxo key is more than one byte type"); - } - // Set the stream to unserialize with witness since this is always a valid network transaction - OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() & ~SERIALIZE_TRANSACTION_NO_WITNESS); - UnserializeFromVector(os, non_witness_utxo); - break; - } - case PSBT_IN_WITNESS_UTXO: - if (!witness_utxo.IsNull()) { - throw std::ios_base::failure("Duplicate Key, input witness utxo already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Witness utxo key is more than one byte type"); - } - UnserializeFromVector(s, witness_utxo); - break; - case PSBT_IN_PARTIAL_SIG: - { - // Make sure that the key is the size of pubkey + 1 - if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { - throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey"); - } - // Read in the pubkey from key - CPubKey pubkey(key.begin() + 1, key.end()); - if (!pubkey.IsFullyValid()) { - throw std::ios_base::failure("Invalid pubkey"); - } - if (partial_sigs.count(pubkey.GetID()) > 0) { - throw std::ios_base::failure("Duplicate Key, input partial signature for pubkey already provided"); - } - - // Read in the signature from value - std::vector<unsigned char> sig; - s >> sig; - - // Add to list - partial_sigs.emplace(pubkey.GetID(), SigPair(pubkey, std::move(sig))); - break; - } - case PSBT_IN_SIGHASH: - if (sighash_type > 0) { - throw std::ios_base::failure("Duplicate Key, input sighash type already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Sighash type key is more than one byte type"); - } - UnserializeFromVector(s, sighash_type); - break; - case PSBT_IN_REDEEMSCRIPT: - { - if (!redeem_script.empty()) { - throw std::ios_base::failure("Duplicate Key, input redeemScript already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Input redeemScript key is more than one byte type"); - } - s >> redeem_script; - break; - } - case PSBT_IN_WITNESSSCRIPT: - { - if (!witness_script.empty()) { - throw std::ios_base::failure("Duplicate Key, input witnessScript already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Input witnessScript key is more than one byte type"); - } - s >> witness_script; - break; - } - case PSBT_IN_BIP32_DERIVATION: - { - DeserializeHDKeypaths(s, key, hd_keypaths); - break; - } - case PSBT_IN_SCRIPTSIG: - { - if (!final_script_sig.empty()) { - throw std::ios_base::failure("Duplicate Key, input final scriptSig already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Final scriptSig key is more than one byte type"); - } - s >> final_script_sig; - break; - } - case PSBT_IN_SCRIPTWITNESS: - { - if (!final_script_witness.IsNull()) { - throw std::ios_base::failure("Duplicate Key, input final scriptWitness already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Final scriptWitness key is more than one byte type"); - } - UnserializeFromVector(s, final_script_witness.stack); - break; - } - // Unknown stuff - default: - if (unknown.count(key) > 0) { - throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); - } - // Read in the value - std::vector<unsigned char> val_bytes; - s >> val_bytes; - unknown.emplace(std::move(key), std::move(val_bytes)); - break; - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of an input map"); - } - } - - template <typename Stream> - PSBTInput(deserialize_type, Stream& s) { - Unserialize(s); - } -}; - -/** A structure for PSBTs which contains per output information */ -struct PSBTOutput -{ - CScript redeem_script; - CScript witness_script; - std::map<CPubKey, KeyOriginInfo> hd_keypaths; - std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; - - bool IsNull() const; - void FillSignatureData(SignatureData& sigdata) const; - void FromSignatureData(const SignatureData& sigdata); - void Merge(const PSBTOutput& output); - bool IsSane() const; - PSBTOutput() {} - - template <typename Stream> - inline void Serialize(Stream& s) const { - // Write the redeem script - if (!redeem_script.empty()) { - SerializeToVector(s, PSBT_OUT_REDEEMSCRIPT); - s << redeem_script; - } - - // Write the witness script - if (!witness_script.empty()) { - SerializeToVector(s, PSBT_OUT_WITNESSSCRIPT); - s << witness_script; - } - - // Write any hd keypaths - SerializeHDKeypaths(s, hd_keypaths, PSBT_OUT_BIP32_DERIVATION); - - // Write unknown things - for (auto& entry : unknown) { - s << entry.first; - s << entry.second; - } - - s << PSBT_SEPARATOR; - } - - - template <typename Stream> - inline void Unserialize(Stream& s) { - // Read loop - bool found_sep = false; - while(!s.empty()) { - // Read - std::vector<unsigned char> key; - s >> key; - - // the key is empty if that was actually a separator byte - // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) { - found_sep = true; - break; - } - - // First byte of key is the type - unsigned char type = key[0]; - - // Do stuff based on type - switch(type) { - case PSBT_OUT_REDEEMSCRIPT: - { - if (!redeem_script.empty()) { - throw std::ios_base::failure("Duplicate Key, output redeemScript already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Output redeemScript key is more than one byte type"); - } - s >> redeem_script; - break; - } - case PSBT_OUT_WITNESSSCRIPT: - { - if (!witness_script.empty()) { - throw std::ios_base::failure("Duplicate Key, output witnessScript already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Output witnessScript key is more than one byte type"); - } - s >> witness_script; - break; - } - case PSBT_OUT_BIP32_DERIVATION: - { - DeserializeHDKeypaths(s, key, hd_keypaths); - break; - } - // Unknown stuff - default: { - if (unknown.count(key) > 0) { - throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); - } - // Read in the value - std::vector<unsigned char> val_bytes; - s >> val_bytes; - unknown.emplace(std::move(key), std::move(val_bytes)); - break; - } - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of an output map"); - } - } - - template <typename Stream> - PSBTOutput(deserialize_type, Stream& s) { - Unserialize(s); - } -}; - -/** A version of CTransaction with the PSBT format*/ -struct PartiallySignedTransaction -{ - boost::optional<CMutableTransaction> tx; - std::vector<PSBTInput> inputs; - std::vector<PSBTOutput> outputs; - std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; - - bool IsNull() const; - void Merge(const PartiallySignedTransaction& psbt); - bool IsSane() const; - PartiallySignedTransaction() {} - PartiallySignedTransaction(const PartiallySignedTransaction& psbt_in) : tx(psbt_in.tx), inputs(psbt_in.inputs), outputs(psbt_in.outputs), unknown(psbt_in.unknown) {} - explicit PartiallySignedTransaction(const CMutableTransaction& tx); - - // Only checks if they refer to the same transaction - friend bool operator==(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b) - { - return a.tx->GetHash() == b.tx->GetHash(); - } - friend bool operator!=(const PartiallySignedTransaction& a, const PartiallySignedTransaction &b) - { - return !(a == b); - } - - template <typename Stream> - inline void Serialize(Stream& s) const { - - // magic bytes - s << PSBT_MAGIC_BYTES; - - // unsigned tx flag - SerializeToVector(s, PSBT_GLOBAL_UNSIGNED_TX); - - // Write serialized tx to a stream - OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); - SerializeToVector(os, *tx); - - // Write the unknown things - for (auto& entry : unknown) { - s << entry.first; - s << entry.second; - } - - // Separator - s << PSBT_SEPARATOR; - - // Write inputs - for (const PSBTInput& input : inputs) { - s << input; - } - // Write outputs - for (const PSBTOutput& output : outputs) { - s << output; - } - } - - - template <typename Stream> - inline void Unserialize(Stream& s) { - // Read the magic bytes - uint8_t magic[5]; - s >> magic; - if (!std::equal(magic, magic + 5, PSBT_MAGIC_BYTES)) { - throw std::ios_base::failure("Invalid PSBT magic bytes"); - } - - // Read global data - bool found_sep = false; - while(!s.empty()) { - // Read - std::vector<unsigned char> key; - s >> key; - - // the key is empty if that was actually a separator byte - // This is a special case for key lengths 0 as those are not allowed (except for separator) - if (key.empty()) { - found_sep = true; - break; - } - - // First byte of key is the type - unsigned char type = key[0]; - - // Do stuff based on type - switch(type) { - case PSBT_GLOBAL_UNSIGNED_TX: - { - if (tx) { - throw std::ios_base::failure("Duplicate Key, unsigned tx already provided"); - } else if (key.size() != 1) { - throw std::ios_base::failure("Global unsigned tx key is more than one byte type"); - } - CMutableTransaction mtx; - // Set the stream to serialize with non-witness since this should always be non-witness - OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS); - UnserializeFromVector(os, mtx); - tx = std::move(mtx); - // Make sure that all scriptSigs and scriptWitnesses are empty - for (const CTxIn& txin : tx->vin) { - if (!txin.scriptSig.empty() || !txin.scriptWitness.IsNull()) { - throw std::ios_base::failure("Unsigned tx does not have empty scriptSigs and scriptWitnesses."); - } - } - break; - } - // Unknown stuff - default: { - if (unknown.count(key) > 0) { - throw std::ios_base::failure("Duplicate Key, key for unknown value already provided"); - } - // Read in the value - std::vector<unsigned char> val_bytes; - s >> val_bytes; - unknown.emplace(std::move(key), std::move(val_bytes)); - } - } - } - - if (!found_sep) { - throw std::ios_base::failure("Separator is missing at the end of the global map"); - } - - // Make sure that we got an unsigned tx - if (!tx) { - throw std::ios_base::failure("No unsigned transcation was provided"); - } - - // Read input data - unsigned int i = 0; - while (!s.empty() && i < tx->vin.size()) { - PSBTInput input; - s >> input; - inputs.push_back(input); - - // Make sure the non-witness utxo matches the outpoint - if (input.non_witness_utxo && input.non_witness_utxo->GetHash() != tx->vin[i].prevout.hash) { - throw std::ios_base::failure("Non-witness UTXO does not match outpoint hash"); - } - ++i; - } - // Make sure that the number of inputs matches the number of inputs in the transaction - if (inputs.size() != tx->vin.size()) { - throw std::ios_base::failure("Inputs provided does not match the number of inputs in transaction."); - } - - // Read output data - i = 0; - while (!s.empty() && i < tx->vout.size()) { - PSBTOutput output; - s >> output; - outputs.push_back(output); - ++i; - } - // Make sure that the number of outputs matches the number of outputs in the transaction - if (outputs.size() != tx->vout.size()) { - throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction."); - } - // Sanity check - if (!IsSane()) { - throw std::ios_base::failure("PSBT is not sane."); - } - } - - template <typename Stream> - PartiallySignedTransaction(deserialize_type, Stream& s) { - Unserialize(s); - } -}; - /** Produce a script signature using a generic signature creator. */ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); @@ -738,12 +204,6 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); -/** Checks whether a PSBTInput is already signed. */ -bool PSBTInputSigned(PSBTInput& input); - -/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ -bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL); - /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); void UpdateInput(CTxIn& input, const SignatureData& data); diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index fedeeac39b..b55547bc63 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -141,7 +141,7 @@ std::string EncodeBase64(const std::string& str) return EncodeBase64((const unsigned char*)str.c_str(), str.size()); } -std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) +std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid) { static const int decode64_table[256] = { @@ -183,14 +183,14 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid) ++p; } valid = valid && (p - e) % 4 == 0 && p - q < 4; - if (pfInvalid) *pfInvalid = !valid; + if (pf_invalid) *pf_invalid = !valid; return ret; } -std::string DecodeBase64(const std::string& str) +std::string DecodeBase64(const std::string& str, bool* pf_invalid) { - std::vector<unsigned char> vchRet = DecodeBase64(str.c_str()); + std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid); return std::string((const char*)vchRet.data(), vchRet.size()); } @@ -210,7 +210,7 @@ std::string EncodeBase32(const std::string& str) return EncodeBase32((const unsigned char*)str.c_str(), str.size()); } -std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) +std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid) { static const int decode32_table[256] = { @@ -252,14 +252,14 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid) ++p; } valid = valid && (p - e) % 8 == 0 && p - q < 8; - if (pfInvalid) *pfInvalid = !valid; + if (pf_invalid) *pf_invalid = !valid; return ret; } -std::string DecodeBase32(const std::string& str) +std::string DecodeBase32(const std::string& str, bool* pf_invalid) { - std::vector<unsigned char> vchRet = DecodeBase32(str.c_str()); + std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid); return std::string((const char*)vchRet.data(), vchRet.size()); } diff --git a/src/util/strencodings.h b/src/util/strencodings.h index e392055f27..59eefff566 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -44,12 +44,12 @@ bool IsHex(const std::string& str); * Return true if the string is a hex number, optionally prefixed with "0x" */ bool IsHexNumber(const std::string& str); -std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr); -std::string DecodeBase64(const std::string& str); +std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid = nullptr); +std::string DecodeBase64(const std::string& str, bool* pf_invalid = nullptr); std::string EncodeBase64(const unsigned char* pch, size_t len); std::string EncodeBase64(const std::string& str); -std::vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid = nullptr); -std::string DecodeBase32(const std::string& str); +std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid = nullptr); +std::string DecodeBase32(const std::string& str, bool* pf_invalid = nullptr); std::string EncodeBase32(const unsigned char* pch, size_t len); std::string EncodeBase32(const std::string& str); diff --git a/src/validation.cpp b/src/validation.cpp index dbdc1afb35..1806bc1268 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4347,7 +4347,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) return true; try { - CBlock &block = const_cast<CBlock&>(chainparams.GenesisBlock()); + const CBlock& block = chainparams.GenesisBlock(); CDiskBlockPos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); if (blockPos.IsNull()) return error("%s: writing genesis block to disk failed", __func__); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index c64a85c910..99d880daa0 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -397,7 +397,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, std::string& er fs::path walletDir = env->Directory(); LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr)); - LogPrintf("Using wallet %s\n", walletFile); + LogPrintf("Using wallet %s\n", file_path.string()); // Wallet file must be a plain filename without a directory if (walletFile != fs::basename(walletFile) + fs::extension(walletFile)) diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp new file mode 100644 index 0000000000..761e7b7dd7 --- /dev/null +++ b/src/wallet/psbtwallet.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <wallet/psbtwallet.h> + +bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, TransactionError& error, bool& complete, int sighash_type, bool sign, bool bip32derivs) +{ + LOCK(pwallet->cs_wallet); + // Get all of the previous transactions + complete = true; + for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { + const CTxIn& txin = psbtx.tx->vin[i]; + PSBTInput& input = psbtx.inputs.at(i); + + if (PSBTInputSigned(input)) { + continue; + } + + // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness. + if (!input.IsSane()) { + error = TransactionError::INVALID_PSBT; + return false; + } + + // If we have no utxo, grab it from the wallet. + if (!input.non_witness_utxo && input.witness_utxo.IsNull()) { + const uint256& txhash = txin.prevout.hash; + const auto it = pwallet->mapWallet.find(txhash); + if (it != pwallet->mapWallet.end()) { + const CWalletTx& wtx = it->second; + // We only need the non_witness_utxo, which is a superset of the witness_utxo. + // The signing code will switch to the smaller witness_utxo if this is ok. + input.non_witness_utxo = wtx.tx; + } + } + + // Get the Sighash type + if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) { + error = TransactionError::SIGHASH_MISMATCH; + return false; + } + + complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type); + } + + // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change + for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { + const CTxOut& out = psbtx.tx->vout.at(i); + PSBTOutput& psbt_out = psbtx.outputs.at(i); + + // Fill a SignatureData with output info + SignatureData sigdata; + psbt_out.FillSignatureData(sigdata); + + MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1); + ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata); + psbt_out.FromSignatureData(sigdata); + } + + return true; +} diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h new file mode 100644 index 0000000000..b679f5c6ba --- /dev/null +++ b/src/wallet/psbtwallet.h @@ -0,0 +1,36 @@ +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_PSBTWALLET_H +#define BITCOIN_WALLET_PSBTWALLET_H + +#include <node/transaction.h> +#include <psbt.h> +#include <primitives/transaction.h> +#include <wallet/wallet.h> + +/** + * Fills out a PSBT with information from the wallet. Fills in UTXOs if we have + * them. Tries to sign if sign=true. Sets `complete` if the PSBT is now complete + * (i.e. has all required signatures or signature-parts, and is ready to + * finalize.) Sets `error` and returns false if something goes wrong. + * + * @param[in] pwallet pointer to a wallet + * @param[in] &psbtx reference to PartiallySignedTransaction to fill in + * @param[out] &error reference to UniValue to fill with error info on failure + * @param[out] &complete indicates whether the PSBT is now complete + * @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify) + * @param[in] sign whether to sign or not + * @param[in] bip32derivs whether to fill in bip32 derivation information if available + * return true on success, false on error (and fills in `error`) + */ +bool FillPSBT(const CWallet* pwallet, + PartiallySignedTransaction& psbtx, + TransactionError& error, + bool& complete, + int sighash_type = 1 /* SIGHASH_ALL */, + bool sign = true, + bool bip32derivs = false); + +#endif // BITCOIN_WALLET_PSBTWALLET_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ac752252e7..ee77739986 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -13,6 +13,7 @@ #include <validation.h> #include <key_io.h> #include <net.h> +#include <node/transaction.h> #include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> @@ -30,6 +31,7 @@ #include <util/moneystr.h> #include <wallet/coincontrol.h> #include <wallet/feebumper.h> +#include <wallet/psbtwallet.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <wallet/walletdb.h> @@ -3962,60 +3964,6 @@ void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubK hd_keypaths.emplace(vchPubKey, std::move(info)); } -bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) -{ - LOCK(pwallet->cs_wallet); - // Get all of the previous transactions - bool complete = true; - for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - const CTxIn& txin = psbtx.tx->vin[i]; - PSBTInput& input = psbtx.inputs.at(i); - - if (PSBTInputSigned(input)) { - continue; - } - - // Verify input looks sane. This will check that we have at most one uxto, witness or non-witness. - if (!input.IsSane()) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "PSBT input is not sane."); - } - - // If we have no utxo, grab it from the wallet. - if (!input.non_witness_utxo && input.witness_utxo.IsNull()) { - const uint256& txhash = txin.prevout.hash; - const auto it = pwallet->mapWallet.find(txhash); - if (it != pwallet->mapWallet.end()) { - const CWalletTx& wtx = it->second; - // We only need the non_witness_utxo, which is a superset of the witness_utxo. - // The signing code will switch to the smaller witness_utxo if this is ok. - input.non_witness_utxo = wtx.tx; - } - } - - // Get the Sighash type - if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) { - throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match."); - } - - complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), psbtx, i, sighash_type); - } - - // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change - for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { - const CTxOut& out = psbtx.tx->vout.at(i); - PSBTOutput& psbt_out = psbtx.outputs.at(i); - - // Fill a SignatureData with output info - SignatureData sigdata; - psbt_out.FillSignatureData(sigdata); - - MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1); - ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata); - psbt_out.FromSignatureData(sigdata); - } - return complete; -} - UniValue walletprocesspsbt(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -4060,7 +4008,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) // Unserialize the transaction PartiallySignedTransaction psbtx; std::string error; - if (!DecodePSBT(psbtx, request.params[0].get_str(), error)) { + if (!DecodeBase64PSBT(psbtx, request.params[0].get_str(), error)) { throw JSONRPCError(RPC_DESERIALIZATION_ERROR, strprintf("TX decode failed %s", error)); } @@ -4070,7 +4018,11 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) // Fill transaction with our data and also sign bool sign = request.params[1].isNull() ? true : request.params[1].get_bool(); bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool(); - bool complete = FillPSBT(pwallet, psbtx, nHashType, sign, bip32derivs); + bool complete = true; + TransactionError err; + if (!FillPSBT(pwallet, psbtx, err, complete, nHashType, sign, bip32derivs)) { + throw JSONRPCTransactionError(err); + } UniValue result(UniValue::VOBJ); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -4184,7 +4136,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) // Fill transaction with out data but don't sign bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool(); - FillPSBT(pwallet, psbtx, 1, false, bip32derivs); + bool complete = true; + TransactionError err; + if (!FillPSBT(pwallet, psbtx, err, complete, 1, false, bip32derivs)) { + throw JSONRPCTransactionError(err); + } // Serialize the PSBT CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index abd7750874..58053bde59 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -30,5 +30,4 @@ bool EnsureWalletIsAvailable(CWallet *, bool avoidException); UniValue getaddressinfo(const JSONRPCRequest& request); UniValue signrawtransactionwithwallet(const JSONRPCRequest& request); -bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false); #endif //BITCOIN_WALLET_RPCWALLET_H diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 9918eeb89f..e89d4121bc 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -5,6 +5,7 @@ #include <key_io.h> #include <script/sign.h> #include <util/strencodings.h> +#include <wallet/psbtwallet.h> #include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <univalue.h> @@ -60,7 +61,9 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test) ssData >> psbtx; // Fill transaction with our data - FillPSBT(&m_wallet, psbtx, SIGHASH_ALL, false, true); + TransactionError err; + bool complete = true; + FillPSBT(&m_wallet, psbtx, err, complete, SIGHASH_ALL, false, true); // Get the final tx CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 628f3fe803..797f051189 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -17,7 +17,7 @@ namespace WalletTool { static void WalletToolReleaseWallet(CWallet* wallet) { wallet->WalletLogPrintf("Releasing wallet\n"); - wallet->Flush(); + wallet->Flush(true); delete wallet; } @@ -112,7 +112,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path); if (wallet_instance) { WalletShowInfo(wallet_instance.get()); - wallet_instance->Flush(); + wallet_instance->Flush(true); } } else if (command == "info") { if (!fs::exists(path)) { @@ -127,7 +127,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name) std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path); if (!wallet_instance) return false; WalletShowInfo(wallet_instance.get()); - wallet_instance->Flush(); + wallet_instance->Flush(true); } else { fprintf(stderr, "Invalid command: %s\n", command.c_str()); return false; diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index a82a5d0208..c98f105828 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -293,5 +293,8 @@ class PSBTTest(BitcoinTestFramework): psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True) self.nodes[0].decodepsbt(psbt['psbt']) + # Test decoding error: invalid base64 + assert_raises_rpc_error(-22, "TX decode failed invalid base64", self.nodes[0].decodepsbt, ";definitely not base64;") + if __name__ == '__main__': PSBTTest().main() |