aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml9
-rw-r--r--Makefile.am5
-rw-r--r--build-aux/m4/bitcoin_qt.m427
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj3
-rw-r--r--doc/dependencies.md2
-rw-r--r--doc/release-notes-15393.md4
-rw-r--r--share/setup.nsi.in1
-rw-r--r--src/Makefile.am6
-rw-r--r--src/chainparams.cpp4
-rw-r--r--src/core_io.h6
-rw-r--r--src/core_read.cpp17
-rw-r--r--src/interfaces/wallet.cpp4
-rw-r--r--src/interfaces/wallet.h3
-rw-r--r--src/net_processing.cpp25
-rw-r--r--src/node/transaction.cpp115
-rw-r--r--src/node/transaction.h43
-rw-r--r--src/psbt.cpp283
-rw-r--r--src/psbt.h574
-rw-r--r--src/qt/bitcoin.cpp3
-rw-r--r--src/qt/bitcoingui.cpp8
-rw-r--r--src/qt/bitcoingui.h1
-rw-r--r--src/qt/paymentserver.cpp26
-rw-r--r--src/qt/test/apptests.cpp2
-rw-r--r--src/qt/test/rpcnestedtests.cpp2
-rw-r--r--src/qt/walletcontroller.cpp16
-rw-r--r--src/qt/walletcontroller.h1
-rw-r--r--src/rpc/rawtransaction.cpp120
-rw-r--r--src/rpc/util.cpp28
-rw-r--r--src/rpc/util.h4
-rw-r--r--src/script/sign.cpp221
-rw-r--r--src/script/sign.h540
-rw-r--r--src/util/strencodings.cpp16
-rw-r--r--src/util/strencodings.h8
-rw-r--r--src/validation.cpp2
-rw-r--r--src/wallet/db.cpp2
-rw-r--r--src/wallet/psbtwallet.cpp62
-rw-r--r--src/wallet/psbtwallet.h36
-rw-r--r--src/wallet/rpcwallet.cpp70
-rw-r--r--src/wallet/rpcwallet.h1
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp5
-rw-r--r--src/wallet/wallettool.cpp6
-rwxr-xr-xtest/functional/rpc_psbt.py3
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()