aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.qt.include7
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/chain.h30
-rw-r--r--src/httpserver.cpp2
-rw-r--r--src/i2p.cpp65
-rw-r--r--src/i2p.h2
-rw-r--r--src/index/coinstatsindex.cpp11
-rw-r--r--src/init.cpp26
-rw-r--r--src/kernel/chainparams.cpp8
-rw-r--r--src/kernel/coinstats.cpp64
-rw-r--r--src/kernel/coinstats.h4
-rw-r--r--src/net.cpp77
-rw-r--r--src/net.h13
-rw-r--r--src/net_processing.cpp7
-rw-r--r--src/netaddress.h6
-rw-r--r--src/netbase.cpp14
-rw-r--r--src/netbase.h65
-rw-r--r--src/netgroup.cpp4
-rw-r--r--src/node/blockstorage.cpp2
-rw-r--r--src/node/blockstorage.h1
-rw-r--r--src/qt/bitcoingui.cpp12
-rw-r--r--src/qt/coincontroldialog.cpp14
-rw-r--r--src/rpc/blockchain.cpp17
-rw-r--r--src/rpc/net.cpp6
-rw-r--r--src/script/miniscript.h10
-rw-r--r--src/test/fuzz/i2p.cpp58
-rw-r--r--src/test/fuzz/miniscript.cpp61
-rw-r--r--src/test/fuzz/util/net.cpp6
-rw-r--r--src/test/i2p_tests.cpp94
-rw-r--r--src/test/miniscript_tests.cpp154
-rw-r--r--src/test/net_tests.cpp96
-rw-r--r--src/test/validation_tests.cpp4
-rw-r--r--src/torcontrol.cpp2
-rw-r--r--src/util/trace.h4
-rw-r--r--src/validation.cpp9
-rw-r--r--src/wallet/transaction.h11
-rw-r--r--src/wallet/wallet.cpp2
37 files changed, 548 insertions, 421 deletions
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 7852d1a2fa..c235c3c4da 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -358,11 +358,14 @@ SECONDARY: $(QT_QM)
$(srcdir)/qt/bitcoinstrings.cpp: FORCE
@test -n $(XGETTEXT) || echo "xgettext is required for updating translations"
- $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES)
+ $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py \
+ $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) \
+ $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) \
+ $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
# The resulted bitcoin_en.xlf source file should follow Transifex requirements.
# See: https://docs.transifex.com/formats/xliff#how-to-distinguish-between-a-source-file-and-a-translation-file
-translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
+translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM)
@test -n $(LUPDATE) || echo "lupdate is required for updating translations"
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/bitcoin_en.ts
@test -n $(LCONVERT) || echo "lconvert is required for updating translations"
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index d66f5bf53a..b610dabd07 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -280,7 +280,6 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/headerssync.cpp \
test/fuzz/hex.cpp \
test/fuzz/http_request.cpp \
- test/fuzz/i2p.cpp \
test/fuzz/integer.cpp \
test/fuzz/key.cpp \
test/fuzz/key_io.cpp \
diff --git a/src/chain.h b/src/chain.h
index 4bf2001f74..f9121fb861 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -42,13 +42,13 @@ static constexpr int64_t MAX_BLOCK_TIME_GAP = 90 * 60;
class CBlockFileInfo
{
public:
- unsigned int nBlocks; //!< number of blocks stored in file
- unsigned int nSize; //!< number of used bytes of block file
- unsigned int nUndoSize; //!< number of used bytes in the undo file
- unsigned int nHeightFirst; //!< lowest height of block in file
- unsigned int nHeightLast; //!< highest height of block in file
- uint64_t nTimeFirst; //!< earliest time of block in file
- uint64_t nTimeLast; //!< latest time of block in file
+ unsigned int nBlocks{}; //!< number of blocks stored in file
+ unsigned int nSize{}; //!< number of used bytes of block file
+ unsigned int nUndoSize{}; //!< number of used bytes in the undo file
+ unsigned int nHeightFirst{}; //!< lowest height of block in file
+ unsigned int nHeightLast{}; //!< highest height of block in file
+ uint64_t nTimeFirst{}; //!< earliest time of block in file
+ uint64_t nTimeLast{}; //!< latest time of block in file
SERIALIZE_METHODS(CBlockFileInfo, obj)
{
@@ -61,21 +61,7 @@ public:
READWRITE(VARINT(obj.nTimeLast));
}
- void SetNull()
- {
- nBlocks = 0;
- nSize = 0;
- nUndoSize = 0;
- nHeightFirst = 0;
- nHeightLast = 0;
- nTimeFirst = 0;
- nTimeLast = 0;
- }
-
- CBlockFileInfo()
- {
- SetNull();
- }
+ CBlockFileInfo() {}
std::string ToString() const;
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index add6494f06..647e36adb3 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -682,7 +682,7 @@ CService HTTPRequest::GetPeer() const
evhttp_connection_get_peer(con, (char**)&address, &port);
#endif // HAVE_EVHTTP_CONNECTION_GET_PEER_CONST_CHAR
- peer = LookupNumeric(address, port);
+ peer = MaybeFlipIPv6toCJDNS(LookupNumeric(address, port));
}
return peer;
}
diff --git a/src/i2p.cpp b/src/i2p.cpp
index 5a3dde54ce..05a5dde396 100644
--- a/src/i2p.cpp
+++ b/src/i2p.cpp
@@ -12,6 +12,7 @@
#include <netaddress.h>
#include <netbase.h>
#include <random.h>
+#include <sync.h>
#include <tinyformat.h>
#include <util/fs.h>
#include <util/readwritefile.h>
@@ -153,27 +154,59 @@ bool Session::Listen(Connection& conn)
bool Session::Accept(Connection& conn)
{
- try {
- while (!*m_interrupt) {
- Sock::Event occurred;
- if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
- throw std::runtime_error("wait on socket failed");
- }
+ AssertLockNotHeld(m_mutex);
- if (occurred == 0) {
- // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
- continue;
- }
+ std::string errmsg;
+ bool disconnect{false};
- const std::string& peer_dest =
- conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
+ while (!*m_interrupt) {
+ Sock::Event occurred;
+ if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
+ errmsg = "wait on socket failed";
+ break;
+ }
- conn.peer = CService(DestB64ToAddr(peer_dest), I2P_SAM31_PORT);
+ if (occurred == 0) {
+ // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
+ continue;
+ }
- return true;
+ std::string peer_dest;
+ try {
+ peer_dest = conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
+ } catch (const std::runtime_error& e) {
+ errmsg = e.what();
+ break;
}
- } catch (const std::runtime_error& e) {
- Log("Error accepting: %s", e.what());
+
+ CNetAddr peer_addr;
+ try {
+ peer_addr = DestB64ToAddr(peer_dest);
+ } catch (const std::runtime_error& e) {
+ // The I2P router is expected to send the Base64 of the connecting peer,
+ // but it may happen that something like this is sent instead:
+ // STREAM STATUS RESULT=I2P_ERROR MESSAGE="Session was closed"
+ // In that case consider the session damaged and close it right away,
+ // even if the control socket is alive.
+ if (peer_dest.find("RESULT=I2P_ERROR") != std::string::npos) {
+ errmsg = strprintf("unexpected reply that hints the session is unusable: %s", peer_dest);
+ disconnect = true;
+ } else {
+ errmsg = e.what();
+ }
+ break;
+ }
+
+ conn.peer = CService(peer_addr, I2P_SAM31_PORT);
+
+ return true;
+ }
+
+ Log("Error accepting%s: %s", disconnect ? " (will close the session)" : "", errmsg);
+ if (disconnect) {
+ LOCK(m_mutex);
+ Disconnect();
+ } else {
CheckControlSock();
}
return false;
diff --git a/src/i2p.h b/src/i2p.h
index cb9da64816..375abaccfc 100644
--- a/src/i2p.h
+++ b/src/i2p.h
@@ -105,7 +105,7 @@ public:
* completion the `peer` member will be set to the address of the incoming peer.
* @return true on success
*/
- bool Accept(Connection& conn);
+ bool Accept(Connection& conn) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
/**
* Connect to an I2P peer.
diff --git a/src/index/coinstatsindex.cpp b/src/index/coinstatsindex.cpp
index 9dab8ca901..ecd3fd21b5 100644
--- a/src/index/coinstatsindex.cpp
+++ b/src/index/coinstatsindex.cpp
@@ -15,9 +15,10 @@
#include <undo.h>
#include <validation.h>
+using kernel::ApplyCoinHash;
using kernel::CCoinsStats;
using kernel::GetBogoSize;
-using kernel::TxOutSer;
+using kernel::RemoveCoinHash;
static constexpr uint8_t DB_BLOCK_HASH{'s'};
static constexpr uint8_t DB_BLOCK_HEIGHT{'t'};
@@ -166,7 +167,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
continue;
}
- m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ ApplyCoinHash(m_muhash, outpoint, coin);
if (tx->IsCoinBase()) {
m_total_coinbase_amount += coin.out.nValue;
@@ -187,7 +188,7 @@ bool CoinStatsIndex::CustomAppend(const interfaces::BlockInfo& block)
Coin coin{tx_undo.vprevout[j]};
COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
- m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ RemoveCoinHash(m_muhash, outpoint, coin);
m_total_prevout_spent_amount += coin.out.nValue;
@@ -443,7 +444,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
continue;
}
- m_muhash.Remove(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ RemoveCoinHash(m_muhash, outpoint, coin);
if (tx->IsCoinBase()) {
m_total_coinbase_amount -= coin.out.nValue;
@@ -464,7 +465,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
Coin coin{tx_undo.vprevout[j]};
COutPoint outpoint{tx->vin[j].prevout.hash, tx->vin[j].prevout.n};
- m_muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ ApplyCoinHash(m_muhash, outpoint, coin);
m_total_prevout_spent_amount -= coin.out.nValue;
diff --git a/src/init.cpp b/src/init.cpp
index 22100dfbf9..42331d37e8 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1314,30 +1314,24 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
if (args.IsArgSet("-onlynet")) {
- std::set<enum Network> nets;
+ g_reachable_nets.RemoveAll();
for (const std::string& snet : args.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
- nets.insert(net);
- }
- for (int n = 0; n < NET_MAX; n++) {
- enum Network net = (enum Network)n;
- assert(IsReachable(net));
- if (!nets.count(net))
- SetReachable(net, false);
+ g_reachable_nets.Add(net);
}
}
if (!args.IsArgSet("-cjdnsreachable")) {
- if (args.IsArgSet("-onlynet") && IsReachable(NET_CJDNS)) {
+ if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_CJDNS)) {
return InitError(
_("Outbound connections restricted to CJDNS (-onlynet=cjdns) but "
"-cjdnsreachable is not provided"));
}
- SetReachable(NET_CJDNS, false);
+ g_reachable_nets.Remove(NET_CJDNS);
}
- // Now IsReachable(NET_CJDNS) is true if:
+ // Now g_reachable_nets.Contains(NET_CJDNS) is true if:
// 1. -cjdnsreachable is given and
// 2.1. -onlynet is not given or
// 2.2. -onlynet=cjdns is given
@@ -1345,7 +1339,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Requesting DNS seeds entails connecting to IPv4/IPv6, which -onlynet options may prohibit:
// If -dnsseed=1 is explicitly specified, abort. If it's left unspecified by the user, we skip
// the DNS seeds by adjusting -dnsseed in InitParameterInteraction.
- if (args.GetBoolArg("-dnsseed") == true && !IsReachable(NET_IPV4) && !IsReachable(NET_IPV6)) {
+ if (args.GetBoolArg("-dnsseed") == true && !g_reachable_nets.Contains(NET_IPV4) && !g_reachable_nets.Contains(NET_IPV6)) {
return InitError(strprintf(_("Incompatible options: -dnsseed=1 was explicitly specified, but -onlynet forbids connections to IPv4/IPv6")));
};
@@ -1375,7 +1369,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
onion_proxy = addrProxy;
}
- const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && IsReachable(NET_ONION)};
+ const bool onlynet_used_with_onion{args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_ONION)};
// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
// -noonion (or -onion=0) disables connecting to .onion entirely
@@ -1410,7 +1404,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
"reaching the Tor network is not provided: none of -proxy, -onion or "
"-listenonion is given"));
}
- SetReachable(NET_ONION, false);
+ g_reachable_nets.Remove(NET_ONION);
}
for (const std::string& strAddr : args.GetArgs("-externalip")) {
@@ -1885,12 +1879,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
SetProxy(NET_I2P, Proxy{addr.value()});
} else {
- if (args.IsArgSet("-onlynet") && IsReachable(NET_I2P)) {
+ if (args.IsArgSet("-onlynet") && g_reachable_nets.Contains(NET_I2P)) {
return InitError(
_("Outbound connections restricted to i2p (-onlynet=i2p) but "
"-i2psam is not provided"));
}
- SetReachable(NET_I2P, false);
+ g_reachable_nets.Remove(NET_I2P);
}
connOptions.m_i2p_accept_incoming = args.GetBoolArg("-i2pacceptincoming", DEFAULT_I2P_ACCEPT_INCOMING);
diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp
index 3ac8756e41..73ba330ff0 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -269,7 +269,7 @@ public:
m_assumeutxo_data = {
{
.height = 2'500'000,
- .hash_serialized = AssumeutxoHash{uint256S("0x2a8fdefef3bf75fa00540ccaaaba4b5281bea94229327bdb0f7416ef1e7a645c")},
+ .hash_serialized = AssumeutxoHash{uint256S("0xf841584909f68e47897952345234e37fcd9128cd818f41ee6c3ca68db8071be7")},
.nChainTx = 66484552,
.blockhash = uint256S("0x0000000000000093bcb68c03a9a168ae252572d348a2eaeba2cdf9231d73206f")
}
@@ -378,7 +378,7 @@ public:
m_assumeutxo_data = {
{
.height = 160'000,
- .hash_serialized = AssumeutxoHash{uint256S("0x5225141cb62dee63ab3be95f9b03d60801f264010b1816d4bd00618b2736e7be")},
+ .hash_serialized = AssumeutxoHash{uint256S("0xfe0a44309b74d6b5883d246cb419c6221bcccf0b308c9b59b7d70783dbdf928a")},
.nChainTx = 2289496,
.blockhash = uint256S("0x0000003ca3c99aff040f2563c2ad8f8ec88bd0fd6b8f0895cfaf1ef90353a62c")
}
@@ -494,14 +494,14 @@ public:
m_assumeutxo_data = {
{
.height = 110,
- .hash_serialized = AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")},
+ .hash_serialized = AssumeutxoHash{uint256S("0x6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1")},
.nChainTx = 111,
.blockhash = uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c")
},
{
// For use by test/functional/feature_assumeutxo.py
.height = 299,
- .hash_serialized = AssumeutxoHash{uint256S("0xef45ccdca5898b6c2145e4581d2b88c56564dd389e4bd75a1aaf6961d3edd3c0")},
+ .hash_serialized = AssumeutxoHash{uint256S("0x61d9c2b29a2571a5fe285fe2d8554f91f93309666fc9b8223ee96338de25ff53")},
.nChainTx = 300,
.blockhash = uint256S("0x7e0517ef3ea6ecbed9117858e42eedc8eb39e8698a38dcbd1b3962a283233f4c")
},
diff --git a/src/kernel/coinstats.cpp b/src/kernel/coinstats.cpp
index 527433f45e..9bd755ed27 100644
--- a/src/kernel/coinstats.cpp
+++ b/src/kernel/coinstats.cpp
@@ -48,15 +48,35 @@ uint64_t GetBogoSize(const CScript& script_pub_key)
script_pub_key.size() /* scriptPubKey */;
}
-DataStream TxOutSer(const COutPoint& outpoint, const Coin& coin)
+template <typename T>
+static void TxOutSer(T& ss, const COutPoint& outpoint, const Coin& coin)
{
- DataStream ss{};
ss << outpoint;
- ss << static_cast<uint32_t>(coin.nHeight * 2 + coin.fCoinBase);
+ ss << static_cast<uint32_t>((coin.nHeight << 1) + coin.fCoinBase);
ss << coin.out;
- return ss;
}
+static void ApplyCoinHash(HashWriter& ss, const COutPoint& outpoint, const Coin& coin)
+{
+ TxOutSer(ss, outpoint, coin);
+}
+
+void ApplyCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin)
+{
+ DataStream ss{};
+ TxOutSer(ss, outpoint, coin);
+ muhash.Insert(MakeUCharSpan(ss));
+}
+
+void RemoveCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin)
+{
+ DataStream ss{};
+ TxOutSer(ss, outpoint, coin);
+ muhash.Remove(MakeUCharSpan(ss));
+}
+
+static void ApplyCoinHash(std::nullptr_t, const COutPoint& outpoint, const Coin& coin) {}
+
//! Warning: be very careful when changing this! assumeutxo and UTXO snapshot
//! validation commitments are reliant on the hash constructed by this
//! function.
@@ -69,32 +89,13 @@ DataStream TxOutSer(const COutPoint& outpoint, const Coin& coin)
//! It is also possible, though very unlikely, that a change in this
//! construction could cause a previously invalid (and potentially malicious)
//! UTXO snapshot to be considered valid.
-static void ApplyHash(HashWriter& ss, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
-{
- for (auto it = outputs.begin(); it != outputs.end(); ++it) {
- if (it == outputs.begin()) {
- ss << hash;
- ss << VARINT(it->second.nHeight * 2 + it->second.fCoinBase ? 1u : 0u);
- }
-
- ss << VARINT(it->first + 1);
- ss << it->second.out.scriptPubKey;
- ss << VARINT_MODE(it->second.out.nValue, VarIntMode::NONNEGATIVE_SIGNED);
-
- if (it == std::prev(outputs.end())) {
- ss << VARINT(0u);
- }
- }
-}
-
-static void ApplyHash(std::nullptr_t, const uint256& hash, const std::map<uint32_t, Coin>& outputs) {}
-
-static void ApplyHash(MuHash3072& muhash, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
+template <typename T>
+static void ApplyHash(T& hash_obj, const uint256& hash, const std::map<uint32_t, Coin>& outputs)
{
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
COutPoint outpoint = COutPoint(hash, it->first);
Coin coin = it->second;
- muhash.Insert(MakeUCharSpan(TxOutSer(outpoint, coin)));
+ ApplyCoinHash(hash_obj, outpoint, coin);
}
}
@@ -118,8 +119,6 @@ static bool ComputeUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, c
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
assert(pcursor);
- PrepareHash(hash_obj, stats);
-
uint256 prevkey;
std::map<uint32_t, Coin> outputs;
while (pcursor->Valid()) {
@@ -180,15 +179,6 @@ std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsV
return stats;
}
-// The legacy hash serializes the hashBlock
-static void PrepareHash(HashWriter& ss, const CCoinsStats& stats)
-{
- ss << stats.hashBlock;
-}
-// MuHash does not need the prepare step
-static void PrepareHash(MuHash3072& muhash, CCoinsStats& stats) {}
-static void PrepareHash(std::nullptr_t, CCoinsStats& stats) {}
-
static void FinalizeHash(HashWriter& ss, CCoinsStats& stats)
{
stats.hashSerialized = ss.GetHash();
diff --git a/src/kernel/coinstats.h b/src/kernel/coinstats.h
index 54d0e4f664..c0c363a842 100644
--- a/src/kernel/coinstats.h
+++ b/src/kernel/coinstats.h
@@ -6,6 +6,7 @@
#define BITCOIN_KERNEL_COINSTATS_H
#include <consensus/amount.h>
+#include <crypto/muhash.h>
#include <streams.h>
#include <uint256.h>
@@ -72,7 +73,8 @@ struct CCoinsStats {
uint64_t GetBogoSize(const CScript& script_pub_key);
-DataStream TxOutSer(const COutPoint& outpoint, const Coin& coin);
+void ApplyCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin);
+void RemoveCoinHash(MuHash3072& muhash, const COutPoint& outpoint, const Coin& coin);
std::optional<CCoinsStats> ComputeUTXOStats(CoinStatsHashType hash_type, CCoinsView* view, node::BlockManager& blockman, const std::function<void()>& interruption_point = {});
} // namespace kernel
diff --git a/src/net.cpp b/src/net.cpp
index 994abd986d..09a3d8617a 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -115,7 +115,6 @@ bool fDiscover = true;
bool fListen = true;
GlobalMutex g_maplocalhost_mutex;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
-static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
std::string strSubVersion;
size_t CSerializedNetMsg::GetMemoryUsage() const noexcept
@@ -232,7 +231,7 @@ static int GetnScore(const CService& addr)
{
CService addrLocal = pnode->GetAddrLocal();
return fDiscover && pnode->addr.IsRoutable() && addrLocal.IsRoutable() &&
- IsReachable(addrLocal.GetNetwork());
+ g_reachable_nets.Contains(addrLocal);
}
std::optional<CService> GetLocalAddrForPeer(CNode& node)
@@ -270,22 +269,6 @@ std::optional<CService> GetLocalAddrForPeer(CNode& node)
return std::nullopt;
}
-/**
- * If an IPv6 address belongs to the address range used by the CJDNS network and
- * the CJDNS network is reachable (-cjdnsreachable config is set), then change
- * the type from NET_IPV6 to NET_CJDNS.
- * @param[in] service Address to potentially convert.
- * @return a copy of `service` either unmodified or changed to CJDNS.
- */
-CService MaybeFlipIPv6toCJDNS(const CService& service)
-{
- CService ret{service};
- if (ret.IsIPv6() && ret.HasCJDNSPrefix() && IsReachable(NET_CJDNS)) {
- ret.m_net = NET_CJDNS;
- }
- return ret;
-}
-
// learn a new local address
bool AddLocal(const CService& addr_, int nScore)
{
@@ -297,7 +280,7 @@ bool AddLocal(const CService& addr_, int nScore)
if (!fDiscover && nScore < LOCAL_MANUAL)
return false;
- if (!IsReachable(addr))
+ if (!g_reachable_nets.Contains(addr))
return false;
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
@@ -327,25 +310,6 @@ void RemoveLocal(const CService& addr)
mapLocalHost.erase(addr);
}
-void SetReachable(enum Network net, bool reachable)
-{
- if (net == NET_UNROUTABLE || net == NET_INTERNAL)
- return;
- LOCK(g_maplocalhost_mutex);
- vfLimited[net] = !reachable;
-}
-
-bool IsReachable(enum Network net)
-{
- LOCK(g_maplocalhost_mutex);
- return !vfLimited[net];
-}
-
-bool IsReachable(const CNetAddr &addr)
-{
- return IsReachable(addr.GetNetwork());
-}
-
/** vote for a local address */
bool SeenLocal(const CService& addr)
{
@@ -375,17 +339,6 @@ CNode* CConnman::FindNode(const CNetAddr& ip)
return nullptr;
}
-CNode* CConnman::FindNode(const CSubNet& subNet)
-{
- LOCK(m_nodes_mutex);
- for (CNode* pnode : m_nodes) {
- if (subNet.Match(static_cast<CNetAddr>(pnode->addr))) {
- return pnode;
- }
- }
- return nullptr;
-}
-
CNode* CConnman::FindNode(const std::string& addrName)
{
LOCK(m_nodes_mutex);
@@ -2433,7 +2386,7 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (net == NET_UNROUTABLE || net == NET_INTERNAL) continue;
- if (IsReachable(net) && addrman.Size(net, std::nullopt) == 0) {
+ if (g_reachable_nets.Contains(net) && addrman.Size(net, std::nullopt) == 0) {
networks.insert(net);
}
}
@@ -2453,7 +2406,7 @@ bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
LOCK(m_nodes_mutex);
for (const auto net : nets) {
- if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
+ if (g_reachable_nets.Contains(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
network = net;
return true;
}
@@ -2683,7 +2636,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (anchor && !m_anchors.empty()) {
const CAddress addr = m_anchors.back();
m_anchors.pop_back();
- if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
+ if (!addr.IsValid() || IsLocal(addr) || !g_reachable_nets.Contains(addr) ||
!HasAllDesirableServiceFlags(addr.nServices) ||
outbound_ipv46_peer_netgroups.count(m_netgroupman.GetGroup(addr))) continue;
addrConnect = addr;
@@ -2738,8 +2691,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
break;
}
- if (!IsReachable(addr))
+ if (!g_reachable_nets.Contains(addr)) {
continue;
+ }
// only consider very recently tried nodes after 30 failed attempts
if (current_time - addr_last_try < 10min && nTries < 30) {
@@ -2974,6 +2928,13 @@ void CConnman::ThreadI2PAcceptIncoming()
bool advertising_listen_addr = false;
i2p::Connection conn;
+ auto SleepOnFailure = [&]() {
+ interruptNet.sleep_for(err_wait);
+ if (err_wait < err_wait_cap) {
+ err_wait += 1s;
+ }
+ };
+
while (!interruptNet) {
if (!m_i2p_sam_session->Listen(conn)) {
@@ -2981,12 +2942,7 @@ void CConnman::ThreadI2PAcceptIncoming()
RemoveLocal(conn.me);
advertising_listen_addr = false;
}
-
- interruptNet.sleep_for(err_wait);
- if (err_wait < err_wait_cap) {
- err_wait *= 2;
- }
-
+ SleepOnFailure();
continue;
}
@@ -2996,11 +2952,14 @@ void CConnman::ThreadI2PAcceptIncoming()
}
if (!m_i2p_sam_session->Accept(conn)) {
+ SleepOnFailure();
continue;
}
CreateNodeFromAcceptedSocket(std::move(conn.sock), NetPermissionFlags::None,
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
+
+ err_wait = err_wait_begin;
}
}
diff --git a/src/net.h b/src/net.h
index e8e31f72e4..ddee34168a 100644
--- a/src/net.h
+++ b/src/net.h
@@ -160,24 +160,12 @@ enum
/** Returns a local address that we should advertise to this peer. */
std::optional<CService> GetLocalAddrForPeer(CNode& node);
-/**
- * Mark a network as reachable or unreachable (no automatic connects to it)
- * @note Networks are reachable by default
- */
-void SetReachable(enum Network net, bool reachable);
-/** @returns true if the network is reachable, false otherwise */
-bool IsReachable(enum Network net);
-/** @returns true if the address is in a reachable network, false otherwise */
-bool IsReachable(const CNetAddr& addr);
-
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
void RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
CService GetLocalAddress(const CNode& peer);
-CService MaybeFlipIPv6toCJDNS(const CService& service);
-
extern bool fDiscover;
extern bool fListen;
@@ -1341,7 +1329,6 @@ private:
uint64_t CalculateKeyedNetGroup(const CAddress& ad) const;
CNode* FindNode(const CNetAddr& ip);
- CNode* FindNode(const CSubNet& subNet);
CNode* FindNode(const std::string& addrName);
CNode* FindNode(const CService& addr);
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index f07769272c..3bfb606037 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3830,14 +3830,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
continue;
}
++num_proc;
- bool fReachable = IsReachable(addr);
+ const bool reachable{g_reachable_nets.Contains(addr)};
if (addr.nTime > current_a_time - 10min && !peer->m_getaddr_sent && vAddr.size() <= 10 && addr.IsRoutable()) {
// Relay to a limited number of other nodes
- RelayAddress(pfrom.GetId(), addr, fReachable);
+ RelayAddress(pfrom.GetId(), addr, reachable);
}
// Do not store addresses outside our network
- if (fReachable)
+ if (reachable) {
vAddrOk.push_back(addr);
+ }
}
peer->m_addr_processed += num_proc;
peer->m_addr_rate_limited += num_rate_limit;
diff --git a/src/netaddress.h b/src/netaddress.h
index ad09f16799..08dd77c0ff 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -81,6 +81,10 @@ static const std::array<uint8_t, 6> INTERNAL_IN_IPV6_PREFIX{
0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 // 0xFD + sha256("bitcoin")[0:5].
};
+/// All CJDNS addresses start with 0xFC. See
+/// https://github.com/cjdelisle/cjdns/blob/master/doc/Whitepaper.md#pulling-it-all-together
+static constexpr uint8_t CJDNS_PREFIX{0xFC};
+
/// Size of IPv4 address (in bytes).
static constexpr size_t ADDR_IPV4_SIZE = 4;
@@ -174,7 +178,7 @@ public:
[[nodiscard]] bool IsTor() const { return m_net == NET_ONION; }
[[nodiscard]] bool IsI2P() const { return m_net == NET_I2P; }
[[nodiscard]] bool IsCJDNS() const { return m_net == NET_CJDNS; }
- [[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == 0xfc; }
+ [[nodiscard]] bool HasCJDNSPrefix() const { return m_addr[0] == CJDNS_PREFIX; }
bool IsLocal() const;
bool IsRoutable() const;
bool IsInternal() const;
diff --git a/src/netbase.cpp b/src/netbase.cpp
index ca1a80d72f..5e1e121bfe 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -32,6 +32,8 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP;
std::chrono::milliseconds g_socks5_recv_timeout = 20s;
static std::atomic<bool> interruptSocks5Recv(false);
+ReachableNets g_reachable_nets;
+
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
{
addrinfo ai_hint{};
@@ -651,9 +653,10 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
const size_t slash_pos{subnet_str.find_last_of('/')};
const std::string str_addr{subnet_str.substr(0, slash_pos)};
- const std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)};
+ std::optional<CNetAddr> addr{LookupHost(str_addr, /*fAllowLookup=*/false)};
if (addr.has_value()) {
+ addr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
if (slash_pos != subnet_str.npos) {
const std::string netmask_str{subnet_str.substr(slash_pos + 1)};
uint8_t netmask;
@@ -772,3 +775,12 @@ bool IsBadPort(uint16_t port)
}
return false;
}
+
+CService MaybeFlipIPv6toCJDNS(const CService& service)
+{
+ CService ret{service};
+ if (ret.IsIPv6() && ret.HasCJDNSPrefix() && g_reachable_nets.Contains(NET_CJDNS)) {
+ ret.m_net = NET_CJDNS;
+ }
+ return ret;
+}
diff --git a/src/netbase.h b/src/netbase.h
index 1da4f5c51d..8b7da4109f 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <string>
#include <type_traits>
+#include <unordered_set>
#include <vector>
extern int nConnectTimeout;
@@ -65,6 +66,61 @@ struct ProxyCredentials
};
/**
+ * List of reachable networks. Everything is reachable by default.
+ */
+class ReachableNets {
+public:
+ void Add(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ {
+ AssertLockNotHeld(m_mutex);
+ LOCK(m_mutex);
+ m_reachable.insert(net);
+ }
+
+ void Remove(Network net) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ {
+ AssertLockNotHeld(m_mutex);
+ LOCK(m_mutex);
+ m_reachable.erase(net);
+ }
+
+ void RemoveAll() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ {
+ AssertLockNotHeld(m_mutex);
+ LOCK(m_mutex);
+ m_reachable.clear();
+ }
+
+ [[nodiscard]] bool Contains(Network net) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ {
+ AssertLockNotHeld(m_mutex);
+ LOCK(m_mutex);
+ return m_reachable.count(net) > 0;
+ }
+
+ [[nodiscard]] bool Contains(const CNetAddr& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
+ {
+ AssertLockNotHeld(m_mutex);
+ return Contains(addr.GetNetwork());
+ }
+
+private:
+ mutable Mutex m_mutex;
+
+ std::unordered_set<Network> m_reachable GUARDED_BY(m_mutex){
+ NET_UNROUTABLE,
+ NET_IPV4,
+ NET_IPV6,
+ NET_ONION,
+ NET_I2P,
+ NET_CJDNS,
+ NET_INTERNAL
+ };
+};
+
+extern ReachableNets g_reachable_nets;
+
+/**
* Wrapper for getaddrinfo(3). Do not use directly: call Lookup/LookupHost/LookupNumeric/LookupSubNet.
*/
std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup);
@@ -251,4 +307,13 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
*/
bool IsBadPort(uint16_t port);
+/**
+ * If an IPv6 address belongs to the address range used by the CJDNS network and
+ * the CJDNS network is reachable (-cjdnsreachable config is set), then change
+ * the type from NET_IPV6 to NET_CJDNS.
+ * @param[in] service Address to potentially convert.
+ * @return a copy of `service` either unmodified or changed to CJDNS.
+ */
+CService MaybeFlipIPv6toCJDNS(const CService& service);
+
#endif // BITCOIN_NETBASE_H
diff --git a/src/netgroup.cpp b/src/netgroup.cpp
index 2d4d231f2b..a03927b152 100644
--- a/src/netgroup.cpp
+++ b/src/netgroup.cpp
@@ -52,8 +52,8 @@ std::vector<unsigned char> NetGroupManager::GetGroup(const CNetAddr& address) co
} else if (address.IsCJDNS()) {
// Treat in the same way as Tor and I2P because the address in all of
// them is "random" bytes (derived from a public key). However in CJDNS
- // the first byte is a constant 0xfc, so the random bytes come after it.
- // Thus skip the constant 8 bits at the start.
+ // the first byte is a constant (see CJDNS_PREFIX), so the random bytes
+ // come after it. Thus skip the constant 8 bits at the start.
nBits = 12;
} else if (address.IsHeNet()) {
// for he.net, use /36 groups
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index f8f1aab551..53f616de23 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -254,7 +254,7 @@ void BlockManager::PruneOneBlockFile(const int fileNumber)
}
}
- m_blockfile_info[fileNumber].SetNull();
+ m_blockfile_info.at(fileNumber) = CBlockFileInfo{};
m_dirty_fileinfo.insert(fileNumber);
}
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index ac97728c05..ba44d31581 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -32,7 +32,6 @@
class BlockValidationState;
class CAutoFile;
class CBlock;
-class CBlockFileInfo;
class CBlockUndo;
class CChainParams;
class Chainstate;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 720f5584d6..fd71938b60 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -392,7 +392,7 @@ void BitcoinGUI::createActions()
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
connect(usedReceivingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedReceivingAddresses);
connect(openAction, &QAction::triggered, this, &BitcoinGUI::openClicked);
- connect(m_open_wallet_menu, &QMenu::aboutToShow, m_wallet_controller, [this] {
+ connect(m_open_wallet_menu, &QMenu::aboutToShow, [this] {
m_open_wallet_menu->clear();
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
const std::string& path = i.first;
@@ -409,7 +409,7 @@ void BitcoinGUI::createActions()
continue;
}
- connect(action, &QAction::triggered, m_wallet_controller, [this, path] {
+ connect(action, &QAction::triggered, [this, path] {
auto activity = new OpenWalletActivity(m_wallet_controller, this);
connect(activity, &OpenWalletActivity::opened, this, &BitcoinGUI::setCurrentWallet, Qt::QueuedConnection);
connect(activity, &OpenWalletActivity::opened, rpcConsole, &RPCConsole::setCurrentWallet, Qt::QueuedConnection);
@@ -421,7 +421,7 @@ void BitcoinGUI::createActions()
action->setEnabled(false);
}
});
- connect(m_restore_wallet_action, &QAction::triggered, m_wallet_controller, [this] {
+ connect(m_restore_wallet_action, &QAction::triggered, [this] {
//: Name of the wallet data file format.
QString name_data_file = tr("Wallet Data");
@@ -447,14 +447,14 @@ void BitcoinGUI::createActions()
auto backup_file_path = fs::PathFromString(backup_file.toStdString());
activity->restore(backup_file_path, wallet_name.toStdString());
});
- connect(m_close_wallet_action, &QAction::triggered, m_wallet_controller, [this] {
+ connect(m_close_wallet_action, &QAction::triggered, [this] {
m_wallet_controller->closeWallet(walletFrame->currentWalletModel(), this);
});
connect(m_create_wallet_action, &QAction::triggered, this, &BitcoinGUI::createWallet);
- connect(m_close_all_wallets_action, &QAction::triggered, m_wallet_controller, [this] {
+ connect(m_close_all_wallets_action, &QAction::triggered, [this] {
m_wallet_controller->closeAllWallets(this);
});
- connect(m_migrate_wallet_action, &QAction::triggered, m_wallet_controller, [this] {
+ connect(m_migrate_wallet_action, &QAction::triggered, [this] {
auto activity = new MigrateWalletActivity(m_wallet_controller, this);
connect(activity, &MigrateWalletActivity::migrated, this, &BitcoinGUI::setCurrentWallet);
activity->migrate(walletFrame->currentWalletModel());
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index 38c7656e00..70aa9bc8da 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -421,7 +421,19 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
std::vector<unsigned char> witnessprogram;
if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram))
{
- nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
+ // add input skeleton bytes (outpoint, scriptSig size, nSequence)
+ nBytesInputs += (32 + 4 + 1 + 4);
+
+ if (witnessversion == 0) { // P2WPKH
+ // 1 WU (witness item count) + 72 WU (ECDSA signature with len byte) + 34 WU (pubkey with len byte)
+ nBytesInputs += 107 / WITNESS_SCALE_FACTOR;
+ } else if (witnessversion == 1) { // P2TR key-path spend
+ // 1 WU (witness item count) + 65 WU (Schnorr signature with len byte)
+ nBytesInputs += 66 / WITNESS_SCALE_FACTOR;
+ } else {
+ // not supported, should be unreachable
+ throw std::runtime_error("Trying to spend future segwit version script");
+ }
fWitness = true;
}
else if(ExtractDestination(out.txout.scriptPubKey, address))
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 229681094f..7b84747a3f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -820,7 +820,7 @@ static RPCHelpMan pruneblockchain()
CoinStatsHashType ParseHashType(const std::string& hash_type_input)
{
- if (hash_type_input == "hash_serialized_2") {
+ if (hash_type_input == "hash_serialized_3") {
return CoinStatsHashType::HASH_SERIALIZED;
} else if (hash_type_input == "muhash") {
return CoinStatsHashType::MUHASH;
@@ -867,7 +867,7 @@ static RPCHelpMan gettxoutsetinfo()
"\nReturns statistics about the unspent transaction output set.\n"
"Note this call may take some time if you are not using coinstatsindex.\n",
{
- {"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_2"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_2' (the legacy algorithm), 'muhash', 'none'."},
+ {"hash_type", RPCArg::Type::STR, RPCArg::Default{"hash_serialized_3"}, "Which UTXO set hash should be calculated. Options: 'hash_serialized_3' (the legacy algorithm), 'muhash', 'none'."},
{"hash_or_height", RPCArg::Type::NUM, RPCArg::DefaultHint{"the current best block"}, "The block hash or height of the target height (only available with coinstatsindex).",
RPCArgOptions{
.skip_type_check = true,
@@ -882,7 +882,7 @@ static RPCHelpMan gettxoutsetinfo()
{RPCResult::Type::STR_HEX, "bestblock", "The hash of the block at which these statistics are calculated"},
{RPCResult::Type::NUM, "txouts", "The number of unspent transaction outputs"},
{RPCResult::Type::NUM, "bogosize", "Database-independent, meaningless metric indicating the UTXO set size"},
- {RPCResult::Type::STR_HEX, "hash_serialized_2", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_2' hash_type is chosen)"},
+ {RPCResult::Type::STR_HEX, "hash_serialized_3", /*optional=*/true, "The serialized hash (only present if 'hash_serialized_3' hash_type is chosen)"},
{RPCResult::Type::STR_HEX, "muhash", /*optional=*/true, "The serialized hash (only present if 'muhash' hash_type is chosen)"},
{RPCResult::Type::NUM, "transactions", /*optional=*/true, "The number of transactions with unspent outputs (not available when coinstatsindex is used)"},
{RPCResult::Type::NUM, "disk_size", /*optional=*/true, "The estimated size of the chainstate on disk (not available when coinstatsindex is used)"},
@@ -942,7 +942,7 @@ static RPCHelpMan gettxoutsetinfo()
}
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_2 hash type cannot be queried for a specific block");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "hash_serialized_3 hash type cannot be queried for a specific block");
}
if (!index_requested) {
@@ -971,7 +971,7 @@ static RPCHelpMan gettxoutsetinfo()
ret.pushKV("txouts", (int64_t)stats.nTransactionOutputs);
ret.pushKV("bogosize", (int64_t)stats.nBogoSize);
if (hash_type == CoinStatsHashType::HASH_SERIALIZED) {
- ret.pushKV("hash_serialized_2", stats.hashSerialized.GetHex());
+ ret.pushKV("hash_serialized_3", stats.hashSerialized.GetHex());
}
if (hash_type == CoinStatsHashType::MUHASH) {
ret.pushKV("muhash", stats.hashSerialized.GetHex());
@@ -2737,6 +2737,7 @@ static RPCHelpMan loadtxoutset()
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
NodeContext& node = EnsureAnyNodeContext(request.context);
+ ChainstateManager& chainman = EnsureChainman(node);
fs::path path{AbsPathForConfigVal(EnsureArgsman(node), fs::u8path(request.params[0].get_str()))};
FILE* file{fsbridge::fopen(path, "rb")};
@@ -2751,14 +2752,16 @@ static RPCHelpMan loadtxoutset()
afile >> metadata;
uint256 base_blockhash = metadata.m_base_blockhash;
+ if (!chainman.GetParams().AssumeutxoForBlockhash(base_blockhash).has_value()) {
+ throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to load UTXO snapshot, "
+ "assumeutxo block hash in snapshot metadata not recognized (%s)", base_blockhash.ToString()));
+ }
int max_secs_to_wait_for_headers = 60 * 10;
CBlockIndex* snapshot_start_block = nullptr;
LogPrintf("[snapshot] waiting to see blockheader %s in headers chain before snapshot activation\n",
base_blockhash.ToString());
- ChainstateManager& chainman = EnsureChainman(node);
-
while (max_secs_to_wait_for_headers > 0) {
snapshot_start_block = WITH_LOCK(::cs_main,
return chainman.m_blockman.LookupBlockIndex(base_blockhash));
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 5a68bf9e1f..63788c3a03 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -585,8 +585,8 @@ static UniValue GetNetworksInfo()
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
obj.pushKV("name", GetNetworkName(network));
- obj.pushKV("limited", !IsReachable(network));
- obj.pushKV("reachable", IsReachable(network));
+ obj.pushKV("limited", !g_reachable_nets.Contains(network));
+ obj.pushKV("reachable", g_reachable_nets.Contains(network));
obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
networks.push_back(obj);
@@ -730,7 +730,7 @@ static RPCHelpMan setban()
if (!isSubnet) {
const std::optional<CNetAddr> addr{LookupHost(request.params[0].get_str(), false)};
if (addr.has_value()) {
- netAddr = addr.value();
+ netAddr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
}
}
else
diff --git a/src/script/miniscript.h b/src/script/miniscript.h
index d6bded959d..76b952350b 100644
--- a/src/script/miniscript.h
+++ b/src/script/miniscript.h
@@ -1105,13 +1105,15 @@ private:
}
internal::WitnessSize CalcWitnessSize() const {
+ const uint32_t sig_size = IsTapscript(m_script_ctx) ? 1 + 65 : 1 + 72;
+ const uint32_t pubkey_size = IsTapscript(m_script_ctx) ? 1 + 32 : 1 + 33;
switch (fragment) {
case Fragment::JUST_0: return {{}, 0};
case Fragment::JUST_1:
case Fragment::OLDER:
case Fragment::AFTER: return {0, {}};
- case Fragment::PK_K: return {1 + 72, 1};
- case Fragment::PK_H: return {1 + 72 + 1 + 33, 1 + 1 + 33};
+ case Fragment::PK_K: return {sig_size, 1};
+ case Fragment::PK_H: return {sig_size + pubkey_size, 1 + pubkey_size};
case Fragment::SHA256:
case Fragment::RIPEMD160:
case Fragment::HASH256:
@@ -1131,8 +1133,8 @@ private:
case Fragment::OR_C: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), {}};
case Fragment::OR_D: return {subs[0]->ws.sat | (subs[0]->ws.dsat + subs[1]->ws.sat), subs[0]->ws.dsat + subs[1]->ws.dsat};
case Fragment::OR_I: return {(subs[0]->ws.sat + 1 + 1) | (subs[1]->ws.sat + 1), (subs[0]->ws.dsat + 1 + 1) | (subs[1]->ws.dsat + 1)};
- case Fragment::MULTI: return {k * (1 + 72) + 1, k + 1};
- case Fragment::MULTI_A: return {k * (1 + 65) + static_cast<uint32_t>(keys.size()) - k, static_cast<uint32_t>(keys.size())};
+ case Fragment::MULTI: return {k * sig_size + 1, k + 1};
+ case Fragment::MULTI_A: return {k * sig_size + static_cast<uint32_t>(keys.size()) - k, static_cast<uint32_t>(keys.size())};
case Fragment::WRAP_A:
case Fragment::WRAP_N:
case Fragment::WRAP_S:
diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp
deleted file mode 100644
index 943595f8a4..0000000000
--- a/src/test/fuzz/i2p.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2020-2022 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 <common/args.h>
-#include <i2p.h>
-#include <netaddress.h>
-#include <netbase.h>
-#include <test/fuzz/FuzzedDataProvider.h>
-#include <test/fuzz/fuzz.h>
-#include <test/fuzz/util.h>
-#include <test/fuzz/util/net.h>
-#include <test/util/setup_common.h>
-#include <util/threadinterrupt.h>
-
-void initialize_i2p()
-{
- static const auto testing_setup = MakeNoLogFileContext<>();
-}
-
-FUZZ_TARGET(i2p, .init = initialize_i2p)
-{
- FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
-
- // Mock CreateSock() to create FuzzedSock.
- auto CreateSockOrig = CreateSock;
- CreateSock = [&fuzzed_data_provider](const CService&) {
- return std::make_unique<FuzzedSock>(fuzzed_data_provider);
- };
-
- const CService sam_proxy;
- CThreadInterrupt interrupt;
-
- i2p::sam::Session sess{gArgs.GetDataDirNet() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
-
- i2p::Connection conn;
-
- if (sess.Listen(conn)) {
- if (sess.Accept(conn)) {
- try {
- (void)conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
- } catch (const std::runtime_error&) {
- }
- }
- }
-
- const CService to;
- bool proxy_error;
-
- if (sess.Connect(to, conn, proxy_error)) {
- try {
- conn.sock->SendComplete("verack\n", 10ms, interrupt);
- } catch (const std::runtime_error&) {
- }
- }
-
- CreateSock = CreateSockOrig;
-}
diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp
index 1c4596b6fc..8c73edfa9d 100644
--- a/src/test/fuzz/miniscript.cpp
+++ b/src/test/fuzz/miniscript.cpp
@@ -70,6 +70,7 @@ struct TestData {
sig.push_back(1); // SIGHASH_ALL
dummy_sigs.insert({pubkey, {sig, i & 1}});
assert(privkey.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX));
+ schnorr_sig.push_back(1); // Maximally-sized signature has sighash byte
schnorr_sigs.emplace(XOnlyPubKey{pubkey}, std::make_pair(std::move(schnorr_sig), i & 1));
std::vector<unsigned char> hash;
@@ -113,7 +114,9 @@ struct TestData {
struct ParserContext {
typedef CPubKey Key;
- MsCtx script_ctx{MsCtx::P2WSH};
+ const MsCtx script_ctx;
+
+ constexpr ParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {}
bool KeyCompare(const Key& a, const Key& b) const {
return a < b;
@@ -178,11 +181,13 @@ struct ParserContext {
MsCtx MsContext() const {
return script_ctx;
}
-} PARSER_CTX;
+};
//! Context that implements naive conversion from/to script only, for roundtrip testing.
struct ScriptParserContext {
- MsCtx script_ctx{MsCtx::P2WSH};
+ const MsCtx script_ctx;
+
+ constexpr ScriptParserContext(MsCtx ctx) noexcept : script_ctx(ctx) {}
//! For Script roundtrip we never need the key from a key hash.
struct Key {
@@ -228,10 +233,13 @@ struct ScriptParserContext {
MsCtx MsContext() const {
return script_ctx;
}
-} SCRIPT_PARSER_CONTEXT;
+};
//! Context to produce a satisfaction for a Miniscript node using the pre-computed data.
-struct SatisfierContext: ParserContext {
+struct SatisfierContext : ParserContext {
+
+ constexpr SatisfierContext(MsCtx ctx) noexcept : ParserContext(ctx) {}
+
// Timelock challenges satisfaction. Make the value (deterministically) vary to explore different
// paths.
bool CheckAfter(uint32_t value) const { return value % 2; }
@@ -267,12 +275,10 @@ struct SatisfierContext: ParserContext {
miniscript::Availability SatHASH160(const std::vector<unsigned char>& hash, std::vector<unsigned char>& preimage) const {
return LookupHash(hash, preimage, TEST_DATA.hash160_preimages);
}
-} SATISFIER_CTX;
+};
//! Context to check a satisfaction against the pre-computed data.
-struct CheckerContext: BaseSignatureChecker {
- TestData *test_data;
-
+const struct CheckerContext: BaseSignatureChecker {
// Signature checker methods. Checks the right dummy signature is used.
bool CheckECDSASignature(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& vchPubKey,
const CScript& scriptCode, SigVersion sigversion) const override
@@ -294,7 +300,7 @@ struct CheckerContext: BaseSignatureChecker {
} CHECKER_CTX;
//! Context to check for duplicates when instancing a Node.
-struct KeyComparator {
+const struct KeyComparator {
bool KeyCompare(const CPubKey& a, const CPubKey& b) const {
return a < b;
}
@@ -1027,15 +1033,15 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
if (!node) return;
// Check that it roundtrips to text representation
- PARSER_CTX.script_ctx = script_ctx;
- std::optional<std::string> str{node->ToString(PARSER_CTX)};
+ const ParserContext parser_ctx{script_ctx};
+ std::optional<std::string> str{node->ToString(parser_ctx)};
assert(str);
- auto parsed = miniscript::FromString(*str, PARSER_CTX);
+ auto parsed = miniscript::FromString(*str, parser_ctx);
assert(parsed);
assert(*parsed == *node);
// Check consistency between script size estimation and real size.
- auto script = node->ToScript(PARSER_CTX);
+ auto script = node->ToScript(parser_ctx);
assert(node->ScriptSize() == script.size());
// Check consistency of "x" property with the script (type K is excluded, because it can end
@@ -1049,12 +1055,12 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
if (!node->IsValidTopLevel()) return;
// Check roundtrip to script
- auto decoded = miniscript::FromScript(script, PARSER_CTX);
+ auto decoded = miniscript::FromScript(script, parser_ctx);
assert(decoded);
// Note we can't use *decoded == *node because the miniscript representation may differ, so we check that:
// - The script corresponding to that decoded form matches exactly
// - The type matches exactly
- assert(decoded->ToScript(PARSER_CTX) == script);
+ assert(decoded->ToScript(parser_ctx) == script);
assert(decoded->GetType() == node->GetType());
// Optionally pad the script or the witness in order to increase the sensitivity of the tests of
@@ -1091,7 +1097,7 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
}
}
- SATISFIER_CTX.script_ctx = script_ctx;
+ const SatisfierContext satisfier_ctx{script_ctx};
// Get the ScriptPubKey for this script, filling spend data if it's Taproot.
TaprootBuilder builder;
@@ -1099,11 +1105,11 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
// Run malleable satisfaction algorithm.
std::vector<std::vector<unsigned char>> stack_mal;
- const bool mal_success = node->Satisfy(SATISFIER_CTX, stack_mal, false) == miniscript::Availability::YES;
+ const bool mal_success = node->Satisfy(satisfier_ctx, stack_mal, false) == miniscript::Availability::YES;
// Run non-malleable satisfaction algorithm.
std::vector<std::vector<unsigned char>> stack_nonmal;
- const bool nonmal_success = node->Satisfy(SATISFIER_CTX, stack_nonmal, true) == miniscript::Availability::YES;
+ const bool nonmal_success = node->Satisfy(satisfier_ctx, stack_nonmal, true) == miniscript::Availability::YES;
if (nonmal_success) {
// Non-malleable satisfactions are bounded by the satisfaction size plus:
@@ -1114,6 +1120,9 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p
// If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it.
assert(mal_success);
assert(stack_nonmal == stack_mal);
+ // Compute witness size (excluding script push, control block, and witness count encoding).
+ const size_t wit_size = GetSerializeSize(stack_nonmal, PROTOCOL_VERSION) - GetSizeOfCompactSize(stack_nonmal.size());
+ assert(wit_size <= *node->GetWitnessSize());
// Test non-malleable satisfaction.
witness_nonmal.stack.insert(witness_nonmal.stack.end(), std::make_move_iterator(stack_nonmal.begin()), std::make_move_iterator(stack_nonmal.end()));
@@ -1229,13 +1238,13 @@ FUZZ_TARGET(miniscript_string, .init = FuzzInit)
if (buffer.empty()) return;
FuzzedDataProvider provider(buffer.data(), buffer.size());
auto str = provider.ConsumeBytesAsString(provider.remaining_bytes() - 1);
- PARSER_CTX.script_ctx = (MsCtx)provider.ConsumeBool();
- auto parsed = miniscript::FromString(str, PARSER_CTX);
+ const ParserContext parser_ctx{(MsCtx)provider.ConsumeBool()};
+ auto parsed = miniscript::FromString(str, parser_ctx);
if (!parsed) return;
- const auto str2 = parsed->ToString(PARSER_CTX);
+ const auto str2 = parsed->ToString(parser_ctx);
assert(str2);
- auto parsed2 = miniscript::FromString(*str2, PARSER_CTX);
+ auto parsed2 = miniscript::FromString(*str2, parser_ctx);
assert(parsed2);
assert(*parsed == *parsed2);
}
@@ -1247,9 +1256,9 @@ FUZZ_TARGET(miniscript_script)
const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
if (!script) return;
- SCRIPT_PARSER_CONTEXT.script_ctx = (MsCtx)fuzzed_data_provider.ConsumeBool();
- const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT);
+ const ScriptParserContext script_parser_ctx{(MsCtx)fuzzed_data_provider.ConsumeBool()};
+ const auto ms = miniscript::FromScript(*script, script_parser_ctx);
if (!ms) return;
- assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script);
+ assert(ms->ToScript(script_parser_ctx) == *script);
}
diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp
index d23e997719..5a286c05d2 100644
--- a/src/test/fuzz/util/net.cpp
+++ b/src/test/fuzz/util/net.cpp
@@ -36,7 +36,11 @@ CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
} else if (network == Network::NET_IPV6) {
if (fuzzed_data_provider.remaining_bytes() >= 16) {
in6_addr v6_addr = {};
- memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
+ auto addr_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(16);
+ if (addr_bytes[0] == CJDNS_PREFIX) { // Avoid generating IPv6 addresses that look like CJDNS.
+ addr_bytes[0] = 0x55; // Just an arbitrary number, anything != CJDNS_PREFIX would do.
+ }
+ memcpy(v6_addr.s6_addr, addr_bytes.data(), 16);
net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
}
} else if (network == Network::NET_INTERNAL) {
diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp
index b2e1ae43be..5b8b0e9215 100644
--- a/src/test/i2p_tests.cpp
+++ b/src/test/i2p_tests.cpp
@@ -16,14 +16,34 @@
#include <memory>
#include <string>
-BOOST_FIXTURE_TEST_SUITE(i2p_tests, BasicTestingSetup)
+/// Save the log level and the value of CreateSock and restore them when the test ends.
+class EnvTestingSetup : public BasicTestingSetup
+{
+public:
+ explicit EnvTestingSetup(const ChainType chainType = ChainType::MAIN,
+ const std::vector<const char*>& extra_args = {})
+ : BasicTestingSetup{chainType, extra_args},
+ m_prev_log_level{LogInstance().LogLevel()},
+ m_create_sock_orig{CreateSock}
+ {
+ LogInstance().SetLogLevel(BCLog::Level::Trace);
+ }
+
+ ~EnvTestingSetup()
+ {
+ CreateSock = m_create_sock_orig;
+ LogInstance().SetLogLevel(m_prev_log_level);
+ }
+
+private:
+ const BCLog::Level m_prev_log_level;
+ const std::function<std::unique_ptr<Sock>(const CService&)> m_create_sock_orig;
+};
+
+BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
BOOST_AUTO_TEST_CASE(unlimited_recv)
{
- const auto prev_log_level{LogInstance().LogLevel()};
- LogInstance().SetLogLevel(BCLog::Level::Trace);
- auto CreateSockOrig = CreateSock;
-
// Mock CreateSock() to create MockSock.
CreateSock = [](const CService&) {
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
@@ -40,9 +60,69 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
bool proxy_error;
BOOST_REQUIRE(!session.Connect(CService{}, conn, proxy_error));
}
+}
+
+BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
+{
+ size_t num_sockets{0};
+ CreateSock = [&num_sockets](const CService&) {
+ // clang-format off
+ ++num_sockets;
+ // First socket is the control socket for creating the session.
+ if (num_sockets == 1) {
+ return std::make_unique<StaticContentsSock>(
+ // reply to HELLO
+ "HELLO REPLY RESULT=OK VERSION=3.1\n"
+ // reply to DEST GENERATE
+ "DEST REPLY PUB=WnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqLE4SD-yjT48UNI7qiTUfIPiDitCoiTTz2cr4QGfw89rBQAEAAcAAA== PRIV=WnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqLE4SD-yjT48UNI7qiTUfIPiDitCoiTTz2cr4QGfw89rBQAEAAcAAOvuCIKTyv5f~1QgGq7XQl-IqBULTB5WzB3gw5yGPtd1p0AeoADrq1ccZggLPQ4ZLUsGK-HVw373rcTfvxrcuwenqVjiN4tbbYLWtP7xXGWj6fM6HyORhU63GphrjEePpMUHDHXd3o7pWGM-ieVVQSK~1MzF9P93pQWI3Do52EeNAayz4HbpPjNhVBzG1hUEFwznfPmUZBPuaOR4-uBm1NEWEuONlNOCctE4-U0Ukh94z-Qb55U5vXjR5G4apmBblr68t6Wm1TKlzpgFHzSqLryh3stWqrOKY1H0z9eZ2z1EkHFOpD5LyF6nf51e-lV7HLMl44TYzoEHK8RRVodtLcW9lacVdBpv~tOzlZERIiDziZODPETENZMz5oy9DQ7UUw==\n"
+ // reply to SESSION CREATE
+ "SESSION STATUS RESULT=OK\n"
+ // dummy to avoid reporting EOF on the socket
+ "a"
+ );
+ }
+ // Subsequent sockets are for recreating the session or for listening and accepting incoming connections.
+ if (num_sockets % 2 == 0) {
+ // Replies to Listen() and Accept()
+ return std::make_unique<StaticContentsSock>(
+ // reply to HELLO
+ "HELLO REPLY RESULT=OK VERSION=3.1\n"
+ // reply to STREAM ACCEPT
+ "STREAM STATUS RESULT=OK\n"
+ // continued reply to STREAM ACCEPT, violating the protocol described at
+ // https://geti2p.net/en/docs/api/samv3#Accept%20Response
+ // should be base64, something like
+ // "IchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLlSreVaCuCS5sdb-8ToWULWP7kt~lRPDeUNxQMq3cRSBBQAEAAcAAA==\n"
+ "STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"Session was closed\"\n"
+ );
+ } else {
+ // Another control socket, but without creating a destination (it is cached in the session).
+ return std::make_unique<StaticContentsSock>(
+ // reply to HELLO
+ "HELLO REPLY RESULT=OK VERSION=3.1\n"
+ // reply to SESSION CREATE
+ "SESSION STATUS RESULT=OK\n"
+ // dummy to avoid reporting EOF on the socket
+ "a"
+ );
+ }
+ // clang-format on
+ };
- CreateSock = CreateSockOrig;
- LogInstance().SetLogLevel(prev_log_level);
+ CThreadInterrupt interrupt;
+ i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
+ CService{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656},
+ &interrupt);
+
+ i2p::Connection conn;
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_DEBUG_LOG("Creating persistent SAM session");
+ ASSERT_DEBUG_LOG("Persistent SAM session" /* ... created */);
+ ASSERT_DEBUG_LOG("Error accepting");
+ ASSERT_DEBUG_LOG("Destroying SAM session");
+ BOOST_REQUIRE(session.Listen(conn));
+ BOOST_REQUIRE(!session.Accept(conn));
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp
index 5c70678fd1..996c379962 100644
--- a/src/test/miniscript_tests.cpp
+++ b/src/test/miniscript_tests.cpp
@@ -77,6 +77,7 @@ struct TestData {
sig.push_back(1); // sighash byte
signatures.emplace(pubkey, sig);
BOOST_CHECK(key.SignSchnorr(MESSAGE_HASH, schnorr_sig, nullptr, EMPTY_AUX));
+ schnorr_sig.push_back(1); // Maximally sized Schnorr sigs have a sighash byte.
schnorr_signatures.emplace(XOnlyPubKey{pubkey}, schnorr_sig);
// Compute various hashes
@@ -126,7 +127,9 @@ typedef std::pair<ChallengeType, uint32_t> Challenge;
struct KeyConverter {
typedef CPubKey Key;
- miniscript::MiniscriptContext m_script_ctx{miniscript::MiniscriptContext::P2WSH};
+ const miniscript::MiniscriptContext m_script_ctx;
+
+ constexpr KeyConverter(miniscript::MiniscriptContext ctx) noexcept : m_script_ctx{ctx} {}
bool KeyCompare(const Key& a, const Key& b) const {
return a < b;
@@ -189,14 +192,13 @@ struct KeyConverter {
miniscript::MiniscriptContext MsContext() const {
return m_script_ctx;
}
-
- void SetContext(miniscript::MiniscriptContext ctx) {
- m_script_ctx = ctx;
- }
};
/** A class that encapsulates all signing/hash revealing operations. */
struct Satisfier : public KeyConverter {
+
+ Satisfier(miniscript::MiniscriptContext ctx) noexcept : KeyConverter{ctx} {}
+
//! Which keys/timelocks/hash preimages are available.
std::set<Challenge> supported;
@@ -286,9 +288,6 @@ public:
}
};
-//! Singleton instance of KeyConverter.
-KeyConverter CONVERTER;
-
//! Public key to be used as internal key for dummy Taproot spends.
const std::vector<unsigned char> NUMS_PK{ParseHex("50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0")};
@@ -350,8 +349,7 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
std::vector<Challenge> challist(challenges.begin(), challenges.end());
for (int iter = 0; iter < 3; ++iter) {
Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
- Satisfier satisfier;
- satisfier.SetContext(converter.MsContext());
+ Satisfier satisfier(converter.MsContext());
TestSignatureChecker checker(satisfier);
bool prev_mal_success = false, prev_nonmal_success = false;
// Go over all challenges involved in this miniscript in random order.
@@ -370,6 +368,8 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
// Run non-malleable satisfaction algorithm.
CScriptWitness witness_nonmal;
const bool nonmal_success = node->Satisfy(satisfier, witness_nonmal.stack, true) == miniscript::Availability::YES;
+ // Compute witness size (excluding script push, control block, and witness count encoding).
+ const size_t wit_size = GetSerializeSize(witness_nonmal.stack, PROTOCOL_VERSION) - GetSizeOfCompactSize(witness_nonmal.stack.size());
SatisfactionToWitness(converter.MsContext(), witness_nonmal, script, builder);
if (nonmal_success) {
@@ -381,6 +381,7 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
// If a non-malleable satisfaction exists, the malleable one must also exist, and be identical to it.
BOOST_CHECK(mal_success);
BOOST_CHECK(witness_nonmal.stack == witness_mal.stack);
+ assert(wit_size <= *node->GetWitnessSize());
// Test non-malleable satisfaction.
ScriptError serror;
@@ -473,14 +474,23 @@ void Test(const std::string& ms, const std::string& hexscript, int mode, const K
}
void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode,
- int opslimit = -1, int stacklimit = -1, std::optional<uint32_t> max_wit_size = std::nullopt,
- std::optional<uint32_t> stack_exec = {})
+ int opslimit, int stacklimit, std::optional<uint32_t> max_wit_size,
+ std::optional<uint32_t> max_tap_wit_size,
+ std::optional<uint32_t> stack_exec)
+{
+ KeyConverter wsh_converter(miniscript::MiniscriptContext::P2WSH);
+ Test(ms, hexscript, mode, wsh_converter, opslimit, stacklimit, max_wit_size, stack_exec);
+ KeyConverter tap_converter(miniscript::MiniscriptContext::TAPSCRIPT);
+ Test(ms, hextapscript == "=" ? hexscript : hextapscript, mode, tap_converter, opslimit, stacklimit, max_tap_wit_size, stack_exec);
+}
+
+void Test(const std::string& ms, const std::string& hexscript, const std::string& hextapscript, int mode)
{
- CONVERTER.SetContext(miniscript::MiniscriptContext::P2WSH);
- Test(ms, hexscript, mode, CONVERTER, opslimit, stacklimit, max_wit_size, stack_exec);
- CONVERTER.SetContext(miniscript::MiniscriptContext::TAPSCRIPT);
- Test(ms, hextapscript == "=" ? hexscript : hextapscript, mode, CONVERTER, opslimit, stacklimit, max_wit_size, stack_exec);
+ Test(ms, hexscript, hextapscript, mode,
+ /*opslimit=*/-1, /*stacklimit=*/-1,
+ /*max_wit_size=*/std::nullopt, /*max_tap_wit_size=*/std::nullopt, /*stack_exec=*/std::nullopt);
}
+
} // namespace
BOOST_FIXTURE_TEST_SUITE(miniscript_tests, BasicTestingSetup)
@@ -545,53 +555,54 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
Test("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_k
Test("pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", "76a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac", "76a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG); // alias to c:pk_h
-
// Randomly generated test set that covers the majority of type and node type combinations
- Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", "=", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3, 3);
- Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 2 + 1 + 2 * 73, 7);
- Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", "?", TESTMODE_VALID | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 1 + 2 * 73 + 2, 8);
- Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", "=", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2, 2);
- Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33, 4);
- Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 13, 5, 1 + 3 * 73, 10);
- Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 15, 7, 2 + 1 + 3 * 73 + 1, 10);
- Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33, 3);
- Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 11, 5, 2 + 1 + 2 * 73 + 33, 8);
- Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 4, 1 + 2 * 73 + 2, 8);
- Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", "=", TESTMODE_VALID, 12, 1, 33, 4);
- Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", "=", TESTMODE_VALID, 16, 2, 33 + 33, 4);
- Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", "=", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33, 4);
- Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 13, 6, 1 + 2 * 73 + 1 + 73 + 1, 10);
- Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", "=", TESTMODE_VALID, 14, 2, 33 + 2, 4);
- Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", "=", TESTMODE_VALID, 15, 2, 1 + 33, 3);
- Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 8, 2, 33 + 73, 4);
- Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 10, 5, 1 + 2 * 73 + 73, 9);
- Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", "=", TESTMODE_VALID, 14, 2, 33 + 33, 4);
- Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", "=", TESTMODE_VALID, 20, 2, 33 + 33, 4);
- Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", "630320a107b16920c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73, 3);
- Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", "76a9141a7ac36cfa8431ab2395d701b0050045ae4a37d188ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33, 6);
- Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b26920fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73, 5);
- Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1, 3);
- Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", "6360b26976a9144d4421361c3289bdad06441ffaee8be8e786f1ad886776a91460d4a7bcbd08f58e58bd208d1069837d7adb16ae8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73, 4);
- Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", "76a91421ab1a140d0d305b8ff62bdb887d9fef82c9899e88ac7364204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73, 5);
- Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914a63d1e4d2ed109246c600ec8c19cce546b65b1cc886776a9144d4421361c3289bdad06441ffaee8be8e786f1ad8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73, 5);
- Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914ceedcb44b38bdbcb614d872223964fd3dca8a434886776a914f678d9b79045452c8c64e9309d0f0046056e26c588686776a914a2a75e1819afa208f6c89ae0da43021116dfcb0c8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73, 5);
- Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", "6376a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac6476a9149b652a14674a506079f574d20ca7daef6f9a66bb886776a914ceedcb44b38bdbcb614d872223964fd3dca8a43488686720d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73, 6);
- Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2, 4);
- Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2, 5);
+ Test("lltvln:after(1231488000)", "6300676300676300670400046749b1926869516868", "=", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 3, 3, 3);
+ Test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 2 + 1 + 2 * 73, 0, 7);
+ Test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", "?", TESTMODE_VALID | TESTMODE_TAPSCRIPT_INVALID, 14, 5, 2 + 1 + 2 * 73 + 2, 0, 8);
+ Test("j:and_v(vdv:after(1567547623),older(2016))", "829263766304e7e06e5db169686902e007b268", "=", TESTMODE_VALID | TESTMODE_NONMAL, 11, 1, 2, 2, 2);
+ Test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", TESTMODE_VALID | TESTMODE_NONMAL, 12, 3, 2 + 33 + 33, 2 + 33 + 33, 4);
+ Test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 13, 5, 1 + 3 * 73, 0, 10);
+ Test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TAPSCRIPT_INVALID, 15, 7, 2 + 1 + 3 * 73 + 1, 0, 10);
+ Test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", TESTMODE_VALID, 16, 1, 33, 33, 3);
+ Test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 11, 5, 2 + 1 + 2 * 73 + 33, 0, 8);
+ Test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 14, 4, 1 + 2 * 73 + 2, 0, 8);
+ Test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", "=", TESTMODE_VALID, 12, 1, 33, 33, 4);
+ Test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", "=", TESTMODE_VALID, 16, 2, 33 + 33, 33 + 33, 4);
+ Test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", "=", TESTMODE_VALID | TESTMODE_NONMAL, 15, 2, 33 + 33, 33 + 33, 4);
+ Test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 13, 6, 1 + 2 * 73 + 1 + 73 + 1, 0, 10);
+ Test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", "=", TESTMODE_VALID, 14, 2, 33 + 2, 33 + 2, 4);
+ Test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", "=", TESTMODE_VALID, 15, 2, 1 + 33, 1 + 33, 3);
+ Test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", "?", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 8, 2, 33 + 73, 0, 4);
+ Test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TAPSCRIPT_INVALID, 10, 5, 1 + 2 * 73 + 73, 0, 9);
+ Test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", "=", TESTMODE_VALID, 14, 2, 33 + 33, 33 + 33, 4);
+ Test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", "=", TESTMODE_VALID, 20, 2, 33 + 33, 33 + 33, 4);
+ Test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", "630320a107b16920c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", TESTMODE_VALID | TESTMODE_NONMAL, 10, 2, 2 + 73, 2 + 66, 3);
+ Test("thresh(2,c:pk_h(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a9145dedfbf9ea599dd4e3ca6a80b333c472fd0b3f6988ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", "76a9141a7ac36cfa8431ab2395d701b0050045ae4a37d188ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", TESTMODE_VALID, 18, 4, 1 + 34 + 33 + 33, 1 + 33 + 33 + 33, 6);
+ Test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b26920fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", TESTMODE_VALID | TESTMODE_NEEDSIG, 13, 3, 33 + 2 + 73, 33 + 2 + 66, 5);
+ Test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", "20daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_TIMELOCKMIX, 12, 2, 73 + 1, 66 + 1, 3);
+ Test("c:or_i(and_v(v:older(16),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)),pk_h(026a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4))", "6360b26976a9149fc5dbe5efdce10374a4dd4053c93af540211718886776a9142fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f8868ac", "6360b26976a9144d4421361c3289bdad06441ffaee8be8e786f1ad886776a91460d4a7bcbd08f58e58bd208d1069837d7adb16ae8868ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 12, 3, 2 + 34 + 73, 2 + 33 + 66, 4);
+ Test("or_d(c:pk_h(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", "76a91421ab1a140d0d305b8ff62bdb887d9fef82c9899e88ac7364204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", TESTMODE_VALID | TESTMODE_NONMAL, 13, 3, 1 + 34 + 73, 1 + 33 + 66, 5);
+ Test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914dd100be7d9aea5721158ebde6d6a1fd8fff93bb1886776a9149fc5dbe5efdce10374a4dd4053c93af5402117188868ac", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a914a63d1e4d2ed109246c600ec8c19cce546b65b1cc886776a9144d4421361c3289bdad06441ffaee8be8e786f1ad8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 18, 3, 33 + 34 + 73, 33 + 33 + 66, 5);
+ Test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a9149652d86bedf43ad264362e6e6eba6eb764508127886776a914751e76e8199196d454941c45d1b3a323f1433bd688686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914ceedcb44b38bdbcb614d872223964fd3dca8a434886776a914f678d9b79045452c8c64e9309d0f0046056e26c588686776a914a2a75e1819afa208f6c89ae0da43021116dfcb0c8868ac", TESTMODE_VALID | TESTMODE_NEEDSIG, 23, 4, 2 + 33 + 34 + 73, 2 + 33 + 33 + 66, 5);
+ Test("c:or_i(andor(c:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk_h(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01),pk_h(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", "6376a914fcd35ddacad9f2d5be5e464639441c6065e6955d88ac6476a91406afd46bcdfd22ef94ac122aa11f241244a37ecc886776a9149652d86bedf43ad264362e6e6eba6eb7645081278868672102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", "6376a914fd1690c37fa3b0f04395ddc9415b220ab1ccc59588ac6476a9149b652a14674a506079f574d20ca7daef6f9a66bb886776a914ceedcb44b38bdbcb614d872223964fd3dca8a43488686720d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e68ac", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG, 17, 5, 2 + 34 + 73 + 34 + 73, 2 + 33 + 66 + 33 + 66, 6);
+ Test("thresh(1,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670400ca9a3bb16951686c936b6300670164b16951686c935187", TESTMODE_VALID, 18, 3, 73 + 2 + 2, 66 + 2 + 2, 4);
+ Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),altv:after(1000000000),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac6c936b6300670400ca9a3bb16951686c936b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_TIMELOCKMIX, 22, 4, 73 + 73 + 2 + 2, 66 + 66 + 2 + 2, 5);
// Additional Tapscript-related tests
// Edge cases when parsing multi_a from script:
// - no pubkey at all
// - no pubkey before a CHECKSIGADD
// - no pubkey before the CHECKSIG
+ constexpr KeyConverter tap_converter{miniscript::MiniscriptContext::TAPSCRIPT};
+ constexpr KeyConverter wsh_converter{miniscript::MiniscriptContext::P2WSH};
const auto no_pubkey{ParseHex("ac519c")};
- BOOST_CHECK(miniscript::FromScript({no_pubkey.begin(), no_pubkey.end()}, CONVERTER) == nullptr);
+ BOOST_CHECK(miniscript::FromScript({no_pubkey.begin(), no_pubkey.end()}, tap_converter) == nullptr);
const auto incomplete_multi_a{ParseHex("ba20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")};
- BOOST_CHECK(miniscript::FromScript({incomplete_multi_a.begin(), incomplete_multi_a.end()}, CONVERTER) == nullptr);
+ BOOST_CHECK(miniscript::FromScript({incomplete_multi_a.begin(), incomplete_multi_a.end()}, tap_converter) == nullptr);
const auto incomplete_multi_a_2{ParseHex("ac2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba519c")};
- BOOST_CHECK(miniscript::FromScript({incomplete_multi_a_2.begin(), incomplete_multi_a_2.end()}, CONVERTER) == nullptr);
+ BOOST_CHECK(miniscript::FromScript({incomplete_multi_a_2.begin(), incomplete_multi_a_2.end()}, tap_converter) == nullptr);
// Can use multi_a under Tapscript but not P2WSH.
- Test("and_v(v:multi_a(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "?", "20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aac205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba529d0400046749b1", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4, 2, {}, 3);
+ Test("and_v(v:multi_a(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "?", "20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aac205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba529d0400046749b1", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4, 2, {}, {}, 3);
// Can use more than 20 keys in a multi_a.
std::string ms_str_multi_a{"multi_a(1,"};
for (size_t i = 0; i < 21; ++i) {
@@ -599,9 +610,9 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
if (i < 20) ms_str_multi_a += ",";
}
ms_str_multi_a += ")";
- Test(ms_str_multi_a, "?", "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba20f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9ba20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ba202f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4ba20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ba205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcba202f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ba20acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeba20a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ba20774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cbba20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aba20f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8ba20499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4ba20d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080eba20e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0aba20defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34ba205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba202b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6cba204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ba20352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5ba519c", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 22, 21, {}, 22);
+ Test(ms_str_multi_a, "?", "2079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac20c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ba20f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9ba20e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13ba202f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4ba20fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ba205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcba202f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ba20acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeba20a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ba20774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cbba20d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85aba20f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8ba20499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4ba20d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080eba20e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0aba20defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34ba205601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7ccba202b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6cba204ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ba20352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5ba519c", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 22, 21, {}, {}, 22);
// Since 'd:' is 'u' we can use it directly inside a thresh. But we can't under P2WSH.
- Test("thresh(2,dv:older(42),s:pk(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "7663012ab269687c205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcac937c20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 12, 3, {}, 4);
+ Test("thresh(2,dv:older(42),s:pk(025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", "?", "7663012ab269687c205cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bcac937c20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac935287", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 12, 3, {}, {}, 4);
// We can have a script that has more than 201 ops (n = 99), that needs a stack size > 100 (n = 110), or has a
// script that is larger than 3600 bytes (n = 200). All that can't be under P2WSH.
for (const auto pk_count: {99, 110, 200}) {
@@ -611,7 +622,7 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
}
ms_str_large += "pk(" + HexStr(g_testdata->pubkeys[pk_count - 1]) + ")";
ms_str_large.insert(ms_str_large.end(), pk_count - 1, ')');
- Test(ms_str_large, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, pk_count + (pk_count - 1) * 3, pk_count, {}, pk_count + 1);
+ Test(ms_str_large, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, pk_count + (pk_count - 1) * 3, pk_count, {}, {}, pk_count + 1);
}
// We can have a script that reaches a stack size of 1000 during execution.
std::string ms_stack_limit;
@@ -621,25 +632,27 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
}
ms_stack_limit += "pk(" + HexStr(g_testdata->pubkeys[0]) + ")";
ms_stack_limit.insert(ms_stack_limit.end(), count, ')');
- const auto ms_stack_ok{miniscript::FromString(ms_stack_limit, CONVERTER)};
+ const auto ms_stack_ok{miniscript::FromString(ms_stack_limit, tap_converter)};
BOOST_CHECK(ms_stack_ok && ms_stack_ok->CheckStackSize());
- Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, 1 + count + 1);
+ Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1);
// But one more element on the stack during execution will make it fail. And we'd detect that.
count++;
ms_stack_limit = "and_b(older(1),a:" + ms_stack_limit + ")";
- const auto ms_stack_nok{miniscript::FromString(ms_stack_limit, CONVERTER)};
+ const auto ms_stack_nok{miniscript::FromString(ms_stack_limit, tap_converter)};
BOOST_CHECK(ms_stack_nok && !ms_stack_nok->CheckStackSize());
- Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, 1 + count + 1);
+ Test(ms_stack_limit, "?", "?", TESTMODE_VALID | TESTMODE_NONMAL | TESTMODE_NEEDSIG | TESTMODE_P2WSH_INVALID, 4 * count + 1, 1, {}, {}, 1 + count + 1);
// Misc unit tests
// A Script with a non minimal push is invalid
std::vector<unsigned char> nonminpush = ParseHex("0000210232780000feff00ffffffffffff21ff005f00ae21ae00000000060602060406564c2102320000060900fe00005f00ae21ae00100000060606060606000000000000000000000000000000000000000000000000000000000000000000");
const CScript nonminpush_script(nonminpush.begin(), nonminpush.end());
- BOOST_CHECK(miniscript::FromScript(nonminpush_script, CONVERTER) == nullptr);
+ BOOST_CHECK(miniscript::FromScript(nonminpush_script, wsh_converter) == nullptr);
+ BOOST_CHECK(miniscript::FromScript(nonminpush_script, tap_converter) == nullptr);
// A non-minimal VERIFY (<key> CHECKSIG VERIFY 1)
std::vector<unsigned char> nonminverify = ParseHex("2103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7ac6951");
const CScript nonminverify_script(nonminverify.begin(), nonminverify.end());
- BOOST_CHECK(miniscript::FromScript(nonminverify_script, CONVERTER) == nullptr);
+ BOOST_CHECK(miniscript::FromScript(nonminverify_script, wsh_converter) == nullptr);
+ BOOST_CHECK(miniscript::FromScript(nonminverify_script, tap_converter) == nullptr);
// A threshold as large as the number of subs is valid.
Test("thresh(2,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),altv:after(100))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", "20d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac6b6300670164b16951686c935287", TESTMODE_VALID | TESTMODE_NEEDSIG | TESTMODE_NONMAL);
// A threshold of 1 is valid.
@@ -649,8 +662,7 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
// A threshold with a k null is invalid
Test("thresh(0,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "2103d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65ac7c2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac935187", "=", TESTMODE_INVALID);
// For CHECKMULTISIG the OP cost is the number of keys, but the stack size is the number of sigs (+1)
- CONVERTER.SetContext(miniscript::MiniscriptContext::P2WSH);
- const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", CONVERTER);
+ const auto ms_multi = miniscript::FromString("multi(1,03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)", wsh_converter);
BOOST_CHECK(ms_multi);
BOOST_CHECK_EQUAL(*ms_multi->GetOps(), 4); // 3 pubkeys + CMS
BOOST_CHECK_EQUAL(*ms_multi->GetStackSize(), 2); // 1 sig + dummy elem
@@ -661,33 +673,33 @@ BOOST_AUTO_TEST_CASE(fixed_tests)
// Unfortunately, this rule is consensus for Taproot but only policy for P2WSH. Therefore we can't
// (for now) have 'd:' be 'u'. This tests we can't use a 'd:' wrapper for a thresh, which requires
// its subs to all be 'u' (taken from https://github.com/rust-bitcoin/rust-miniscript/discussions/341).
- const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", CONVERTER);
+ const auto ms_minimalif = miniscript::FromString("thresh(3,c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),sc:pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),sc:pk_k(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798),sdv:older(32))", wsh_converter);
BOOST_CHECK(ms_minimalif && !ms_minimalif->IsValid());
// A Miniscript with duplicate keys is not sane
- const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ const auto ms_dup1 = miniscript::FromString("and_v(v:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter);
BOOST_CHECK(ms_dup1);
BOOST_CHECK(!ms_dup1->IsSane() && !ms_dup1->CheckDuplicateKey());
// Same with a disjunction, and different key nodes (pk and pkh)
- const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", CONVERTER);
+ const auto ms_dup2 = miniscript::FromString("or_b(c:pk_k(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),ac:pk_h(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65))", wsh_converter);
BOOST_CHECK(ms_dup2 && !ms_dup2->IsSane() && !ms_dup2->CheckDuplicateKey());
// Same when the duplicates are leaves or a larger tree
- const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ const auto ms_dup3 = miniscript::FromString("or_i(and_b(pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556)),and_b(older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter);
BOOST_CHECK(ms_dup3 && !ms_dup3->IsSane() && !ms_dup3->CheckDuplicateKey());
// Same when the duplicates are on different levels in the tree
- const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", CONVERTER);
+ const auto ms_dup4 = miniscript::FromString("thresh(2,pkh(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65),s:pk(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556),a:and_b(dv:older(1),s:pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)))", wsh_converter);
BOOST_CHECK(ms_dup4 && !ms_dup4->IsSane() && !ms_dup4->CheckDuplicateKey());
// Sanity check the opposite is true, too. An otherwise sane Miniscript with no duplicate keys is sane.
- const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", CONVERTER);
+ const auto ms_nondup = miniscript::FromString("pk(03d30199d74fb5a22d47b6e054e2f378cedacffcb89904a61d75d0dbd407143e65)", wsh_converter);
BOOST_CHECK(ms_nondup && ms_nondup->CheckDuplicateKey() && ms_nondup->IsSane());
// Test we find the first insane sub closer to be a leaf node. This fragment is insane for two reasons:
// 1. It can be spent without a signature
// 2. It contains timelock mixes
// We'll report the timelock mix error, as it's "deeper" (closer to be a leaf node) than the "no 's' property"
// error is.
- const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", CONVERTER);
+ const auto ms_ins = miniscript::FromString("or_i(and_b(after(1),a:after(1000000000)),pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204))", wsh_converter);
BOOST_CHECK(ms_ins && ms_ins->IsValid() && !ms_ins->IsSane());
const auto insane_sub = ms_ins->FindInsaneSub();
- BOOST_CHECK(insane_sub && *insane_sub->ToString(CONVERTER) == "and_b(after(1),a:after(1000000000))");
+ BOOST_CHECK(insane_sub && *insane_sub->ToString(wsh_converter) == "and_b(after(1),a:after(1000000000))");
// Timelock tests
Test("after(100)", "?", "?", TESTMODE_VALID | TESTMODE_NONMAL); // only heightlock
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 796716d6cb..7c98c382e4 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -718,47 +718,55 @@ BOOST_AUTO_TEST_CASE(get_local_addr_for_peer_port)
BOOST_AUTO_TEST_CASE(LimitedAndReachable_Network)
{
- BOOST_CHECK(IsReachable(NET_IPV4));
- BOOST_CHECK(IsReachable(NET_IPV6));
- BOOST_CHECK(IsReachable(NET_ONION));
- BOOST_CHECK(IsReachable(NET_I2P));
- BOOST_CHECK(IsReachable(NET_CJDNS));
-
- SetReachable(NET_IPV4, false);
- SetReachable(NET_IPV6, false);
- SetReachable(NET_ONION, false);
- SetReachable(NET_I2P, false);
- SetReachable(NET_CJDNS, false);
-
- BOOST_CHECK(!IsReachable(NET_IPV4));
- BOOST_CHECK(!IsReachable(NET_IPV6));
- BOOST_CHECK(!IsReachable(NET_ONION));
- BOOST_CHECK(!IsReachable(NET_I2P));
- BOOST_CHECK(!IsReachable(NET_CJDNS));
-
- SetReachable(NET_IPV4, true);
- SetReachable(NET_IPV6, true);
- SetReachable(NET_ONION, true);
- SetReachable(NET_I2P, true);
- SetReachable(NET_CJDNS, true);
-
- BOOST_CHECK(IsReachable(NET_IPV4));
- BOOST_CHECK(IsReachable(NET_IPV6));
- BOOST_CHECK(IsReachable(NET_ONION));
- BOOST_CHECK(IsReachable(NET_I2P));
- BOOST_CHECK(IsReachable(NET_CJDNS));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_ONION));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_I2P));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS));
+
+ g_reachable_nets.Remove(NET_IPV4);
+ g_reachable_nets.Remove(NET_IPV6);
+ g_reachable_nets.Remove(NET_ONION);
+ g_reachable_nets.Remove(NET_I2P);
+ g_reachable_nets.Remove(NET_CJDNS);
+
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV4));
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_IPV6));
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_ONION));
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_I2P));
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_CJDNS));
+
+ g_reachable_nets.Add(NET_IPV4);
+ g_reachable_nets.Add(NET_IPV6);
+ g_reachable_nets.Add(NET_ONION);
+ g_reachable_nets.Add(NET_I2P);
+ g_reachable_nets.Add(NET_CJDNS);
+
+ BOOST_CHECK(g_reachable_nets.Contains(NET_IPV4));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_IPV6));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_ONION));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_I2P));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_CJDNS));
}
BOOST_AUTO_TEST_CASE(LimitedAndReachable_NetworkCaseUnroutableAndInternal)
{
- BOOST_CHECK(IsReachable(NET_UNROUTABLE));
- BOOST_CHECK(IsReachable(NET_INTERNAL));
-
- SetReachable(NET_UNROUTABLE, false);
- SetReachable(NET_INTERNAL, false);
-
- BOOST_CHECK(IsReachable(NET_UNROUTABLE)); // Ignored for both networks
- BOOST_CHECK(IsReachable(NET_INTERNAL));
+ // Should be reachable by default.
+ BOOST_CHECK(g_reachable_nets.Contains(NET_UNROUTABLE));
+ BOOST_CHECK(g_reachable_nets.Contains(NET_INTERNAL));
+
+ g_reachable_nets.RemoveAll();
+
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_UNROUTABLE));
+ BOOST_CHECK(!g_reachable_nets.Contains(NET_INTERNAL));
+
+ g_reachable_nets.Add(NET_IPV4);
+ g_reachable_nets.Add(NET_IPV6);
+ g_reachable_nets.Add(NET_ONION);
+ g_reachable_nets.Add(NET_I2P);
+ g_reachable_nets.Add(NET_CJDNS);
+ g_reachable_nets.Add(NET_UNROUTABLE);
+ g_reachable_nets.Add(NET_INTERNAL);
}
CNetAddr UtilBuildAddress(unsigned char p1, unsigned char p2, unsigned char p3, unsigned char p4)
@@ -776,13 +784,13 @@ BOOST_AUTO_TEST_CASE(LimitedAndReachable_CNetAddr)
{
CNetAddr addr = UtilBuildAddress(0x001, 0x001, 0x001, 0x001); // 1.1.1.1
- SetReachable(NET_IPV4, true);
- BOOST_CHECK(IsReachable(addr));
+ g_reachable_nets.Add(NET_IPV4);
+ BOOST_CHECK(g_reachable_nets.Contains(addr));
- SetReachable(NET_IPV4, false);
- BOOST_CHECK(!IsReachable(addr));
+ g_reachable_nets.Remove(NET_IPV4);
+ BOOST_CHECK(!g_reachable_nets.Contains(addr));
- SetReachable(NET_IPV4, true); // have to reset this, because this is stateful.
+ g_reachable_nets.Add(NET_IPV4); // have to reset this, because this is stateful.
}
@@ -790,7 +798,7 @@ BOOST_AUTO_TEST_CASE(LocalAddress_BasicLifecycle)
{
CService addr = CService(UtilBuildAddress(0x002, 0x001, 0x001, 0x001), 1000); // 2.1.1.1:1000
- SetReachable(NET_IPV4, true);
+ g_reachable_nets.Add(NET_IPV4);
BOOST_CHECK(!IsLocal(addr));
BOOST_CHECK(AddLocal(addr, 1000));
@@ -915,7 +923,7 @@ BOOST_AUTO_TEST_CASE(advertise_local_address)
ConnectionType::OUTBOUND_FULL_RELAY,
/*inbound_onion=*/false);
};
- SetReachable(NET_CJDNS, true);
+ g_reachable_nets.Add(NET_CJDNS);
CAddress addr_ipv4{Lookup("1.2.3.4", 8333, false).value(), NODE_NONE};
BOOST_REQUIRE(addr_ipv4.IsValid());
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 2692037273..14440571eb 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -137,11 +137,11 @@ BOOST_AUTO_TEST_CASE(test_assumeutxo)
}
const auto out110 = *params->AssumeutxoForHeight(110);
- BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618");
+ BOOST_CHECK_EQUAL(out110.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1");
BOOST_CHECK_EQUAL(out110.nChainTx, 111U);
const auto out110_2 = *params->AssumeutxoForBlockhash(uint256S("0x696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c"));
- BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618");
+ BOOST_CHECK_EQUAL(out110_2.hash_serialized.ToString(), "6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1");
BOOST_CHECK_EQUAL(out110_2.nChainTx, 111U);
}
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 4c99aa5746..60cf31a964 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -409,7 +409,7 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
//
// If NET_ONION is not reachable, then none of -proxy or -onion was given.
// Since we are here, then -torcontrol and -torpassword were given.
- SetReachable(NET_ONION, true);
+ g_reachable_nets.Add(NET_ONION);
}
}
diff --git a/src/util/trace.h b/src/util/trace.h
index 051921a0d2..1fe743f043 100644
--- a/src/util/trace.h
+++ b/src/util/trace.h
@@ -5,6 +5,10 @@
#ifndef BITCOIN_UTIL_TRACE_H
#define BITCOIN_UTIL_TRACE_H
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#ifdef ENABLE_TRACING
#include <sys/sdt.h>
diff --git a/src/validation.cpp b/src/validation.cpp
index 290db8c9b2..a6cab6b095 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -5399,6 +5399,11 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
coins_count - coins_left);
return false;
}
+ if (!MoneyRange(coin.out.nValue)) {
+ LogPrintf("[snapshot] bad snapshot data after deserializing %d coins - bad tx out value\n",
+ coins_count - coins_left);
+ return false;
+ }
coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
@@ -5711,8 +5716,8 @@ void ChainstateManager::MaybeRebalanceCaches()
assert(ibd_usable || snapshot_usable);
if (ibd_usable && !snapshot_usable) {
- LogPrintf("[snapshot] allocating all cache to the IBD chainstate\n");
- // Allocate everything to the IBD chainstate.
+ // Allocate everything to the IBD chainstate. This will always happen
+ // when we are not using a snapshot.
m_ibd_chainstate->ResizeCoinsCaches(m_total_coinstip_cache, m_total_coinsdb_cache);
}
else if (snapshot_usable && !ibd_usable) {
diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h
index fa6c8d6f3b..0d0821e857 100644
--- a/src/wallet/transaction.h
+++ b/src/wallet/transaction.h
@@ -29,10 +29,12 @@ struct TxStateConfirmed {
int position_in_block;
explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
+ std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
};
//! State of transaction added to mempool.
struct TxStateInMempool {
+ std::string toString() const { return strprintf("InMempool"); }
};
//! State of rejected transaction that conflicts with a confirmed block.
@@ -41,6 +43,7 @@ struct TxStateConflicted {
int conflicting_block_height;
explicit TxStateConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
+ std::string toString() const { return strprintf("Conflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
};
//! State of transaction not confirmed or conflicting with a known block and
@@ -51,6 +54,7 @@ struct TxStateInactive {
bool abandoned;
explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
+ std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
};
//! State of transaction loaded in an unrecognized state with unexpected hash or
@@ -62,6 +66,7 @@ struct TxStateUnrecognized {
int index;
TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
+ std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
};
//! All possible CWalletTx states
@@ -109,6 +114,12 @@ static inline int TxStateSerializedIndex(const TxState& state)
}, state);
}
+//! Return TxState or SyncTxState as a string for logging or debugging.
+template<typename T>
+std::string TxStateString(const T& state)
+{
+ return std::visit([](const auto& s) { return s.toString(); }, state);
+}
/**
* Cachable amount subdivided into watchonly and spendable parts.
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 8f98624403..162d7f9ec7 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1130,7 +1130,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
}
//// debug print
- WalletLogPrintf("AddToWallet %s %s%s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
+ WalletLogPrintf("AddToWallet %s %s%s %s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""), TxStateString(state));
// Write to disk
if (fInsertedNew || fUpdated)