aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build_msvc/bitcoin-qt/bitcoin-qt.vcxproj2
-rw-r--r--build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj3
-rw-r--r--build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.qt.include6
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/addrman.cpp18
-rw-r--r--src/addrman_impl.h6
-rw-r--r--src/bench/mempool_stress.cpp43
-rw-r--r--src/bitcoin-wallet.cpp8
-rw-r--r--src/init/bitcoin-gui.cpp47
-rw-r--r--src/init/bitcoin-qt.cpp42
-rw-r--r--src/init/bitcoin-wallet.cpp12
-rw-r--r--src/net_processing.cpp6
-rw-r--r--src/netaddress.h36
-rw-r--r--src/qt/bitcoin.cpp5
-rw-r--r--src/qt/test/test_main.cpp4
-rw-r--r--src/test/addrman_tests.cpp22
-rw-r--r--src/test/fuzz/addrman.cpp33
-rw-r--r--src/test/fuzz/tx_pool.cpp4
-rw-r--r--src/txmempool.cpp55
-rw-r--r--src/txmempool.h2
-rw-r--r--src/validation.cpp8
-rw-r--r--src/validation.h3
-rw-r--r--src/wallet/bdb.cpp2
-rw-r--r--src/wallet/sqlite.cpp2
-rw-r--r--src/wallet/test/coinselector_tests.cpp13
-rw-r--r--src/wallet/transaction.h3
-rw-r--r--src/wallet/wallet.cpp6
-rw-r--r--src/wallet/wallet.h8
-rw-r--r--src/wallet/walletdb.cpp6
-rwxr-xr-xtest/functional/feature_filelock.py2
-rwxr-xr-xtest/functional/feature_segwit.py600
-rwxr-xr-xtest/functional/mempool_accept.py6
-rwxr-xr-xtest/functional/p2p_segwit.py60
-rw-r--r--test/functional/test_framework/blocktools.py6
-rwxr-xr-xtest/functional/test_framework/script_util.py13
-rwxr-xr-xtest/functional/test_framework/wallet_util.py9
-rwxr-xr-xtest/functional/test_runner.py1
-rwxr-xr-xtest/functional/tool_wallet.py2
-rwxr-xr-xtest/functional/wallet_multiwallet.py4
41 files changed, 634 insertions, 479 deletions
diff --git a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
index 724dae1969..2800a42767 100644
--- a/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
+++ b/build_msvc/bitcoin-qt/bitcoin-qt.vcxproj
@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\qt\main.cpp" />
- <ClCompile Include="..\..\src\init\bitcoind.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
<ResourceCompile Include="..\..\src\qt\res\bitcoin-qt-res.rc" />
</ItemGroup>
<ItemGroup>
diff --git a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
index 40c5db5522..affb60425b 100644
--- a/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
+++ b/build_msvc/bitcoin-wallet/bitcoin-wallet.vcxproj
@@ -10,6 +10,9 @@
</PropertyGroup>
<ItemGroup>
<ClCompile Include="..\..\src\bitcoin-wallet.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-wallet.cpp">
+ <ObjectFileName>$(IntDir)init_bitcoin-wallet.obj</ObjectFileName>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libbitcoinconsensus\libbitcoinconsensus.vcxproj">
diff --git a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
index 08b12bdd85..f9948b6f13 100644
--- a/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
+++ b/build_msvc/test_bitcoin-qt/test_bitcoin-qt.vcxproj
@@ -8,7 +8,7 @@
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<ItemGroup>
- <ClCompile Include="..\..\src\init\bitcoind.cpp" />
+ <ClCompile Include="..\..\src\init\bitcoin-qt.cpp" />
<ClCompile Include="..\..\src\test\util\setup_common.cpp" />
<ClCompile Include="..\..\src\qt\test\addressbooktests.cpp" />
<ClCompile Include="..\..\src\qt\test\apptests.cpp" />
diff --git a/src/Makefile.am b/src/Makefile.am
index 856e6f1985..3f5c3a2f4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -703,6 +703,7 @@ bitcoin_tx_LDADD += $(BOOST_LIBS)
# bitcoin-wallet binary #
bitcoin_wallet_SOURCES = bitcoin-wallet.cpp
+bitcoin_wallet_SOURCES += init/bitcoin-wallet.cpp
bitcoin_wallet_CPPFLAGS = $(bitcoin_bin_cppflags)
bitcoin_wallet_CXXFLAGS = $(bitcoin_bin_cxxflags)
bitcoin_wallet_LDFLAGS = $(bitcoin_bin_ldflags)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 1e3d75a8d8..e6ea8bdf61 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -338,15 +338,15 @@ bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
qt_bitcoin_qt_CPPFLAGS = $(bitcoin_qt_cppflags)
qt_bitcoin_qt_CXXFLAGS = $(bitcoin_qt_cxxflags)
-qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
+qt_bitcoin_qt_SOURCES = $(bitcoin_qt_sources) init/bitcoin-qt.cpp
qt_bitcoin_qt_LDADD = $(bitcoin_qt_ldadd)
qt_bitcoin_qt_LDFLAGS = $(bitcoin_qt_ldflags)
qt_bitcoin_qt_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
bitcoin_gui_CPPFLAGS = $(bitcoin_qt_cppflags)
bitcoin_gui_CXXFLAGS = $(bitcoin_qt_cxxflags)
-bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoind.cpp
-bitcoin_gui_LDADD = $(bitcoin_qt_ldadd)
+bitcoin_gui_SOURCES = $(bitcoin_qt_sources) init/bitcoin-gui.cpp
+bitcoin_gui_LDADD = $(bitcoin_qt_ldadd) $(LIBBITCOIN_IPC) $(LIBBITCOIN_UTIL) $(LIBMULTIPROCESS_LIBS)
bitcoin_gui_LDFLAGS = $(bitcoin_qt_ldflags)
bitcoin_gui_LIBTOOLFLAGS = $(bitcoin_qt_libtoolflags)
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 8a5521eeb5..797e1f9a97 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -28,7 +28,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
qt_test_test_bitcoin_qt_SOURCES = \
- init/bitcoind.cpp \
+ init/bitcoin-qt.cpp \
qt/test/apptests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
diff --git a/src/addrman.cpp b/src/addrman.cpp
index cf6e847b04..86f56052c1 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -395,7 +395,7 @@ void AddrManImpl::Unserialize(Stream& s_)
}
}
-AddrInfo* AddrManImpl::Find(const CNetAddr& addr, int* pnId)
+AddrInfo* AddrManImpl::Find(const CService& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -553,10 +553,6 @@ void AddrManImpl::Good_(const CService& addr, bool test_before_evict, int64_t nT
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
info.nLastSuccess = nTime;
info.nLastTry = nTime;
@@ -685,10 +681,6 @@ void AddrManImpl::Attempt_(const CService& addr, bool fCountFailure, int64_t nTi
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
info.nLastTry = nTime;
if (fCountFailure && info.nLastCountAttempt < nLastGood) {
@@ -816,10 +808,6 @@ void AddrManImpl::Connected_(const CService& addr, int64_t nTime)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
int64_t nUpdateInterval = 20 * 60;
if (nTime - info.nTime > nUpdateInterval)
@@ -838,10 +826,6 @@ void AddrManImpl::SetServices_(const CService& addr, ServiceFlags nServices)
AddrInfo& info = *pinfo;
- // check whether we are talking about the exact same CService (including same port)
- if (info != addr)
- return;
-
// update info
info.nServices = nServices;
}
diff --git a/src/addrman_impl.h b/src/addrman_impl.h
index 1dc7f25f9c..f8191d6b85 100644
--- a/src/addrman_impl.h
+++ b/src/addrman_impl.h
@@ -179,8 +179,8 @@ private:
//! table with information about all nIds
std::unordered_map<int, AddrInfo> mapInfo GUARDED_BY(cs);
- //! find an nId based on its network address
- std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
+ //! find an nId based on its network address and port.
+ std::unordered_map<CService, int, CServiceHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
//! This is mutable because it is unobservable outside the class, so any
@@ -225,7 +225,7 @@ private:
const std::vector<bool> m_asmap;
//! Find an entry.
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ AddrInfo* Find(const CService& addr, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
AddrInfo* Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
diff --git a/src/bench/mempool_stress.cpp b/src/bench/mempool_stress.cpp
index f28768efc8..a0a82ea359 100644
--- a/src/bench/mempool_stress.cpp
+++ b/src/bench/mempool_stress.cpp
@@ -6,6 +6,7 @@
#include <policy/policy.h>
#include <test/util/setup_common.h>
#include <txmempool.h>
+#include <validation.h>
#include <vector>
@@ -26,14 +27,8 @@ struct Available {
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
};
-static void ComplexMemPool(benchmark::Bench& bench)
+static std::vector<CTransactionRef> CreateOrderedCoins(FastRandomContext& det_rand, int childTxs, int min_ancestors)
{
- int childTxs = 800;
- if (bench.complexityN() > 1) {
- childTxs = static_cast<int>(bench.complexityN());
- }
-
- FastRandomContext det_rand{true};
std::vector<Available> available_coins;
std::vector<CTransactionRef> ordered_coins;
// Create some base transactions
@@ -58,8 +53,10 @@ static void ComplexMemPool(benchmark::Bench& bench)
size_t idx = det_rand.randrange(available_coins.size());
Available coin = available_coins[idx];
uint256 hash = coin.ref->GetHash();
- // biased towards taking just one ancestor, but maybe more
- size_t n_to_take = det_rand.randrange(2) == 0 ? 1 : 1+det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
+ // biased towards taking min_ancestors parents, but maybe more
+ size_t n_to_take = det_rand.randrange(2) == 0 ?
+ min_ancestors :
+ min_ancestors + det_rand.randrange(coin.ref->vout.size() - coin.vin_left);
for (size_t i = 0; i < n_to_take; ++i) {
tx.vin.emplace_back();
tx.vin.back().prevout = COutPoint(hash, coin.vin_left++);
@@ -79,6 +76,17 @@ static void ComplexMemPool(benchmark::Bench& bench)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
+ return ordered_coins;
+}
+
+static void ComplexMemPool(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ int childTxs = 800;
+ if (bench.complexityN() > 1) {
+ childTxs = static_cast<int>(bench.complexityN());
+ }
+ std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 1);
const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN);
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
@@ -91,4 +99,21 @@ static void ComplexMemPool(benchmark::Bench& bench)
});
}
+static void MempoolCheck(benchmark::Bench& bench)
+{
+ FastRandomContext det_rand{true};
+ const int childTxs = bench.complexityN() > 1 ? static_cast<int>(bench.complexityN()) : 2000;
+ const std::vector<CTransactionRef> ordered_coins = CreateOrderedCoins(det_rand, childTxs, /* min_ancestors */ 5);
+ const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(CBaseChainParams::MAIN, {"-checkmempool=1"});
+ CTxMemPool pool;
+ LOCK2(cs_main, pool.cs);
+ const CCoinsViewCache& coins_tip = testing_setup.get()->m_node.chainman->ActiveChainstate().CoinsTip();
+ for (auto& tx : ordered_coins) AddTx(tx, pool);
+
+ bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
+ pool.check(coins_tip, /* spendheight */ 2);
+ });
+}
+
BENCHMARK(ComplexMemPool);
+BENCHMARK(MempoolCheck);
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 21d4df5b01..77e0bd9a16 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -8,6 +8,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
+#include <interfaces/init.h>
#include <logging.h>
#include <util/system.h>
#include <util/translation.h>
@@ -84,6 +85,13 @@ int main(int argc, char* argv[])
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
#endif
+
+ int exit_status;
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status);
+ if (!init) {
+ return exit_status;
+ }
+
SetupEnvironment();
RandomInit();
try {
diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp
new file mode 100644
index 0000000000..c549ed3cc0
--- /dev/null
+++ b/src/init/bitcoin-gui.cpp
@@ -0,0 +1,47 @@
+// Copyright (c) 2021 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 <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/ipc.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+const char* EXE_NAME = "bitcoin-gui";
+
+class BitcoinGuiInit : public interfaces::Init
+{
+public:
+ BitcoinGuiInit(const char* arg0) : m_ipc(interfaces::MakeIpc(EXE_NAME, arg0, *this))
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ interfaces::Ipc* ipc() override { return m_ipc.get(); }
+ NodeContext m_node;
+ std::unique_ptr<interfaces::Ipc> m_ipc;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinGuiInit>(argc > 0 ? argv[0] : "");
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp
new file mode 100644
index 0000000000..d71177e885
--- /dev/null
+++ b/src/init/bitcoin-qt.cpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2021 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 <interfaces/chain.h>
+#include <interfaces/echo.h>
+#include <interfaces/init.h>
+#include <interfaces/node.h>
+#include <interfaces/wallet.h>
+#include <node/context.h>
+#include <util/system.h>
+
+#include <memory>
+
+namespace init {
+namespace {
+class BitcoinQtInit : public interfaces::Init
+{
+public:
+ BitcoinQtInit()
+ {
+ m_node.args = &gArgs;
+ m_node.init = this;
+ }
+ std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); }
+ std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); }
+ std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override
+ {
+ return MakeWalletClient(chain, *Assert(m_node.args));
+ }
+ std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
+ NodeContext m_node;
+};
+} // namespace
+} // namespace init
+
+namespace interfaces {
+std::unique_ptr<Init> MakeGuiInit(int argc, char* argv[])
+{
+ return std::make_unique<init::BitcoinQtInit>();
+}
+} // namespace interfaces
diff --git a/src/init/bitcoin-wallet.cpp b/src/init/bitcoin-wallet.cpp
new file mode 100644
index 0000000000..e9dcde72fe
--- /dev/null
+++ b/src/init/bitcoin-wallet.cpp
@@ -0,0 +1,12 @@
+// Copyright (c) 2021 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 <interfaces/init.h>
+
+namespace interfaces {
+std::unique_ptr<Init> MakeWalletInit(int argc, char* argv[], int& exit_status)
+{
+ return std::make_unique<Init>();
+}
+} // namespace interfaces
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 13f13d5285..9f3aa5b4a3 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -2299,7 +2299,8 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
break;
}
}
- m_mempool.check(m_chainman.ActiveChainstate());
+ CChainState& active_chainstate = m_chainman.ActiveChainstate();
+ m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
}
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& peer,
@@ -3262,7 +3263,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
const TxValidationState& state = result.m_state;
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
- m_mempool.check(m_chainman.ActiveChainstate());
+ CChainState& active_chainstate = m_chainman.ActiveChainstate();
+ m_mempool.check(active_chainstate.CoinsTip(), active_chainstate.m_chain.Height() + 1);
// As this version of the transaction was acceptable, we can forget about any
// requests for it.
m_txrequest.ForgetTxHash(tx.GetHash());
diff --git a/src/netaddress.h b/src/netaddress.h
index 66c8c48f08..57eb8bc72f 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -253,7 +253,6 @@ public:
}
}
- friend class CNetAddrHash;
friend class CSubNet;
private:
@@ -467,22 +466,6 @@ private:
}
};
-class CNetAddrHash
-{
-public:
- size_t operator()(const CNetAddr& a) const noexcept
- {
- CSipHasher hasher(m_salt_k0, m_salt_k1);
- hasher.Write(a.m_net);
- hasher.Write(a.m_addr.data(), a.m_addr.size());
- return static_cast<size_t>(hasher.Finalize());
- }
-
-private:
- const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
- const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
-};
-
class CSubNet
{
protected:
@@ -565,6 +548,25 @@ public:
READWRITEAS(CNetAddr, obj);
READWRITE(Using<BigEndianFormatter<2>>(obj.port));
}
+
+ friend class CServiceHash;
+};
+
+class CServiceHash
+{
+public:
+ size_t operator()(const CService& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.port);
+ hasher.Write(a.m_addr.data(), a.m_addr.size());
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
+ const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
};
#endif // BITCOIN_NETADDRESS_H
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 5b586b9d89..3f412d8f19 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -13,7 +13,6 @@
#include <interfaces/handler.h>
#include <interfaces/init.h>
#include <interfaces/node.h>
-#include <node/context.h>
#include <node/ui_interface.h>
#include <noui.h>
#include <qt/bitcoingui.h>
@@ -464,9 +463,7 @@ int GuiMain(int argc, char* argv[])
std::tie(argc, argv) = winArgs.get();
#endif
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
SetupEnvironment();
util::ThreadSetInternalName("main");
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index 55d00bb37e..b26cddf4ae 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -52,9 +52,7 @@ int main(int argc, char* argv[])
BasicTestingSetup dummy{CBaseChainParams::REGTEST};
}
- NodeContext node_context;
- int unused_exit_status;
- std::unique_ptr<interfaces::Init> init = interfaces::MakeNodeInit(node_context, argc, argv, unused_exit_status);
+ std::unique_ptr<interfaces::Init> init = interfaces::MakeGuiInit(argc, argv);
gArgs.ForceSetArg("-listen", "0");
gArgs.ForceSetArg("-listenonion", "0");
gArgs.ForceSetArg("-discover", "0");
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index bd6f470219..991bfa5efc 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -89,7 +89,7 @@ public:
deterministic = makeDeterministic;
}
- AddrInfo* Find(const CNetAddr& addr, int* pnId = nullptr)
+ AddrInfo* Find(const CService& addr, int* pnId = nullptr)
{
LOCK(m_impl->cs);
return m_impl->Find(addr, pnId);
@@ -222,15 +222,15 @@ BOOST_AUTO_TEST_CASE(addrman_ports)
BOOST_CHECK_EQUAL(addrman.size(), 1U);
CService addr1_port = ResolveService("250.1.1.1", 8334);
- BOOST_CHECK(!addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK(addrman.Add({CAddress(addr1_port, NODE_NONE)}, source));
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
auto addr_ret2 = addrman.Select().first;
- BOOST_CHECK_EQUAL(addr_ret2.ToString(), "250.1.1.1:8333");
+ BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333" || addr_ret2.ToString() == "250.1.1.1:8334");
- // Test: Add same IP but diff port to tried table, it doesn't get added.
- // Perhaps this is not ideal behavior but it is the current behavior.
+ // Test: Add same IP but diff port to tried table; this converts the entry with
+ // the specified port to tried, but not the other.
addrman.Good(CAddress(addr1_port, NODE_NONE));
- BOOST_CHECK_EQUAL(addrman.size(), 1U);
+ BOOST_CHECK_EQUAL(addrman.size(), 2U);
bool newOnly = true;
auto addr_ret3 = addrman.Select(newOnly).first;
BOOST_CHECK_EQUAL(addr_ret3.ToString(), "250.1.1.1:8333");
@@ -369,18 +369,18 @@ BOOST_AUTO_TEST_CASE(addrman_find)
CNetAddr source2 = ResolveIP("250.1.2.2");
BOOST_CHECK(addrman.Add({addr1}, source1));
- BOOST_CHECK(!addrman.Add({addr2}, source2));
+ BOOST_CHECK(addrman.Add({addr2}, source2));
BOOST_CHECK(addrman.Add({addr3}, source1));
- // Test: ensure Find returns an IP matching what we searched on.
+ // Test: ensure Find returns an IP/port matching what we searched on.
AddrInfo* info1 = addrman.Find(addr1);
BOOST_REQUIRE(info1);
BOOST_CHECK_EQUAL(info1->ToString(), "250.1.2.1:8333");
- // Test 18; Find does not discriminate by port number.
+ // Test; Find discriminates by port number.
AddrInfo* info2 = addrman.Find(addr2);
BOOST_REQUIRE(info2);
- BOOST_CHECK_EQUAL(info2->ToString(), info1->ToString());
+ BOOST_CHECK_EQUAL(info2->ToString(), "250.1.2.1:9999");
// Test: Find returns another IP matching what we searched on.
AddrInfo* info3 = addrman.Find(addr3);
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 8df3707fc9..c6df6a0e61 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -137,24 +137,29 @@ public:
// Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
// Keys may be different.
- using AddrInfoHasher = std::function<size_t(const AddrInfo&)>;
- using AddrInfoEq = std::function<bool(const AddrInfo&, const AddrInfo&)>;
-
- CNetAddrHash netaddr_hasher;
-
- AddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const AddrInfo& a) {
- return netaddr_hasher(static_cast<CNetAddr>(a)) ^ netaddr_hasher(a.source) ^
- a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried;
+ auto addrinfo_hasher = [](const AddrInfo& a) {
+ CSipHasher hasher(0, 0);
+ auto addr_key = a.GetKey();
+ auto source_key = a.source.GetAddrBytes();
+ hasher.Write(a.nLastSuccess);
+ hasher.Write(a.nAttempts);
+ hasher.Write(a.nRefCount);
+ hasher.Write(a.fInTried);
+ hasher.Write(a.GetNetwork());
+ hasher.Write(a.source.GetNetwork());
+ hasher.Write(addr_key.size());
+ hasher.Write(source_key.size());
+ hasher.Write(addr_key.data(), addr_key.size());
+ hasher.Write(source_key.data(), source_key.size());
+ return (size_t)hasher.Finalize();
};
- AddrInfoEq addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
- return static_cast<CNetAddr>(lhs) == static_cast<CNetAddr>(rhs) &&
- lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
- lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
- lhs.fInTried == rhs.fInTried;
+ auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
+ return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
+ std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
};
- using Addresses = std::unordered_set<AddrInfo, AddrInfoHasher, AddrInfoEq>;
+ using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
const size_t num_addresses{m_impl->mapInfo.size()};
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index 6201cc813c..17b5ef88b9 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -81,7 +81,7 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
{
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
@@ -97,7 +97,7 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CCh
std::vector<uint256> all_txids;
tx_pool.queryHashes(all_txids);
assert(all_txids.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
}
SyncWithValidationInterfaceQueue();
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 70084ea1d1..9bc2377c63 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -5,6 +5,7 @@
#include <txmempool.h>
+#include <coins.h>
#include <consensus/consensus.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
@@ -671,16 +672,7 @@ void CTxMemPool::clear()
_clear();
}
-static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
-{
- TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
- CAmount txfee = 0;
- bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee);
- assert(fCheckResult);
- UpdateCoins(tx, mempoolDuplicate, std::numeric_limits<int>::max());
-}
-
-void CTxMemPool::check(CChainState& active_chainstate) const
+void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const
{
if (m_check_ratio == 0) return;
@@ -693,20 +685,16 @@ void CTxMemPool::check(CChainState& active_chainstate) const
uint64_t checkTotal = 0;
CAmount check_total_fee{0};
uint64_t innerUsage = 0;
+ uint64_t prev_ancestor_count{0};
- CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip();
CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip));
- const int64_t spendheight = active_chainstate.m_chain.Height() + 1;
- std::list<const CTxMemPoolEntry*> waitingOnDependants;
- for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
- unsigned int i = 0;
+ for (const auto& it : GetSortedDepthAndScore()) {
checkTotal += it->GetTxSize();
check_total_fee += it->GetFee();
innerUsage += it->DynamicMemoryUsage();
const CTransaction& tx = it->GetTx();
innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
- bool fDependsWait = false;
CTxMemPoolEntry::Parents setParentCheck;
for (const CTxIn &txin : tx.vin) {
// Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
@@ -714,17 +702,17 @@ void CTxMemPool::check(CChainState& active_chainstate) const
if (it2 != mapTx.end()) {
const CTransaction& tx2 = it2->GetTx();
assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
- fDependsWait = true;
setParentCheck.insert(*it2);
- } else {
- assert(active_coins_tip.HaveCoin(txin.prevout));
}
+ // We are iterating through the mempool entries sorted in order by ancestor count.
+ // All parents must have been checked before their children and their coins added to
+ // the mempoolDuplicate coins cache.
+ assert(mempoolDuplicate.HaveCoin(txin.prevout));
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
assert(it3 != mapNextTx.end());
assert(it3->first == &txin.prevout);
assert(it3->second == &tx);
- i++;
}
auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool {
return a.GetTx().GetHash() == b.GetTx().GetHash();
@@ -751,6 +739,9 @@ void CTxMemPool::check(CChainState& active_chainstate) const
assert(it->GetSizeWithAncestors() == nSizeCheck);
assert(it->GetSigOpCostWithAncestors() == nSigOpCheck);
assert(it->GetModFeesWithAncestors() == nFeesCheck);
+ // Sanity check: we are walking in ascending ancestor count order.
+ assert(prev_ancestor_count <= it->GetCountWithAncestors());
+ prev_ancestor_count = it->GetCountWithAncestors();
// Check children against mapNextTx
CTxMemPoolEntry::Children setChildrenCheck;
@@ -769,24 +760,12 @@ void CTxMemPool::check(CChainState& active_chainstate) const
// just a sanity check, not definitive that this calc is correct...
assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize());
- if (fDependsWait)
- waitingOnDependants.push_back(&(*it));
- else {
- CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
- }
- }
- unsigned int stepsSinceLastRemove = 0;
- while (!waitingOnDependants.empty()) {
- const CTxMemPoolEntry* entry = waitingOnDependants.front();
- waitingOnDependants.pop_front();
- if (!mempoolDuplicate.HaveInputs(entry->GetTx())) {
- waitingOnDependants.push_back(entry);
- stepsSinceLastRemove++;
- assert(stepsSinceLastRemove < waitingOnDependants.size());
- } else {
- CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
- stepsSinceLastRemove = 0;
- }
+ TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
+ CAmount txfee = 0;
+ assert(!tx.IsCoinBase());
+ assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
+ for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
+ AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
}
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
uint256 hash = it->second->GetHash();
diff --git a/src/txmempool.h b/src/txmempool.h
index c63522225a..90b2aee371 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -622,7 +622,7 @@ public:
* all inputs are in the mapNextTx array). If sanity-checking is turned off,
* check does nothing.
*/
- void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ void check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
// addUnchecked must updated state for all ancestors of a given transaction,
// to track size/count of descendant transactions. First version of
diff --git a/src/validation.cpp b/src/validation.cpp
index 8d64a9a460..207cdc8233 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1236,12 +1236,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
AddCoins(inputs, tx, nHeight);
}
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
-{
- CTxUndo txundo;
- UpdateCoins(tx, inputs, txundo, nHeight);
-}
-
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness;
@@ -2487,7 +2481,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
// any disconnected transactions back to the mempool.
MaybeUpdateMempoolForReorg(disconnectpool, true);
}
- if (m_mempool) m_mempool->check(*this);
+ if (m_mempool) m_mempool->check(this->CoinsTip(), this->m_chain.Height() + 1);
CheckForkWarningConditions();
diff --git a/src/validation.h b/src/validation.h
index 1c23b3ad5c..4da8ec8d24 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -229,9 +229,6 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
const Package& txns, bool test_accept)
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-/** Apply the effects of this transaction on the UTXO set represented by view */
-void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
-
/** Transaction validation functions */
/**
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp
index 2290e119fd..74fc10ab25 100644
--- a/src/wallet/bdb.cpp
+++ b/src/wallet/bdb.cpp
@@ -132,7 +132,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
fs::path pathIn = fs::PathFromString(strPath);
TryCreateDirectories(pathIn);
if (!LockDirectory(pathIn, ".walletlock")) {
- LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance of bitcoin may be using it.\n", strPath);
+ LogPrintf("Cannot obtain a lock on wallet directory %s. Another instance may be using it.\n", strPath);
err = strprintf(_("Error initializing wallet database environment %s!"), fs::quoted(fs::PathToString(Directory())));
return false;
}
diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp
index 650e083e8e..c493b96248 100644
--- a/src/wallet/sqlite.cpp
+++ b/src/wallet/sqlite.cpp
@@ -228,7 +228,7 @@ void SQLiteDatabase::Open()
// Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode.
int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
- throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?\n");
+ throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " PACKAGE_NAME "?\n");
}
ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
if (ret != SQLITE_OK) {
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index e880e13845..8606924bb3 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -71,13 +71,18 @@ static void add_coin(std::vector<COutput>& coins, CWallet& wallet, const CAmount
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = wallet.AddToWallet(MakeTransactionRef(std::move(tx)), /* confirm= */ {});
+ uint256 txid = tx.GetHash();
+
+ LOCK(wallet.cs_wallet);
+ auto ret = wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(txid), std::forward_as_tuple(MakeTransactionRef(std::move(tx))));
+ assert(ret.second);
+ CWalletTx& wtx = (*ret.first).second;
if (fIsFromMe)
{
- wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
- wtx->m_is_cache_empty = false;
+ wtx.m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
+ wtx.m_is_cache_empty = false;
}
- COutput output(wallet, *wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
+ COutput output(wallet, wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
coins.push_back(output);
}
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index 6fc1bd1eed..1ccef31056 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -162,7 +162,8 @@ public:
int block_height;
uint256 hashBlock;
int nIndex;
- Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
+ Confirmation(Status status = UNCONFIRMED, int block_height = 0, uint256 block_hash = uint256(), int block_index = 0)
+ : status{status}, block_height{block_height}, hashBlock{block_hash}, nIndex{block_index} {}
};
Confirmation m_confirm;
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 342e9ead89..4eb9d5560d 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1212,7 +1212,7 @@ void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmatio
void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t mempool_sequence) {
LOCK(cs_wallet);
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
@@ -1253,7 +1253,7 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
// distinguishing between conflicted and unconfirmed transactions are
// imperfect, and could be improved in general, see
// https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
- SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(tx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
@@ -1281,7 +1281,7 @@ void CWallet::blockDisconnected(const CBlock& block, int height)
m_last_block_processed_height = height - 1;
m_last_block_processed = block.hashPrevBlock;
for (const CTransactionRef& ptx : block.vtx) {
- SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /* block height */ 0, /* block hash */ {}, /* index */ 0});
+ SyncTransaction(ptx, {CWalletTx::Status::UNCONFIRMED, /*block_height=*/0, /*block_hash=*/{}, /*block_index=*/0});
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 767b24adbb..c911eb461c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -260,9 +260,9 @@ private:
void AddToSpends(const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
- * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * Add a transaction to the wallet, or update it. confirm.block_* should
* be set when the transaction was known to be included in a block. When
- * pIndex == nullptr, then wallet state is not updated in AddToWallet, but
+ * block_hash.IsNull(), then wallet state is not updated in AddToWallet, but
* notifications happen and cached balances are marked dirty.
*
* If fUpdate is true, existing transactions will be updated.
@@ -270,7 +270,7 @@ private:
* assumption that any further notification of a transaction that was considered
* abandoned is an indication that it is not safe to be considered abandoned.
* Abandoned state should probably be more carefully tracked via different
- * posInBlock signals or by checking mempool presence when necessary.
+ * chain notifications or by checking mempool presence when necessary.
*
* Should be called with rescanning_old_block set to true, if the transaction is
* not discovered in real time, but during a rescan of old blocks.
@@ -285,8 +285,6 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected/ScanForWalletTransactions.
- * Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index a6839f1f78..c920d4af51 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1188,9 +1188,9 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
/** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{
-#ifdef USE_BDB
- return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
-#elif USE_SQLITE
+#ifdef USE_SQLITE
return std::make_unique<SQLiteDatabase>("", "", true);
+#elif USE_BDB
+ return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "");
#endif
}
diff --git a/test/functional/feature_filelock.py b/test/functional/feature_filelock.py
index e09107802b..0fc654e10a 100755
--- a/test/functional/feature_filelock.py
+++ b/test/functional/feature_filelock.py
@@ -35,7 +35,7 @@ class FilelockTest(BitcoinTestFramework):
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
if descriptors:
- expected_msg = "Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ expected_msg = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
else:
expected_msg = "Error: Error initializing wallet database environment"
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-walletdir={wallet_dir}', f'-wallet={wallet_name}', '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 2f9ab34e99..acb7469c6a 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -17,6 +17,7 @@ from test_framework.blocktools import (
send_to_witness,
witness_script,
)
+from test_framework.descriptors import descsum_create
from test_framework.messages import (
COIN,
COutPoint,
@@ -29,8 +30,6 @@ from test_framework.script import (
CScript,
OP_0,
OP_1,
- OP_2,
- OP_CHECKMULTISIG,
OP_DROP,
OP_TRUE,
)
@@ -38,6 +37,7 @@ from test_framework.script_util import (
key_to_p2pk_script,
key_to_p2pkh_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2sh_script,
script_to_p2wsh_script,
)
@@ -49,6 +49,9 @@ from test_framework.util import (
assert_raises_rpc_error,
try_rpc,
)
+from test_framework.wallet_util import (
+ get_generate_key,
+)
NODE_0 = 0
NODE_2 = 2
@@ -142,13 +145,39 @@ class SegWitTest(BitcoinTestFramework):
p2sh_ids = [] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh
wit_ids = [] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness
for i in range(3):
- newaddress = self.nodes[i].getnewaddress()
- self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"])
- multiscript = CScript([OP_1, bytes.fromhex(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
- p2sh_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'p2sh-segwit')['address']
- bip173_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'bech32')['address']
+ key = get_generate_key()
+ self.pubkey.append(key.pubkey)
+
+ multiscript = keys_to_multisig_script([self.pubkey[-1]])
+ p2sh_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'p2sh-segwit')['address']
+ bip173_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'bech32')['address']
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))
+
+ p2sh_ms_desc = descsum_create(f"sh(wsh(multi(1,{key.privkey})))")
+ bip173_ms_desc = descsum_create(f"wsh(multi(1,{key.privkey}))")
+ assert_equal(self.nodes[i].deriveaddresses(p2sh_ms_desc)[0], p2sh_ms_addr)
+ assert_equal(self.nodes[i].deriveaddresses(bip173_ms_desc)[0], bip173_ms_addr)
+
+ sh_wpkh_desc = descsum_create(f"sh(wpkh({key.privkey}))")
+ wpkh_desc = descsum_create(f"wpkh({key.privkey})")
+ assert_equal(self.nodes[i].deriveaddresses(sh_wpkh_desc)[0], key.p2sh_p2wpkh_addr)
+ assert_equal(self.nodes[i].deriveaddresses(wpkh_desc)[0], key.p2wpkh_addr)
+
+ if self.options.descriptors:
+ res = self.nodes[i].importdescriptors([
+ {"desc": p2sh_ms_desc, "timestamp": "now"},
+ {"desc": bip173_ms_desc, "timestamp": "now"},
+ {"desc": sh_wpkh_desc, "timestamp": "now"},
+ {"desc": wpkh_desc, "timestamp": "now"},
+ ])
+ else:
+ # The nature of the legacy wallet is that this import results in also adding all of the necessary scripts
+ res = self.nodes[i].importmulti([
+ {"desc": p2sh_ms_desc, "timestamp": "now"},
+ ])
+ assert all([r["success"] for r in res])
+
p2sh_ids.append([])
wit_ids.append([])
for _ in range(2):
@@ -311,284 +340,285 @@ class SegWitTest(BitcoinTestFramework):
# Mine a block to clear the gbt cache again.
self.generate(self.nodes[0], 1)
- self.log.info("Verify behaviour of importaddress and listunspent")
-
- # Some public keys to be used later
- pubkeys = [
- "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
- "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
- "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
- "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
- "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
- "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
- "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
- ]
-
- # Import a compressed key and an uncompressed key, generate some multisig addresses
- self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
- uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
- self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
- compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
- assert not self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed']
- assert self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed']
-
- self.nodes[0].importpubkey(pubkeys[0])
- compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
- self.nodes[0].importpubkey(pubkeys[1])
- compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
- self.nodes[0].importpubkey(pubkeys[2])
- uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
-
- spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
- spendable_after_importaddress = [] # These outputs should be seen after importaddress
- solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
- unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
- solvable_anytime = [] # These outputs should be solvable after importpubkey
- unseen_anytime = [] # These outputs should never be seen
-
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
- compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address'])
-
- # Test multisig_without_privkey
- # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
- # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
-
- multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
- script = CScript([OP_2, bytes.fromhex(pubkeys[3]), bytes.fromhex(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
- solvable_after_importaddress.append(script_to_p2sh_script(script))
-
- for i in compressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # p2sh multisig with compressed keys should always be spendable
- spendable_anytime.extend([p2sh])
- # bare multisig can be watched and signed, but is not treated as ours
- solvable_after_importaddress.extend([bare])
- # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with compressed keys should always be spendable
- spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
- spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
- # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
- spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in uncompressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # p2sh multisig with uncompressed keys should always be spendable
- spendable_anytime.extend([p2sh])
- # bare multisig can be watched and signed, but is not treated as ours
- solvable_after_importaddress.extend([bare])
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with uncompressed keys should always be spendable
- spendable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
- spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # Witness output types with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- for i in compressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- for i in uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
- solvable_after_importaddress.extend([bare, p2sh])
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # normal P2PKH and P2PK with uncompressed keys should always be seen
- solvable_anytime.extend([p2pkh, p2pk])
- # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
- solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
- # Witness output types with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
-
- op1 = CScript([OP_1])
- op0 = CScript([OP_0])
- # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
- unsolvable_address_key = bytes.fromhex("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
- unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
- unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
- p2shop0 = script_to_p2sh_script(op0)
- p2wshop1 = script_to_p2wsh_script(op1)
- unsolvable_after_importaddress.append(unsolvablep2pkh)
- unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
- unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
- unsolvable_after_importaddress.append(p2wshop1)
- unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
- unsolvable_after_importaddress.append(p2shop0)
-
- spendable_txid = []
- solvable_txid = []
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
- self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
-
- importlist = []
- for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- bare = bytes.fromhex(v['hex'])
- importlist.append(bare.hex())
- importlist.append(script_to_p2wsh_script(bare).hex())
- else:
- pubkey = bytes.fromhex(v['pubkey'])
- p2pk = key_to_p2pk_script(pubkey)
- p2pkh = key_to_p2pkh_script(pubkey)
- importlist.append(p2pk.hex())
- importlist.append(p2pkh.hex())
- importlist.append(key_to_p2wpkh_script(pubkey).hex())
- importlist.append(script_to_p2wsh_script(p2pk).hex())
- importlist.append(script_to_p2wsh_script(p2pkh).hex())
-
- importlist.append(unsolvablep2pkh.hex())
- importlist.append(unsolvablep2wshp2pkh.hex())
- importlist.append(op1.hex())
- importlist.append(p2wshop1.hex())
-
- for i in importlist:
- # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
- # exceptions and continue.
- try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
-
- self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
- self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
-
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
- self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
- solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
- self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- # Repeat some tests. This time we don't add witness scripts with importaddress
- # Import a compressed key and an uncompressed key, generate some multisig addresses
- self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
- uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
- self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
- compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
-
- self.nodes[0].importpubkey(pubkeys[5])
- compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
- self.nodes[0].importpubkey(pubkeys[6])
- uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
-
- unseen_anytime = [] # These outputs should never be seen
- solvable_anytime = [] # These outputs should be solvable after importpubkey
- unseen_anytime = [] # These outputs should never be seen
-
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
- compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
- uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])['address'])
- compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
-
- premature_witaddress = []
-
- for i in compressed_spendable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- premature_witaddress.append(script_to_p2sh(p2wsh))
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH are always spendable
- spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in uncompressed_spendable_address + uncompressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
- unseen_anytime.extend([p2wsh, p2sh_p2wsh])
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
- unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- for i in compressed_solvable_address:
- v = self.nodes[0].getaddressinfo(i)
- if v['isscript']:
- [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
- premature_witaddress.append(script_to_p2sh(p2wsh))
- else:
- [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
- # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
- solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
-
- self.mine_and_test_listunspent(spendable_anytime, 2)
- self.mine_and_test_listunspent(solvable_anytime, 1)
- self.mine_and_test_listunspent(unseen_anytime, 0)
-
- # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
- v1_addr = program_to_witness(1, [3, 5])
- v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
- v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
- assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr)
- assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
-
- # Check that spendable outputs are really spendable
- self.create_and_mine_tx_from_txids(spendable_txid)
-
- # import all the private keys so solvable addresses become spendable
- self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
- self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
- self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
- self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
- self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
- self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
- self.create_and_mine_tx_from_txids(solvable_txid)
-
- # Test that importing native P2WPKH/P2WSH scripts works
- for use_p2wsh in [False, True]:
- if use_p2wsh:
- scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
- transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
- else:
- scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
- transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
-
- self.nodes[1].importaddress(scriptPubKey, "", False)
- rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
- rawtxfund = self.nodes[1].signrawtransactionwithwallet(rawtxfund)["hex"]
- txid = self.nodes[1].sendrawtransaction(rawtxfund)
-
- assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
- assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
-
- # Assert it is properly saved
- self.restart_node(1)
- assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
- assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+ if not self.options.descriptors:
+ self.log.info("Verify behaviour of importaddress and listunspent")
+
+ # Some public keys to be used later
+ pubkeys = [
+ "0363D44AABD0F1699138239DF2F042C3282C0671CC7A76826A55C8203D90E39242", # cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb
+ "02D3E626B3E616FC8662B489C123349FECBFC611E778E5BE739B257EAE4721E5BF", # cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97
+ "04A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538A62F5BD8EC85C2477F39650BD391EA6250207065B2A81DA8B009FC891E898F0E", # 91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV
+ "02A47F2CBCEFFA7B9BCDA184E7D5668D3DA6F9079AD41E422FA5FD7B2D458F2538", # cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd
+ "036722F784214129FEB9E8129D626324F3F6716555B603FFE8300BBCB882151228", # cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66
+ "0266A8396EE936BF6D99D17920DB21C6C7B1AB14C639D5CD72B300297E416FD2EC", # cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K
+ "0450A38BD7F0AC212FEBA77354A9B036A32E0F7C81FC4E0C5ADCA7C549C4505D2522458C2D9AE3CEFD684E039194B72C8A10F9CB9D4764AB26FCC2718D421D3B84", # 92h2XPssjBpsJN5CqSP7v9a7cf2kgDunBC6PDFwJHMACM1rrVBJ
+ ]
+
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("92e6XLo5jVAVwrQKPNTs93oQco8f8sDNBcpv73Dsrs397fQtFQn")
+ uncompressed_spendable_address = ["mvozP4UwyGD2mGZU4D2eMvMLPB9WkMmMQu"]
+ self.nodes[0].importprivkey("cNC8eQ5dg3mFAVePDX4ddmPYpPbw41r9bm2jd1nLJT77e6RrzTRR")
+ compressed_spendable_address = ["mmWQubrDomqpgSYekvsU7HWEVjLFHAakLe"]
+ assert not self.nodes[0].getaddressinfo(uncompressed_spendable_address[0])['iscompressed']
+ assert self.nodes[0].getaddressinfo(compressed_spendable_address[0])['iscompressed']
+
+ self.nodes[0].importpubkey(pubkeys[0])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[0])]
+ self.nodes[0].importpubkey(pubkeys[1])
+ compressed_solvable_address.append(key_to_p2pkh(pubkeys[1]))
+ self.nodes[0].importpubkey(pubkeys[2])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[2])]
+
+ spendable_anytime = [] # These outputs should be seen anytime after importprivkey and addmultisigaddress
+ spendable_after_importaddress = [] # These outputs should be seen after importaddress
+ solvable_after_importaddress = [] # These outputs should be seen after importaddress but not spendable
+ unsolvable_after_importaddress = [] # These outputs should be unsolvable after importaddress
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address'])
+
+ # Test multisig_without_privkey
+ # We have 2 public keys without private keys, use addmultisigaddress to add to wallet.
+ # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
+
+ multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
+ script = keys_to_multisig_script([pubkeys[3], pubkeys[4]])
+ solvable_after_importaddress.append(script_to_p2sh_script(script))
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # p2sh multisig with compressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
+ # P2WSH and P2SH(P2WSH) multisig with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with compressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+ # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in uncompressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # p2sh multisig with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2sh])
+ # bare multisig can be watched and signed, but is not treated as ours
+ solvable_after_importaddress.extend([bare])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be spendable
+ spendable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress
+ spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # Witness output types with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ # Multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh])
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ for i in uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # Base uncompressed multisig without private is not seen after addmultisigaddress, but seen after importaddress
+ solvable_after_importaddress.extend([bare, p2sh])
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # normal P2PKH and P2PK with uncompressed keys should always be seen
+ solvable_anytime.extend([p2pkh, p2pk])
+ # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress
+ solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh])
+ # Witness output types with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh])
+
+ op1 = CScript([OP_1])
+ op0 = CScript([OP_0])
+ # 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
+ unsolvable_address_key = bytes.fromhex("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
+ unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
+ unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
+ p2shop0 = script_to_p2sh_script(op0)
+ p2wshop1 = script_to_p2wsh_script(op1)
+ unsolvable_after_importaddress.append(unsolvablep2pkh)
+ unsolvable_after_importaddress.append(unsolvablep2wshp2pkh)
+ unsolvable_after_importaddress.append(op1) # OP_1 will be imported as script
+ unsolvable_after_importaddress.append(p2wshop1)
+ unseen_anytime.append(op0) # OP_0 will be imported as P2SH address with no script provided
+ unsolvable_after_importaddress.append(p2shop0)
+
+ spendable_txid = []
+ solvable_txid = []
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime, 1))
+ self.mine_and_test_listunspent(spendable_after_importaddress + solvable_after_importaddress + unseen_anytime + unsolvable_after_importaddress, 0)
+
+ importlist = []
+ for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ bare = bytes.fromhex(v['hex'])
+ importlist.append(bare.hex())
+ importlist.append(script_to_p2wsh_script(bare).hex())
+ else:
+ pubkey = bytes.fromhex(v['pubkey'])
+ p2pk = key_to_p2pk_script(pubkey)
+ p2pkh = key_to_p2pkh_script(pubkey)
+ importlist.append(p2pk.hex())
+ importlist.append(p2pkh.hex())
+ importlist.append(key_to_p2wpkh_script(pubkey).hex())
+ importlist.append(script_to_p2wsh_script(p2pk).hex())
+ importlist.append(script_to_p2wsh_script(p2pkh).hex())
+
+ importlist.append(unsolvablep2pkh.hex())
+ importlist.append(unsolvablep2wshp2pkh.hex())
+ importlist.append(op1.hex())
+ importlist.append(p2wshop1.hex())
+
+ for i in importlist:
+ # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC
+ # exceptions and continue.
+ try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True)
+
+ self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only
+ self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ spendable_txid.append(self.mine_and_test_listunspent(spendable_anytime + spendable_after_importaddress, 2))
+ solvable_txid.append(self.mine_and_test_listunspent(solvable_anytime + solvable_after_importaddress, 1))
+ self.mine_and_test_listunspent(unsolvable_after_importaddress, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Repeat some tests. This time we don't add witness scripts with importaddress
+ # Import a compressed key and an uncompressed key, generate some multisig addresses
+ self.nodes[0].importprivkey("927pw6RW8ZekycnXqBQ2JS5nPyo1yRfGNN8oq74HeddWSpafDJH")
+ uncompressed_spendable_address = ["mguN2vNSCEUh6rJaXoAVwY3YZwZvEmf5xi"]
+ self.nodes[0].importprivkey("cMcrXaaUC48ZKpcyydfFo8PxHAjpsYLhdsp6nmtB3E2ER9UUHWnw")
+ compressed_spendable_address = ["n1UNmpmbVUJ9ytXYXiurmGPQ3TRrXqPWKL"]
+
+ self.nodes[0].importpubkey(pubkeys[5])
+ compressed_solvable_address = [key_to_p2pkh(pubkeys[5])]
+ self.nodes[0].importpubkey(pubkeys[6])
+ uncompressed_solvable_address = [key_to_p2pkh(pubkeys[6])]
+
+ unseen_anytime = [] # These outputs should never be seen
+ solvable_anytime = [] # These outputs should be solvable after importpubkey
+ unseen_anytime = [] # These outputs should never be seen
+
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address'])
+ compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address'])
+ uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])['address'])
+ compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address'])
+
+ premature_witaddress = []
+
+ for i in compressed_spendable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH are always spendable
+ spendable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in uncompressed_spendable_address + uncompressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ # P2WSH and P2SH(P2WSH) multisig with uncompressed keys are never seen
+ unseen_anytime.extend([p2wsh, p2sh_p2wsh])
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2WPKH, P2SH_P2WPKH with uncompressed keys are never seen
+ unseen_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ for i in compressed_solvable_address:
+ v = self.nodes[0].getaddressinfo(i)
+ if v['isscript']:
+ [bare, p2sh, p2wsh, p2sh_p2wsh] = self.p2sh_address_to_script(v)
+ premature_witaddress.append(script_to_p2sh(p2wsh))
+ else:
+ [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v)
+ # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable
+ solvable_anytime.extend([p2wpkh, p2sh_p2wpkh])
+
+ self.mine_and_test_listunspent(spendable_anytime, 2)
+ self.mine_and_test_listunspent(solvable_anytime, 1)
+ self.mine_and_test_listunspent(unseen_anytime, 0)
+
+ # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works
+ v1_addr = program_to_witness(1, [3, 5])
+ v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
+ v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr)
+ assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")
+
+ # Check that spendable outputs are really spendable
+ self.create_and_mine_tx_from_txids(spendable_txid)
+
+ # import all the private keys so solvable addresses become spendable
+ self.nodes[0].importprivkey("cPiM8Ub4heR9NBYmgVzJQiUH1if44GSBGiqaeJySuL2BKxubvgwb")
+ self.nodes[0].importprivkey("cPpAdHaD6VoYbW78kveN2bsvb45Q7G5PhaPApVUGwvF8VQ9brD97")
+ self.nodes[0].importprivkey("91zqCU5B9sdWxzMt1ca3VzbtVm2YM6Hi5Rxn4UDtxEaN9C9nzXV")
+ self.nodes[0].importprivkey("cPQFjcVRpAUBG8BA9hzr2yEzHwKoMgLkJZBBtK9vJnvGJgMjzTbd")
+ self.nodes[0].importprivkey("cQGtcm34xiLjB1v7bkRa4V3aAc9tS2UTuBZ1UnZGeSeNy627fN66")
+ self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K")
+ self.create_and_mine_tx_from_txids(solvable_txid)
+
+ # Test that importing native P2WPKH/P2WSH scripts works
+ for use_p2wsh in [False, True]:
+ if use_p2wsh:
+ scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a"
+ transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000"
+ else:
+ scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87"
+ transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000"
+
+ self.nodes[1].importaddress(scriptPubKey, "", False)
+ rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex']
+ rawtxfund = self.nodes[1].signrawtransactionwithwallet(rawtxfund)["hex"]
+ txid = self.nodes[1].sendrawtransaction(rawtxfund)
+
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
+
+ # Assert it is properly saved
+ self.restart_node(1)
+ assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid)
+ assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid)
def mine_and_test_listunspent(self, script_list, ismine):
utxo = find_spendable_utxo(self.nodes[0], 50)
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 2ee440bcb7..71be2b4a82 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -22,13 +22,11 @@ from test_framework.messages import (
from test_framework.script import (
CScript,
OP_0,
- OP_2,
- OP_3,
- OP_CHECKMULTISIG,
OP_HASH160,
OP_RETURN,
)
from test_framework.script_util import (
+ keys_to_multisig_script,
script_to_p2sh_script,
)
from test_framework.util import (
@@ -283,7 +281,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
key = ECKey()
key.generate()
pubkey = key.get_pubkey().get_bytes()
- tx.vout[0].scriptPubKey = CScript([OP_2, pubkey, pubkey, pubkey, OP_3, OP_CHECKMULTISIG]) # Some bare multisig script (2-of-3)
+ tx.vout[0].scriptPubKey = keys_to_multisig_script([pubkey] * 3, k=2) # Some bare multisig script (2-of-3)
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bare-multisig'}],
rawtxs=[tx.serialize().hex()],
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index 4e21d08e5c..5195c9fd04 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -8,7 +8,12 @@ import random
import struct
import time
-from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment, WITNESS_COMMITMENT_HEADER
+from test_framework.blocktools import (
+ WITNESS_COMMITMENT_HEADER,
+ add_witness_commitment,
+ create_block,
+ create_coinbase,
+)
from test_framework.key import ECKey
from test_framework.messages import (
BIP125_SEQUENCE_NUMBER,
@@ -194,7 +199,7 @@ class SegWitTest(BitcoinTestFramework):
self.num_nodes = 2
# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.
self.extra_args = [
- ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1"],
+ ["-acceptnonstdtxn=1", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}", "-whitelist=noban@127.0.0.1", "-par=1"],
["-acceptnonstdtxn=0", f"-testactivationheight=segwit@{SEGWIT_HEIGHT}"],
]
self.supports_cli = False
@@ -505,8 +510,8 @@ class SegWitTest(BitcoinTestFramework):
# 'block-validation-failed' (if script check threads > 1) or
# 'non-mandatory-script-verify-flag (Witness program was passed an
# empty witness)' (otherwise).
- # TODO: support multiple acceptable reject reasons.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=False,
+ reason='non-mandatory-script-verify-flag (Witness program was passed an empty witness)')
self.utxo.pop(0)
self.utxo.append(UTXO(txid, 2, value))
@@ -786,7 +791,7 @@ class SegWitTest(BitcoinTestFramework):
block_3.rehash()
block_3.solve()
- test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False, reason='bad-witness-merkle-match')
# Add a different commitment with different nonce, but in the
# right location, and with some funds burned(!).
@@ -852,7 +857,7 @@ class SegWitTest(BitcoinTestFramework):
# Change the nonce -- should not cause the block to be permanently
# failed
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)]
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-witness-merkle-match')
# Changing the witness reserved value doesn't change the block hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
@@ -861,7 +866,7 @@ class SegWitTest(BitcoinTestFramework):
@subtest # type: ignore
def test_witness_block_size(self):
# TODO: Test that non-witness carrying blocks can't exceed 1MB
- # Skipping this test for now; this is covered in p2p-fullblocktest.py
+ # Skipping this test for now; this is covered in feature_block.py
# Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB.
block = self.build_next_block()
@@ -917,7 +922,7 @@ class SegWitTest(BitcoinTestFramework):
# limit
assert len(block.serialize()) > 2 * 1024 * 1024
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-blk-weight')
# Now resize the second transaction to make the block fit.
cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0])
@@ -989,7 +994,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx])
# Extra witness data should not be allowed.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness provided for non-witness script)')
# Try extra signature data. Ok if we're not spending a witness output.
block.vtx[1].wit.vtxinwit = []
@@ -1014,7 +1020,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx2])
# This has extra witness data, so it should fail.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Stack size must be exactly one after execution)')
# Now get rid of the extra witness, but add extra scriptSig data
tx2.vin[0].scriptSig = CScript([OP_TRUE])
@@ -1026,7 +1033,8 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
# This has extra signature data for a witness input, so it should fail.
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness requires empty scriptSig)')
# Now get rid of the extra scriptsig on the witness input, and verify
# success (even with extra scriptsig data in the non-witness input)
@@ -1064,7 +1072,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Push value size limit exceeded)')
# Now reduce the length of the stack element
tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (MAX_SCRIPT_ELEMENT_SIZE)
@@ -1104,7 +1113,8 @@ class SegWitTest(BitcoinTestFramework):
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script is too big)')
# Try again with one less byte in the witness script
witness_script = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE])
@@ -1176,7 +1186,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-txnmrklroot')
# Now try using a too short vtxinwit
tx2.wit.vtxinwit.pop()
@@ -1184,6 +1194,8 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
+ # This block doesn't result in a specific reject reason, but an iostream exception:
+ # "Exception 'CDataStream::read(): end of data: unspecified iostream_category error' (...) caught"
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
# Now make one of the intermediate witnesses be incorrect
@@ -1193,7 +1205,8 @@ class SegWitTest(BitcoinTestFramework):
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Operation not valid with the current stack size)')
# Fix the broken witness and the block should be accepted.
tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_script]
@@ -1415,7 +1428,7 @@ class SegWitTest(BitcoinTestFramework):
self.sync_blocks()
block2 = self.build_next_block()
self.update_witness_block_with_transactions(block2, [spend_tx])
- test_witness_block(self.nodes[0], self.test_node, block2, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block2, accepted=False, reason='bad-txns-premature-spend-of-coinbase')
# Advancing one more block should allow the spend.
self.generate(self.nodes[0], 1)
@@ -1564,13 +1577,17 @@ class SegWitTest(BitcoinTestFramework):
# Too-large input value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue + 1, key)
self.update_witness_block_with_transactions(block, [tx])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script evaluated without error '
+ 'but finished with a false/empty top stack element')
# Too-small input value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue - 1, key)
block.vtx.pop() # remove last tx
self.update_witness_block_with_transactions(block, [tx])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Script evaluated without error '
+ 'but finished with a false/empty top stack element')
# Now try correct value
sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue, key)
@@ -1672,7 +1689,8 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin[0].scriptSig = CScript([signature, pubkey])
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx, tx2])
- test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block, accepted=False,
+ reason='non-mandatory-script-verify-flag (Witness requires empty scriptSig)')
# Move the signature to the witness.
block.vtx.pop()
@@ -1918,7 +1936,7 @@ class SegWitTest(BitcoinTestFramework):
block_2 = self.build_next_block()
self.update_witness_block_with_transactions(block_2, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block_2, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_2, accepted=False, reason='bad-blk-sigops')
# Try dropping the last input in tx2, and add an output that has
# too many sigops (contributing to legacy sigop count).
@@ -1931,7 +1949,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.rehash()
block_3 = self.build_next_block()
self.update_witness_block_with_transactions(block_3, [tx2])
- test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False)
+ test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False, reason='bad-blk-sigops')
# If we drop the last checksig in this output, the tx should succeed.
block_4 = self.build_next_block()
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 85e3c2a383..5d0113465f 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -32,13 +32,13 @@ from .script import (
CScriptNum,
CScriptOp,
OP_1,
- OP_CHECKMULTISIG,
OP_RETURN,
OP_TRUE,
)
from .script_util import (
key_to_p2pk_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2wsh_script,
)
from .util import assert_equal
@@ -209,7 +209,7 @@ def witness_script(use_p2wsh, pubkey):
pkscript = key_to_p2wpkh_script(pubkey)
else:
# 1-of-1 multisig
- witness_script = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
+ witness_script = keys_to_multisig_script([pubkey])
pkscript = script_to_p2wsh_script(witness_script)
return pkscript.hex()
@@ -218,7 +218,7 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
Optionally wrap the segwit output using P2SH."""
if use_p2wsh:
- program = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
+ program = keys_to_multisig_script([pubkey])
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
else:
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
index 82a9067dd2..cbc4a560db 100755
--- a/test/functional/test_framework/script_util.py
+++ b/test/functional/test_framework/script_util.py
@@ -5,7 +5,9 @@
"""Useful Script constants and utils."""
from test_framework.script import (
CScript,
+ CScriptOp,
OP_0,
+ OP_CHECKMULTISIG,
OP_CHECKSIG,
OP_DUP,
OP_EQUAL,
@@ -41,6 +43,17 @@ def key_to_p2pk_script(key):
return CScript([key, OP_CHECKSIG])
+def keys_to_multisig_script(keys, *, k=None):
+ n = len(keys)
+ if k is None: # n-of-n multisig by default
+ k = n
+ assert k <= n
+ op_k = CScriptOp.encode_op_n(k)
+ op_n = CScriptOp.encode_op_n(n)
+ checked_keys = [check_key(key) for key in keys]
+ return CScript([op_k] + checked_keys + [op_n, OP_CHECKMULTISIG])
+
+
def keyhash_to_p2pkh_script(hash):
assert len(hash) == 20
return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG])
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index 1ee55aa3b7..c307ded542 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -15,15 +15,10 @@ from test_framework.address import (
script_to_p2wsh,
)
from test_framework.key import ECKey
-from test_framework.script import (
- CScript,
- OP_2,
- OP_3,
- OP_CHECKMULTISIG,
-)
from test_framework.script_util import (
key_to_p2pkh_script,
key_to_p2wpkh_script,
+ keys_to_multisig_script,
script_to_p2sh_script,
script_to_p2wsh_script,
)
@@ -92,7 +87,7 @@ def get_multisig(node):
addr = node.getaddressinfo(node.getnewaddress())
addrs.append(addr['address'])
pubkeys.append(addr['pubkey'])
- script_code = CScript([OP_2] + [bytes.fromhex(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
+ script_code = keys_to_multisig_script(pubkeys, k=2)
witness_script = script_to_p2wsh_script(script_code)
return Multisig(privkeys=[node.dumpprivkey(addr) for addr in addrs],
pubkeys=pubkeys,
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index b91b294108..916cd94b79 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -100,6 +100,7 @@ BASE_SCRIPTS = [
'p2p_compactblocks.py',
'p2p_compactblocks_blocksonly.py',
'feature_segwit.py --legacy-wallet',
+ 'feature_segwit.py --descriptors',
# vv Tests less than 2m vv
'wallet_basic.py --legacy-wallet',
'wallet_basic.py --descriptors',
diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py
index 3a0daf04ad..cebaa02524 100755
--- a/test/functional/tool_wallet.py
+++ b/test/functional/tool_wallet.py
@@ -193,7 +193,7 @@ class ToolWalletTest(BitcoinTestFramework):
locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
error = 'Error initializing wallet database environment "{}"!'.format(locked_dir)
if self.options.descriptors:
- error = "SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ error = f"SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
self.assert_raises_tool_error(
error,
'-wallet=' + self.default_wallet_name,
diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py
index d4768f5043..68ca005649 100755
--- a/test/functional/wallet_multiwallet.py
+++ b/test/functional/wallet_multiwallet.py
@@ -202,7 +202,7 @@ class MultiWalletTest(BitcoinTestFramework):
self.restart_node(0, ['-nowallet', '-walletdir=' + competing_wallet_dir])
self.nodes[0].createwallet(self.default_wallet_name)
if self.options.descriptors:
- exp_stderr = r"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?"
+ exp_stderr = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
else:
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\S*\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
@@ -303,7 +303,7 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load duplicate wallets
path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", "wallet.dat")
if self.options.descriptors:
- assert_raises_rpc_error(-4, "Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another bitcoind?", self.nodes[0].loadwallet, wallet_names[0])
+ assert_raises_rpc_error(-4, f"Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?", self.nodes[0].loadwallet, wallet_names[0])
else:
assert_raises_rpc_error(-35, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])