diff options
Diffstat (limited to 'src')
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; @@ -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; } } @@ -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) |